Browse Source

big refactoring

master
Alison Watson 1 month ago
parent
commit
343629861c
66 changed files with 2487 additions and 2075 deletions
  1. +59
    -52
      Cargo.lock
  2. +19
    -16
      Cargo.toml
  3. +1
    -1
      README.md
  4. +28
    -42
      bl/main.rs
  5. +61
    -0
      fw/build.rs
  6. +1
    -1
      fw/conf.rs
  7. +1
    -1
      fw/data.rs
  8. +0
    -0
      fw/data/color.rs
  9. +35
    -24
      fw/data/deflate.rs
  10. +10
    -48
      fw/data/image.rs
  11. +85
    -83
      fw/data/model.rs
  12. +283
    -0
      fw/data/read.rs
  13. +1
    -1
      fw/data/shader.rs
  14. +2
    -2
      fw/data/text.rs
  15. +0
    -0
      fw/data/uniforms.rs
  16. +1
    -0
      fw/data/vertex.rs
  17. +209
    -0
      fw/data/vfs.rs
  18. +18
    -16
      fw/ffi.rs
  19. +5
    -2
      fw/hal.rs
  20. +0
    -0
      fw/hal/ctx.rs
  21. +3
    -3
      fw/hal/evt.rs
  22. +10
    -9
      fw/hal/sdl.rs
  23. +39
    -21
      fw/hal/win.rs
  24. +64
    -9
      fw/iter.rs
  25. +7
    -12
      fw/lib.rs
  26. +0
    -0
      fw/math.rs
  27. +6
    -2
      fw/meta.rs
  28. +272
    -231
      fw/render.rs
  29. +359
    -0
      fw/render/_image.rs
  30. +61
    -0
      fw/render/_sampler.rs
  31. +59
    -40
      fw/render/buffer.rs
  32. +0
    -0
      fw/render/conf.rs
  33. +4
    -4
      fw/render/model.rs
  34. +4
    -2
      fw/render/shader.rs
  35. +34
    -28
      fw/render/texture.rs
  36. +14
    -0
      fw/types.rs
  37. +8
    -0
      fw/vire.rs
  38. +139
    -0
      fw/vire/llvm.cxx
  39. +64
    -72
      fw/vire/llvm.rs
  40. +0
    -0
      fw/vire/parser.rs
  41. +1
    -1
      fw/vire/parser/tki.rs
  42. +1
    -1
      fw/vire/parser/tok.rs
  43. +58
    -0
      fw/vire/rt.rs
  44. +14
    -54
      fw/vire/rt/function.rs
  45. +0
    -2
      fw/vire/rt/nat.rs
  46. +117
    -0
      fw/vire/rt/types.rs
  47. +0
    -13
      glsl/main.frag
  48. +0
    -48
      glsl/main.vert
  49. +1
    -1
      ma/Cargo.toml
  50. +329
    -0
      ma/lib.rs
  51. +0
    -0
      res/blonkus.png
  52. +0
    -0
      res/blonkus.svgz
  53. +0
    -0
      res/blonkusicon.png
  54. +0
    -0
      res/vire.png
  55. +0
    -0
      res/vire.svgz
  56. +0
    -36
      source/framework/build.rs
  57. +0
    -230
      source/framework/data/bit.rs
  58. +0
    -79
      source/framework/data/read.rs
  59. +0
    -255
      source/framework/data/vfs.rs
  60. +0
    -5
      source/framework/types.rs
  61. +0
    -4
      source/framework/vire.rs
  62. +0
    -156
      source/framework/vire/compiler.rs
  63. +0
    -172
      source/framework/vire/compiler/types.rs
  64. +0
    -252
      source/macros/lib.rs
  65. +0
    -36
      source/main_test/main.rs
  66. +0
    -8
      tools/shader

+ 59
- 52
Cargo.lock View File

@@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3

[[package]]
name = "ash"
version = "0.32.1"
@@ -27,21 +29,21 @@ version = "0.1.0"
dependencies = [
"ash",
"bitflags",
"blonkus_ma",
"blonkus-ma",
"cc",
"easy-cast",
"glam",
"half",
"libc",
"seahash",
"serde",
"shh",
"smallvec",
"smol_str",
"thiserror",
"toml",
"twox-hash",
]

[[package]]
name = "blonkus_ma"
name = "blonkus-ma"
version = "0.1.0"
dependencies = [
"proc-macro2",
@@ -50,10 +52,10 @@ dependencies = [
]

[[package]]
name = "cfg-if"
version = "0.1.10"
name = "cc"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"

[[package]]
name = "cfg-if"
@@ -61,6 +63,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"

[[package]]
name = "easy-cast"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bd102ee8c418348759919b83b81cdbdc933ffe29740b903df448b4bafaa348e"

[[package]]
name = "glam"
version = "0.13.1"
@@ -77,10 +85,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3"

[[package]]
name = "libc"
version = "0.2.93"
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]

[[package]]
name = "libloading"
@@ -88,7 +99,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"winapi",
]

