Browse Source

Add V7 tar support, fix some redundancies

master
Alison Watson 11 months ago
parent
commit
a4425ee985
  1. 16
      bl/main.rs
  2. 8
      fw/data/read.rs
  3. 20
      fw/data/text.rs
  4. 143
      fw/data/vfs.rs
  5. 51
      fw/data/vfs/phy.rs
  6. 118
      fw/data/vfs/sub.rs
  7. 2
      fw/hal.rs
  8. 4
      fw/hal/win.rs
  9. 8
      fw/render.rs

16
bl/main.rs

@ -1,8 +1,12 @@
#![cfg(not(test))]
use blonkus_fw::*;
use blonkus_fw as fw;
fn entry(_conf: &fw::conf::Conf) -> Result<(), Box<dyn std::error::Error>> {
let mut vfs = fw::data::vfs::Vfs::default();
vfs.add_path("testres")?;
eprintln!("{:#?}", vfs);
fn entry(_conf: &conf::Conf) -> Result<(), Box<dyn std::error::Error>> {
//let start_time = std::time::Instant::now();
/*
@ -69,13 +73,15 @@ fn entry(_conf: &conf::Conf) -> Result<(), Box<dyn std::error::Error>> {
}
fn main() {
let conf = conf::Conf::read("blonkus.toml").unwrap_or_else(|err| {
let conf = fw::conf::Conf::read("blonkus.toml").unwrap_or_else(|err| {
match err {
| conf::ErrConfLoad::NoFile => println!("{}", err),
| fw::conf::ErrConfLoad::NoFile => {
println!("{}", err)
}
| _ => eprintln!("{}", err),
};
println!("Using default configuration.");
conf::Conf::default()
fw::conf::Conf::default()
});
if let Err(e) = entry(&conf) {

8
fw/data/read.rs

@ -78,7 +78,9 @@ macro_rules! bits_impl {
/// This function will return `None` if there are not enough bits left
/// in the buffer.
#[inline]
pub const fn $be(b: &[u8], cr_bit: usize, mut width: usize) -> Option<$t> {
pub const fn $be(
b: &[u8], cr_bit: usize, mut width: usize,
) -> Option<$t> {
if width == 0 {
return Some(0);
}
@ -120,7 +122,9 @@ macro_rules! bits_impl {
/// This function will return `None` if there are not enough bits left
/// in the buffer.
#[inline]
pub const fn $le(b: &[u8], cr_bit: usize, mut width: usize) -> Option<$t> {
pub const fn $le(
b: &[u8], cr_bit: usize, mut width: usize,
) -> Option<$t> {
match width {
| 0 => Some(0),
| 1 => {

20
fw/data/text.rs

@ -64,7 +64,11 @@ impl fmt::Display for Position {
impl fmt::Debug for Position {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Position({:?}@{}:{})", self.name(), self.line, self.colu)
f.debug_tuple("Position")
.field(&self.name())
.field(&self.line)
.field(&self.colu)
.finish()
}
}
@ -114,4 +118,18 @@ pub const fn radix(c: char) -> u8 {
}
}
pub fn disp_ascii(buf: impl AsRef<[u8]>) -> String {
use std::{ascii, iter::once};
once('"')
.chain(
buf.as_ref()
.iter()
.map(|&byt| ascii::escape_default(byt))
.flatten()
.map(|byt| byt as char)
.chain(once('"')),
)
.collect()
}
// EOF

143
fw/data/vfs.rs

@ -1,3 +1,6 @@
mod phy;
mod sub;
use super::deflate;
use std::{
cell::UnsafeCell,
@ -14,8 +17,8 @@ use std::{
pub enum ErrVfs {
#[error("Bad file type for `{0}'")]
FileType(FileName),
#[error("Invalid magic number")]
InvalidMagic,
#[error("Invalid tar format")]
InvalidFormat,
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
@ -32,29 +35,16 @@ pub struct Vfs {
files: HashMap<FileName, Box<dyn File>>,
}
fn from_utf8_null(v: &[u8]) -> Result<&str, Utf8Error> {
Ok(std::str::from_utf8(v)?.trim_end_matches(|c| c == '\0' || c == ' '))
}
fn path_to_local(top_path: &Path, path: &Path) -> FileName {
FileName::new(path.strip_prefix(top_path).unwrap().to_string_lossy())
}
impl fmt::Debug for Vfs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Vfs { ")?;
for name in self.files.keys() {
write!(f, "{:#?}, ", name)?;
}
f.write_str(" }")
f.debug_list().entries(self.files.keys()).finish()
}
}
pub trait File {
fn data(&self) -> &[u8] {
unsafe {
let buf = self.buffer().get().as_mut();
let buf = buf.unwrap_or_else(|| std::hint::unreachable_unchecked());
let buf = self.buffer().get().as_mut().unwrap();
if !buf.is_empty() {
&*buf
} else {
@ -78,129 +68,18 @@ pub trait File {
fn buffer(&self) -> &FileData;
}
struct FilePhysical {
path: PathBuf,
data: FileData,
}
struct FileTar<R: Read + Seek> {
read: Rc<UnsafeCell<R>>,
area: (u64, u64),
data: FileData,
}
impl File for FilePhysical {
fn load(&self) -> Vec<u8> {
fs::read(&self.path).unwrap_or_else(|_| Vec::new())
}
fn buffer(&self) -> &FileData {
&self.data
}
}
impl<R: Read + Seek> File for FileTar<R> {
fn load(&self) -> Vec<u8> {
let rd = unsafe { &mut *self.read.get() };
if rd.seek(io::SeekFrom::Start(self.area.0)).is_ok() {
let mut v = Vec::new();
if rd.take(self.area.1).read_to_end(&mut v).is_ok() {
return v;
}
}
Vec::new()
}
fn buffer(&self) -> &FileData {
&self.data
}
}
impl Vfs {
pub fn read_path(&mut self, path: &Path) -> Result<&mut Self, ErrVfs> {
pub fn add_path(&mut self, path: impl AsRef<Path>) -> Result<(), ErrVfs> {
let path = path.as_ref();
if path.is_dir() {
self.read_dir(path)
self.add_dir(path)
} else if path.is_file() {
self.read_tar(fs::File::open(path)?)
self.add_tar(fs::File::open(path)?)
} else {
Err(ErrVfs::FileType(FileName::new(path.to_string_lossy())))
}
}
pub fn read_dir(&mut self, path: &Path) -> Result<&mut Self, ErrVfs> {
fn recurse(
top_path: &Path, cur_path: &Path, vfs: &mut Vfs,
) -> Result<(), ErrVfs> {
for ent in fs::read_dir(cur_path)? {
let path = ent?.path();
if path.is_dir() {
recurse(top_path, &path, vfs)?;
} else if fs::read_link(&path).is_ok() {
return Err(ErrVfs::FileType(path_to_local(top_path, &path)));
} else {
let name = path_to_local(top_path, &path);
vfs.files.insert(
name,
Box::new(FilePhysical { path, data: Default::default() }),
);
}
}
Ok(())
}
recurse(path, path, self)?;
Ok(self)
}
pub fn read_tar<R: 'static + Read + Seek>(
&mut self, rd: R,
) -> Result<&mut Self, ErrVfs> {
let mut head = [0; 512];
let read = Rc::new(UnsafeCell::new(rd));
let rd = unsafe { &mut *read.get() };
loop {
rd.read_exact(&mut head)?;
if head.iter().all(|&x| x == 0) {
break;
} else if &head[257..265] != b"ustar\000" {
return Err(ErrVfs::InvalidMagic);
}
let size = u64::from_str_radix(from_utf8_null(&head[124..136])?, 8)?;
let ftyp = char::from(head[156]);
let name = {
let pfx = from_utf8_null(&head[345..500])?;
let sfx = from_utf8_null(&head[0..100])?;
pfx.chars().chain(sfx.chars()).collect::<FileName>()
};
match ftyp {
| 'g' | 'x' | 'A'..='Z' | '5' => {
// skip extensions and directories entirely
}
| '0' | '\0' | '7' => {
// file plus padding
let strt = rd.stream_position()?;
rd.seek(io::SeekFrom::Start(strt + size + 512 - size % 512))?;
self.files.insert(
name,
Box::new(FileTar {
read: read.clone(),
area: (strt, size),
data: Default::default(),
}),
);
}
| _ => {
// some unsupported type
return Err(ErrVfs::FileType(name));
}
}
}
Ok(self)
}
pub fn get(&self, name: &str) -> Option<&dyn File> {
self.files.get(name).map(|b| b.as_ref())
}

51
fw/data/vfs/phy.rs

@ -0,0 +1,51 @@
use super::*;
struct FilePhy {
path: PathBuf,
data: FileData,
}
impl File for FilePhy {
fn load(&self) -> Vec<u8> {
fs::read(&self.path).unwrap_or_else(|_| Vec::new())
}
fn buffer(&self) -> &FileData {
&self.data
}
}
impl Vfs {
pub fn add_dir(&mut self, path: impl AsRef<Path>) -> Result<(), ErrVfs> {
fn path_to_local(top_path: &Path, path: &Path) -> FileName {
FileName::new(path.strip_prefix(top_path).unwrap().to_string_lossy())
}
fn recurse(
top_path: &Path, cur_path: &Path, vfs: &mut Vfs,
) -> Result<(), ErrVfs> {
for ent in fs::read_dir(cur_path)? {
let path = ent?.path();
if path.is_dir() {
recurse(top_path, &path, vfs)?;
} else if fs::read_link(&path).is_ok() {
return Err(ErrVfs::FileType(path_to_local(top_path, &path)));
} else {
let name = path_to_local(top_path, &path);
vfs.files.insert(
name,
Box::new(FilePhy { path, data: FileData::default() }),
);
}
}
Ok(())
}
recurse(path.as_ref(), path.as_ref(), self)?;
Ok(())
}
}
// EOF

118
fw/data/vfs/sub.rs

@ -0,0 +1,118 @@
use super::*;
struct FileSub<R: Read + Seek> {
read: Rc<UnsafeCell<R>>,
area: (u64, u64),
data: FileData,
}
impl<R: Read + Seek> File for FileSub<R> {
fn load(&self) -> Vec<u8> {
let rd = unsafe { &mut *self.read.get() };
if rd.seek(io::SeekFrom::Start(self.area.0)).is_ok() {
let mut v = Vec::new();
if rd.take(self.area.1).read_to_end(&mut v).is_ok() {
return v;
}
}
Vec::new()
}
fn buffer(&self) -> &FileData {
&self.data
}
}
impl Vfs {
pub fn add_tar<R: 'static + Read + Seek>(
&mut self, rd: R,
) -> Result<(), ErrVfs> {
enum FileType {
Skip,
File,
Unsupported,
}
fn utf_bstr(v: &[u8]) -> Result<&str, Utf8Error> {
Ok(std::str::from_utf8(v)?
.trim_end_matches(|c| c == '\0' || c == ' '))
}
fn is_v7_head(mut head: [u8; 512]) -> bool {
if let Ok(checksum) = utf_bstr(&head[148..156]) {
if let Ok(checksum) = u32::from_str_radix(checksum, 8) {
head[148..156].fill(b' ');
let check =
head.iter().fold(0, |out, &inp| out + u32::from(inp));
if checksum == check {
return true;
}
}
}
false
}
let mut head = [0; 512];
let read = Rc::new(UnsafeCell::new(rd));
let rd = unsafe { &mut *read.get() };
loop {
rd.read_exact(&mut head)?;
if head.iter().all(|&x| x == 0) {
break;
}
let size;
let name;
let ftyp;
if &head[257..265] == b"ustar\000" {
size = u64::from_str_radix(utf_bstr(&head[124..136])?, 8)?;
name = {
let pfx = utf_bstr(&head[345..500])?;
let sfx = utf_bstr(&head[0..100])?;
pfx.chars().chain(sfx.chars()).collect::<FileName>()
};
ftyp = match char::from(head[156]) {
| 'g' | 'x' | 'A'..='Z' | '5' => FileType::Skip,
| '0' | '\0' | '7' => FileType::File,
| _ => FileType::Unsupported,
};
} else if is_v7_head(head) {
size = u64::from_str_radix(utf_bstr(&head[124..136])?, 8)?;
name = FileName::new(utf_bstr(&head[0..100])?);
ftyp = if name.ends_with('/') {
FileType::Skip
} else {
FileType::File
};
} else {
return Err(ErrVfs::InvalidFormat);
}
match ftyp {
| FileType::Skip => {}
| FileType::File => {
let strt = rd.stream_position()?;
rd.seek(io::SeekFrom::Start(strt + (size + 511 & !511)))?;
self.files.insert(
name,
Box::new(FileSub {
read: read.clone(),
area: (strt, size),
data: FileData::default(),
}),
);
}
| FileType::Unsupported => {
return Err(ErrVfs::FileType(name));
}
}
}
Ok(())
}
}
// EOF

2
fw/hal.rs

@ -19,7 +19,7 @@ impl Err {
///
/// This function will cause undefined behaviour if used from
/// multiple threads.
pub(self) unsafe fn new_sdl() -> Self {
unsafe fn new_sdl() -> Self {
Self::Sdl(unsafe {
std::ffi::CStr::from_ptr(sdl::get_error()).to_string_lossy()
})

4
fw/hal/win.rs

@ -38,9 +38,7 @@ impl<'a> Window<'a> {
/// # Safety
///
/// This function provides a safe API over a raw C API.
pub fn vulkan_instance_extensions(
&self,
) -> Result<Vec<ffi::Nts>, Err> {
pub fn vulkan_instance_extensions(&self) -> Result<Vec<ffi::Nts>, Err> {
let mut count = 0;
let res = unsafe {
sdl::vulkan_get_instance_extensions(

8
fw/render.rs

@ -9,20 +9,20 @@ mod texture;
pub mod conf;
pub(self) use self::conf::Conf;
pub(self) use crate::{
use self::conf::Conf;
use crate::{
c_str,
data::{self, uniforms::Uniforms, vertex::Vertex},
hal::win::Window,
types::{stkvec, Cast, Conv, StkVec},
};
use crate::{stkvec_r, vec_e, vec_r};
pub(self) use ash::{
use ash::{
extensions::khr,
version::{DeviceV1_0, EntryV1_0, InstanceV1_0},
vk,
};
pub(self) use std::{
use std::{
ffi::{CStr, CString},
mem::size_of,
};

Loading…
Cancel
Save