@@ -110,9 +121,9 @@ dependencies = [

[[package]]
name = "proc-macro2"
version = "1.0.26"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
dependencies = [
"unicode-xid",
]
@@ -126,36 +137,32 @@ dependencies = [
"proc-macro2",
]

[[package]]
name = "seahash"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"

[[package]]
name = "serde"
version = "1.0.125"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
dependencies = [
"serde_derive",
]

[[package]]
name = "serde_derive"
version = "1.0.125"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

[[package]]
name = "shh"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5205eb079ac8be8ec77b7470aff4a050610d42c32819deee362ca414c926d3ab"
dependencies = [
"libc",
"winapi",
]

[[package]]
name = "smallvec"
version = "1.6.1"
@@ -164,42 +171,46 @@ checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"

[[package]]
name = "smol_str"
version = "0.1.17"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ca0f7ce3a29234210f0f4f0b56f8be2e722488b95cb522077943212da3b32eb"
checksum = "b203e79e90905594272c1c97c7af701533d42adaab0beb3859018e477d54a3b0"

[[package]]
name = "spirv-std"
version = "0.4.0-alpha.4"
version = "0.4.0-alpha.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "895a86b30e3dab8b15ca414bdf1d5d3f132374debb8bf03ca5e89109f674a171"
checksum = "0e2676f3e58345b7c742b266b5bec022445b43e6471b9a00895faec03a52723e"
dependencies = [
"bitflags",
"num-traits",
"spirv-std-macros",
"spirv-types",
]

[[package]]
name = "spirv-std-macros"
version = "0.4.0-alpha.4"
version = "0.4.0-alpha.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b76d9abb9f30d0da7c54e164efecbfab6c8af3493ae41d43c38ebfbb80b9894"
checksum = "207e8515dfbb48bff03f64bf5f8388d4e0f2f526ee769cc6556df98477098ca5"
dependencies = [
"heck",
"proc-macro2",
"quote",
"spirv-types",
"syn",
]

[[package]]
name = "static_assertions"
version = "1.1.0"
name = "spirv-types"
version = "0.4.0-alpha.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
checksum = "71cc4b8f7ec707459fdeddb4f137109947045592f5b0c139f7bf1360058bac6b"

[[package]]
name = "syn"
version = "1.0.69"
version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84"
dependencies = [
"proc-macro2",
"quote",
@@ -208,18 +219,18 @@ dependencies = [

[[package]]
name = "thiserror"
version = "1.0.24"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e"
checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88"
dependencies = [
"thiserror-impl",
]

[[package]]
name = "thiserror-impl"
version = "1.0.24"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0"
checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c"
dependencies = [
"proc-macro2",
"quote",
@@ -236,20 +247,16 @@ dependencies = [
]

[[package]]
name = "twox-hash"
version = "1.6.0"
name = "unicode-segmentation"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04f8ab788026715fa63b31960869617cba39117e520eb415b0139543e325ab59"
dependencies = [
"cfg-if 0.1.10",
"static_assertions",
]
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"

[[package]]
name = "unicode-xid"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"

[[package]]
name = "winapi"


+ 19
- 16
Cargo.toml View File

@@ -12,10 +12,12 @@ publish = false
keywords = ["gamedev", "engine"]
categories = ["game-development"]
resolver = "2"
build = "source/framework/build.rs"
build = "fw/build.rs"

[dependencies]
blonkus_ma = { path = "source/macros" }
# macros:
# - blonkus-ma for our macros
blonkus-ma = { path = "ma" }

# types:
# - bitflags for FFI usage
@@ -28,32 +30,33 @@ smol_str = "~0.1"
thiserror = "~1.0"

# i/o:
# - serde for serializing and deserializing our own formats
# - shh for hacking around LLVM crimes
# - serde for config / description files
# - toml for config / description files
serde = { version = "~1.0", features = ["derive"] }
shh = "~1.0"
toml = "~0.5"

# math:
# - easy-cast for safer casting
# - glam for linear algebra
# - half for half-floats in certain areas
glam = "~0.13"
half = "~1.7"
easy-cast = "~0.4"
glam = "~0.13"
half = "~1.7"

# hashing:
# - intaglio for symbol tables?
# - intaglio for symbol interning?
# - rustc-hash for things like mesh names?
# - twox-hash for file deduplication
# - seahash for file deduplication
#intaglio = "~1.2"
#rustc-hash = { version = "~1.1", default-features = false }
twox-hash = { version = "~1.6", default-features = false }
seahash = { version = "~4.1" }

# bindings:
# - ash for the renderer implementation
# - libc for binding SDL2 and LLVM
ash = "~0.32"
libc = "~0.2"
ash = "~0.32"

[build-dependencies]
cc = "1.0"

[profile.dev]
opt-level = 1
@@ -65,10 +68,10 @@ lto = "thin"

[lib]
name = "blonkus_fw"
path = "source/framework/lib.rs"
path = "fw/lib.rs"

[[bin]]
name = "blonkus_mt"
path = "source/main_test/main.rs"
name = "blonkus"
path = "bl/main.rs"

# EOF

+ 1
- 1
README.md View File

@@ -1,5 +1,5 @@
# BLONKUS

![blonkus stylized logo](resource/blonkus.png)
![blonkus stylized logo](https://git.greyserv.net/marrub/blonkus/raw/branch/master/res/blonkus.png)

it's a thing

source/main_test/entry.rs → bl/main.rs View File

@@ -1,44 +1,22 @@
#![allow(unused_imports)]
#![allow(unused_variables)]
#![allow(dead_code)]

use ash::{version::DeviceV1_0, vk};
use blonkus_fw::{
conf,
data::{self, vfs},
hal,
math::*,
meta,
render::Renderer,
//vire,
};
use std::{io::Cursor, rc::Rc};

pub fn run(conf: &conf::Conf) -> Result<(), Box<dyn std::error::Error>> {
let start_time = std::time::Instant::now();

let mut vfs = vfs::Vfs::default();
vfs.add_arc(vfs::Arc::read_path("testres".as_ref())?);
#![cfg(not(test))]

/*
let ctx = vire::compiler::Context::new()?;

let datum = vire::parser::Datum::parse_all(
vire::parser::tki::TokenIter::from_data(
"main.vire.gz",
vfs.get("main.vire.gz")?.text()?,
)?,
)?;
use blonkus_fw::*;

ctx.compile_all(datum)?;
fn entry(_conf: &conf::Conf) -> Result<(), Box<dyn std::error::Error>> {
//let start_time = std::time::Instant::now();

ctx.execute()?;
/*
let mut vfs = data::vfs::Vfs::default();
vfs.add_arc(data::vfs::Arc::read_path("testres".as_ref())?);
*/

//blonkus_fw::vire::rt::Rt::new()?;

/*
let hal = hal::ctx::Context::new()?;
let window = hal::win::Window::new(&hal, meta::ffi::NAME, 640, 480)?;

let mut ren = Renderer::new(
let mut ren = render::Renderer::new(
&window,
&conf.render,
&[
@@ -57,7 +35,7 @@ pub fn run(conf: &conf::Conf) -> Result<(), Box<dyn std::error::Error>> {

/*
let img = {
let arc = vfs::Arc::read_tar(
let arc = data::vfs::Arc::read_tar(
&mut vfs.get("scientist/hltex000.texture")?.data(),
)?;
let img = data::image::MipImage::read(&*arc)?;
@@ -72,14 +50,6 @@ pub fn run(conf: &conf::Conf) -> Result<(), Box<dyn std::error::Error>> {
vfs.get("scientist/scientist.iqm")?.data(),
))?;
let model = Model::create(&cmd_pool, model.0, model.1)?;

let spir_vert = Spir::read(MAIN_VERT);
let spir_frag = Spir::read(MAIN_FRAG);

let shader_vert = ShaderModule::create(device.clone(), &spir_vert)?;
let shader_frag = ShaderModule::create(device.clone(), &spir_frag)?;

let mut cur_c_frame = 0;
*/

'main_loop: loop {
@@ -93,8 +63,24 @@ pub fn run(conf: &conf::Conf) -> Result<(), Box<dyn std::error::Error>> {

ren.draw_frame()?;
}
*/

Ok(())
}

fn main() {
let conf = conf::Conf::read("blonkus.toml").unwrap_or_else(|err| {
match err {
| conf::ErrConfLoad::NoFile => println!("{}", err),
| _ => eprintln!("{}", err),
};
println!("Using default configuration.");
conf::Conf::default()
});

if let Err(e) = entry(&conf) {
eprintln!("Uncaught error: {}", e);
}
}

// EOF

+ 61
- 0
fw/build.rs View File

@@ -0,0 +1,61 @@
fn llvm_config(args: Vec<&str>) -> String {
std::process::Command::new("llvm-config")
.args(args)
.output()
.map(|out| String::from_utf8(out.stdout).unwrap())
.unwrap()
.trim()
.to_owned()
}

fn split_to_array(string: String) -> Vec<String> {
string
.split(' ')
.filter(|lib| !lib.is_empty())
.map(|lib| lib.to_owned())
.collect()
}

fn with_components(mut v: Vec<&str>) -> Vec<&str> {
v.push("orcerror");
v.push("orcjit");
v.push("native");
v
}

fn main() {
let a_incdr = vec!["--includedir"];
let a_libdr = vec!["--libdir"];
let a_llibs = with_components(vec!["--libs", "--link-static"]);
let a_slibs = with_components(vec!["--system-libs", "--link-static"]);

let incdr = llvm_config(a_incdr);
let libdr = llvm_config(a_libdr);
let llibs = split_to_array(llvm_config(a_llibs));
let slibs = split_to_array(llvm_config(a_slibs));

println!("cargo:rustc-link-search=native={}", libdr);
for lib in llibs.into_iter().chain(slibs.into_iter()) {
println!("cargo:rustc-flags={}", lib);
}

#[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "openbsd"
))]
println!("cargo:rustc-link-lib=c++");

#[cfg(target_os = "linux")]
println!("cargo:rustc-link-lib=stdc++");

cc::Build::new()
.cpp(true)
.warnings_into_errors(true)
.static_flag(true)
.include(incdr)
.file("fw/vire/llvm.cxx")
.compile("helper");
}

// EOF

source/framework/conf.rs → fw/conf.rs View File

@@ -4,7 +4,7 @@ pub struct Conf {
pub render: crate::render::conf::Conf,
}

#[derive(Error, Debug)]
#[derive(thiserror::Error, Debug)]
pub enum ErrConfLoad {
#[error("No existing configuration file")]
NoFile,

source/framework/data.rs → fw/data.rs View File

@@ -1,5 +1,5 @@
pub mod bit;
pub mod color;
pub mod deflate;
pub mod image;
pub mod model;
pub mod read;

source/framework/data/color.rs → fw/data/color.rs View File


source/framework/defl.rs → fw/data/deflate.rs View File

@@ -1,15 +1,15 @@
//! DEFLATE loader.

use crate::{
data::{bit::ReadBits, read},
data::read,
ffi,
types::{stkvec, StkVec},
types::{stkvec, Cast, StkVec},
};
use std::cmp::Ordering;

type AlphabetTable = StkVec<[u16; 320]>;

#[derive(Error, Debug)]
#[derive(thiserror::Error, Debug)]
pub enum Err {
#[error("Bad stream block type")]
BlockType,
@@ -84,7 +84,7 @@ pub fn read_gzip(b: &[u8]) -> Option<&[u8]> {
let b = if fl & FEXT == 0 {
b
} else {
b.get(read::u16le_sz(b.get(0..2)?, 0) + 2..)?
b.get(usize::from(read::u16le(b.get(0..2)?, 0)) + 2..)?
};
let b =
if fl & FNAM == 0 { b } else { b.get(ffi::end_of_cstr(b) + 1..)? };
@@ -97,17 +97,17 @@ pub fn read_gzip(b: &[u8]) -> Option<&[u8]> {
}
}

/// Decompresses a DEFLATE compressed bitstream.
/// Decompresses a DEFLATE compressed bitstream starting at the `p`-th bit.
///
/// # Errors
///
/// Returns `Err` if the bit-stream fails to parse.
pub fn read_defl(b: &[u8], p: &mut usize) -> Result<Vec<u8>, Err> {
pub fn read_deflate_from(b: &[u8], p: &mut usize) -> Result<Vec<u8>, Err> {
let mut v = Vec::new();

loop {
let bfinal = u8::read_bits_le(b, *p, 1).ok_or(Err::BlockBits)?;
let bstype = u8::read_bits_le(b, *p + 1, 2).ok_or(Err::BlockBits)?;
let bfinal = read::ble_u8(b, *p, 1).ok_or(Err::BlockBits)?;
let bstype = read::ble_u8(b, *p + 1, 2).ok_or(Err::BlockBits)?;
*p += 3;

match bstype {
@@ -123,6 +123,15 @@ pub fn read_defl(b: &[u8], p: &mut usize) -> Result<Vec<u8>, Err> {
}
}

/// Decompresses a DEFLATE compressed bitstream.
///
/// # Errors
///
/// Returns `Err` if the bit-stream fails to parse.
pub fn read_deflate(b: &[u8]) -> Result<Vec<u8>, Err> {
read_deflate_from(b, &mut 0)
}

fn st_dynamic(b: &[u8], p: &mut usize, v: &mut Vec<u8>) -> Result<(), Err> {
const CODE_ORDERING: [usize; 19] =
[16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
@@ -134,9 +143,9 @@ fn st_dynamic(b: &[u8], p: &mut usize, v: &mut Vec<u8>) -> Result<(), Err> {
// read the header: number of literal alphabet codes, number of
// distance alphabet codes, and number of lengths for decoding the
// alphabet
let hlits = usize::read_bits_le(b, *p, 5).ok_or(Err::DynStream)?;
let hdist = usize::read_bits_le(b, *p + 5, 5).ok_or(Err::DynStream)?;
let hclen = usize::read_bits_le(b, *p + 10, 4).ok_or(Err::DynStream)?;
let hlits = read::ble_usz(b, *p, 5).ok_or(Err::DynStream)?;
let hdist = read::ble_usz(b, *p + 5, 5).ok_or(Err::DynStream)?;
let hclen = read::ble_usz(b, *p + 10, 4).ok_or(Err::DynStream)?;
*p += 14;

let hlits = NHLITS + hlits;
@@ -147,7 +156,7 @@ fn st_dynamic(b: &[u8], p: &mut usize, v: &mut Vec<u8>) -> Result<(), Err> {
// compressed
let mut table = [0; 19];
for &i in CODE_ORDERING.iter().take(hclen) {
table[i] = u16::read_bits_le(b, *p, 3).ok_or(Err::DynStream)?;
table[i] = read::ble_u16(b, *p, 3).ok_or(Err::DynStream)?;
*p += 3;
}

@@ -190,7 +199,7 @@ fn st_literal(b: &[u8], p: &mut usize, v: &mut Vec<u8>) -> Result<(), Err> {
// copy data directly from byte boundary
let bound = *p / 8 + 1;
let b = &b[bound..];
let len = read::u16le_sz(b.get(0..2).ok_or(Err::Literal)?, 0);
let len = read::u16le(b.get(0..2).ok_or(Err::Literal)?, 0).into();
let b = &b[4..];
*p += bound * 8 + 4 * 8 + len * 8;

@@ -227,7 +236,7 @@ fn read_alphabet(
| 16 => {
// copy previous code 3-6 times
let lst = alpha[i - 1];
let len = u8::read_bits_le(b, *p, 2).ok_or(Err::BitsCode)? + 3;
let len = read::ble_u8(b, *p, 2).ok_or(Err::BitsCode)? + 3;
*p += 2;

for _ in 0..len {
@@ -236,7 +245,7 @@ fn read_alphabet(
}
| 17 => {
// repeat '0' 3-10 times
let len = u8::read_bits_le(b, *p, 3).ok_or(Err::Bits0)? + 3;
let len = read::ble_u8(b, *p, 3).ok_or(Err::Bits0)? + 3;
*p += 3;

for _ in 0..len {
@@ -245,7 +254,7 @@ fn read_alphabet(
}
| 18 => {
// repeat '0' 11-138 times
let len = u8::read_bits_le(b, *p, 7).ok_or(Err::Bits0)? + 11;
let len = read::ble_u8(b, *p, 7).ok_or(Err::Bits0)? + 11;
*p += 7;

for _ in 0..len {
@@ -310,7 +319,7 @@ fn output_tables(
}

let bit = LEN_EXTRA_BITS[sym];
let len = usize::read_bits_le(b, *p, bit).ok_or(Err::Pair)?;
let len = read::ble_usz(b, *p, bit).ok_or(Err::Pair)?;

*p += bit;

@@ -322,7 +331,7 @@ fn output_tables(
let sym = usize::from(table_dst.decode(b, p)?);

let bit = DST_EXTRA_BITS[sym];
let dst = usize::read_bits_le(b, *p, bit).ok_or(Err::Pair)?;
let dst = read::ble_usz(b, *p, bit).ok_or(Err::Pair)?;

*p += bit;

@@ -344,7 +353,7 @@ fn output_tables(

impl HuffmanTable {
fn read(table: &[u16]) -> Result<Self, Err> {
let mut syms = vec![0; table.len()];
let mut syms = stkvec![0; table.len()];
let mut nums = [0; 16];

// count the number of symbols for each bit length
@@ -364,13 +373,13 @@ impl HuffmanTable {
}

// make the actual bit pattern table
for (n, &length) in table.iter().enumerate() {
for (&length, n) in table.iter().zip(0..table.len().cast()) {
// length 0 means this code isn't used, so only try to make bit
// patterns for codes that actually exist
if length != 0 {
// make sure to make each offset unique
let offset = &mut ofs[usize::from(length)];
syms[*offset] = n as u16;
syms[*offset] = n;
*offset += 1;
}
}
@@ -382,14 +391,16 @@ impl HuffmanTable {
let mut codes = 0;
let mut first = 0;
let mut index = 0;
let mut i = 1;

for i in 1..=15 {
while i < 16 {
// add bit from file
codes |= usize::read_bits_le(b, *p, 1).ok_or(Err::Table)?;
codes |= read::ble_usz(b, *p, 1).ok_or(Err::Table)?;
*p += 1;

// check our symbol table for this one (quick tree check)
let count = self.nums[i];
i += 1;

if (codes as isize) - (count as isize) < (first as isize) {
return Ok(self.syms[index + codes - first]);
@@ -410,7 +421,7 @@ impl HuffmanTable {
#[derive(Debug)]
struct HuffmanTable {
nums: [usize; 16],
syms: Vec<u16>,
syms: StkVec<[u16; 288]>,
}

// EOF

source/framework/data/image.rs → fw/data/image.rs View File

@@ -1,7 +1,11 @@
use crate::data::{color::Color, read, vfs};
use crate::{
data::{color::Color, read, vfs},
iter::MaybeRev,
};
use std::io::{self, Read};

#[derive(Error, Debug)]
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum ErrImageRead {
#[error("Bad color encoding in texture")]
Encoding,
@@ -25,12 +29,6 @@ pub struct MipImage {
heigh: usize,
}

#[derive(Clone, Debug)]
enum MaybeRev<I> {
Not(I),
Rev(I),
}

impl Image {
pub fn get(&self, x: usize, y: usize) -> Option<&Color> {
if x < self.width && y < self.heigh {
@@ -93,8 +91,8 @@ impl Image {
// compile size info
let comps = if alpha { 4 } else { 3 };

let width = read::u16le_sz(&head, 12);
let heigh = read::u16le_sz(&head, 14);
let width = read::u16le(&head, 12).into();
let heigh = read::u16le(&head, 14).into();
let wsize = width * heigh;
let wscan = width * comps;

@@ -150,14 +148,14 @@ impl MipImage {
self.datum.is_empty()
}

pub fn read(arc: &vfs::Arc) -> Result<Self, ErrImageRead> {
pub fn read(vfs: &vfs::Vfs) -> Result<Self, ErrImageRead> {
let mut datum = Vec::new();
let mut width = 0;
let mut heigh = 0;

let mut i = 0;

while let Some(file) = arc.get(&i.to_string()) {
while let Some(file) = vfs.get(&i.to_string()) {
let img = Image::read(&mut file.data())?;

if i == 0 {
@@ -186,40 +184,4 @@ impl std::ops::Index<(usize, usize)> for Image {
}
}

impl<I> MaybeRev<I> {
const fn rev_if(it: I, rev: bool) -> Self {
if rev {
Self::Rev(it)
} else {
Self::Not(it)
}
}
}

impl<I> Iterator for MaybeRev<I>
where
I: Iterator + DoubleEndedIterator,
{
type Item = <I as Iterator>::Item;

fn next(&mut self) -> Option<Self::Item> {
match self {
| Self::Not(it) => it.next(),
| Self::Rev(it) => it.next_back(),
}
}
}

impl<I> ExactSizeIterator for MaybeRev<I>
where
I: ExactSizeIterator + DoubleEndedIterator,
{
fn len(&self) -> usize {
match self {
| Self::Not(it) => it.len(),
| Self::Rev(it) => it.len(),
}
}
}

// EOF

source/framework/data/model.rs → fw/data/model.rs View File

@@ -2,7 +2,7 @@ use crate::{
data::{read, vertex::Vertex},
ffi,
math::*,
types::StkStr,
types::{Cast, StkStr},
};
use std::{
collections::HashMap,
@@ -11,7 +11,8 @@ use std::{
str::Utf8Error,
};

#[derive(Error, Debug)]
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum Err {
#[error(transparent)]
Io(#[from] io::Error),
@@ -41,6 +42,7 @@ struct Pose {
}

struct Joint {
#[allow(dead_code)]
nam: StkStr,
mat: Mat4,
inv: Mat4,
@@ -77,7 +79,7 @@ const FMT_F16: u32 = 6;
const FMT_F32: u32 = 7;
const FMT_F64: u32 = 8;

fn fmt_check(fm: u32) -> Result<(), Err> {
const fn fmt_check(fm: u32) -> Result<(), Err> {
match fm {
| FMT_I8 | FMT_U8 | FMT_I16 | FMT_U16 | FMT_I32 | FMT_U32 | FMT_F16
| FMT_F32 | FMT_F64 => Ok(()),
@@ -97,30 +99,30 @@ fn fmt_size(fm: u32) -> usize {

fn fmt_read_abs(fm: u32, vec: &[u8], pos: usize) -> f32 {
match fm {
| FMT_I8 => f32::from(vec[pos] as i8),
| FMT_U8 => f32::from(vec[pos]),
| FMT_I16 => f32::from(read::i16le_16(vec, pos)),
| FMT_U16 => f32::from(read::u16le_16(vec, pos)),
| FMT_I32 => read::i32le_32(vec, pos) as f32,
| FMT_U32 => read::u32le_32(vec, pos) as f32,
| FMT_F16 => read::f16le_32(vec, pos),
| FMT_F32 => read::f32le_32(vec, pos),
| FMT_F64 => read::f64le_32(vec, pos),
| FMT_I8 => read::i8x(vec, pos).into(),
| FMT_U8 => read::u8x(vec, pos).into(),
| FMT_I16 => read::i16le(vec, pos).into(),
| FMT_U16 => read::u16le(vec, pos).into(),
| FMT_I32 => read::i32le(vec, pos).cast(),
| FMT_U32 => read::u32le(vec, pos).cast(),
| FMT_F16 => read::f16le(vec, pos).to_f32(),
| FMT_F32 => read::f32le(vec, pos),
| FMT_F64 => read::f64le(vec, pos) as f32,
| _ => unsafe { std::hint::unreachable_unchecked() },
}
}

fn fmt_read_nrm(fm: u32, vec: &[u8], pos: usize) -> f32 {
match fm {
| FMT_I8 => f32::abs(f32::from(vec[pos] as i8) / 128.0),
| FMT_U8 => f32::from(vec[pos]) / 255.0,
| FMT_I16 => f32::abs(f32::from(read::i16le_16(vec, pos)) / 32768.0),
| FMT_U16 => f32::from(read::u16le_16(vec, pos)) / 65535.0,
| FMT_I32 => (read::i32le_32(vec, pos) as f64 / 2147483648.0) as f32,
| FMT_U32 => (read::u32le_32(vec, pos) as f64 / 4294967295.0) as f32,
| FMT_F16 => f32::clamp(read::f16le_32(vec, pos), 0.0, 1.0),
| FMT_F32 => f32::clamp(read::f32le_32(vec, pos), 0.0, 1.0),
| FMT_F64 => f32::clamp(read::f64le_64(vec, pos) as f32, 0.0, 1.0),
| FMT_I8 => f32::from(read::i8x(vec, pos)).abs() / 128.0,
| FMT_U8 => f32::from(read::u8x(vec, pos)) / 255.0,
| FMT_I16 => f32::from(read::i16le(vec, pos)).abs() / 32768.0,
| FMT_U16 => f32::from(read::u16le(vec, pos)) / 65535.0,
| FMT_I32 => (f64::from(read::i32le(vec, pos)) / 2147483648.0) as f32,
| FMT_U32 => (f64::from(read::u32le(vec, pos)) / 4294967295.0) as f32,
| FMT_F16 => f32::clamp(read::f16le(vec, pos).to_f32(), 0.0, 1.0),
| FMT_F32 => f32::clamp(read::f32le(vec, pos), 0.0, 1.0),
| FMT_F64 => f32::clamp(read::f64le(vec, pos) as f32, 0.0, 1.0),
| _ => unsafe { std::hint::unreachable_unchecked() },
}
}
@@ -172,25 +174,25 @@ impl Model {
return Err(Err::Magic);
}

let num_text = read::u32le_sz(&head, 28);
let ofs_text = read::u32le_64(&head, 32);
let num_mesh = read::u32le_sz(&head, 36);
let ofs_mesh = read::u32le_64(&head, 40);
let num_vinf = read::u32le_sz(&head, 44);
let num_vert = read::u32le_sz(&head, 48);
let ofs_vinf = read::u32le_64(&head, 52);
let num_tris = read::u32le_sz(&head, 56);
let ofs_tris = read::u32le_64(&head, 60);
let num_join = read::u32le_sz(&head, 68);
let ofs_join = read::u32le_64(&head, 72);
let num_pose = read::u32le_sz(&head, 76);
let ofs_pose = read::u32le_64(&head, 80);
let num_anim = read::u32le_sz(&head, 84);
let ofs_anim = read::u32le_64(&head, 88);
let num_frms = read::u32le_sz(&head, 92);
let num_frmc = read::u32le_sz(&head, 96);
let ofs_frms = read::u32le_64(&head, 100);
let ofs_bnds = read::u32le_64(&head, 104);
let num_text = read::u32le(&head, 28) as usize;
let ofs_text = read::u32le(&head, 32).into();
let num_mesh = read::u32le(&head, 36) as usize;
let ofs_mesh = read::u32le(&head, 40).into();
let num_vinf = read::u32le(&head, 44) as usize;
let num_vert = read::u32le(&head, 48) as usize;
let ofs_vinf = read::u32le(&head, 52).into();
let num_tris = read::u32le(&head, 56) as usize;
let ofs_tris = read::u32le(&head, 60).into();
let num_join = read::u32le(&head, 68) as usize;
let ofs_join = read::u32le(&head, 72).into();
let num_pose = read::u32le(&head, 76) as usize;
let ofs_pose = read::u32le(&head, 80).into();
//let num_anim = read::u32le(&head, 84) as usize;
//let ofs_anim = read::u32le(&head, 88).into();
let num_frms = read::u32le(&head, 92) as usize;
let num_frmc = read::u32le(&head, 96) as usize;
let ofs_frms = read::u32le(&head, 100).into();
//let ofs_bnds = read::u32le(&head, 104).into();

if num_join != 0 && num_pose != 0 && num_pose != num_join {
return Err(Err::NumPoses);
@@ -208,29 +210,28 @@ impl Model {
rd.seek(SeekFrom::Start(ofs_vinf))?;

for array_def in read::hunk(rd, num_vinf * 20)?.chunks(20) {
let ty = read::u32le_32(array_def, 0);
let fl = read::u32le_32(array_def, 4);
let fm = read::u32le_32(array_def, 8);
let sz = read::u32le_32(array_def, 12);
let of = read::u32le_64(array_def, 16);
let ty = read::u32le(array_def, 0);
//let fl = read::u32le(array_def, 4);
let fm = read::u32le(array_def, 8);
let sz = read::u32le(array_def, 12) as usize;
let of = read::u32le(array_def, 16).into();

fmt_check(fm)?;

rd.seek(SeekFrom::Start(of))?;

let obj_size = fmt_size(fm);
let obj_amnt = sz as usize;
let obj_blks = obj_size * obj_amnt;
let obj_blks = obj_size * sz;

let hunk = read::hunk(rd, obj_blks * num_vert)?;

let v2 = |v: &mut Vec<Vec3>, rd: fn(u32, &[u8], usize) -> f32| {
if obj_amnt != 2 {
if sz != 2 {
Err(Err::VaFormat)
} else {
for vec in hunk.chunks(obj_blks) {
let x = rd(fm, vec, obj_size * 0);
let y = rd(fm, vec, obj_size * 1);
let x = rd(fm, vec, 0);
let y = rd(fm, vec, obj_size);
v.push(Vec3::new(x, y, 0.0));
}
Ok(())
@@ -238,12 +239,12 @@ impl Model {
};

let v3 = |v: &mut Vec<Vec3>, rd: fn(u32, &[u8], usize) -> f32| {
if obj_amnt != 3 {
if sz != 3 {
Err(Err::VaFormat)
} else {
for vec in hunk.chunks(obj_blks) {
let x = rd(fm, vec, obj_size * 0);
let y = rd(fm, vec, obj_size * 1);
let x = rd(fm, vec, 0);
let y = rd(fm, vec, obj_size);
let z = rd(fm, vec, obj_size * 2);
v.push(Vec3::new(x, y, z));
}
@@ -252,12 +253,12 @@ impl Model {
};

let v4 = |v: &mut Vec<Vec4>, rd: fn(u32, &[u8], usize) -> f32| {
if obj_amnt != 4 {
if sz != 4 {
Err(Err::VaFormat)
} else {
for vec in hunk.chunks(obj_blks) {
let x = rd(fm, vec, obj_size * 0);
let y = rd(fm, vec, obj_size * 1);
let x = rd(fm, vec, 0);
let y = rd(fm, vec, obj_size);
let z = rd(fm, vec, obj_size * 2);
let w = rd(fm, vec, obj_size * 3);
v.push(Vec4::new(x, y, z, w));
@@ -290,14 +291,15 @@ impl Model {
wgt_v.into_iter(),
clr_v.into_iter(),
)) {
let pos = pos.unwrap_or_else(|| Vec3::ZERO);
let tex = tex.unwrap_or_else(|| Vec3::ZERO);
let nrm = nrm.unwrap_or_else(|| Vec3::ZERO);
let tan = tan.unwrap_or_else(|| Vec4::ZERO);
let idx = idx.unwrap_or_else(|| Vec4::ZERO);
let wgt = wgt.unwrap_or_else(|| Vec4::ZERO);
let clr = clr.unwrap_or_else(|| Vec4::ONE);
vert_dat.push(Vertex { pos, tex, nrm, tan, idx, wgt, clr });
vert_dat.push(Vertex {
pos: pos.unwrap_or(Vec3::ZERO),
tex: tex.unwrap_or(Vec3::ZERO),
nrm: nrm.unwrap_or(Vec3::ZERO),
tan: tan.unwrap_or(Vec4::ZERO),
idx: idx.unwrap_or(Vec4::ZERO),
wgt: wgt.unwrap_or(Vec4::ZERO),
clr: clr.unwrap_or(Vec4::ONE),
});
}

// collect index info
@@ -306,9 +308,9 @@ impl Model {
rd.seek(SeekFrom::Start(ofs_tris))?;

for tri in read::hunk(rd, num_tris * 4 * 3)?.chunks(4 * 3) {
indx_dat.push(read::u32le_32(tri, 0));
indx_dat.push(read::u32le_32(tri, 4));
indx_dat.push(read::u32le_32(tri, 8));
indx_dat.push(read::u32le(tri, 0));
indx_dat.push(read::u32le(tri, 4));
indx_dat.push(read::u32le(tri, 8));
}

// collect text
@@ -322,12 +324,12 @@ impl Model {
rd.seek(SeekFrom::Start(ofs_mesh))?;

for mesh in read::hunk(rd, num_mesh * 4 * 6)?.chunks(4 * 6) {
let nam = read::u32le_sz(mesh, 0);
let mat = read::u32le_sz(mesh, 4);
let vtx = read::u32le_sz(mesh, 8);
let vnu = read::u32le_sz(mesh, 12);
let idx = read::u32le_sz(mesh, 16) * 3;
let inu = read::u32le_sz(mesh, 20) * 3;
let nam = read::u32le(mesh, 0) as usize;
let mat = read::u32le(mesh, 4) as usize;
let vtx = read::u32le(mesh, 8) as usize;
let vnu = read::u32le(mesh, 12) as usize;
let idx = read::u32le(mesh, 16) as usize * 3;
let inu = read::u32le(mesh, 20) as usize * 3;

let nam = StkStr::new(from_stab(&stab, nam)?);
let mat = StkStr::new(from_stab(&stab, mat)?);
@@ -344,9 +346,9 @@ impl Model {
rd.seek(SeekFrom::Start(ofs_join))?;

for join in read::hunk(rd, num_join * 4 * 12)?.chunks(4 * 12) {
let nam = read::u32le_sz(join, 0);
let par = read::u32le_sz(join, 4);
let trs = read::array(read::f32le_32, join, [0.0; 10], 4, 8);
let nam = read::u32le(join, 0) as usize;
let par = read::u32le(join, 4) as usize;
let trs = read::array(read::f32le, join, [0.0; 10], 4, 8);

let nam = StkStr::new(from_stab(&stab, nam)?);

@@ -372,7 +374,7 @@ impl Model {
rd.seek(SeekFrom::Start(ofs_frms))?;

for samp in read::hunk(rd, num_frms * num_frmc * 2)?.chunks(2) {
frames.push(read::u16le_16(samp, 0));
frames.push(read::u16le(samp, 0));
}

// collect pose info
@@ -381,10 +383,10 @@ impl Model {
rd.seek(SeekFrom::Start(ofs_pose))?;

for pose in read::hunk(rd, num_pose * 4 * 22)?.chunks(4 * 22) {
let par = read::u32le_sz(pose, 0);
let msk = read::u32le_32(pose, 4);
let ofs = read::array(read::f32le_32, pose, [0.0; 10], 4, 8);
let scl = read::array(read::f32le_32, pose, [0.0; 10], 4, 48);
let par = read::u32le(pose, 0) as usize;
let msk = read::u32le(pose, 4);
let ofs = read::array(read::f32le, pose, [0.0; 10], 4, 8);
let scl = read::array(read::f32le, pose, [0.0; 10], 4, 48);

poses.push(Pose { par, msk, ofs, scl });
}
@@ -399,10 +401,10 @@ impl Model {
for (pose, joint) in poses.iter().zip(joints.iter()) {
let mut ofs = pose.ofs;

for i in 0..10 {
for (i, off) in ofs.iter_mut().enumerate() {
if pose.msk & 1 << i != 0 {
let frm = f32::from(*frame.next().ok_or(Err::FrameCount)?);
ofs[i] += frm * pose.scl[i];
*off += frm * pose.scl[i];
}
}


+ 283
- 0
fw/data/read.rs View File

@@ -0,0 +1,283 @@
use half::f16;
use std::io::{self, Read};

pub const fn i8x(b: &[u8], p: usize) -> i8 {
i8::from_ne_bytes([b[p]])
}

pub const fn u8x(b: &[u8], p: usize) -> u8 {
b[p]
}

pub const fn i16le(b: &[u8], p: usize) -> i16 {
i16::from_le_bytes([b[p], b[p + 1]])
}

pub const fn u16le(b: &[u8], p: usize) -> u16 {
u16::from_le_bytes([b[p], b[p + 1]])
}

pub const fn i32le(b: &[u8], p: usize) -> i32 {
i32::from_le_bytes([b[p], b[p + 1], b[p + 2], b[p + 3]])
}

pub const fn u32le(b: &[u8], p: usize) -> u32 {
u32::from_le_bytes([b[p], b[p + 1], b[p + 2], b[p + 3]])
}

pub fn f16le(b: &[u8], p: usize) -> f16 {
f16::from_le_bytes([b[p], b[p + 1]])
}

pub fn f32le(b: &[u8], p: usize) -> f32 {
f32::from_le_bytes([b[p], b[p + 1], b[p + 2], b[p + 3]])
}

pub fn f64le(b: &[u8], p: usize) -> f64 {
f64::from_le_bytes([
b[p],
b[p + 1],
b[p + 2],
b[p + 3],
b[p + 4],
b[p + 5],
b[p + 6],
b[p + 7],
])
}

pub fn array<T, const N: usize>(
f: impl Fn(&[u8], usize) -> T, b: &[u8], mut v: [T; N], step: usize,
beg: usize,
) -> [T; N] {
for (p, v) in (beg..beg + N * step).step_by(step).zip(v.iter_mut()) {
*v = f(b, p);
}

v
}

pub fn hunk(rd: &mut impl Read, size: usize) -> io::Result<Vec<u8>> {
let mut data = Vec::with_capacity(size);
unsafe {
data.set_len(size);
}

rd.read_exact(&mut data)?;

Ok(data)
}

macro_rules! bits_impl {
($t:ty, $be:ident, $le:ident) => {
/// Reads `width` bits from `b` starting at bit `cr_bit` into an
/// integer, most significant bit first.
///
/// # Errors
///
/// 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> {
if width == 0 {
return Some(0);
}

let last = (cr_bit + width - 1) / 8;

if last >= b.len() {
return None;
}

let mut byte_ptr = cr_bit / 8;
let mut bits_ptr = cr_bit % 8;

let mut res = 0;

while width > 0 {
width -= 1;
res <<= 1;

if b[byte_ptr] & 1 << bits_ptr != 0 {
res |= 1;
}

bits_ptr += 1;

if bits_ptr > 7 {
bits_ptr = 0;
byte_ptr += 1;
}
}

Some(res)
}

/// The same as `bbe*`, but least-significant bit first.
///
/// # Errors
///
/// 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> {
match width {
| 0 => Some(0),
| 1 => {
let byte = cr_bit / 8;
if byte < b.len() {
let bitn = cr_bit % 8;
Some(if b[byte] & 1 << bitn != 0 { 1 } else { 0 })
} else {
None
}
}
| _ => {
let last = (cr_bit + width - 1) / 8;

if last >= b.len() {
return None;
}

let mut byte_ptr = cr_bit / 8;
let mut bits_ptr = cr_bit % 8;

let mut res = 0;
let mut bit = 1;

while width > 0 {
width -= 1;

if b[byte_ptr] & 1 << bits_ptr != 0 {
res |= bit;
}

bit <<= 1;
bits_ptr += 1;

if bits_ptr > 7 {
bits_ptr = 0;
byte_ptr += 1;
}
}

Some(res)
}
}
}
};
}

bits_impl!(u8, bbe_u8, ble_u8);
bits_impl!(u16, bbe_u16, ble_u16);
bits_impl!(u32, bbe_u32, ble_u32);
bits_impl!(u64, bbe_u64, ble_u64);
bits_impl!(usize, bbe_usz, ble_usz);

#[test]
fn bit_tests() {
const INPUT: &[u8] = &[
0b01100101, 0b10101010, 0b00010000, 0b00000000, 0b11111111, 0b11100001,
0b10101100, 0b00110011, 0b10100101, 0b11100000, 0b00000111, 0b00000001,
0b11001010, 0b10101111, 0b00101011, 0b01101010, 0b11010101, 0b10100011,
0b01010101, 0b11000001,
];

let mut p = 0;

let n = bbe_u8(INPUT, p, 3).unwrap();
assert_eq!(n, 0b101);
p += 3;

let n = bbe_u64(INPUT, p, 63).unwrap();
assert_eq!(
n,
0b001100101010100001000000000001111111110000111001101011100110010,
);
p += 63;

let n = bbe_u8(INPUT, p, 4).unwrap();
assert_eq!(n, 0b1001);
p += 4;

let n = bbe_u8(INPUT, p, 7).unwrap();
assert_eq!(n, 0b0100000);
p += 7;

let n = bbe_u32(INPUT, p, 17).unwrap();
assert_eq!(n, 0b11111100000100000);
p += 17;

let n = bbe_u32(INPUT, p, 27).unwrap();
assert_eq!(n, 0b000101001111110101110101000);
p += 27;

let n = bbe_u64(INPUT, p, 33).unwrap();
assert_eq!(n, 0b101011010101011110001011010101010);
p += 33;

let n = bbe_usz(INPUT, p, 6).unwrap();
assert_eq!(n, 0b000011);
p += 6;

assert!(bbe_u8(INPUT, p, 1).is_none());
assert!(bbe_u8(INPUT, p, 2).is_none());

p = 0;

let n = ble_u8(INPUT, 0, 3).unwrap();
assert_eq!(n, 0b101);
p += 3;

let n = ble_u64(INPUT, p, 59).unwrap();
assert_eq!(
n,
0b11001110101100111000011111111100000000000100001010101001100,
);
p += 59;

let n = ble_u8(INPUT, p, 1).unwrap();
assert_eq!(n, 0b0);
p += 1;

let n = ble_u8(INPUT, p, 1).unwrap();
assert_eq!(n, 0b0);
p += 1;

let n = ble_u8(INPUT, p, 1).unwrap();
assert_eq!(n, 0b1);
p += 1;

let n = ble_u8(INPUT, p, 1).unwrap();
assert_eq!(n, 0b0);
p += 1;

let n = ble_u8(INPUT, p, 4).unwrap();
assert_eq!(n, 0b1001);
p += 4;

let n = ble_u8(INPUT, p, 7).unwrap();
assert_eq!(n, 0b0000010);
p += 7;

let n = ble_u32(INPUT, p, 17).unwrap();
assert_eq!(n, 0b00000100000111111);
p += 17;

let n = ble_u32(INPUT, p, 27).unwrap();
assert_eq!(n, 0b000101011101011111100101000);
p += 27;

let n = ble_u64(INPUT, p, 33).unwrap();
assert_eq!(n, 0b010101010110100011110101010110101);
p += 33;

let n = ble_usz(INPUT, p, 6).unwrap();
assert_eq!(n, 0b110000);
p += 6;

assert!(ble_u8(INPUT, p, 1).is_none());
assert!(ble_u8(INPUT, p, 2).is_none());
}

// EOF

source/framework/data/shader.rs → fw/data/shader.rs View File

@@ -20,7 +20,7 @@ impl Module {
let mut code = Vec::with_capacity(bytecode.len() / 4);

for word in bytecode.chunks_exact(4) {
code.push(read::u32le_32(word, 0));
code.push(read::u32le(word, 0));
}

let size = code.len() * 4;

source/framework/data/text.rs → fw/data/text.rs View File

@@ -107,8 +107,8 @@ pub fn ellipsize_small_str(inp: &str) -> ([u8; 32], usize) {
pub const fn radix(c: char) -> u8 {
c as u8
- match c {
| 'a'..='z' => 0x61 - 10,
| 'A'..='Z' => 0x41 - 10,
| 'a'..='z' => 0x61,
| 'A'..='Z' => 0x41,
| '0'..='9' => 0x30,
| _ => 0,
}

source/framework/data/uniforms.rs → fw/data/uniforms.rs View File


source/framework/data/vertex.rs → fw/data/vertex.rs View File

@@ -1,6 +1,7 @@
use crate::math::{Vec3, Vec4};

#[repr(packed)]
#[derive(blonkus_ma::FieldInfo)]
pub struct Vertex {
pub pos: Vec3,
pub tex: Vec3,

+ 209
- 0
fw/data/vfs.rs View File

@@ -0,0 +1,209 @@
use super::deflate;
use std::{
cell::UnsafeCell,
collections::HashMap,
fmt, fs,
io::{self, Read, Seek},
path::{Path, PathBuf},
rc::Rc,
str::Utf8Error,
};

#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum ErrVfs {
#[error("Bad file type for `{0}'")]
FileType(FileName),
#[error("Invalid magic number")]
InvalidMagic,
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
ParseInt(#[from] std::num::ParseIntError),
#[error(transparent)]
Utf8(#[from] Utf8Error),
}

pub type FileName = crate::types::StkStr;
pub type FileData = UnsafeCell<Vec<u8>>;

#[derive(Default)]
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(" }")
}
}

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());
if !buf.is_empty() {
&*buf
} else {
let mut v = self.load();
let nv = deflate::read_gzip(&v);
let nv = nv.and_then(|gz| deflate::read_deflate(gz).ok());
if let Some(nv) = nv {
v = nv;
}
*buf = v;
(*buf).as_ref()
}
}
}

fn text(&self) -> Result<&str, Utf8Error> {
std::str::from_utf8(self.data())
}

fn load(&self) -> Vec<u8>;
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> {
if path.is_dir() {
self.read_dir(path)
} else if path.is_file() {
self.read_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())
}
}

// EOF

source/framework/ffi.rs → fw/ffi.rs View File

@@ -7,9 +7,10 @@ use std::{
/// Creates a C string from a literal.
#[macro_export]
macro_rules! c_str {
($s:expr) => {
std::concat!($s, "\0").as_ptr() as crate::ffi::Nts
};
($s:expr) => {{
const S: &'static str = std::concat!($s, "\0");
S.as_ptr() as crate::ffi::Nts
}};
}

#[macro_export]
@@ -34,14 +35,15 @@ pub enum Bool {
True,
}

impl Bool {
#[inline]
pub const fn is_true(&self) -> bool {
matches!(self, Self::True)
}
}

pub type Nts = *const raw::c_char;
pub type NtsMut = *mut raw::c_char;

/// An owned null-terminated string vector.
#[derive(Debug)]
pub struct CStringVec {
sv: Vec<CString>,
cv: Vec<Nts>,
}

#[inline]
pub fn cstr_from_slice(s: &[u8]) -> Option<&str> {
@@ -58,7 +60,7 @@ pub fn cstr_from_slice(s: &[u8]) -> Option<&str> {
/// should not cause any undefined behaviour in any case.
#[inline]
pub unsafe fn cstr_from_ptr(s: Nts) -> Option<&'static str> {
CStr::from_ptr(s).to_str().ok()
unsafe { CStr::from_ptr(s).to_str().ok() }
}

#[inline]
@@ -70,11 +72,11 @@ pub fn end_of_cstr(s: &[u8]) -> usize {
}
}

/// An owned null-terminated string vector.
#[derive(Debug)]
pub struct CStringVec {
sv: Vec<CString>,
cv: Vec<Nts>,
impl Bool {
#[inline]
pub const fn is_true(&self) -> bool {
matches!(self, Self::True)
}
}

impl Default for CStringVec {

source/framework/hal.rs → fw/hal.rs View File

@@ -5,7 +5,8 @@ pub mod win;
#[doc(hidden)]
pub mod sdl;

#[derive(Error, Debug)]
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum Err {
#[error("SDL Error: {0}")]
Sdl(std::borrow::Cow<'static, str>),
@@ -19,7 +20,9 @@ impl Err {
/// This function will cause undefined behaviour if used from
/// multiple threads.
pub(self) unsafe fn new_sdl() -> Self {
Self::Sdl(std::ffi::CStr::from_ptr(sdl::get_error()).to_string_lossy())
Self::Sdl(unsafe {
std::ffi::CStr::from_ptr(sdl::get_error()).to_string_lossy()
})
}
}


source/framework/hal/ctx.rs → fw/hal/ctx.rs View File


source/framework/hal/evt.rs → fw/hal/evt.rs View File

@@ -10,8 +10,8 @@ impl Iterator for EventIterator {
type Item = Event;

fn next(&mut self) -> Option<Self::Item> {
unsafe {
loop {
loop {
unsafe {
let mut event = std::mem::MaybeUninit::uninit();

if sdl::poll_event(event.as_mut_ptr()) != 1 {
@@ -22,8 +22,8 @@ impl Iterator for EventIterator {
return Some(evt);
}
}
None
}
None
}
}


source/framework/hal/sdl.rs → fw/hal/sdl.rs View File

@@ -1,10 +1,12 @@
use crate::ffi::Bool;
use bitflags::bitflags;
use crate::{
ffi::{Bool, Nts},
opaque_struct,
};
use std::os::raw;

pub const WINDOW_POS_UNDEF: raw::c_int = 0x1FFF0000;

bitflags! {
bitflags::bitflags! {
#[repr(transparent)]
pub struct InitFlags: u32 {
const TIMER = 0x01;
@@ -18,7 +20,7 @@ bitflags! {
}
}

bitflags! {
bitflags::bitflags! {
#[repr(transparent)]
pub struct WindowFlags: u32 {
const FULLSCREEN = 0x00000001;
@@ -150,7 +152,7 @@ extern "C" {

// errors
#[link_name = "SDL_GetError"]
pub fn get_error() -> *const raw::c_char;
pub fn get_error() -> Nts;

// events
#[link_name = "SDL_PollEvent"]
@@ -159,8 +161,8 @@ extern "C" {
// windows
#[link_name = "SDL_CreateWindow"]
pub fn create_window(
title: *const raw::c_char, x: raw::c_int, y: raw::c_int, w: raw::c_int,
h: raw::c_int, flags: WindowFlags,
title: Nts, x: raw::c_int, y: raw::c_int, w: raw::c_int, h: raw::c_int,
flags: WindowFlags,
) -> *mut Window;

#[link_name = "SDL_DestroyWindow"]
@@ -169,8 +171,7 @@ extern "C" {
// vulkan
#[link_name = "SDL_Vulkan_GetInstanceExtensions"]
pub fn vulkan_get_instance_extensions(
window: *mut Window, p_count: *mut raw::c_uint,
p_names: *mut *const raw::c_char,
window: *mut Window, p_count: *mut raw::c_uint, p_names: *mut Nts,
) -> Bool;

#[link_name = "SDL_Vulkan_CreateSurface"]

source/framework/hal/win.rs → fw/hal/win.rs View File

@@ -1,5 +1,5 @@
use super::*;
use crate::ffi;
use crate::{ffi, types::Cast};
use ash::{version::InstanceV1_0, vk};
use std::marker::PhantomData;

@@ -31,45 +31,63 @@ impl<'a> Window<'a> {
}
}

pub unsafe fn vulkan_instance_extensions(
/// Returns a vector of null-terminated string pointers
/// describing all available Vulkan instance extensions for
/// this window.
///
/// # Safety
///
/// This function provides a safe API over a raw C API.
pub fn vulkan_instance_extensions(
&self,
) -> Result<Vec<ffi::Nts>, Err> {
let mut count = 0;
let res = sdl::vulkan_get_instance_extensions(
self.handle,
&mut count,
std::ptr::null_mut(),
);
if res.is_true() {
let mut names = vec![std::ptr::null(); count as usize];
let res = sdl::vulkan_get_instance_extensions(
let res = unsafe {
sdl::vulkan_get_instance_extensions(
self.handle,
&mut count,
names.as_mut_ptr(),
);
std::ptr::null_mut(),
)
};
if res.is_true() {
let mut names = vec![std::ptr::null(); count.cast()];
let res = unsafe {
sdl::vulkan_get_instance_extensions(
self.handle,
&mut count,
names.as_mut_ptr(),
)
};
if res.is_true() {
Ok(names)
} else {
Err(Err::new_sdl())
Err(unsafe { Err::new_sdl() })
}
} else {
Err(Err::new_sdl())
Err(unsafe { Err::new_sdl() })
}
}

pub unsafe fn vulkan_create_surface(
/// Creates a [`vk::SurfaceKHR`] for this window.
///
/// # Safety
///
/// This function provides a safe API over a raw C API.
pub fn vulkan_create_surface(
&self, instance: &ash::Instance,
) -> Result<vk::SurfaceKHR, Err> {
let mut surface = vk::SurfaceKHR::null();
let res = sdl::vulkan_create_surface(
self.handle,
instance.handle(),
&mut surface,
);
let res = unsafe {
sdl::vulkan_create_surface(
self.handle,
instance.handle(),
&mut surface,
)
};
if res.is_true() {
Ok(surface)
} else {
Err(Err::new_sdl())
Err(unsafe { Err::new_sdl() })
}
}
}

source/framework/iter.rs → fw/iter.rs View File

@@ -4,28 +4,28 @@ macro_rules! coll {
IntoIterator::into_iter($it).map($xp).collect::<$t>()
};
(<$t:ty> $xp:expr; $sz:expr) => {
coll![<$t> (0..$sz), |_| $xp]
crate::coll![<$t> (0..$sz), |_| $xp]
};
}

#[macro_export]
macro_rules! vec_r {
($it:expr, $xp:expr) => {coll![<Result<Vec<_>, _>> $it, $xp]};
($xp:expr; $sz:expr) => {coll![<Result<Vec<_>, _>> $xp; $sz]};
($it:expr, $xp:expr) => {crate::coll![<Result<Vec<_>, _>> $it, $xp]};
($xp:expr; $sz:expr) => {crate::coll![<Result<Vec<_>, _>> $xp; $sz]};
}

#[macro_export]
macro_rules! vec_e {
($it:expr, $xp:expr) => {coll![<Vec<_>> $it, $xp]};
($xp:expr; $sz:expr) => {coll![<Vec<_>> $xp; $sz]};
($it:expr, $xp:expr) => {crate::coll![<Vec<_>> $it, $xp]};
($xp:expr; $sz:expr) => {crate::coll![<Vec<_>> $xp; $sz]};
}

#[macro_export]
macro_rules! stkvec_r {
(<$t:ty> $it:expr, $xp:expr) => {coll![<Result<StkVec<$t>, _>> $it, $xp]};
(<$t:ty> $xp:expr; $sz:expr) => {coll![<Result<StkVec<$t>, _>> $xp; $sz]};
($it:expr, $xp:expr) => {coll![<Result<StkVec<_>, _>> $it, $xp]};
($xp:expr; $sz:expr) => {coll![<Result<StkVec<_>, _>> $xp; $sz]};
(<$t:ty> $it:expr, $xp:expr) => {crate::coll![<Result<StkVec<$t>, _>> $it, $xp]};
(<$t:ty> $xp:expr; $sz:expr) => {crate::coll![<Result<StkVec<$t>, _>> $xp; $sz]};
($it:expr, $xp:expr) => {crate::coll![<Result<StkVec<_>, _>> $it, $xp]};
($xp:expr; $sz:expr) => {crate::coll![<Result<StkVec<_>, _>> $xp; $sz]};
}

macro_rules! zips {
@@ -82,4 +82,59 @@ zips!(A / a, B / b, C / c, D / d, E / e, F / f, G / g, H / h, I / i, J / j);
pub struct OpenZip<T>(pub T);
pub struct ClosedZip<T>(pub T);

#[derive(Clone, Debug)]
pub enum MaybeRev<I> {
Not(I),
Rev(I),
}

impl<I> MaybeRev<I> {
#[inline]
pub const fn rev_if(it: I, rev: bool) -> Self {
if rev {
Self::Rev(it)
} else {
Self::Not(it)
}
}

#[inline]
pub const fn rev(it: I) -> Self {
Self::Rev(it)
}

#[inline]
pub const fn not(it: I) -> Self {
Self::Not(it)
}
}

impl<I> Iterator for MaybeRev<I>
where
I: Iterator + DoubleEndedIterator,
{
type Item = <I as Iterator>::Item;

#[inline]
fn next(&mut self) -> Option<Self::Item> {
match self {