From cdb0423401935fe077fe9a35589b8c5671b45b49 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Tue, 13 Aug 2019 18:03:48 -0400 Subject: [PATCH 001/335] Initial commit --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..faa27f7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/target +**/*.rs.bk +Cargo.lock + +*.iml +.idea +.vscode \ No newline at end of file From 50f832a25d5f509496c1097761819ba7b62ba16d Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Tue, 13 Aug 2019 18:11:24 -0400 Subject: [PATCH 002/335] Adding luajit submodule --- .gitmodules | 3 +++ luajit | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 luajit diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e7ae3a7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "luajit"] + path = luajit + url = https://github.com/LuaJIT/LuaJIT.git diff --git a/luajit b/luajit new file mode 160000 index 0000000..f0e865d --- /dev/null +++ b/luajit @@ -0,0 +1 @@ +Subproject commit f0e865dd4861520258299d0f2a56491bd9d602e1 From f144a4ff6a2d1a1b723d8d1eb0399f4519532785 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Thu, 15 Aug 2019 19:13:42 -0400 Subject: [PATCH 003/335] Add build script and generated ffi --- Cargo.toml | 13 + bindgen.sh | 26 ++ build.rs | 71 +++ examples/hello.lua | 3 + examples/lua.rs | 54 +++ ffi.h | 4 + src/ffi.rs | 1113 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 163 +++++++ tests/test.rs | 40 ++ 9 files changed, 1487 insertions(+) create mode 100644 Cargo.toml create mode 100644 bindgen.sh create mode 100644 build.rs create mode 100644 examples/hello.lua create mode 100644 examples/lua.rs create mode 100644 ffi.h create mode 100644 src/ffi.rs create mode 100644 src/lib.rs create mode 100644 tests/test.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e407f7c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "luajit-sys" +version = "0.0.1" +authors = ["Aaron Loucks "] +edition = "2018" +keywords = ["lua", "luajit", "script"] + +[dependencies] +libc = "0.2" + +[build-dependencies] +cc = "1.0.40" +fs_extra = "1.1.0" \ No newline at end of file diff --git a/bindgen.sh b/bindgen.sh new file mode 100644 index 0000000..5dd058d --- /dev/null +++ b/bindgen.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +BINDGEN_VERSION=$(bindgen --version) + +bindgen -o src/ffi.rs \ + --raw-line "/// Generated with: ${BINDGEN_VERSION}" \ + --whitelist-var "LUA.*" \ + --whitelist-var "LUAJIT.*" \ + --whitelist-type "lua_.*" \ + --whitelist-type "luaL_.*" \ + --whitelist-function "lua_.*" \ + --whitelist-function "luaL_.*" \ + --whitelist-function "luaJIT.*" \ + --ctypes-prefix "libc" \ + --use-core \ + --impl-debug \ + ffi.h -- -I luajit/src + +sed -i -e 's/pub fn \(luaJIT_[^\(]*\)/\/\/\/ \n pub fn \1/' src/ffi.rs +sed -i -e 's/pub fn \(lua_[^\(]*\)/\/\/\/ \n pub fn \1/' src/ffi.rs +sed -i -e 's/pub fn \(luaL_[^\(]*\)/\/\/\/ \n pub fn \1/' src/ffi.rs +sed -i -e 's/pub type \(lua_[^\=]*\)/\/\/\/ \n pub type \1/' src/ffi.rs +sed -i -e 's/pub struct \(lua_[^\{]*\)/\/\/\/ \n pub struct \1/' src/ffi.rs +sed -i -e 's/pub struct \(luaL_[^\{]*\)/\/\/\/ \n pub struct \1/' src/ffi.rs + +cargo +stable fmt \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..d96daef --- /dev/null +++ b/build.rs @@ -0,0 +1,71 @@ +use cc; +use fs_extra::dir; +use fs_extra::dir::CopyOptions; +use std::env; +use std::process::{Command, Stdio}; + +fn main() { + let target = env::var("TARGET").unwrap(); + + let luajit_dir = format!("{}\\luajit", env!("CARGO_MANIFEST_DIR")); + let out_dir = env::var("OUT_DIR").unwrap(); + let src_dir = format!("{}\\luajit\\src", out_dir); + + if cfg!(target_env = "msvc") { + let lib_path = format!("{}\\lua51.lib", &src_dir); + if !std::fs::metadata(&lib_path).is_ok() { + let cl_exe: cc::Tool = cc::windows_registry::find_tool(&target, "cl.exe").unwrap(); + let msvsbuild_bat = format!("{}\\msvcbuild.bat", &src_dir); + + let mut copy_options = CopyOptions::new(); + copy_options.overwrite = true; + dir::copy(&luajit_dir, &out_dir, ©_options).unwrap(); + + let mut buildcmd = Command::new(msvsbuild_bat); + for (name, value) in cl_exe.env() { + buildcmd.env(name, value); + } + buildcmd.env("Configuration", "Release"); + buildcmd.args(&["static"]); + buildcmd.current_dir(&src_dir); + buildcmd.stderr(Stdio::inherit()); + + let mut child = buildcmd.spawn().expect("failed to run msvcbuild.bat"); + + if !child + .wait() + .map(|status| status.success()) + .map_err(|_| false) + .unwrap_or(false) + { + panic!("Failed to build luajit"); + } + } + println!("cargo:rustc-link-search=native={}", src_dir); + println!("cargo:rustc-link-lib=static=lua51"); + } else { + let lib_path = format!("{}\\luajit.a", &src_dir); + if !std::fs::metadata(&lib_path).is_ok() { + let mut copy_options = CopyOptions::new(); + copy_options.overwrite = true; + dir::copy(&luajit_dir, &out_dir, ©_options).unwrap(); + + let mut buildcmd = Command::new("make"); + buildcmd.current_dir(&src_dir); + buildcmd.stderr(Stdio::inherit()); + + let mut child = buildcmd.spawn().expect("failed to run make"); + + if !child + .wait() + .map(|status| status.success()) + .map_err(|_| false) + .unwrap_or(false) + { + panic!("Failed to build luajit"); + } + } + println!("cargo:rustc-link-search=native={}", src_dir); + println!("cargo:rustc-link-lib=static=luajit"); + } +} diff --git a/examples/hello.lua b/examples/hello.lua new file mode 100644 index 0000000..87a4120 --- /dev/null +++ b/examples/hello.lua @@ -0,0 +1,3 @@ +print("Hello from lua") + +return 1 + 2 diff --git a/examples/lua.rs b/examples/lua.rs new file mode 100644 index 0000000..aa45cae --- /dev/null +++ b/examples/lua.rs @@ -0,0 +1,54 @@ +use std::env; +use std::ffi::{CStr, CString}; +use std::ptr; + +use luajit_sys as sys; + +unsafe fn run_script(script_name: String, script_src: String) { + let lua = sys::luaL_newstate(); + assert_ne!(lua, ptr::null_mut()); + sys::luaL_openlibs(lua); + let script_data = script_src.as_bytes(); + let script_name = CString::new(script_name).unwrap(); + let mut error = sys::luaL_loadbuffer( + lua, + script_data.as_ptr() as _, + script_data.len() as _, + script_name.as_ptr() as _, + ); + if error != 0 { + eprintln!("luaL_loadbuffer failed"); + } else { + error = sys::lua_pcall(lua, 0, 1, 0); + if error != 0 { + eprintln!("lua_pcall failed"); + } + } + let idx = sys::lua_gettop(lua); + if sys::lua_isnoneornil(lua, idx) != 1 { + let s = sys::lua_tostring(lua, idx); + assert_ne!(s, ptr::null(), "lua_tostring returned null"); + let result = CStr::from_ptr(s).to_string_lossy().to_string(); + println!("script result: {}", result); + } + sys::lua_close(lua); +} + +fn main() { + if let Some(script_name) = env::args().skip(1).next() { + let script_src = std::fs::read_to_string(&script_name) + .unwrap_or_else(|e| panic!("failed to read file: '{}' {:?}", &script_name, e)); + unsafe { + run_script(script_name, script_src); + } + } else { + println!( + "{} FILE", + env::current_exe() + .unwrap() + .file_name() + .unwrap() + .to_string_lossy() + ); + } +} diff --git a/ffi.h b/ffi.h new file mode 100644 index 0000000..a9fe40a --- /dev/null +++ b/ffi.h @@ -0,0 +1,4 @@ +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +#include "luajit.h" \ No newline at end of file diff --git a/src/ffi.rs b/src/ffi.rs new file mode 100644 index 0000000..76db086 --- /dev/null +++ b/src/ffi.rs @@ -0,0 +1,1113 @@ +/* automatically generated by rust-bindgen */ + +// Generated with: bindgen 0.49.1 + +pub const LUA_LDIR: &'static [u8; 7usize] = b"!\\lua\\\0"; +pub const LUA_CDIR: &'static [u8; 3usize] = b"!\\\0"; +pub const LUA_PATH_DEFAULT: &'static [u8; 38usize] = + b".\\?.lua;!\\lua\\?.lua;!\\lua\\?\\init.lua;\0"; +pub const LUA_CPATH_DEFAULT: &'static [u8; 30usize] = b".\\?.dll;!\\?.dll;!\\loadall.dll\0"; +pub const LUA_PATH: &'static [u8; 9usize] = b"LUA_PATH\0"; +pub const LUA_CPATH: &'static [u8; 10usize] = b"LUA_CPATH\0"; +pub const LUA_INIT: &'static [u8; 9usize] = b"LUA_INIT\0"; +pub const LUA_DIRSEP: &'static [u8; 2usize] = b"\\\0"; +pub const LUA_PATHSEP: &'static [u8; 2usize] = b";\0"; +pub const LUA_PATH_MARK: &'static [u8; 2usize] = b"?\0"; +pub const LUA_EXECDIR: &'static [u8; 2usize] = b"!\0"; +pub const LUA_IGMARK: &'static [u8; 2usize] = b"-\0"; +pub const LUA_PATH_CONFIG: &'static [u8; 11usize] = b"\\\n;\n?\n!\n-\n\0"; +pub const LUAI_MAXSTACK: u32 = 65500; +pub const LUAI_MAXCSTACK: u32 = 8000; +pub const LUAI_GCPAUSE: u32 = 200; +pub const LUAI_GCMUL: u32 = 200; +pub const LUA_MAXCAPTURES: u32 = 32; +pub const LUA_IDSIZE: u32 = 60; +pub const LUA_NUMBER_SCAN: &'static [u8; 4usize] = b"%lf\0"; +pub const LUA_NUMBER_FMT: &'static [u8; 6usize] = b"%.14g\0"; +pub const LUAI_MAXNUMBER2STR: u32 = 32; +pub const LUA_INTFRMLEN: &'static [u8; 2usize] = b"l\0"; +pub const LUA_VERSION: &'static [u8; 8usize] = b"Lua 5.1\0"; +pub const LUA_RELEASE: &'static [u8; 10usize] = b"Lua 5.1.4\0"; +pub const LUA_VERSION_NUM: u32 = 501; +pub const LUA_COPYRIGHT: &'static [u8; 41usize] = b"Copyright (C) 1994-2008 Lua.org, PUC-Rio\0"; +pub const LUA_AUTHORS: &'static [u8; 49usize] = + b"R. Ierusalimschy, L. H. de Figueiredo & W. Celes\0"; +pub const LUA_SIGNATURE: &'static [u8; 5usize] = b"\x1BLua\0"; +pub const LUA_MULTRET: i32 = -1; +pub const LUA_REGISTRYINDEX: i32 = -10000; +pub const LUA_ENVIRONINDEX: i32 = -10001; +pub const LUA_GLOBALSINDEX: i32 = -10002; +pub const LUA_OK: u32 = 0; +pub const LUA_YIELD: u32 = 1; +pub const LUA_ERRRUN: u32 = 2; +pub const LUA_ERRSYNTAX: u32 = 3; +pub const LUA_ERRMEM: u32 = 4; +pub const LUA_ERRERR: u32 = 5; +pub const LUA_TNONE: i32 = -1; +pub const LUA_TNIL: u32 = 0; +pub const LUA_TBOOLEAN: u32 = 1; +pub const LUA_TLIGHTUSERDATA: u32 = 2; +pub const LUA_TNUMBER: u32 = 3; +pub const LUA_TSTRING: u32 = 4; +pub const LUA_TTABLE: u32 = 5; +pub const LUA_TFUNCTION: u32 = 6; +pub const LUA_TUSERDATA: u32 = 7; +pub const LUA_TTHREAD: u32 = 8; +pub const LUA_MINSTACK: u32 = 20; +pub const LUA_GCSTOP: u32 = 0; +pub const LUA_GCRESTART: u32 = 1; +pub const LUA_GCCOLLECT: u32 = 2; +pub const LUA_GCCOUNT: u32 = 3; +pub const LUA_GCCOUNTB: u32 = 4; +pub const LUA_GCSTEP: u32 = 5; +pub const LUA_GCSETPAUSE: u32 = 6; +pub const LUA_GCSETSTEPMUL: u32 = 7; +pub const LUA_GCISRUNNING: u32 = 9; +pub const LUA_HOOKCALL: u32 = 0; +pub const LUA_HOOKRET: u32 = 1; +pub const LUA_HOOKLINE: u32 = 2; +pub const LUA_HOOKCOUNT: u32 = 3; +pub const LUA_HOOKTAILRET: u32 = 4; +pub const LUA_MASKCALL: u32 = 1; +pub const LUA_MASKRET: u32 = 2; +pub const LUA_MASKLINE: u32 = 4; +pub const LUA_MASKCOUNT: u32 = 8; +pub const LUA_FILEHANDLE: &'static [u8; 6usize] = b"FILE*\0"; +pub const LUA_COLIBNAME: &'static [u8; 10usize] = b"coroutine\0"; +pub const LUA_MATHLIBNAME: &'static [u8; 5usize] = b"math\0"; +pub const LUA_STRLIBNAME: &'static [u8; 7usize] = b"string\0"; +pub const LUA_TABLIBNAME: &'static [u8; 6usize] = b"table\0"; +pub const LUA_IOLIBNAME: &'static [u8; 3usize] = b"io\0"; +pub const LUA_OSLIBNAME: &'static [u8; 3usize] = b"os\0"; +pub const LUA_LOADLIBNAME: &'static [u8; 8usize] = b"package\0"; +pub const LUA_DBLIBNAME: &'static [u8; 6usize] = b"debug\0"; +pub const LUA_BITLIBNAME: &'static [u8; 4usize] = b"bit\0"; +pub const LUA_JITLIBNAME: &'static [u8; 4usize] = b"jit\0"; +pub const LUA_FFILIBNAME: &'static [u8; 4usize] = b"ffi\0"; +pub const LUA_ERRFILE: u32 = 6; +pub const LUA_NOREF: i32 = -2; +pub const LUA_REFNIL: i32 = -1; +pub const LUAJIT_VERSION: &'static [u8; 19usize] = b"LuaJIT 2.1.0-beta3\0"; +pub const LUAJIT_VERSION_NUM: u32 = 20100; +pub const LUAJIT_COPYRIGHT: &'static [u8; 34usize] = b"Copyright (C) 2005-2017 Mike Pall\0"; +pub const LUAJIT_URL: &'static [u8; 19usize] = b"http://luajit.org/\0"; +pub const LUAJIT_MODE_MASK: u32 = 255; +pub const LUAJIT_MODE_OFF: u32 = 0; +pub const LUAJIT_MODE_ON: u32 = 256; +pub const LUAJIT_MODE_FLUSH: u32 = 512; +pub type va_list = __builtin_va_list; +#[repr(C)] +#[derive(Debug, Copy, Clone)] +/// +pub struct lua_State { + _unused: [u8; 0], +} +/// +pub type lua_CFunction = + ::core::option::Option libc::c_int>; +/// +pub type lua_Reader = ::core::option::Option< + unsafe extern "C" fn( + L: *mut lua_State, + ud: *mut libc::c_void, + sz: *mut usize, + ) -> *const libc::c_char, +>; +/// +pub type lua_Writer = ::core::option::Option< + unsafe extern "C" fn( + L: *mut lua_State, + p: *const libc::c_void, + sz: usize, + ud: *mut libc::c_void, + ) -> libc::c_int, +>; +/// +pub type lua_Alloc = ::core::option::Option< + unsafe extern "C" fn( + ud: *mut libc::c_void, + ptr: *mut libc::c_void, + osize: usize, + nsize: usize, + ) -> *mut libc::c_void, +>; +/// +pub type lua_Number = f64; +/// +pub type lua_Integer = isize; +extern "C" { + /// + pub fn lua_newstate(f: lua_Alloc, ud: *mut libc::c_void) -> *mut lua_State; +} +extern "C" { + /// + pub fn lua_close(L: *mut lua_State); +} +extern "C" { + /// + pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State; +} +extern "C" { + /// + pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction; +} +extern "C" { + /// + pub fn lua_gettop(L: *mut lua_State) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_settop(L: *mut lua_State, idx: libc::c_int); +} +extern "C" { + /// + pub fn lua_pushvalue(L: *mut lua_State, idx: libc::c_int); +} +extern "C" { + /// + pub fn lua_remove(L: *mut lua_State, idx: libc::c_int); +} +extern "C" { + /// + pub fn lua_insert(L: *mut lua_State, idx: libc::c_int); +} +extern "C" { + /// + pub fn lua_replace(L: *mut lua_State, idx: libc::c_int); +} +extern "C" { + /// + pub fn lua_checkstack(L: *mut lua_State, sz: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_xmove(from: *mut lua_State, to: *mut lua_State, n: libc::c_int); +} +extern "C" { + /// + pub fn lua_isnumber(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_isstring(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_iscfunction(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_isuserdata(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_type(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_typename(L: *mut lua_State, tp: libc::c_int) -> *const libc::c_char; +} +extern "C" { + /// + pub fn lua_equal(L: *mut lua_State, idx1: libc::c_int, idx2: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_rawequal(L: *mut lua_State, idx1: libc::c_int, idx2: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_lessthan(L: *mut lua_State, idx1: libc::c_int, idx2: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_tonumber(L: *mut lua_State, idx: libc::c_int) -> lua_Number; +} +extern "C" { + /// + pub fn lua_tointeger(L: *mut lua_State, idx: libc::c_int) -> lua_Integer; +} +extern "C" { + /// + pub fn lua_toboolean(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_tolstring( + L: *mut lua_State, + idx: libc::c_int, + len: *mut usize, + ) -> *const libc::c_char; +} +extern "C" { + /// + pub fn lua_objlen(L: *mut lua_State, idx: libc::c_int) -> usize; +} +extern "C" { + /// + pub fn lua_tocfunction(L: *mut lua_State, idx: libc::c_int) -> lua_CFunction; +} +extern "C" { + /// + pub fn lua_touserdata(L: *mut lua_State, idx: libc::c_int) -> *mut libc::c_void; +} +extern "C" { + /// + pub fn lua_tothread(L: *mut lua_State, idx: libc::c_int) -> *mut lua_State; +} +extern "C" { + /// + pub fn lua_topointer(L: *mut lua_State, idx: libc::c_int) -> *const libc::c_void; +} +extern "C" { + /// + pub fn lua_pushnil(L: *mut lua_State); +} +extern "C" { + /// + pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number); +} +extern "C" { + /// + pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer); +} +extern "C" { + /// + pub fn lua_pushlstring(L: *mut lua_State, s: *const libc::c_char, l: usize); +} +extern "C" { + /// + pub fn lua_pushstring(L: *mut lua_State, s: *const libc::c_char); +} +extern "C" { + /// + pub fn lua_pushvfstring( + L: *mut lua_State, + fmt: *const libc::c_char, + argp: va_list, + ) -> *const libc::c_char; +} +extern "C" { + /// + pub fn lua_pushfstring(L: *mut lua_State, fmt: *const libc::c_char, ...) + -> *const libc::c_char; +} +extern "C" { + /// + pub fn lua_pushcclosure(L: *mut lua_State, fn_: lua_CFunction, n: libc::c_int); +} +extern "C" { + /// + pub fn lua_pushboolean(L: *mut lua_State, b: libc::c_int); +} +extern "C" { + /// + pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut libc::c_void); +} +extern "C" { + /// + pub fn lua_pushthread(L: *mut lua_State) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_gettable(L: *mut lua_State, idx: libc::c_int); +} +extern "C" { + /// + pub fn lua_getfield(L: *mut lua_State, idx: libc::c_int, k: *const libc::c_char); +} +extern "C" { + /// + pub fn lua_rawget(L: *mut lua_State, idx: libc::c_int); +} +extern "C" { + /// + pub fn lua_rawgeti(L: *mut lua_State, idx: libc::c_int, n: libc::c_int); +} +extern "C" { + /// + pub fn lua_createtable(L: *mut lua_State, narr: libc::c_int, nrec: libc::c_int); +} +extern "C" { + /// + pub fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut libc::c_void; +} +extern "C" { + /// + pub fn lua_getmetatable(L: *mut lua_State, objindex: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_getfenv(L: *mut lua_State, idx: libc::c_int); +} +extern "C" { + /// + pub fn lua_settable(L: *mut lua_State, idx: libc::c_int); +} +extern "C" { + /// + pub fn lua_setfield(L: *mut lua_State, idx: libc::c_int, k: *const libc::c_char); +} +extern "C" { + /// + pub fn lua_rawset(L: *mut lua_State, idx: libc::c_int); +} +extern "C" { + /// + pub fn lua_rawseti(L: *mut lua_State, idx: libc::c_int, n: libc::c_int); +} +extern "C" { + /// + pub fn lua_setmetatable(L: *mut lua_State, objindex: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_setfenv(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_call(L: *mut lua_State, nargs: libc::c_int, nresults: libc::c_int); +} +extern "C" { + /// + pub fn lua_pcall( + L: *mut lua_State, + nargs: libc::c_int, + nresults: libc::c_int, + errfunc: libc::c_int, + ) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_cpcall(L: *mut lua_State, func: lua_CFunction, ud: *mut libc::c_void) + -> libc::c_int; +} +extern "C" { + /// + pub fn lua_load( + L: *mut lua_State, + reader: lua_Reader, + dt: *mut libc::c_void, + chunkname: *const libc::c_char, + ) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut libc::c_void) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_yield(L: *mut lua_State, nresults: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_resume(L: *mut lua_State, narg: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_status(L: *mut lua_State) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_gc(L: *mut lua_State, what: libc::c_int, data: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_error(L: *mut lua_State) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_next(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_concat(L: *mut lua_State, n: libc::c_int); +} +extern "C" { + /// + pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut libc::c_void) -> lua_Alloc; +} +extern "C" { + /// + pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut libc::c_void); +} +extern "C" { + /// + pub fn lua_setlevel(from: *mut lua_State, to: *mut lua_State); +} +/// +pub type lua_Hook = + ::core::option::Option; +extern "C" { + /// + pub fn lua_getstack(L: *mut lua_State, level: libc::c_int, ar: *mut lua_Debug) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_getinfo( + L: *mut lua_State, + what: *const libc::c_char, + ar: *mut lua_Debug, + ) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_getlocal( + L: *mut lua_State, + ar: *const lua_Debug, + n: libc::c_int, + ) -> *const libc::c_char; +} +extern "C" { + /// + pub fn lua_setlocal( + L: *mut lua_State, + ar: *const lua_Debug, + n: libc::c_int, + ) -> *const libc::c_char; +} +extern "C" { + /// + pub fn lua_getupvalue( + L: *mut lua_State, + funcindex: libc::c_int, + n: libc::c_int, + ) -> *const libc::c_char; +} +extern "C" { + /// + pub fn lua_setupvalue( + L: *mut lua_State, + funcindex: libc::c_int, + n: libc::c_int, + ) -> *const libc::c_char; +} +extern "C" { + /// + pub fn lua_sethook( + L: *mut lua_State, + func: lua_Hook, + mask: libc::c_int, + count: libc::c_int, + ) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_gethook(L: *mut lua_State) -> lua_Hook; +} +extern "C" { + /// + pub fn lua_gethookmask(L: *mut lua_State) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_gethookcount(L: *mut lua_State) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_upvalueid(L: *mut lua_State, idx: libc::c_int, n: libc::c_int) -> *mut libc::c_void; +} +extern "C" { + /// + pub fn lua_upvaluejoin( + L: *mut lua_State, + idx1: libc::c_int, + n1: libc::c_int, + idx2: libc::c_int, + n2: libc::c_int, + ); +} +extern "C" { + /// + pub fn lua_loadx( + L: *mut lua_State, + reader: lua_Reader, + dt: *mut libc::c_void, + chunkname: *const libc::c_char, + mode: *const libc::c_char, + ) -> libc::c_int; +} +extern "C" { + /// + pub fn lua_version(L: *mut lua_State) -> *const lua_Number; +} +extern "C" { + /// + pub fn lua_copy(L: *mut lua_State, fromidx: libc::c_int, toidx: libc::c_int); +} +extern "C" { + /// + pub fn lua_tonumberx( + L: *mut lua_State, + idx: libc::c_int, + isnum: *mut libc::c_int, + ) -> lua_Number; +} +extern "C" { + /// + pub fn lua_tointegerx( + L: *mut lua_State, + idx: libc::c_int, + isnum: *mut libc::c_int, + ) -> lua_Integer; +} +extern "C" { + /// + pub fn lua_isyieldable(L: *mut lua_State) -> libc::c_int; +} +#[repr(C)] +#[derive(Copy, Clone)] +/// +pub struct lua_Debug { + pub event: libc::c_int, + pub name: *const libc::c_char, + pub namewhat: *const libc::c_char, + pub what: *const libc::c_char, + pub source: *const libc::c_char, + pub currentline: libc::c_int, + pub nups: libc::c_int, + pub linedefined: libc::c_int, + pub lastlinedefined: libc::c_int, + pub short_src: [libc::c_char; 60usize], + pub i_ci: libc::c_int, +} +#[test] +fn bindgen_test_layout_lua_Debug() { + assert_eq!( + ::core::mem::size_of::(), + 120usize, + concat!("Size of: ", stringify!(lua_Debug)) + ); + assert_eq!( + ::core::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(lua_Debug)) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).event as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(lua_Debug), + "::", + stringify!(event) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).name as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(lua_Debug), + "::", + stringify!(name) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).namewhat as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(lua_Debug), + "::", + stringify!(namewhat) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).what as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(lua_Debug), + "::", + stringify!(what) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).source as *const _ as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(lua_Debug), + "::", + stringify!(source) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).currentline as *const _ as usize }, + 40usize, + concat!( + "Offset of field: ", + stringify!(lua_Debug), + "::", + stringify!(currentline) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).nups as *const _ as usize }, + 44usize, + concat!( + "Offset of field: ", + stringify!(lua_Debug), + "::", + stringify!(nups) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).linedefined as *const _ as usize }, + 48usize, + concat!( + "Offset of field: ", + stringify!(lua_Debug), + "::", + stringify!(linedefined) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).lastlinedefined as *const _ as usize }, + 52usize, + concat!( + "Offset of field: ", + stringify!(lua_Debug), + "::", + stringify!(lastlinedefined) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).short_src as *const _ as usize }, + 56usize, + concat!( + "Offset of field: ", + stringify!(lua_Debug), + "::", + stringify!(short_src) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).i_ci as *const _ as usize }, + 116usize, + concat!( + "Offset of field: ", + stringify!(lua_Debug), + "::", + stringify!(i_ci) + ) + ); +} +impl ::core::fmt::Debug for lua_Debug { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + write ! ( f , "lua_Debug {{ event: {:?}, name: {:?}, namewhat: {:?}, what: {:?}, source: {:?}, currentline: {:?}, nups: {:?}, linedefined: {:?}, lastlinedefined: {:?}, short_src: [...], i_ci: {:?} }}" , self . event , self . name , self . namewhat , self . what , self . source , self . currentline , self . nups , self . linedefined , self . lastlinedefined , self . i_ci ) + } +} +extern "C" { + /// + pub fn luaL_openlibs(L: *mut lua_State); +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +/// +pub struct luaL_Reg { + pub name: *const libc::c_char, + pub func: lua_CFunction, +} +#[test] +fn bindgen_test_layout_luaL_Reg() { + assert_eq!( + ::core::mem::size_of::(), + 16usize, + concat!("Size of: ", stringify!(luaL_Reg)) + ); + assert_eq!( + ::core::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(luaL_Reg)) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).name as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(luaL_Reg), + "::", + stringify!(name) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).func as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(luaL_Reg), + "::", + stringify!(func) + ) + ); +} +extern "C" { + /// + pub fn luaL_openlib( + L: *mut lua_State, + libname: *const libc::c_char, + l: *const luaL_Reg, + nup: libc::c_int, + ); +} +extern "C" { + /// + pub fn luaL_register(L: *mut lua_State, libname: *const libc::c_char, l: *const luaL_Reg); +} +extern "C" { + /// + pub fn luaL_getmetafield( + L: *mut lua_State, + obj: libc::c_int, + e: *const libc::c_char, + ) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_callmeta( + L: *mut lua_State, + obj: libc::c_int, + e: *const libc::c_char, + ) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_typerror( + L: *mut lua_State, + narg: libc::c_int, + tname: *const libc::c_char, + ) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_argerror( + L: *mut lua_State, + numarg: libc::c_int, + extramsg: *const libc::c_char, + ) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_checklstring( + L: *mut lua_State, + numArg: libc::c_int, + l: *mut usize, + ) -> *const libc::c_char; +} +extern "C" { + /// + pub fn luaL_optlstring( + L: *mut lua_State, + numArg: libc::c_int, + def: *const libc::c_char, + l: *mut usize, + ) -> *const libc::c_char; +} +extern "C" { + /// + pub fn luaL_checknumber(L: *mut lua_State, numArg: libc::c_int) -> lua_Number; +} +extern "C" { + /// + pub fn luaL_optnumber(L: *mut lua_State, nArg: libc::c_int, def: lua_Number) -> lua_Number; +} +extern "C" { + /// + pub fn luaL_checkinteger(L: *mut lua_State, numArg: libc::c_int) -> lua_Integer; +} +extern "C" { + /// + pub fn luaL_optinteger(L: *mut lua_State, nArg: libc::c_int, def: lua_Integer) -> lua_Integer; +} +extern "C" { + /// + pub fn luaL_checkstack(L: *mut lua_State, sz: libc::c_int, msg: *const libc::c_char); +} +extern "C" { + /// + pub fn luaL_checktype(L: *mut lua_State, narg: libc::c_int, t: libc::c_int); +} +extern "C" { + /// + pub fn luaL_checkany(L: *mut lua_State, narg: libc::c_int); +} +extern "C" { + /// + pub fn luaL_newmetatable(L: *mut lua_State, tname: *const libc::c_char) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_checkudata( + L: *mut lua_State, + ud: libc::c_int, + tname: *const libc::c_char, + ) -> *mut libc::c_void; +} +extern "C" { + /// + pub fn luaL_where(L: *mut lua_State, lvl: libc::c_int); +} +extern "C" { + /// + pub fn luaL_error(L: *mut lua_State, fmt: *const libc::c_char, ...) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_checkoption( + L: *mut lua_State, + narg: libc::c_int, + def: *const libc::c_char, + lst: *const *const libc::c_char, + ) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_ref(L: *mut lua_State, t: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_unref(L: *mut lua_State, t: libc::c_int, ref_: libc::c_int); +} +extern "C" { + /// + pub fn luaL_loadfile(L: *mut lua_State, filename: *const libc::c_char) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_loadbuffer( + L: *mut lua_State, + buff: *const libc::c_char, + sz: usize, + name: *const libc::c_char, + ) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_loadstring(L: *mut lua_State, s: *const libc::c_char) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_newstate() -> *mut lua_State; +} +extern "C" { + /// + pub fn luaL_gsub( + L: *mut lua_State, + s: *const libc::c_char, + p: *const libc::c_char, + r: *const libc::c_char, + ) -> *const libc::c_char; +} +extern "C" { + /// + pub fn luaL_findtable( + L: *mut lua_State, + idx: libc::c_int, + fname: *const libc::c_char, + szhint: libc::c_int, + ) -> *const libc::c_char; +} +extern "C" { + /// + pub fn luaL_fileresult( + L: *mut lua_State, + stat: libc::c_int, + fname: *const libc::c_char, + ) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_execresult(L: *mut lua_State, stat: libc::c_int) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_loadfilex( + L: *mut lua_State, + filename: *const libc::c_char, + mode: *const libc::c_char, + ) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_loadbufferx( + L: *mut lua_State, + buff: *const libc::c_char, + sz: usize, + name: *const libc::c_char, + mode: *const libc::c_char, + ) -> libc::c_int; +} +extern "C" { + /// + pub fn luaL_traceback( + L: *mut lua_State, + L1: *mut lua_State, + msg: *const libc::c_char, + level: libc::c_int, + ); +} +extern "C" { + /// + pub fn luaL_setfuncs(L: *mut lua_State, l: *const luaL_Reg, nup: libc::c_int); +} +extern "C" { + /// + pub fn luaL_pushmodule(L: *mut lua_State, modname: *const libc::c_char, sizehint: libc::c_int); +} +extern "C" { + /// + pub fn luaL_testudata( + L: *mut lua_State, + ud: libc::c_int, + tname: *const libc::c_char, + ) -> *mut libc::c_void; +} +extern "C" { + /// + pub fn luaL_setmetatable(L: *mut lua_State, tname: *const libc::c_char); +} +#[repr(C)] +#[derive(Copy, Clone)] +/// +pub struct luaL_Buffer { + pub p: *mut libc::c_char, + pub lvl: libc::c_int, + pub L: *mut lua_State, + pub buffer: [libc::c_char; 512usize], +} +#[test] +fn bindgen_test_layout_luaL_Buffer() { + assert_eq!( + ::core::mem::size_of::(), + 536usize, + concat!("Size of: ", stringify!(luaL_Buffer)) + ); + assert_eq!( + ::core::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(luaL_Buffer)) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).p as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(luaL_Buffer), + "::", + stringify!(p) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).lvl as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(luaL_Buffer), + "::", + stringify!(lvl) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).L as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(luaL_Buffer), + "::", + stringify!(L) + ) + ); + assert_eq!( + unsafe { &(*(::core::ptr::null::())).buffer as *const _ as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(luaL_Buffer), + "::", + stringify!(buffer) + ) + ); +} +impl ::core::fmt::Debug for luaL_Buffer { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + write!( + f, + "luaL_Buffer {{ p: {:?}, lvl: {:?}, L: {:?}, buffer: [...] }}", + self.p, self.lvl, self.L + ) + } +} +extern "C" { + /// + pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Buffer); +} +extern "C" { + /// + pub fn luaL_prepbuffer(B: *mut luaL_Buffer) -> *mut libc::c_char; +} +extern "C" { + /// + pub fn luaL_addlstring(B: *mut luaL_Buffer, s: *const libc::c_char, l: usize); +} +extern "C" { + /// + pub fn luaL_addstring(B: *mut luaL_Buffer, s: *const libc::c_char); +} +extern "C" { + /// + pub fn luaL_addvalue(B: *mut luaL_Buffer); +} +extern "C" { + /// + pub fn luaL_pushresult(B: *mut luaL_Buffer); +} +pub const LUAJIT_MODE_ENGINE: _bindgen_ty_1 = 0; +pub const LUAJIT_MODE_DEBUG: _bindgen_ty_1 = 1; +pub const LUAJIT_MODE_FUNC: _bindgen_ty_1 = 2; +pub const LUAJIT_MODE_ALLFUNC: _bindgen_ty_1 = 3; +pub const LUAJIT_MODE_ALLSUBFUNC: _bindgen_ty_1 = 4; +pub const LUAJIT_MODE_TRACE: _bindgen_ty_1 = 5; +pub const LUAJIT_MODE_WRAPCFUNC: _bindgen_ty_1 = 16; +pub const LUAJIT_MODE_MAX: _bindgen_ty_1 = 17; +pub type _bindgen_ty_1 = i32; +extern "C" { + /// + pub fn luaJIT_setmode(L: *mut lua_State, idx: libc::c_int, mode: libc::c_int) -> libc::c_int; +} +pub type luaJIT_profile_callback = ::core::option::Option< + unsafe extern "C" fn( + data: *mut libc::c_void, + L: *mut lua_State, + samples: libc::c_int, + vmstate: libc::c_int, + ), +>; +extern "C" { + /// + pub fn luaJIT_profile_start( + L: *mut lua_State, + mode: *const libc::c_char, + cb: luaJIT_profile_callback, + data: *mut libc::c_void, + ); +} +extern "C" { + /// + pub fn luaJIT_profile_stop(L: *mut lua_State); +} +extern "C" { + /// + pub fn luaJIT_profile_dumpstack( + L: *mut lua_State, + fmt: *const libc::c_char, + depth: libc::c_int, + len: *mut usize, + ) -> *const libc::c_char; +} +extern "C" { + /// + pub fn luaJIT_version_2_1_0_beta3(); +} +pub type __builtin_va_list = *mut libc::c_char; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e0d799f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,163 @@ +#![no_std] +#![allow(non_snake_case)] +#![allow(non_camel_case_types)] + +//! # LuaJIT 2.1 +//! +//! +//! +//! +//! +//! ## Performance considerations +//! +//! The _Not Yet Implemented_ guide documents which language features will be JIT compiled +//! into native machine code. +//! +//! + +mod ffi; +pub use ffi::*; + +use core::ptr; + +// These are defined as macros + +/// +#[inline] +pub unsafe fn lua_pop(L: *mut lua_State, idx: libc::c_int) { + lua_settop(L, -(idx) - 1) +} + +/// +#[inline] +pub unsafe fn lua_newtable(L: *mut lua_State) { + lua_createtable(L, 0, 0) +} + +/// +#[inline] +pub unsafe fn lua_register(L: *mut lua_State, name: *const libc::c_char, f: lua_CFunction) { + lua_pushcfunction(L, f); + lua_setglobal(L, name); +} + +/// +#[inline] +pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) { + lua_pushcclosure(L, f, 0); +} + +/// +#[inline] +pub unsafe fn lua_strlen(L: *mut lua_State, idx: libc::c_int) -> usize { + lua_objlen(L, idx) +} + +/// +#[inline] +pub unsafe fn lua_isfunction(L: *mut lua_State, idx: libc::c_int) -> libc::c_int { + (lua_type(L, idx) == LUA_TFUNCTION as i32) as i32 +} + +/// +#[inline] +pub unsafe fn lua_istable(L: *mut lua_State, idx: libc::c_int) -> libc::c_int { + (lua_type(L, idx) == LUA_TTABLE as i32) as i32 +} + +/// +#[inline] +pub unsafe fn lua_islightuserdata(L: *mut lua_State, idx: libc::c_int) -> libc::c_int { + (lua_type(L, idx) == LUA_TLIGHTUSERDATA as i32) as i32 +} + +/// +#[inline] +pub unsafe fn lua_isnil(L: *mut lua_State, idx: libc::c_int) -> libc::c_int { + (lua_type(L, idx) == LUA_TNIL as i32) as i32 +} + +/// +#[inline] +pub unsafe fn lua_isboolean(L: *mut lua_State, idx: libc::c_int) -> libc::c_int { + (lua_type(L, idx) == LUA_TBOOLEAN as i32) as i32 +} + +/// +#[inline] +pub unsafe fn lua_isthread(L: *mut lua_State, idx: libc::c_int) -> libc::c_int { + (lua_type(L, idx) == LUA_TTHREAD as i32) as i32 +} + +/// +#[inline] +pub unsafe fn lua_isnone(L: *mut lua_State, idx: libc::c_int) -> libc::c_int { + (lua_type(L, idx) == LUA_TNONE as i32) as i32 +} + +/// +#[inline] +pub unsafe fn lua_isnoneornil(L: *mut lua_State, idx: libc::c_int) -> libc::c_int { + (lua_type(L, idx) <= 0) as i32 +} + +/// +#[inline] +pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &str) { + lua_pushlstring(L, s.as_ptr() as _, s.len() as _); +} + +/// +#[inline] +pub unsafe fn lua_setglobal(L: *mut lua_State, k: *const libc::c_char) { + lua_setfield(L, LUA_GLOBALSINDEX, k); +} + +/// +#[inline] +pub unsafe fn lua_getglobal(L: *mut lua_State, k: *const libc::c_char) { + lua_getfield(L, LUA_GLOBALSINDEX, k) +} + +/// +#[inline] +pub unsafe fn lua_tostring(L: *mut lua_State, idx: libc::c_int) -> *const libc::c_char { + lua_tolstring(L, idx, ptr::null_mut()) +} + +// Additional compatibility items that are defined as macros + +/// `luaL_newstate()` +#[inline] +#[deprecated(since = "Lua 5.1", note = "replace with `luaL_newstate()`")] +pub unsafe fn lua_open() -> *mut lua_State { + luaL_newstate() +} + +/// `lua_pushvalue(L, LUA_REGISTRYINDEX)` +#[inline] +#[deprecated( + since = "Lua 5.1", + note = "replace with `lua_pushvalue(L, LUA_REGISTRYINDEX)`" +)] +pub unsafe fn lua_getregistry(L: *mut lua_State) { + lua_pushvalue(L, LUA_REGISTRYINDEX) +} + +/// `lua_gc(L, LUA_GCCOUNT as _, 0)` +#[inline] +#[deprecated( + since = "Lua 5.1", + note = "replace with `lua_gc(L, LUA_GCCOUNT as _, 0)`" +)] +pub unsafe fn lua_getgccount(L: *mut lua_State) -> libc::c_int { + lua_gc(L, LUA_GCCOUNT as _, 0) +} + +/// `lua_Reader` +#[deprecated(since = "Lua 5.1", note = "replace with `lua_Reader`")] +pub type lua_Chunkreader = lua_Reader; + +/// `lua_Writer` +#[deprecated(since = "Lua 5.1", note = "replace with `lua_Writer`")] +pub type lua_Chunkwriter = lua_Writer; diff --git a/tests/test.rs b/tests/test.rs new file mode 100644 index 0000000..e737156 --- /dev/null +++ b/tests/test.rs @@ -0,0 +1,40 @@ +use luajit_sys as sys; + +#[test] +fn run_script() { + use std::ffi::CStr; + use std::ptr; + + unsafe { + let lua = sys::luaL_newstate(); + assert_ne!(lua, ptr::null_mut()); + sys::luaL_openlibs(lua); + let script_data = b"return 1 + 2"; + let script_name = b"run_script\0"; + let mut error = sys::luaL_loadbuffer( + lua, + script_data.as_ptr() as _, + script_data.len() as _, + script_name.as_ptr() as _, + ); + if error != 0 { + eprintln!("luaL_loadbuffer failed"); + } else { + error = sys::lua_pcall(lua, 0, 1, 0); + if error != 0 { + eprintln!("lua_pcall failed"); + } + } + + let idx = sys::lua_gettop(lua); + println!("lua_gettop = {}", idx); + + let s = sys::lua_tostring(lua, idx); + assert_ne!(s, ptr::null(), "lua_tostring returned null"); + + let result = CStr::from_ptr(s).to_string_lossy().to_string(); + sys::lua_close(lua); + + assert_eq!("3", result); + } +} From dd2810e855ab4baf325a90a37cbef1d6ce1204ff Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Thu, 15 Aug 2019 19:28:35 -0400 Subject: [PATCH 004/335] Add azure-pipelines config --- .azure-pipelines.yml | 47 +++++++++++++++++++++++++++++++++++++++ ci/azure-install-rust.yml | 33 +++++++++++++++++++++++++++ ci/azure-test-all.yml | 19 ++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 .azure-pipelines.yml create mode 100644 ci/azure-install-rust.yml create mode 100644 ci/azure-test-all.yml diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml new file mode 100644 index 0000000..62ff799 --- /dev/null +++ b/.azure-pipelines.yml @@ -0,0 +1,47 @@ +trigger: + - master + +pr: + branches: + include: + - master + +jobs: + - job: Windows + pool: + vmImage: vs2017-win2016 + steps: + - template: ci/azure-install-rust.yml + - template: ci/azure-test-all.yml + strategy: + matrix: + stable: + TOOLCHAIN: stable + nightly: + TOOLCHAIN: nightly + + - job: Linux + pool: + vmImage: ubuntu-16.04 + steps: + - template: ci/azure-install-rust.yml + - template: ci/azure-test-all.yml + strategy: + matrix: + stable: + TOOLCHAIN: stable + nightly: + TOOLCHAIN: nightly + + - job: MacOS + pool: + vmImage: macOS-10.14 + steps: + - template: ci/azure-install-rust.yml + - template: ci/azure-test-all.yml + strategy: + matrix: + stable: + TOOLCHAIN: stable + nightly: + TOOLCHAIN: nightly \ No newline at end of file diff --git a/ci/azure-install-rust.yml b/ci/azure-install-rust.yml new file mode 100644 index 0000000..b304a14 --- /dev/null +++ b/ci/azure-install-rust.yml @@ -0,0 +1,33 @@ +steps: + - bash: | + set -e -x + curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $TOOLCHAIN + source $HOME/.cargo/env + echo "##vso[task.prependpath]$HOME/.cargo/bin" + rustup --version + displayName: Install rustup + condition: eq(variables['Agent.OS'], 'Darwin') +# - script: | +# echo %TOOLCHAIN% +# curl -sSf -o rustup-init.exe https://win.rustup.rs +# rustup-init.exe -v -y --default-toolchain %TOOLCHAIN% +# echo ##vso[task.prependpath]%USERPROFILE%\.cargo\bin +# rustup default %TOOLCHAIN% +# rustup component add rustfmt +# displayName: Install rust (windows) +# condition: eq(variables['Agent.OS'], 'Windows_NT') + - bash: | + set -x + rustup --version + rustup component remove --toolchain $TOOLCHAIN rust-docs || true + rustup default $TOOLCHAIN + rustup update --no-self-update $TOOLCHAIN + rustup toolchain install stable + rustup component add rustfmt --toolchain stable + displayName: Configure rust + - bash: | + set -x + rustc -Vv + cargo -Vv + cargo +stable fmt --version + displayName: Query rustc, cargo, and rustfmt versions \ No newline at end of file diff --git a/ci/azure-test-all.yml b/ci/azure-test-all.yml new file mode 100644 index 0000000..e2e3f4a --- /dev/null +++ b/ci/azure-test-all.yml @@ -0,0 +1,19 @@ +steps: + - bash: | + set -e -x + cargo +stable fmt --all -- --check + displayName: Check formatting + - bash: | + set -e -x + cargo test --no-run + displayName: Build everything + env: + RUST_BACKTRACE: 1 + CARGO_INCREMENTAL: 0 + - bash: | + set -e -x + cargo test + displayName: Run unit tests + env: + RUST_BACKTRACE: 1 + CARGO_INCREMENTAL: 0 From db8d666c8af6d9a0c3d34baadf4b0790a4d5de56 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Thu, 15 Aug 2019 19:32:01 -0400 Subject: [PATCH 005/335] Fix path separator --- build.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.rs b/build.rs index d96daef..d77dd1a 100644 --- a/build.rs +++ b/build.rs @@ -7,15 +7,15 @@ use std::process::{Command, Stdio}; fn main() { let target = env::var("TARGET").unwrap(); - let luajit_dir = format!("{}\\luajit", env!("CARGO_MANIFEST_DIR")); + let luajit_dir = format!("{}/luajit", env!("CARGO_MANIFEST_DIR")); let out_dir = env::var("OUT_DIR").unwrap(); - let src_dir = format!("{}\\luajit\\src", out_dir); + let src_dir = format!("{}/luajit/src", out_dir); if cfg!(target_env = "msvc") { - let lib_path = format!("{}\\lua51.lib", &src_dir); + let lib_path = format!("{}/lua51.lib", &src_dir); if !std::fs::metadata(&lib_path).is_ok() { let cl_exe: cc::Tool = cc::windows_registry::find_tool(&target, "cl.exe").unwrap(); - let msvsbuild_bat = format!("{}\\msvcbuild.bat", &src_dir); + let msvsbuild_bat = format!("{}/msvcbuild.bat", &src_dir); let mut copy_options = CopyOptions::new(); copy_options.overwrite = true; @@ -44,7 +44,7 @@ fn main() { println!("cargo:rustc-link-search=native={}", src_dir); println!("cargo:rustc-link-lib=static=lua51"); } else { - let lib_path = format!("{}\\luajit.a", &src_dir); + let lib_path = format!("{}/luajit.a", &src_dir); if !std::fs::metadata(&lib_path).is_ok() { let mut copy_options = CopyOptions::new(); copy_options.overwrite = true; From 64b97a3b197e9cd1f107f1a719ac43fb2d440450 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Thu, 15 Aug 2019 19:33:49 -0400 Subject: [PATCH 006/335] Debug pipeline --- build.rs | 12 ++++++++++-- ci/azure-test-all.yml | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/build.rs b/build.rs index d77dd1a..2ff8841 100644 --- a/build.rs +++ b/build.rs @@ -11,17 +11,24 @@ fn main() { let out_dir = env::var("OUT_DIR").unwrap(); let src_dir = format!("{}/luajit/src", out_dir); + dbg!(&luajit_dir); + dbg!(&out_dir); + dbg!(&src_dir); + if cfg!(target_env = "msvc") { let lib_path = format!("{}/lua51.lib", &src_dir); + dbg!(&lib_path); if !std::fs::metadata(&lib_path).is_ok() { let cl_exe: cc::Tool = cc::windows_registry::find_tool(&target, "cl.exe").unwrap(); - let msvsbuild_bat = format!("{}/msvcbuild.bat", &src_dir); + let msvcbuild_bat = format!("{}/msvcbuild.bat", &src_dir); + + dbg!(&msvcbuild_bat); let mut copy_options = CopyOptions::new(); copy_options.overwrite = true; dir::copy(&luajit_dir, &out_dir, ©_options).unwrap(); - let mut buildcmd = Command::new(msvsbuild_bat); + let mut buildcmd = Command::new(msvcbuild_bat); for (name, value) in cl_exe.env() { buildcmd.env(name, value); } @@ -45,6 +52,7 @@ fn main() { println!("cargo:rustc-link-lib=static=lua51"); } else { let lib_path = format!("{}/luajit.a", &src_dir); + dbg!(&lib_path); if !std::fs::metadata(&lib_path).is_ok() { let mut copy_options = CopyOptions::new(); copy_options.overwrite = true; diff --git a/ci/azure-test-all.yml b/ci/azure-test-all.yml index e2e3f4a..738bdba 100644 --- a/ci/azure-test-all.yml +++ b/ci/azure-test-all.yml @@ -17,3 +17,8 @@ steps: env: RUST_BACKTRACE: 1 CARGO_INCREMENTAL: 0 + - bash: | + pwd + find ./target + displayName: List files in ./target + condition: always() From 7c7cb2b56ddc72fa625dcc06bcf86bdc8b1d4314 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Thu, 15 Aug 2019 20:26:56 -0400 Subject: [PATCH 007/335] Checkout submodules in azure-pipelines --- ci/azure-test-all.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/azure-test-all.yml b/ci/azure-test-all.yml index 738bdba..7c08c69 100644 --- a/ci/azure-test-all.yml +++ b/ci/azure-test-all.yml @@ -1,4 +1,6 @@ steps: + - checkout: self + submodules: true - bash: | set -e -x cargo +stable fmt --all -- --check From 4cd8b358d73de0f38301ea3cb5aa68e952b3c37e Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Thu, 15 Aug 2019 20:49:42 -0400 Subject: [PATCH 008/335] Set BUILDMODE=static in Makefile --- build.rs | 5 +- etc/Makefile | 721 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 724 insertions(+), 2 deletions(-) create mode 100644 etc/Makefile diff --git a/build.rs b/build.rs index 2ff8841..7c4c6f9 100644 --- a/build.rs +++ b/build.rs @@ -1,8 +1,8 @@ use cc; use fs_extra::dir; use fs_extra::dir::CopyOptions; -use std::env; use std::process::{Command, Stdio}; +use std::{env, fs}; fn main() { let target = env::var("TARGET").unwrap(); @@ -27,6 +27,7 @@ fn main() { let mut copy_options = CopyOptions::new(); copy_options.overwrite = true; dir::copy(&luajit_dir, &out_dir, ©_options).unwrap(); + fs::copy(format!("etc/Makefile"), format!("{}/Makefile", &src_dir)).unwrap(); let mut buildcmd = Command::new(msvcbuild_bat); for (name, value) in cl_exe.env() { @@ -51,7 +52,7 @@ fn main() { println!("cargo:rustc-link-search=native={}", src_dir); println!("cargo:rustc-link-lib=static=lua51"); } else { - let lib_path = format!("{}/luajit.a", &src_dir); + let lib_path = format!("{}/libluajit.a", &src_dir); dbg!(&lib_path); if !std::fs::metadata(&lib_path).is_ok() { let mut copy_options = CopyOptions::new(); diff --git a/etc/Makefile b/etc/Makefile new file mode 100644 index 0000000..8f3cf60 --- /dev/null +++ b/etc/Makefile @@ -0,0 +1,721 @@ +############################################################################## +# LuaJIT Makefile. Requires GNU Make. +# +# Please read doc/install.html before changing any variables! +# +# Suitable for POSIX platforms (Linux, *BSD, OSX etc.). +# Also works with MinGW and Cygwin on Windows. +# Please check msvcbuild.bat for building with MSVC on Windows. +# +# Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h +############################################################################## + +MAJVER= 2 +MINVER= 1 +RELVER= 0 +ABIVER= 5.1 +NODOTABIVER= 51 + +############################################################################## +############################# COMPILER OPTIONS ############################# +############################################################################## +# These options mainly affect the speed of the JIT compiler itself, not the +# speed of the JIT-compiled code. Turn any of the optional settings on by +# removing the '#' in front of them. Make sure you force a full recompile +# with "make clean", followed by "make" if you change any options. +# +DEFAULT_CC = gcc +# +# LuaJIT builds as a native 32 or 64 bit binary by default. +CC= $(DEFAULT_CC) +# +# Use this if you want to force a 32 bit build on a 64 bit multilib OS. +#CC= $(DEFAULT_CC) -m32 +# +# Since the assembler part does NOT maintain a frame pointer, it's pointless +# to slow down the C part by not omitting it. Debugging, tracebacks and +# unwinding are not affected -- the assembler part has frame unwind +# information and GCC emits it where needed (x64) or with -g (see CCDEBUG). +CCOPT= -O2 -fomit-frame-pointer +# Use this if you want to generate a smaller binary (but it's slower): +#CCOPT= -Os -fomit-frame-pointer +# Note: it's no longer recommended to use -O3 with GCC 4.x. +# The I-Cache bloat usually outweighs the benefits from aggressive inlining. +# +# Target-specific compiler options: +# +# x86/x64 only: For GCC 4.2 or higher and if you don't intend to distribute +# the binaries to a different machine you could also use: -march=native +# +CCOPT_x86= -march=i686 -msse -msse2 -mfpmath=sse +CCOPT_x64= +CCOPT_arm= +CCOPT_arm64= +CCOPT_ppc= +CCOPT_mips= +# +CCDEBUG= +# Uncomment the next line to generate debug information: +#CCDEBUG= -g +# +CCWARN= -Wall +# Uncomment the next line to enable more warnings: +#CCWARN+= -Wextra -Wdeclaration-after-statement -Wredundant-decls -Wshadow -Wpointer-arith +# +############################################################################## + +############################################################################## +################################ BUILD MODE ################################ +############################################################################## +# The default build mode is mixed mode on POSIX. On Windows this is the same +# as dynamic mode. +# +# Mixed mode creates a static + dynamic library and a statically linked luajit. +#BUILDMODE= mixed +# +# Static mode creates a static library and a statically linked luajit. +BUILDMODE= static +# +# Dynamic mode creates a dynamic library and a dynamically linked luajit. +# Note: this executable will only run when the library is installed! +#BUILDMODE= dynamic +# +############################################################################## + +############################################################################## +################################# FEATURES ################################# +############################################################################## +# Enable/disable these features as needed, but make sure you force a full +# recompile with "make clean", followed by "make". +XCFLAGS= +# +# Permanently disable the FFI extension to reduce the size of the LuaJIT +# executable. But please consider that the FFI library is compiled-in, +# but NOT loaded by default. It only allocates any memory, if you actually +# make use of it. +#XCFLAGS+= -DLUAJIT_DISABLE_FFI +# +# Features from Lua 5.2 that are unlikely to break existing code are +# enabled by default. Some other features that *might* break some existing +# code (e.g. __pairs or os.execute() return values) can be enabled here. +# Note: this does not provide full compatibility with Lua 5.2 at this time. +#XCFLAGS+= -DLUAJIT_ENABLE_LUA52COMPAT +# +# Disable the JIT compiler, i.e. turn LuaJIT into a pure interpreter. +#XCFLAGS+= -DLUAJIT_DISABLE_JIT +# +# Some architectures (e.g. PPC) can use either single-number (1) or +# dual-number (2) mode. Uncomment one of these lines to override the +# default mode. Please see LJ_ARCH_NUMMODE in lj_arch.h for details. +#XCFLAGS+= -DLUAJIT_NUMMODE=1 +#XCFLAGS+= -DLUAJIT_NUMMODE=2 +# +# Enable GC64 mode for x64. +#XCFLAGS+= -DLUAJIT_ENABLE_GC64 +# +############################################################################## + +############################################################################## +############################ DEBUGGING SUPPORT ############################# +############################################################################## +# Enable these options as needed, but make sure you force a full recompile +# with "make clean", followed by "make". +# Note that most of these are NOT suitable for benchmarking or release mode! +# +# Use the system provided memory allocator (realloc) instead of the +# bundled memory allocator. This is slower, but sometimes helpful for +# debugging. This option cannot be enabled on x64 without GC64, since +# realloc usually doesn't return addresses in the right address range. +# OTOH this option is mandatory for Valgrind's memcheck tool on x64 and +# the only way to get useful results from it for all other architectures. +#XCFLAGS+= -DLUAJIT_USE_SYSMALLOC +# +# This define is required to run LuaJIT under Valgrind. The Valgrind +# header files must be installed. You should enable debug information, too. +# Use --suppressions=lj.supp to avoid some false positives. +#XCFLAGS+= -DLUAJIT_USE_VALGRIND +# +# This is the client for the GDB JIT API. GDB 7.0 or higher is required +# to make use of it. See lj_gdbjit.c for details. Enabling this causes +# a non-negligible overhead, even when not running under GDB. +#XCFLAGS+= -DLUAJIT_USE_GDBJIT +# +# Turn on assertions for the Lua/C API to debug problems with lua_* calls. +# This is rather slow -- use only while developing C libraries/embeddings. +#XCFLAGS+= -DLUA_USE_APICHECK +# +# Turn on assertions for the whole LuaJIT VM. This significantly slows down +# everything. Use only if you suspect a problem with LuaJIT itself. +#XCFLAGS+= -DLUA_USE_ASSERT +# +############################################################################## +# You probably don't need to change anything below this line! +############################################################################## + +############################################################################## +# Host system detection. +############################################################################## + +ifeq (Windows,$(findstring Windows,$(OS))$(MSYSTEM)$(TERM)) + HOST_SYS= Windows + HOST_RM= del +else + HOST_SYS:= $(shell uname -s) + ifneq (,$(findstring MINGW,$(HOST_SYS))) + HOST_SYS= Windows + HOST_MSYS= mingw + endif + ifneq (,$(findstring MSYS,$(HOST_SYS))) + HOST_SYS= Windows + HOST_MSYS= mingw + endif + ifneq (,$(findstring CYGWIN,$(HOST_SYS))) + HOST_SYS= Windows + HOST_MSYS= cygwin + endif +endif + +############################################################################## +# Flags and options for host and target. +############################################################################## + +# You can override the following variables at the make command line: +# CC HOST_CC STATIC_CC DYNAMIC_CC +# CFLAGS HOST_CFLAGS TARGET_CFLAGS +# LDFLAGS HOST_LDFLAGS TARGET_LDFLAGS TARGET_SHLDFLAGS +# LIBS HOST_LIBS TARGET_LIBS +# CROSS HOST_SYS TARGET_SYS TARGET_FLAGS +# +# Cross-compilation examples: +# make HOST_CC="gcc -m32" CROSS=i586-mingw32msvc- TARGET_SYS=Windows +# make HOST_CC="gcc -m32" CROSS=powerpc-linux-gnu- + +ASOPTIONS= $(CCOPT) $(CCWARN) $(XCFLAGS) $(CFLAGS) +CCOPTIONS= $(CCDEBUG) $(ASOPTIONS) +LDOPTIONS= $(CCDEBUG) $(LDFLAGS) + +HOST_CC= $(CC) +HOST_RM?= rm -f +# If left blank, minilua is built and used. You can supply an installed +# copy of (plain) Lua 5.1 or 5.2, plus Lua BitOp. E.g. with: HOST_LUA=lua +HOST_LUA= + +HOST_XCFLAGS= -I. +HOST_XLDFLAGS= +HOST_XLIBS= +HOST_ACFLAGS= $(CCOPTIONS) $(HOST_XCFLAGS) $(TARGET_ARCH) $(HOST_CFLAGS) +HOST_ALDFLAGS= $(LDOPTIONS) $(HOST_XLDFLAGS) $(HOST_LDFLAGS) +HOST_ALIBS= $(HOST_XLIBS) $(LIBS) $(HOST_LIBS) + +STATIC_CC = $(CROSS)$(CC) +DYNAMIC_CC = $(CROSS)$(CC) -fPIC +TARGET_CC= $(STATIC_CC) +TARGET_STCC= $(STATIC_CC) +TARGET_DYNCC= $(DYNAMIC_CC) +TARGET_LD= $(CROSS)$(CC) +TARGET_AR= $(CROSS)ar rcus +TARGET_STRIP= $(CROSS)strip + +TARGET_LIBPATH= $(or $(PREFIX),/usr/local)/$(or $(MULTILIB),lib) +TARGET_SONAME= libluajit-$(ABIVER).so.$(MAJVER) +TARGET_DYLIBNAME= libluajit-$(ABIVER).$(MAJVER).dylib +TARGET_DYLIBPATH= $(TARGET_LIBPATH)/$(TARGET_DYLIBNAME) +TARGET_DLLNAME= lua$(NODOTABIVER).dll +TARGET_XSHLDFLAGS= -shared -fPIC -Wl,-soname,$(TARGET_SONAME) +TARGET_DYNXLDOPTS= + +TARGET_LFSFLAGS= -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE +TARGET_XCFLAGS= $(TARGET_LFSFLAGS) -U_FORTIFY_SOURCE +TARGET_XLDFLAGS= +TARGET_XLIBS= -lm +TARGET_TCFLAGS= $(CCOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS) +TARGET_ACFLAGS= $(CCOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS) +TARGET_ASFLAGS= $(ASOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS) +TARGET_ALDFLAGS= $(LDOPTIONS) $(TARGET_XLDFLAGS) $(TARGET_FLAGS) $(TARGET_LDFLAGS) +TARGET_ASHLDFLAGS= $(LDOPTIONS) $(TARGET_XSHLDFLAGS) $(TARGET_FLAGS) $(TARGET_SHLDFLAGS) +TARGET_ALIBS= $(TARGET_XLIBS) $(LIBS) $(TARGET_LIBS) + +TARGET_TESTARCH=$(shell $(TARGET_CC) $(TARGET_TCFLAGS) -E lj_arch.h -dM) +ifneq (,$(findstring LJ_TARGET_X64 ,$(TARGET_TESTARCH))) + TARGET_LJARCH= x64 +else +ifneq (,$(findstring LJ_TARGET_X86 ,$(TARGET_TESTARCH))) + TARGET_LJARCH= x86 +else +ifneq (,$(findstring LJ_TARGET_ARM ,$(TARGET_TESTARCH))) + TARGET_LJARCH= arm +else +ifneq (,$(findstring LJ_TARGET_ARM64 ,$(TARGET_TESTARCH))) + ifneq (,$(findstring __AARCH64EB__ ,$(TARGET_TESTARCH))) + TARGET_ARCH= -D__AARCH64EB__=1 + endif + TARGET_LJARCH= arm64 +else +ifneq (,$(findstring LJ_TARGET_PPC ,$(TARGET_TESTARCH))) + ifneq (,$(findstring LJ_LE 1,$(TARGET_TESTARCH))) + TARGET_ARCH= -DLJ_ARCH_ENDIAN=LUAJIT_LE + else + TARGET_ARCH= -DLJ_ARCH_ENDIAN=LUAJIT_BE + endif + TARGET_LJARCH= ppc +else +ifneq (,$(findstring LJ_TARGET_MIPS ,$(TARGET_TESTARCH))) + ifneq (,$(findstring MIPSEL ,$(TARGET_TESTARCH))) + TARGET_ARCH= -D__MIPSEL__=1 + endif + ifneq (,$(findstring LJ_TARGET_MIPS64 ,$(TARGET_TESTARCH))) + TARGET_LJARCH= mips64 + else + TARGET_LJARCH= mips + endif +else + $(error Unsupported target architecture) +endif +endif +endif +endif +endif +endif + +ifneq (,$(findstring LJ_TARGET_PS3 1,$(TARGET_TESTARCH))) + TARGET_SYS= PS3 + TARGET_ARCH+= -D__CELLOS_LV2__ + TARGET_XCFLAGS+= -DLUAJIT_USE_SYSMALLOC + TARGET_XLIBS+= -lpthread +endif + +TARGET_XCFLAGS+= $(CCOPT_$(TARGET_LJARCH)) +TARGET_ARCH+= $(patsubst %,-DLUAJIT_TARGET=LUAJIT_ARCH_%,$(TARGET_LJARCH)) + +ifneq (,$(PREFIX)) +ifneq (/usr/local,$(PREFIX)) + TARGET_XCFLAGS+= -DLUA_ROOT=\"$(PREFIX)\" + ifneq (/usr,$(PREFIX)) + TARGET_DYNXLDOPTS= -Wl,-rpath,$(TARGET_LIBPATH) + endif +endif +endif +ifneq (,$(MULTILIB)) + TARGET_XCFLAGS+= -DLUA_MULTILIB=\"$(MULTILIB)\" +endif +ifneq (,$(LMULTILIB)) + TARGET_XCFLAGS+= -DLUA_LMULTILIB=\"$(LMULTILIB)\" +endif + +############################################################################## +# Target system detection. +############################################################################## + +TARGET_SYS?= $(HOST_SYS) +ifeq (Windows,$(TARGET_SYS)) + TARGET_STRIP+= --strip-unneeded + TARGET_XSHLDFLAGS= -shared + TARGET_DYNXLDOPTS= +else + TARGET_AR+= 2>/dev/null +ifeq (,$(shell $(TARGET_CC) -o /dev/null -c -x c /dev/null -fno-stack-protector 2>/dev/null || echo 1)) + TARGET_XCFLAGS+= -fno-stack-protector +endif +ifeq (Darwin,$(TARGET_SYS)) + ifeq (,$(MACOSX_DEPLOYMENT_TARGET)) + export MACOSX_DEPLOYMENT_TARGET=10.4 + endif + TARGET_STRIP+= -x + TARGET_XSHLDFLAGS= -dynamiclib -single_module -undefined dynamic_lookup -fPIC + TARGET_DYNXLDOPTS= + TARGET_XSHLDFLAGS+= -install_name $(TARGET_DYLIBPATH) -compatibility_version $(MAJVER).$(MINVER) -current_version $(MAJVER).$(MINVER).$(RELVER) + ifeq (x64,$(TARGET_LJARCH)) + TARGET_XLDFLAGS+= -pagezero_size 10000 -image_base 100000000 + TARGET_XSHLDFLAGS+= -image_base 7fff04c4a000 + endif +else +ifeq (iOS,$(TARGET_SYS)) + TARGET_STRIP+= -x + TARGET_XSHLDFLAGS= -dynamiclib -single_module -undefined dynamic_lookup -fPIC + TARGET_DYNXLDOPTS= + TARGET_XSHLDFLAGS+= -install_name $(TARGET_DYLIBPATH) -compatibility_version $(MAJVER).$(MINVER) -current_version $(MAJVER).$(MINVER).$(RELVER) + ifeq (arm64,$(TARGET_LJARCH)) + TARGET_XCFLAGS+= -fno-omit-frame-pointer + endif +else + ifneq (SunOS,$(TARGET_SYS)) + ifneq (PS3,$(TARGET_SYS)) + TARGET_XLDFLAGS+= -Wl,-E + endif + endif + ifeq (Linux,$(TARGET_SYS)) + TARGET_XLIBS+= -ldl + endif + ifeq (GNU/kFreeBSD,$(TARGET_SYS)) + TARGET_XLIBS+= -ldl + endif +endif +endif +endif + +ifneq ($(HOST_SYS),$(TARGET_SYS)) + ifeq (Windows,$(TARGET_SYS)) + HOST_XCFLAGS+= -malign-double -DLUAJIT_OS=LUAJIT_OS_WINDOWS + else + ifeq (Linux,$(TARGET_SYS)) + HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_LINUX + else + ifeq (Darwin,$(TARGET_SYS)) + HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX + else + ifeq (iOS,$(TARGET_SYS)) + HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX + else + HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OTHER + endif + endif + endif + endif +endif + +ifneq (,$(CCDEBUG)) + TARGET_STRIP= @: +endif + +############################################################################## +# Files and pathnames. +############################################################################## + +MINILUA_O= host/minilua.o +MINILUA_LIBS= -lm +MINILUA_T= host/minilua +MINILUA_X= $(MINILUA_T) + +ifeq (,$(HOST_LUA)) + HOST_LUA= $(MINILUA_X) + DASM_DEP= $(MINILUA_T) +endif + +DASM_DIR= ../dynasm +DASM= $(HOST_LUA) $(DASM_DIR)/dynasm.lua +DASM_XFLAGS= +DASM_AFLAGS= +DASM_ARCH= $(TARGET_LJARCH) + +ifneq (,$(findstring LJ_LE 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D ENDIAN_LE +else + DASM_AFLAGS+= -D ENDIAN_BE +endif +ifneq (,$(findstring LJ_ARCH_BITS 64,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D P64 +endif +ifneq (,$(findstring LJ_HASJIT 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D JIT +endif +ifneq (,$(findstring LJ_HASFFI 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D FFI +endif +ifneq (,$(findstring LJ_DUALNUM 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D DUALNUM +endif +ifneq (,$(findstring LJ_ARCH_HASFPU 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D FPU + TARGET_ARCH+= -DLJ_ARCH_HASFPU=1 +else + TARGET_ARCH+= -DLJ_ARCH_HASFPU=0 +endif +ifeq (,$(findstring LJ_ABI_SOFTFP 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D HFABI + TARGET_ARCH+= -DLJ_ABI_SOFTFP=0 +else + TARGET_ARCH+= -DLJ_ABI_SOFTFP=1 +endif +ifneq (,$(findstring LJ_NO_UNWIND 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D NO_UNWIND + TARGET_ARCH+= -DLUAJIT_NO_UNWIND +endif +DASM_AFLAGS+= -D VER=$(subst LJ_ARCH_VERSION_,,$(filter LJ_ARCH_VERSION_%,$(subst LJ_ARCH_VERSION ,LJ_ARCH_VERSION_,$(TARGET_TESTARCH)))) +ifeq (Windows,$(TARGET_SYS)) + DASM_AFLAGS+= -D WIN +endif +ifeq (x64,$(TARGET_LJARCH)) + ifeq (,$(findstring LJ_FR2 1,$(TARGET_TESTARCH))) + DASM_ARCH= x86 + endif +else +ifeq (arm,$(TARGET_LJARCH)) + ifeq (iOS,$(TARGET_SYS)) + DASM_AFLAGS+= -D IOS + endif +else +ifeq (ppc,$(TARGET_LJARCH)) + ifneq (,$(findstring LJ_ARCH_SQRT 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D SQRT + endif + ifneq (,$(findstring LJ_ARCH_ROUND 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D ROUND + endif + ifneq (,$(findstring LJ_ARCH_PPC32ON64 1,$(TARGET_TESTARCH))) + DASM_AFLAGS+= -D GPR64 + endif + ifeq (PS3,$(TARGET_SYS)) + DASM_AFLAGS+= -D PPE -D TOC + endif + ifneq (,$(findstring LJ_ARCH_PPC64 ,$(TARGET_TESTARCH))) + DASM_ARCH= ppc64 + endif +endif +endif +endif + +DASM_FLAGS= $(DASM_XFLAGS) $(DASM_AFLAGS) +DASM_DASC= vm_$(DASM_ARCH).dasc + +BUILDVM_O= host/buildvm.o host/buildvm_asm.o host/buildvm_peobj.o \ + host/buildvm_lib.o host/buildvm_fold.o +BUILDVM_T= host/buildvm +BUILDVM_X= $(BUILDVM_T) + +HOST_O= $(MINILUA_O) $(BUILDVM_O) +HOST_T= $(MINILUA_T) $(BUILDVM_T) + +LJVM_S= lj_vm.S +LJVM_O= lj_vm.o +LJVM_BOUT= $(LJVM_S) +LJVM_MODE= elfasm + +LJLIB_O= lib_base.o lib_math.o lib_bit.o lib_string.o lib_table.o \ + lib_io.o lib_os.o lib_package.o lib_debug.o lib_jit.o lib_ffi.o +LJLIB_C= $(LJLIB_O:.o=.c) + +LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \ + lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \ + lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_strscan.o \ + lj_strfmt.o lj_strfmt_num.o lj_api.o lj_profile.o \ + lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o lj_load.o \ + lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \ + lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \ + lj_mcode.o lj_snap.o lj_record.o lj_crecord.o lj_ffrecord.o \ + lj_asm.o lj_trace.o lj_gdbjit.o \ + lj_ctype.o lj_cdata.o lj_cconv.o lj_ccall.o lj_ccallback.o \ + lj_carith.o lj_clib.o lj_cparse.o \ + lj_lib.o lj_alloc.o lib_aux.o \ + $(LJLIB_O) lib_init.o + +LJVMCORE_O= $(LJVM_O) $(LJCORE_O) +LJVMCORE_DYNO= $(LJVMCORE_O:.o=_dyn.o) + +LIB_VMDEF= jit/vmdef.lua +LIB_VMDEFP= $(LIB_VMDEF) + +LUAJIT_O= luajit.o +LUAJIT_A= libluajit.a +LUAJIT_SO= libluajit.so +LUAJIT_T= luajit + +ALL_T= $(LUAJIT_T) $(LUAJIT_A) $(LUAJIT_SO) $(HOST_T) +ALL_HDRGEN= lj_bcdef.h lj_ffdef.h lj_libdef.h lj_recdef.h lj_folddef.h \ + host/buildvm_arch.h +ALL_GEN= $(LJVM_S) $(ALL_HDRGEN) $(LIB_VMDEFP) +WIN_RM= *.obj *.lib *.exp *.dll *.exe *.manifest *.pdb *.ilk +ALL_RM= $(ALL_T) $(ALL_GEN) *.o host/*.o $(WIN_RM) + +############################################################################## +# Build mode handling. +############################################################################## + +# Mixed mode defaults. +TARGET_O= $(LUAJIT_A) +TARGET_T= $(LUAJIT_T) $(LUAJIT_SO) +TARGET_DEP= $(LIB_VMDEF) $(LUAJIT_SO) + +ifeq (Windows,$(TARGET_SYS)) + TARGET_DYNCC= $(STATIC_CC) + LJVM_MODE= peobj + LJVM_BOUT= $(LJVM_O) + LUAJIT_T= luajit.exe + ifeq (cygwin,$(HOST_MSYS)) + LUAJIT_SO= cyg$(TARGET_DLLNAME) + else + LUAJIT_SO= $(TARGET_DLLNAME) + endif + # Mixed mode is not supported on Windows. And static mode doesn't work well. + # C modules cannot be loaded, because they bind to lua51.dll. + ifneq (static,$(BUILDMODE)) + BUILDMODE= dynamic + TARGET_XCFLAGS+= -DLUA_BUILD_AS_DLL + endif +endif +ifeq (Darwin,$(TARGET_SYS)) + LJVM_MODE= machasm +endif +ifeq (iOS,$(TARGET_SYS)) + LJVM_MODE= machasm +endif +ifeq (SunOS,$(TARGET_SYS)) + BUILDMODE= static +endif +ifeq (PS3,$(TARGET_SYS)) + BUILDMODE= static +endif + +ifeq (Windows,$(HOST_SYS)) + MINILUA_T= host/minilua.exe + BUILDVM_T= host/buildvm.exe + ifeq (,$(HOST_MSYS)) + MINILUA_X= host\minilua + BUILDVM_X= host\buildvm + ALL_RM:= $(subst /,\,$(ALL_RM)) + endif +endif + +ifeq (static,$(BUILDMODE)) + TARGET_DYNCC= @: + TARGET_T= $(LUAJIT_T) + TARGET_DEP= $(LIB_VMDEF) +else +ifeq (dynamic,$(BUILDMODE)) + ifneq (Windows,$(TARGET_SYS)) + TARGET_CC= $(DYNAMIC_CC) + endif + TARGET_DYNCC= @: + LJVMCORE_DYNO= $(LJVMCORE_O) + TARGET_O= $(LUAJIT_SO) + TARGET_XLDFLAGS+= $(TARGET_DYNXLDOPTS) +else +ifeq (Darwin,$(TARGET_SYS)) + TARGET_DYNCC= @: + LJVMCORE_DYNO= $(LJVMCORE_O) +endif +ifeq (iOS,$(TARGET_SYS)) + TARGET_DYNCC= @: + LJVMCORE_DYNO= $(LJVMCORE_O) +endif +endif +endif + +Q= @ +E= @echo +#Q= +#E= @: + +############################################################################## +# Make targets. +############################################################################## + +default all: $(TARGET_T) + +amalg: + @grep "^[+|]" ljamalg.c + $(MAKE) all "LJCORE_O=ljamalg.o" + +clean: + $(HOST_RM) $(ALL_RM) + +libbc: + ./$(LUAJIT_T) host/genlibbc.lua -o host/buildvm_libbc.h $(LJLIB_C) + $(MAKE) all + +depend: + @for file in $(ALL_HDRGEN); do \ + test -f $$file || touch $$file; \ + done + @$(HOST_CC) $(HOST_ACFLAGS) -MM *.c host/*.c | \ + sed -e "s| [^ ]*/dasm_\S*\.h||g" \ + -e "s|^\([^l ]\)|host/\1|" \ + -e "s| lj_target_\S*\.h| lj_target_*.h|g" \ + -e "s| lj_emit_\S*\.h| lj_emit_*.h|g" \ + -e "s| lj_asm_\S*\.h| lj_asm_*.h|g" >Makefile.dep + @for file in $(ALL_HDRGEN); do \ + test -s $$file || $(HOST_RM) $$file; \ + done + +.PHONY: default all amalg clean libbc depend + +############################################################################## +# Rules for generated files. +############################################################################## + +$(MINILUA_T): $(MINILUA_O) + $(E) "HOSTLINK $@" + $(Q)$(HOST_CC) $(HOST_ALDFLAGS) -o $@ $(MINILUA_O) $(MINILUA_LIBS) $(HOST_ALIBS) + +host/buildvm_arch.h: $(DASM_DASC) $(DASM_DEP) $(DASM_DIR)/*.lua + $(E) "DYNASM $@" + $(Q)$(DASM) $(DASM_FLAGS) -o $@ $(DASM_DASC) + +host/buildvm.o: $(DASM_DIR)/dasm_*.h + +$(BUILDVM_T): $(BUILDVM_O) + $(E) "HOSTLINK $@" + $(Q)$(HOST_CC) $(HOST_ALDFLAGS) -o $@ $(BUILDVM_O) $(HOST_ALIBS) + +$(LJVM_BOUT): $(BUILDVM_T) + $(E) "BUILDVM $@" + $(Q)$(BUILDVM_X) -m $(LJVM_MODE) -o $@ + +lj_bcdef.h: $(BUILDVM_T) $(LJLIB_C) + $(E) "BUILDVM $@" + $(Q)$(BUILDVM_X) -m bcdef -o $@ $(LJLIB_C) + +lj_ffdef.h: $(BUILDVM_T) $(LJLIB_C) + $(E) "BUILDVM $@" + $(Q)$(BUILDVM_X) -m ffdef -o $@ $(LJLIB_C) + +lj_libdef.h: $(BUILDVM_T) $(LJLIB_C) + $(E) "BUILDVM $@" + $(Q)$(BUILDVM_X) -m libdef -o $@ $(LJLIB_C) + +lj_recdef.h: $(BUILDVM_T) $(LJLIB_C) + $(E) "BUILDVM $@" + $(Q)$(BUILDVM_X) -m recdef -o $@ $(LJLIB_C) + +$(LIB_VMDEF): $(BUILDVM_T) $(LJLIB_C) + $(E) "BUILDVM $@" + $(Q)$(BUILDVM_X) -m vmdef -o $(LIB_VMDEFP) $(LJLIB_C) + +lj_folddef.h: $(BUILDVM_T) lj_opt_fold.c + $(E) "BUILDVM $@" + $(Q)$(BUILDVM_X) -m folddef -o $@ lj_opt_fold.c + +############################################################################## +# Object file rules. +############################################################################## + +%.o: %.c + $(E) "CC $@" + $(Q)$(TARGET_DYNCC) $(TARGET_ACFLAGS) -c -o $(@:.o=_dyn.o) $< + $(Q)$(TARGET_CC) $(TARGET_ACFLAGS) -c -o $@ $< + +%.o: %.S + $(E) "ASM $@" + $(Q)$(TARGET_DYNCC) $(TARGET_ASFLAGS) -c -o $(@:.o=_dyn.o) $< + $(Q)$(TARGET_CC) $(TARGET_ASFLAGS) -c -o $@ $< + +$(LUAJIT_O): + $(E) "CC $@" + $(Q)$(TARGET_STCC) $(TARGET_ACFLAGS) -c -o $@ $< + +$(HOST_O): %.o: %.c + $(E) "HOSTCC $@" + $(Q)$(HOST_CC) $(HOST_ACFLAGS) -c -o $@ $< + +include Makefile.dep + +############################################################################## +# Target file rules. +############################################################################## + +$(LUAJIT_A): $(LJVMCORE_O) + $(E) "AR $@" + $(Q)$(TARGET_AR) $@ $(LJVMCORE_O) + +# The dependency on _O, but linking with _DYNO is intentional. +$(LUAJIT_SO): $(LJVMCORE_O) + $(E) "DYNLINK $@" + $(Q)$(TARGET_LD) $(TARGET_ASHLDFLAGS) -o $@ $(LJVMCORE_DYNO) $(TARGET_ALIBS) + $(Q)$(TARGET_STRIP) $@ + +$(LUAJIT_T): $(TARGET_O) $(LUAJIT_O) $(TARGET_DEP) + $(E) "LINK $@" + $(Q)$(TARGET_LD) $(TARGET_ALDFLAGS) -o $@ $(LUAJIT_O) $(TARGET_O) $(TARGET_ALIBS) + $(Q)$(TARGET_STRIP) $@ + $(E) "OK Successfully built LuaJIT" + +############################################################################## From 2b9a03d961ef35befebfa098a76f3f3c99116cb4 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Thu, 15 Aug 2019 21:32:21 -0400 Subject: [PATCH 009/335] Set BUILDMODE=static and -fPIC in Makefile --- build.rs | 2 +- etc/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.rs b/build.rs index 7c4c6f9..59b6768 100644 --- a/build.rs +++ b/build.rs @@ -27,7 +27,6 @@ fn main() { let mut copy_options = CopyOptions::new(); copy_options.overwrite = true; dir::copy(&luajit_dir, &out_dir, ©_options).unwrap(); - fs::copy(format!("etc/Makefile"), format!("{}/Makefile", &src_dir)).unwrap(); let mut buildcmd = Command::new(msvcbuild_bat); for (name, value) in cl_exe.env() { @@ -58,6 +57,7 @@ fn main() { let mut copy_options = CopyOptions::new(); copy_options.overwrite = true; dir::copy(&luajit_dir, &out_dir, ©_options).unwrap(); + fs::copy(format!("etc/Makefile"), format!("{}/Makefile", &src_dir)).unwrap(); let mut buildcmd = Command::new("make"); buildcmd.current_dir(&src_dir); diff --git a/etc/Makefile b/etc/Makefile index 8f3cf60..04a7dd2 100644 --- a/etc/Makefile +++ b/etc/Makefile @@ -207,7 +207,7 @@ HOST_ACFLAGS= $(CCOPTIONS) $(HOST_XCFLAGS) $(TARGET_ARCH) $(HOST_CFLAGS) HOST_ALDFLAGS= $(LDOPTIONS) $(HOST_XLDFLAGS) $(HOST_LDFLAGS) HOST_ALIBS= $(HOST_XLIBS) $(LIBS) $(HOST_LIBS) -STATIC_CC = $(CROSS)$(CC) +STATIC_CC = $(CROSS)$(CC) -fPIC DYNAMIC_CC = $(CROSS)$(CC) -fPIC TARGET_CC= $(STATIC_CC) TARGET_STCC= $(STATIC_CC) From 6e5676b8894273b938fedfef407718ddd35efbfe Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Thu, 15 Aug 2019 21:46:56 -0400 Subject: [PATCH 010/335] Disable MACOSX_DEPLOYMENT_TARGET --- etc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/Makefile b/etc/Makefile index 04a7dd2..2046c6c 100644 --- a/etc/Makefile +++ b/etc/Makefile @@ -318,7 +318,7 @@ ifeq (,$(shell $(TARGET_CC) -o /dev/null -c -x c /dev/null -fno-stack-protector endif ifeq (Darwin,$(TARGET_SYS)) ifeq (,$(MACOSX_DEPLOYMENT_TARGET)) - export MACOSX_DEPLOYMENT_TARGET=10.4 +# export MACOSX_DEPLOYMENT_TARGET=10.4 endif TARGET_STRIP+= -x TARGET_XSHLDFLAGS= -dynamiclib -single_module -undefined dynamic_lookup -fPIC From 6a3c526cfba36503ee6ac607b94022b79a073246 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Thu, 15 Aug 2019 22:09:20 -0400 Subject: [PATCH 011/335] Add link args for x86_64-apple-darwin --- .cargo/config | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .cargo/config diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..134419d --- /dev/null +++ b/.cargo/config @@ -0,0 +1,9 @@ +[target.x86_64-apple-darwin] +rustflags = [ + "-C", + "link-arg=-pagezero_size 10000", + +] + +# "-C", +# "link-arg=-image_base 100000000", \ No newline at end of file From e0baa55c2cfbcafa2af6a34c4500cd789b64a705 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sat, 17 Aug 2019 19:59:44 -0400 Subject: [PATCH 012/335] Disable i686 and mingw tests --- .azure-pipelines.yml | 26 +++++++++++++++----------- build.rs | 9 +++++++++ ci/azure-install-rust.yml | 12 +----------- ci/azure-test-all.yml | 12 +++++++++++- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 62ff799..c6d683b 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -15,10 +15,14 @@ jobs: - template: ci/azure-test-all.yml strategy: matrix: - stable: - TOOLCHAIN: stable - nightly: - TOOLCHAIN: nightly + stable-x86_64-msvc: + TOOLCHAIN: stable-x86_64-pc-windows-msvc + # stable-x86_64-gnu: + # TOOLCHAIN: stable-x86_64-pc-windows-gnu + # stable-i686-msvc: + # TOOLCHAIN: stable-i686-pc-windows-msvc + # stable-i686-gnu: + # TOOLCHAIN: stable-i686-pc-windows-gnu - job: Linux pool: @@ -28,9 +32,11 @@ jobs: - template: ci/azure-test-all.yml strategy: matrix: - stable: - TOOLCHAIN: stable - nightly: + stable-x86_64: + TOOLCHAIN: stable-x86_64-unknown-linux-gnu + # stable-i686: + # TOOLCHAIN: stable-i686-unknown-linux-gnu + nightly-x86_64: TOOLCHAIN: nightly - job: MacOS @@ -41,7 +47,5 @@ jobs: - template: ci/azure-test-all.yml strategy: matrix: - stable: - TOOLCHAIN: stable - nightly: - TOOLCHAIN: nightly \ No newline at end of file + stable-x86_64: + TOOLCHAIN: stable-x86_64-apple-darwin \ No newline at end of file diff --git a/build.rs b/build.rs index 59b6768..0957824 100644 --- a/build.rs +++ b/build.rs @@ -63,6 +63,11 @@ fn main() { buildcmd.current_dir(&src_dir); buildcmd.stderr(Stdio::inherit()); + if cfg!(target_pointer_width = "32") { + buildcmd.env("HOST_CC", "gcc -m32"); + buildcmd.arg("-e"); + } + let mut child = buildcmd.spawn().expect("failed to run make"); if !child @@ -77,4 +82,8 @@ fn main() { println!("cargo:rustc-link-search=native={}", src_dir); println!("cargo:rustc-link-lib=static=luajit"); } + + // if cfg!(target_os = "macos") && cfg!(target_pointer_width = "64") { + // // RUSTFLAGS='-C link-args=-pagezero_size 10000 -image_base 100000000' + // } } diff --git a/ci/azure-install-rust.yml b/ci/azure-install-rust.yml index b304a14..cd39cc1 100644 --- a/ci/azure-install-rust.yml +++ b/ci/azure-install-rust.yml @@ -7,19 +7,9 @@ steps: rustup --version displayName: Install rustup condition: eq(variables['Agent.OS'], 'Darwin') -# - script: | -# echo %TOOLCHAIN% -# curl -sSf -o rustup-init.exe https://win.rustup.rs -# rustup-init.exe -v -y --default-toolchain %TOOLCHAIN% -# echo ##vso[task.prependpath]%USERPROFILE%\.cargo\bin -# rustup default %TOOLCHAIN% -# rustup component add rustfmt -# displayName: Install rust (windows) -# condition: eq(variables['Agent.OS'], 'Windows_NT') - bash: | - set -x + set -e -x rustup --version - rustup component remove --toolchain $TOOLCHAIN rust-docs || true rustup default $TOOLCHAIN rustup update --no-self-update $TOOLCHAIN rustup toolchain install stable diff --git a/ci/azure-test-all.yml b/ci/azure-test-all.yml index 7c08c69..1b253f7 100644 --- a/ci/azure-test-all.yml +++ b/ci/azure-test-all.yml @@ -1,6 +1,16 @@ steps: - checkout: self submodules: true +# - script: | +# call "C:\Program Files (x86)\Microsoft Visual Studio\2017\VC\Auxiliary\Build\vcvarsall.bat" x86 +# displayName: Call vcvarsalls.bat x86 +# condition: eq(variables['TOOLCHAIN'], 'stable-i686-pc-windows-msvc') +# - bash: pacman -Syu +# condition: eq(variables['TOOLCHAIN'], 'stable-x86_64-pc-windows-gnu') +# displayName: Update msys +# - bash: sudo apt install gcc-multilib +# condition: eq(variables['TOOLCHAIN'], 'stable-i686-unknown-linux-gnu') +# displayName: Install gcc-multilib - bash: | set -e -x cargo +stable fmt --all -- --check @@ -22,5 +32,5 @@ steps: - bash: | pwd find ./target - displayName: List files in ./target + displayName: List files in target condition: always() From a2b17d749c337bd994d518ae688b6356a5f58fad Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 19 Jan 2020 01:02:08 -0500 Subject: [PATCH 013/335] The bash task in azure seems to find powershells find --- ci/azure-test-all.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/azure-test-all.yml b/ci/azure-test-all.yml index 1b253f7..877ab5a 100644 --- a/ci/azure-test-all.yml +++ b/ci/azure-test-all.yml @@ -31,6 +31,6 @@ steps: CARGO_INCREMENTAL: 0 - bash: | pwd - find ./target + /usr/bin/find ./target displayName: List files in target condition: always() From bb75b4a2fd4758cf44c85aaad2d368ea60fadbf0 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 19 Jan 2020 01:13:31 -0500 Subject: [PATCH 014/335] Add README --- README.md | 3 +++ bindgen.sh | 2 +- build.rs | 3 ++- src/ffi.rs | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..879aa22 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Rust LuaJIT 2 Bindings + +[![Build Status](https://dev.azure.com/aloucks/aloucks/_apis/build/status/aloucks.luajit-sys?branchName=master)](https://dev.azure.com/aloucks/aloucks/_build/latest?definitionId=2&branchName=master) diff --git a/bindgen.sh b/bindgen.sh index 5dd058d..27e7b9e 100644 --- a/bindgen.sh +++ b/bindgen.sh @@ -1,6 +1,6 @@ #!/bin/bash -BINDGEN_VERSION=$(bindgen --version) +BINDGEN_VERSION=$(bindgen --version | grep -v -e '^cargo') bindgen -o src/ffi.rs \ --raw-line "/// Generated with: ${BINDGEN_VERSION}" \ diff --git a/build.rs b/build.rs index 0957824..7f749d7 100644 --- a/build.rs +++ b/build.rs @@ -19,7 +19,8 @@ fn main() { let lib_path = format!("{}/lua51.lib", &src_dir); dbg!(&lib_path); if !std::fs::metadata(&lib_path).is_ok() { - let cl_exe: cc::Tool = cc::windows_registry::find_tool(&target, "cl.exe").unwrap(); + let cl_exe: cc::Tool = + cc::windows_registry::find_tool(&target, "cl.exe").expect("cl.exe not found"); let msvcbuild_bat = format!("{}/msvcbuild.bat", &src_dir); dbg!(&msvcbuild_bat); diff --git a/src/ffi.rs b/src/ffi.rs index 76db086..a33ac54 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -1,6 +1,6 @@ /* automatically generated by rust-bindgen */ -// Generated with: bindgen 0.49.1 +// Generated with: bindgen 0.52.0 pub const LUA_LDIR: &'static [u8; 7usize] = b"!\\lua\\\0"; pub const LUA_CDIR: &'static [u8; 3usize] = b"!\\\0"; From a705ff7d25275b90636e3e2092a242bd25ca54e5 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 19 Jan 2020 01:19:51 -0500 Subject: [PATCH 015/335] Rename to luajit2-sys --- Cargo.toml | 4 ++-- README.md | 2 +- examples/lua.rs | 2 +- tests/test.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e407f7c..9970652 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "luajit-sys" +name = "luajit2-sys" version = "0.0.1" authors = ["Aaron Loucks "] edition = "2018" @@ -10,4 +10,4 @@ libc = "0.2" [build-dependencies] cc = "1.0.40" -fs_extra = "1.1.0" \ No newline at end of file +fs_extra = "1.1.0" diff --git a/README.md b/README.md index 879aa22..6d5e744 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ # Rust LuaJIT 2 Bindings -[![Build Status](https://dev.azure.com/aloucks/aloucks/_apis/build/status/aloucks.luajit-sys?branchName=master)](https://dev.azure.com/aloucks/aloucks/_build/latest?definitionId=2&branchName=master) +[![Build Status](https://dev.azure.com/aloucks/aloucks/_apis/build/status/aloucks.luajit2-sys?branchName=master)](https://dev.azure.com/aloucks/aloucks/_build/latest?definitionId=3&branchName=master) diff --git a/examples/lua.rs b/examples/lua.rs index aa45cae..0784409 100644 --- a/examples/lua.rs +++ b/examples/lua.rs @@ -2,7 +2,7 @@ use std::env; use std::ffi::{CStr, CString}; use std::ptr; -use luajit_sys as sys; +use luajit2_sys as sys; unsafe fn run_script(script_name: String, script_src: String) { let lua = sys::luaL_newstate(); diff --git a/tests/test.rs b/tests/test.rs index e737156..8a018ea 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,4 +1,4 @@ -use luajit_sys as sys; +use luajit2_sys as sys; #[test] fn run_script() { From b00970789a4e8d51f6661d23dd89600048498d5a Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 19 Jan 2020 01:38:51 -0500 Subject: [PATCH 016/335] Add license --- Cargo.toml | 2 + LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE-MIT | 23 ++++++ 3 files changed, 226 insertions(+) create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT diff --git a/Cargo.toml b/Cargo.toml index 9970652..6625e10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,8 @@ version = "0.0.1" authors = ["Aaron Loucks "] edition = "2018" keywords = ["lua", "luajit", "script"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/aloucks/luajit2-sys" [dependencies] libc = "0.2" diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..f007b81 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/LICENSE-2.0 + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..468cd79 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file From 727831d4135d7b3dd7396610ac73cf4e6144bc27 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 19 Jan 2020 01:48:17 -0500 Subject: [PATCH 017/335] Add description --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 6625e10..a293d21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [package] name = "luajit2-sys" +description = "LuaJIT-2.1 FFI Bindings" version = "0.0.1" authors = ["Aaron Loucks "] edition = "2018" From 18c5d88e65318c3d7506c510fa328a82decd5b36 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 19 Jan 2020 14:37:42 -0500 Subject: [PATCH 018/335] Cleanup build.rs Set the following environment variables. The luajit lib name varies by platform (lua51 on msvc and luajit everywhere else). - DEP_LUAJIT_INCLUDE - DEP_LUAJIT_LIB_NAME --- Cargo.toml | 1 + build.rs | 143 +++++++++++++++++++++++++++++------------------------ 2 files changed, 80 insertions(+), 64 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a293d21..9dd5807 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" keywords = ["lua", "luajit", "script"] license = "MIT OR Apache-2.0" repository = "https://github.com/aloucks/luajit2-sys" +links = "luajit" [dependencies] libc = "0.2" diff --git a/build.rs b/build.rs index 7f749d7..08e2bbe 100644 --- a/build.rs +++ b/build.rs @@ -1,12 +1,9 @@ -use cc; use fs_extra::dir; use fs_extra::dir::CopyOptions; +use std::env; use std::process::{Command, Stdio}; -use std::{env, fs}; fn main() { - let target = env::var("TARGET").unwrap(); - let luajit_dir = format!("{}/luajit", env!("CARGO_MANIFEST_DIR")); let out_dir = env::var("OUT_DIR").unwrap(); let src_dir = format!("{}/luajit/src", out_dir); @@ -15,76 +12,94 @@ fn main() { dbg!(&out_dir); dbg!(&src_dir); - if cfg!(target_env = "msvc") { - let lib_path = format!("{}/lua51.lib", &src_dir); - dbg!(&lib_path); - if !std::fs::metadata(&lib_path).is_ok() { - let cl_exe: cc::Tool = - cc::windows_registry::find_tool(&target, "cl.exe").expect("cl.exe not found"); - let msvcbuild_bat = format!("{}/msvcbuild.bat", &src_dir); + // DEP_LUAJIT_INCLUDE + // DEB_LUAJIT_LIB_NAME - dbg!(&msvcbuild_bat); + let lib_name = build_luajit(&luajit_dir, &out_dir, &src_dir); - let mut copy_options = CopyOptions::new(); - copy_options.overwrite = true; - dir::copy(&luajit_dir, &out_dir, ©_options).unwrap(); + println!("cargo:lib-name={}", lib_name); + println!("cargo:include={}", src_dir); + println!("cargo:rustc-link-search=native={}", src_dir); + println!("cargo:rustc-link-lib=static={}", lib_name); - let mut buildcmd = Command::new(msvcbuild_bat); - for (name, value) in cl_exe.env() { - buildcmd.env(name, value); - } - buildcmd.env("Configuration", "Release"); - buildcmd.args(&["static"]); - buildcmd.current_dir(&src_dir); - buildcmd.stderr(Stdio::inherit()); + // if cfg!(target_os = "macos") && cfg!(target_pointer_width = "64") { + // // RUSTFLAGS='-C link-args=-pagezero_size 10000 -image_base 100000000' + // } +} - let mut child = buildcmd.spawn().expect("failed to run msvcbuild.bat"); +#[cfg(target_env = "msvc")] +fn build_luajit(luajit_dir: &str, out_dir: &str, src_dir: &str) -> &'static str { + const LIB_NAME: &'static str = "lua51"; + let lib_path = format!("{}/{}.lib", &src_dir, LIB_NAME); + dbg!(&lib_path); + if !std::fs::metadata(&lib_path).is_ok() { + let target = env::var("TARGET").unwrap(); + let cl_exe: cc::Tool = + cc::windows_registry::find_tool(&target, "cl.exe").expect("cl.exe not found"); + let msvcbuild_bat = format!("{}/msvcbuild.bat", &src_dir); + dbg!(&msvcbuild_bat); - if !child - .wait() - .map(|status| status.success()) - .map_err(|_| false) - .unwrap_or(false) - { - panic!("Failed to build luajit"); - } + let mut copy_options = CopyOptions::new(); + copy_options.overwrite = true; + dir::copy(&luajit_dir, &out_dir, ©_options) + .expect("failed to copy luajit source to out dir"); + + let mut buildcmd = Command::new(msvcbuild_bat); + for (name, value) in cl_exe.env() { + eprintln!("{:?} = {:?}", name, value); + buildcmd.env(name, value); } - println!("cargo:rustc-link-search=native={}", src_dir); - println!("cargo:rustc-link-lib=static=lua51"); - } else { - let lib_path = format!("{}/libluajit.a", &src_dir); - dbg!(&lib_path); - if !std::fs::metadata(&lib_path).is_ok() { - let mut copy_options = CopyOptions::new(); - copy_options.overwrite = true; - dir::copy(&luajit_dir, &out_dir, ©_options).unwrap(); - fs::copy(format!("etc/Makefile"), format!("{}/Makefile", &src_dir)).unwrap(); - let mut buildcmd = Command::new("make"); - buildcmd.current_dir(&src_dir); - buildcmd.stderr(Stdio::inherit()); + buildcmd.env("Configuration", "Release"); + buildcmd.args(&["static"]); + buildcmd.current_dir(&src_dir); + buildcmd.stderr(Stdio::inherit()); - if cfg!(target_pointer_width = "32") { - buildcmd.env("HOST_CC", "gcc -m32"); - buildcmd.arg("-e"); - } + let mut child = buildcmd.spawn().expect("failed to run msvcbuild.bat"); - let mut child = buildcmd.spawn().expect("failed to run make"); - - if !child - .wait() - .map(|status| status.success()) - .map_err(|_| false) - .unwrap_or(false) - { - panic!("Failed to build luajit"); - } + if !child + .wait() + .map(|status| status.success()) + .map_err(|_| false) + .unwrap_or(false) + { + panic!("Failed to build luajit"); } - println!("cargo:rustc-link-search=native={}", src_dir); - println!("cargo:rustc-link-lib=static=luajit"); } - // if cfg!(target_os = "macos") && cfg!(target_pointer_width = "64") { - // // RUSTFLAGS='-C link-args=-pagezero_size 10000 -image_base 100000000' - // } + LIB_NAME +} + +#[cfg(not(target_env = "msvc"))] +fn build_luajit(luajit_dir: &str, out_dir: &str, src_dir: &str) -> &'static str { + const LIB_NAME: &'static str = "luajit"; + let lib_path = format!("{}/lib{}.a", &src_dir, LIB_NAME); + dbg!(&lib_path); + if !std::fs::metadata(&lib_path).is_ok() { + let mut copy_options = CopyOptions::new(); + copy_options.overwrite = true; + dir::copy(&luajit_dir, &out_dir, ©_options).unwrap(); + std::fs::copy(format!("etc/Makefile"), format!("{}/Makefile", &src_dir)).unwrap(); + + let mut buildcmd = Command::new("make"); + buildcmd.current_dir(&src_dir); + buildcmd.stderr(Stdio::inherit()); + + if cfg!(target_pointer_width = "32") { + buildcmd.env("HOST_CC", "gcc -m32"); + buildcmd.arg("-e"); + } + + let mut child = buildcmd.spawn().expect("failed to run make"); + + if !child + .wait() + .map(|status| status.success()) + .map_err(|_| false) + .unwrap_or(false) + { + panic!("Failed to build luajit"); + } + } + LIB_NAME } From 56c586ecbf4a79c0d2a8cb0f88512864919d2e3e Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 19 Jan 2020 15:20:31 -0500 Subject: [PATCH 019/335] Update README --- Cargo.toml | 4 +++- README.md | 46 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9dd5807..9920e43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,14 @@ [package] name = "luajit2-sys" -description = "LuaJIT-2.1 FFI Bindings" version = "0.0.1" +description = "LuaJIT-2.1 FFI Bindings" authors = ["Aaron Loucks "] edition = "2018" keywords = ["lua", "luajit", "script"] license = "MIT OR Apache-2.0" +readme = "README.md" repository = "https://github.com/aloucks/luajit2-sys" +documentation = "https://docs.rs/luajit2-sys" links = "luajit" [dependencies] diff --git a/README.md b/README.md index 6d5e744..7636366 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,47 @@ # Rust LuaJIT 2 Bindings -[![Build Status](https://dev.azure.com/aloucks/aloucks/_apis/build/status/aloucks.luajit2-sys?branchName=master)](https://dev.azure.com/aloucks/aloucks/_build/latest?definitionId=3&branchName=master) +[![crates.io](https://img.shields.io/crates/v/luajit2-sys.svg)](https://crates.io/crates/luajit2-sys) +[![docs.rs](https://docs.rs/luajit2-sys/badge.svg)](https://docs.rs/luajit2-sys) +[![build](https://dev.azure.com/aloucks/aloucks/_apis/build/status/aloucks.luajit2-sys?branchName=master)](https://dev.azure.com/aloucks/aloucks/_build/latest?definitionId=3&branchName=master) + +```toml +[dependencies] +luajit2-sys = "0.0.1" +``` + +## Exported Cargo Environment Variables + +||| +| -- | -- | +| `DEP_LUAJIT_INCLUDE` | Path to the LuaJIT source and headers | +| `DEP_LUAJIT_LIB_NAME` | Platform specfic lib name (`lua51` on Windows and `luajit` everywhere else) | + +## Example + +```rust +use luajit2_sys as sys; +use std::ffi::CStr; + +fn main() { + unsafe { + let lua = sys::luaL_newstate(); + sys::luaL_openlibs(lua); + let script_data = b"return 1 + 2"; + let script_name = b"run_script\0"; + sys::luaL_loadbuffer( + lua, + script_data.as_ptr() as _, + script_data.len() as _, + script_name.as_ptr() as _, + ); + sys::lua_pcall(lua, 0, 1, 0); + let idx = sys::lua_gettop(lua); + let s = sys::lua_tostring(lua, idx); + let result = CStr::from_ptr(s).to_string_lossy().to_string(); + sys::lua_close(lua); + + println!("result: {}", result); + } +} +``` + From 32f67222d7006a8cf2086299f1cd431916768e32 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 19 Jan 2020 15:29:09 -0500 Subject: [PATCH 020/335] Add weekly build to CI --- .azure-pipelines.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index c6d683b..2445ee1 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -6,6 +6,14 @@ pr: include: - master +schedules: + - cron: "0 12 * * 0" + displayName: Weekly Sunday build + branches: + include: + - master + always: true + jobs: - job: Windows pool: From 972f1022ee3add5e5f6128994a34c62bc6ef948f Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 19 Jan 2020 15:31:07 -0500 Subject: [PATCH 021/335] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7636366..b6b8fc6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Rust LuaJIT 2 Bindings +# Rust LuaJIT Bindings [![crates.io](https://img.shields.io/crates/v/luajit2-sys.svg)](https://crates.io/crates/luajit2-sys) [![docs.rs](https://docs.rs/luajit2-sys/badge.svg)](https://docs.rs/luajit2-sys) From f308e7ba665f3dbf9762c11a248cf556cb663865 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 19 Jan 2020 16:24:01 -0500 Subject: [PATCH 022/335] Version 0.0.2 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9920e43..248c937 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "luajit2-sys" -version = "0.0.1" +version = "0.0.2" description = "LuaJIT-2.1 FFI Bindings" authors = ["Aaron Loucks "] edition = "2018" From 2aa8038854e046bd332578a017b2205a8ee48623 Mon Sep 17 00:00:00 2001 From: Aaron Loucks Date: Sun, 19 Jan 2020 16:31:41 -0500 Subject: [PATCH 023/335] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b6b8fc6..873b88e 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ```toml [dependencies] -luajit2-sys = "0.0.1" +luajit2-sys = "0.0.2" ``` ## Exported Cargo Environment Variables From 22ea4d75a1468e611318c8f1b087000c4e1fdb7f Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 2 Mar 2023 08:52:49 +0100 Subject: [PATCH 024/335] fix: Remove obsolete CLI parameter With Oodle being linked statically, there is no need for a parameter to speficy the library location. --- crates/dtmm/src/main.rs | 6 ------ crates/dtmt/src/cmd/build.rs | 24 ++++++++---------------- crates/dtmt/src/cmd/bundle/mod.rs | 8 +------- 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 9ce6192..a89dde5 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -37,12 +37,6 @@ fn main() -> Result<()> { tracing::trace!(default_config_path = %default_config_path.display()); let matches = command!() - .arg(Arg::new("oodle").long("oodle").help( - "The oodle library to load. This may either be:\n\ - - A library name that will be searched for in the system's default paths.\n\ - - A file path relative to the current working directory.\n\ - - An absolute file path.", - )) .arg( Arg::new("config") .long("config") diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index f077094..97f192c 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -17,23 +17,15 @@ use crate::mods::archive::Archive; const PROJECT_CONFIG_NAME: &str = "dtmt.cfg"; pub(crate) fn command_definition() -> Command { - Command::new("build") - .about("Build a project") - .arg( - Arg::new("directory") - .required(false) - .value_parser(value_parser!(PathBuf)) - .help( - "The path to the project to build. \ + Command::new("build").about("Build a project").arg( + Arg::new("directory") + .required(false) + .value_parser(value_parser!(PathBuf)) + .help( + "The path to the project to build. \ If omitted, dtmt will search from the current working directory upward.", - ), - ) - .arg(Arg::new("oodle").long("oodle").help( - "The oodle library to load. This may either be:\n\ - - A library name that will be searched for in the system's default paths.\n\ - - A file path relative to the current working directory.\n\ - - An absolute file path.", - )) + ), + ) } #[tracing::instrument] diff --git a/crates/dtmt/src/cmd/bundle/mod.rs b/crates/dtmt/src/cmd/bundle/mod.rs index 6baf860..0e7c9f7 100644 --- a/crates/dtmt/src/cmd/bundle/mod.rs +++ b/crates/dtmt/src/cmd/bundle/mod.rs @@ -1,4 +1,4 @@ -use clap::{Arg, ArgMatches, Command}; +use clap::{ArgMatches, Command}; use color_eyre::eyre::Result; mod decompress; @@ -10,12 +10,6 @@ pub(crate) fn command_definition() -> Command { Command::new("bundle") .subcommand_required(true) .about("Manipulate the game's bundle files") - .arg(Arg::new("oodle").long("oodle").help( - "The oodle library to load. This may either be:\n\ - - A library name that will be searched for in the system's default paths.\n\ - - A file path relative to the current working directory.\n\ - - An absolute file path.", - )) .subcommand(decompress::command_definition()) .subcommand(extract::command_definition()) .subcommand(inject::command_definition()) From 5df3aa1cb7985421feabbc98793b0786c2d6424f Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 2 Mar 2023 10:55:23 +0100 Subject: [PATCH 025/335] feat(dtmt): Split build command Closes #40. --- crates/dtmt/src/cmd/build.rs | 197 ++++++++++++++++++-------------- crates/dtmt/src/cmd/package.rs | 128 +++++++++++++++++++++ crates/dtmt/src/main.rs | 11 +- crates/dtmt/src/mods/archive.rs | 98 ---------------- 4 files changed, 245 insertions(+), 189 deletions(-) create mode 100644 crates/dtmt/src/cmd/package.rs delete mode 100644 crates/dtmt/src/mods/archive.rs diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index 97f192c..a0db3f2 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -1,3 +1,5 @@ +use std::collections::{HashMap, HashSet}; +use std::ops::Deref; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -8,24 +10,36 @@ use dtmt_shared::ModConfig; use futures::future::try_join_all; use futures::StreamExt; use sdk::filetype::package::Package; +use sdk::murmur::IdString64; use sdk::{Bundle, BundleFile}; use tokio::fs::{self, File}; use tokio::io::AsyncReadExt; - -use crate::mods::archive::Archive; +use tokio::sync::Mutex; const PROJECT_CONFIG_NAME: &str = "dtmt.cfg"; +type FileIndexMap = HashMap>; + pub(crate) fn command_definition() -> Command { - Command::new("build").about("Build a project").arg( - Arg::new("directory") - .required(false) - .value_parser(value_parser!(PathBuf)) - .help( - "The path to the project to build. \ + Command::new("build") + .about("Build a project") + .arg( + Arg::new("directory") + .required(false) + .value_parser(value_parser!(PathBuf)) + .help( + "The path to the project to build. \ If omitted, dtmt will search from the current working directory upward.", - ), - ) + ), + ) + .arg( + Arg::new("out") + .long("out") + .short('o') + .default_value("out") + .value_parser(value_parser!(PathBuf)) + .help("The directory to write output files to."), + ) } #[tracing::instrument] @@ -173,74 +187,79 @@ fn normalize_file_path>(path: P) -> Result { Ok(path) } -#[tracing::instrument(skip_all)] -pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { - let cfg = { - let dir = matches.get_one::("directory").cloned(); - let mut cfg = find_project_config(dir).await?; +#[tracing::instrument] +pub(crate) async fn read_project_config(dir: Option) -> Result { + let mut cfg = find_project_config(dir).await?; - cfg.resources.init = normalize_file_path(cfg.resources.init) - .wrap_err("invalid config field 'resources.init'") + cfg.resources.init = normalize_file_path(cfg.resources.init) + .wrap_err("invalid config field 'resources.init'") + .with_suggestion(|| { + "Specify a file path relative to and child path of the \ + directory where 'dtmt.cfg' is." + .to_string() + }) + .with_suggestion(|| { + "Use 'dtmt new' in a separate directory to generate \ + a valid mod template." + .to_string() + })?; + + if let Some(path) = cfg.resources.data { + let path = normalize_file_path(path) + .wrap_err("invalid config field 'resources.data'") .with_suggestion(|| { "Specify a file path relative to and child path of the \ - directory where 'dtmt.cfg' is." + directory where 'dtmt.cfg' is." .to_string() }) .with_suggestion(|| { "Use 'dtmt new' in a separate directory to generate \ - a valid mod template." + a valid mod template." .to_string() })?; + cfg.resources.data = Some(path); + } - if let Some(path) = cfg.resources.data { - let path = normalize_file_path(path) - .wrap_err("invalid config field 'resources.data'") - .with_suggestion(|| { - "Specify a file path relative to and child path of the \ - directory where 'dtmt.cfg' is." - .to_string() - }) - .with_suggestion(|| { - "Use 'dtmt new' in a separate directory to generate \ - a valid mod template." - .to_string() - })?; - cfg.resources.data = Some(path); - } - - if let Some(path) = cfg.resources.localization { - let path = normalize_file_path(path) - .wrap_err("invalid config field 'resources.localization'") - .with_suggestion(|| { - "Specify a file path relative to and child path of the \ + if let Some(path) = cfg.resources.localization { + let path = normalize_file_path(path) + .wrap_err("invalid config field 'resources.localization'") + .with_suggestion(|| { + "Specify a file path relative to and child path of the \ directory where 'dtmt.cfg' is." - .to_string() - }) - .with_suggestion(|| { - "Use 'dtmt new' in a separate directory to generate \ + .to_string() + }) + .with_suggestion(|| { + "Use 'dtmt new' in a separate directory to generate \ a valid mod template." - .to_string() - })?; - cfg.resources.localization = Some(path); - } + .to_string() + })?; + cfg.resources.localization = Some(path); + } - cfg - }; + Ok(cfg) +} - let dest = { - let mut path = PathBuf::from(&cfg.id); - path.set_extension("zip"); - Arc::new(path) - }; +#[tracing::instrument(skip_all)] +pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { + let cfg = read_project_config(matches.get_one::("directory").cloned()).await?; + tracing::debug!(?cfg); let cfg = Arc::new(cfg); - tracing::debug!(?cfg); + let out_path = matches + .get_one::("out") + .expect("parameter should have default value"); + + fs::create_dir_all(out_path) + .await + .wrap_err_with(|| format!("failed to create output directory '{}'", out_path.display()))?; + + let file_map = Arc::new(Mutex::new(FileIndexMap::new())); let tasks = cfg .packages .iter() - .map(|path| (path, cfg.clone())) - .map(|(path, cfg)| async move { + .map(|path| (path, cfg.clone(), file_map.clone())) + .map(|(path, cfg, file_map)| async move { if path.extension().is_some() { eyre::bail!( "Package name must be specified without file extension: {}", @@ -248,45 +267,53 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> ); } - build_package(path, &cfg.dir).await.wrap_err_with(|| { + let bundle = build_package(path, &cfg.dir).await.wrap_err_with(|| { format!( "failed to build package {} in {}", path.display(), cfg.dir.display() ) - }) + })?; + + let bundle_name = match bundle.name() { + IdString64::Hash(_) => { + eyre::bail!("bundle name must be known as string. got hash") + } + IdString64::String(s) => s.clone(), + }; + + { + let mut file_map = file_map.lock().await; + let map_entry = file_map.entry(bundle_name).or_default(); + + for file in bundle.files() { + map_entry.insert(file.name(false, None)); + } + } + + let name = bundle.name().to_murmur64(); + let path = out_path.join(name.to_string().to_ascii_lowercase()); + + let data = bundle.to_binary()?; + fs::write(&path, data) + .await + .wrap_err_with(|| format!("failed to write bundle to '{}'", path.display())) }); - let bundles = try_join_all(tasks) + try_join_all(tasks) .await .wrap_err("failed to build mod bundles")?; - let config_file = { - let path = cfg.dir.join("dtmt.cfg"); - fs::read(&path) - .await - .wrap_err_with(|| format!("failed to read mod config at {}", path.display()))? - }; - { - let dest = dest.clone(); - let id = cfg.id.clone(); - tokio::task::spawn_blocking(move || { - let mut archive = Archive::new(id); - - archive.add_config(config_file); - - for bundle in bundles { - archive.add_bundle(bundle); - } - - archive - .write(dest.as_ref()) - .wrap_err("failed to write mod archive") - }) - .await??; + let file_map = file_map.lock().await; + let data = serde_sjson::to_string(file_map.deref())?; + let path = out_path.join("files.sjson"); + fs::write(&path, data) + .await + .wrap_err_with(|| format!("failed to write file index to '{}'", path.display()))?; } - tracing::info!("Mod archive written to {}", dest.display()); + tracing::info!("Compiled bundles written to '{}'", out_path.display()); + Ok(()) } diff --git a/crates/dtmt/src/cmd/package.rs b/crates/dtmt/src/cmd/package.rs new file mode 100644 index 0000000..44184f6 --- /dev/null +++ b/crates/dtmt/src/cmd/package.rs @@ -0,0 +1,128 @@ +use std::ffi::OsString; +use std::io::{Cursor, Write}; +use std::path::PathBuf; + +use clap::{value_parser, Arg, ArgMatches, Command}; +use color_eyre::eyre::{Context, Result}; +use color_eyre::Help; +use tokio::fs::{self, DirEntry}; +use tokio_stream::wrappers::ReadDirStream; +use tokio_stream::StreamExt; +use zip::ZipWriter; + +use crate::cmd::build::read_project_config; + +pub(crate) fn command_definition() -> Command { + Command::new("package") + .about("Package compiled bundles for distribution") + .arg( + Arg::new("project") + .required(false) + .value_parser(value_parser!(PathBuf)) + .help( + "The path to the project to build. \ + If omitted, dtmt will search from the current working directory upward.", + ), + ) + .arg( + Arg::new("directory") + .long("directory") + .short('d') + .default_value("out") + .value_parser(value_parser!(PathBuf)) + .help( + "The path to the directory were the compiled bundles were written to. \ + This is the same directory as `dtmt build -o`", + ), + ) + .arg( + Arg::new("out") + .long("out") + .short('o') + .default_value(".") + .value_parser(value_parser!(PathBuf)) + .help("The path to write the packaged file to. May be a directory or a file name."), + ) +} + +async fn process_dir_entry(res: Result) -> Result<(OsString, Vec)> { + let entry = res?; + let path = entry.path(); + + let data = fs::read(&path) + .await + .wrap_err_with(|| format!("failed to read '{}'", path.display()))?; + + Ok((entry.file_name(), data)) +} + +#[tracing::instrument(skip_all)] +pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { + let cfg = read_project_config(matches.get_one::("project").cloned()).await?; + + let dest = { + let mut path = matches + .get_one::("out") + .cloned() + .unwrap_or_else(|| PathBuf::from(".")); + + if path.extension().is_none() { + path.push(format!("{}.zip", cfg.id)) + } + + path + }; + + let data = Cursor::new(Vec::new()); + let mut zip = ZipWriter::new(data); + + zip.add_directory(&cfg.id, Default::default())?; + + let base_path = PathBuf::from(cfg.id); + + { + let name = base_path.join("dtmt.cfg"); + let path = cfg.dir.join("dtmt.cfg"); + + let data = fs::read(&path) + .await + .wrap_err_with(|| format!("failed to read mod config at {}", path.display()))?; + + zip.start_file(name.to_string_lossy(), Default::default())?; + zip.write_all(&data)?; + } + + { + let path = cfg.dir.join( + matches + .get_one::("directory") + .expect("parameter has default value"), + ); + let read_dir = fs::read_dir(&path) + .await + .wrap_err_with(|| format!("failed to read directory '{}'", path.display()))?; + + let stream = ReadDirStream::new(read_dir) + .map(|res| res.wrap_err("failed to read dir entry")) + .then(process_dir_entry); + tokio::pin!(stream); + + while let Some(res) = stream.next().await { + let (name, data) = res?; + + let name = base_path.join(name); + zip.start_file(name.to_string_lossy(), Default::default())?; + zip.write_all(&data)?; + } + }; + + let data = zip.finish()?; + + fs::write(&dest, data.into_inner()) + .await + .wrap_err_with(|| format!("failed to write mod archive to '{}'", dest.display())) + .with_suggestion(|| "Make sure that parent directories exist.".to_string())?; + + tracing::info!("Mod archive written to {}", dest.display()); + Ok(()) +} diff --git a/crates/dtmt/src/main.rs b/crates/dtmt/src/main.rs index dc4853e..d4df73c 100644 --- a/crates/dtmt/src/main.rs +++ b/crates/dtmt/src/main.rs @@ -20,14 +20,11 @@ mod cmd { pub mod dictionary; pub mod murmur; pub mod new; + pub mod package; mod util; pub mod watch; } -mod mods { - pub mod archive; -} - #[derive(Default, Deserialize, Serialize)] struct GlobalConfig { game_dir: Option, @@ -56,6 +53,7 @@ async fn main() -> Result<()> { .subcommand(cmd::dictionary::command_definition()) .subcommand(cmd::murmur::command_definition()) .subcommand(cmd::new::command_definition()) + .subcommand(cmd::package::command_definition()) // .subcommand(cmd::watch::command_definition()) .get_matches(); @@ -126,12 +124,13 @@ async fn main() -> Result<()> { }; match matches.subcommand() { + Some(("build", sub_matches)) => cmd::build::run(ctx, sub_matches).await?, Some(("bundle", sub_matches)) => cmd::bundle::run(ctx, sub_matches).await?, + Some(("dictionary", sub_matches)) => cmd::dictionary::run(ctx, sub_matches).await?, Some(("murmur", sub_matches)) => cmd::murmur::run(ctx, sub_matches).await?, Some(("new", sub_matches)) => cmd::new::run(ctx, sub_matches).await?, - Some(("build", sub_matches)) => cmd::build::run(ctx, sub_matches).await?, + Some(("package", sub_matches)) => cmd::package::run(ctx, sub_matches).await?, Some(("watch", sub_matches)) => cmd::watch::run(ctx, sub_matches).await?, - Some(("dictionary", sub_matches)) => cmd::dictionary::run(ctx, sub_matches).await?, _ => unreachable!( "clap is configured to require a subcommand, and they're all handled above" ), diff --git a/crates/dtmt/src/mods/archive.rs b/crates/dtmt/src/mods/archive.rs deleted file mode 100644 index 37fec19..0000000 --- a/crates/dtmt/src/mods/archive.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::collections::{HashMap, HashSet}; -use std::fs::File; -use std::io::Write; -use std::path::{Path, PathBuf}; - -use color_eyre::eyre::{self, Context}; -use color_eyre::Result; -use sdk::murmur::IdString64; -use sdk::Bundle; -use zip::ZipWriter; - -pub struct Archive { - name: String, - bundles: Vec, - config_file: Option>, -} - -impl Archive { - pub fn new(name: String) -> Self { - Self { - name, - bundles: Vec::new(), - config_file: None, - } - } - - pub fn add_bundle(&mut self, bundle: Bundle) { - self.bundles.push(bundle) - } - - pub fn add_config(&mut self, content: Vec) { - self.config_file = Some(content); - } - - pub fn write

(&self, path: P) -> Result<()> - where - P: AsRef, - { - let config_file = self - .config_file - .as_ref() - .ok_or_else(|| eyre::eyre!("Config file is missing in mod archive"))?; - - let f = File::create(path.as_ref()).wrap_err_with(|| { - format!( - "failed to open file for reading: {}", - path.as_ref().display() - ) - })?; - let mut zip = ZipWriter::new(f); - - zip.add_directory(&self.name, Default::default())?; - - let base_path = PathBuf::from(&self.name); - - { - let name = base_path.join("dtmt.cfg"); - zip.start_file(name.to_string_lossy(), Default::default())?; - zip.write_all(config_file)?; - } - - let mut file_map = HashMap::new(); - - for bundle in self.bundles.iter() { - let bundle_name = match bundle.name() { - IdString64::Hash(_) => eyre::bail!("bundle name must be known as string. got hash"), - IdString64::String(s) => s, - }; - - let map_entry: &mut HashSet<_> = file_map.entry(bundle_name).or_default(); - - for file in bundle.files() { - map_entry.insert(file.name(false, None)); - } - - let name = bundle.name().to_murmur64(); - let path = base_path.join(name.to_string().to_ascii_lowercase()); - - zip.start_file(path.to_string_lossy(), Default::default())?; - - let data = bundle.to_binary()?; - zip.write_all(&data)?; - } - - { - let data = serde_sjson::to_string(&file_map)?; - zip.start_file( - base_path.join("files.sjson").to_string_lossy(), - Default::default(), - )?; - zip.write_all(data.as_bytes())?; - } - - zip.finish()?; - - Ok(()) - } -} From 865dcae43930034e47988a779f031c401c338d93 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 2 Mar 2023 11:38:08 +0100 Subject: [PATCH 026/335] feat(dtmt): Add parameter to deploy built bundles Ref: #40. --- crates/dtmt/src/cmd/build.rs | 64 +++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index a0db3f2..f4a92bf 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -40,6 +40,18 @@ pub(crate) fn command_definition() -> Command { .value_parser(value_parser!(PathBuf)) .help("The directory to write output files to."), ) + .arg( + Arg::new("deploy") + .long("deploy") + .short('d') + .value_parser(value_parser!(PathBuf)) + .help( + "If the path to the game (without the trailing '/bundle') is specified, \ + deploy the newly built bundles. \ + This will not adjust the bundle database or package files, so if files are \ + added or removed, you will have to import into DTMM and re-deploy there.", + ), + ) } #[tracing::instrument] @@ -242,13 +254,20 @@ pub(crate) async fn read_project_config(dir: Option) -> Result Result<()> { let cfg = read_project_config(matches.get_one::("directory").cloned()).await?; - tracing::debug!(?cfg); - let cfg = Arc::new(cfg); + + let game_dir = matches + .get_one::("deploy") + .map(|p| p.join("bundle")); let out_path = matches .get_one::("out") .expect("parameter should have default value"); + tracing::debug!(?cfg, ?game_dir, ?out_path); + + let game_dir = Arc::new(game_dir); + let cfg = Arc::new(cfg); + fs::create_dir_all(out_path) .await .wrap_err_with(|| format!("failed to create output directory '{}'", out_path.display()))?; @@ -258,8 +277,11 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> let tasks = cfg .packages .iter() - .map(|path| (path, cfg.clone(), file_map.clone())) - .map(|(path, cfg, file_map)| async move { + // The closure below would capture the `Arc`s before they could be cloned, + // so instead we need to clone them in a non-move block and inject them + // via parameters. + .map(|path| (path, cfg.clone(), file_map.clone(), game_dir.clone())) + .map(|(path, cfg, file_map, game_dir)| async move { if path.extension().is_some() { eyre::bail!( "Package name must be specified without file extension: {}", @@ -291,13 +313,33 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> } } - let name = bundle.name().to_murmur64(); - let path = out_path.join(name.to_string().to_ascii_lowercase()); - + let name = bundle.name().to_murmur64().to_string().to_ascii_lowercase(); + let path = out_path.join(&name); let data = bundle.to_binary()?; - fs::write(&path, data) + + tracing::trace!( + "Writing bundle {} to '{}'", + bundle.name().display(), + path.display() + ); + fs::write(&path, &data) .await - .wrap_err_with(|| format!("failed to write bundle to '{}'", path.display())) + .wrap_err_with(|| format!("failed to write bundle to '{}'", path.display()))?; + + if let Some(game_dir) = game_dir.as_ref() { + let path = game_dir.join(&name); + + tracing::trace!( + "Deploying bundle {} to '{}'", + bundle.name().display(), + path.display() + ); + fs::write(&path, &data) + .await + .wrap_err_with(|| format!("failed to write bundle to '{}'", path.display()))?; + } + + Ok(()) }); try_join_all(tasks) @@ -315,5 +357,9 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> tracing::info!("Compiled bundles written to '{}'", out_path.display()); + if let Some(game_dir) = game_dir.as_ref() { + tracing::info!("Deployed bundles to '{}'", game_dir.display()); + } + Ok(()) } From dd2ebcf4dffce0d49b83c53a1266b2ed4034ae70 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 2 Mar 2023 11:45:07 +0100 Subject: [PATCH 027/335] docs: Update changelog --- CHANGELOG.adoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 7c100b3..e32ff71 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -2,6 +2,11 @@ == [Unreleased] +=== Added + +- dtmt: split `build` into `build` and `package` +- dtmt: implement deploying built bundles + == 2023-03-01 === Added From 55c6ebf2e90b8e6c57ce594eac27a65237f0e876 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 2 Mar 2023 13:39:56 +0100 Subject: [PATCH 028/335] fix(dtmm): Fix config path on Windows --- crates/dtmm/src/util/config.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/dtmm/src/util/config.rs b/crates/dtmm/src/util/config.rs index d2ae44c..86812b3 100644 --- a/crates/dtmm/src/util/config.rs +++ b/crates/dtmm/src/util/config.rs @@ -60,7 +60,7 @@ pub(crate) struct Config { pub mod_order: Vec, } -#[cfg(not(arget_os = "windows"))] +#[cfg(not(target_os = "windows"))] pub fn get_default_config_path() -> PathBuf { let config_dir = std::env::var("XDG_CONFIG_DIR").unwrap_or_else(|_| { let home = std::env::var("HOME").unwrap_or_else(|_| { @@ -79,7 +79,7 @@ pub fn get_default_config_path() -> PathBuf { PathBuf::from(config_dir).join("dtmm").join("dtmm.cfg") } -#[cfg(not(arget_os = "windows"))] +#[cfg(not(target_os = "windows"))] pub fn get_default_data_dir() -> PathBuf { let data_dir = std::env::var("XDG_DATA_DIR").unwrap_or_else(|_| { let home = std::env::var("HOME").unwrap_or_else(|_| { From ed4a22c97acc41b3589ed8db63bf4a74257ea3ba Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 2 Mar 2023 13:47:54 +0100 Subject: [PATCH 029/335] Revert "feat(sdk): Switch to statically linked Oodle" This reverts commit d08321a6455a7721944b0965855f3e6feb757702. Closes #42. --- Cargo.lock | 89 +- crates/dtmm/Cargo.toml | 2 +- crates/dtmm/src/main.rs | 4 + crates/dtmt/Cargo.toml | 2 +- crates/dtmt/src/cmd/build.rs | 4 + crates/dtmt/src/cmd/bundle/mod.rs | 4 + lib/oodle-sys/.gitignore | 2 + lib/{oodle => oodle-sys}/Cargo.toml | 8 +- lib/oodle-sys/src/lib.rs | 77 ++ lib/oodle-sys/src/library.rs | 154 +++ lib/oodle-sys/src/types.rs | 197 ++++ lib/oodle/build.rs | 44 - lib/oodle/oodle2.h | 1643 --------------------------- lib/oodle/oodle2base.h | 167 --- lib/oodle/src/lib.rs | 145 --- lib/sdk/Cargo.toml | 2 +- lib/sdk/src/bundle/mod.rs | 8 +- 17 files changed, 458 insertions(+), 2094 deletions(-) create mode 100644 lib/oodle-sys/.gitignore rename lib/{oodle => oodle-sys}/Cargo.toml (69%) create mode 100644 lib/oodle-sys/src/lib.rs create mode 100644 lib/oodle-sys/src/library.rs create mode 100644 lib/oodle-sys/src/types.rs delete mode 100644 lib/oodle/build.rs delete mode 100644 lib/oodle/oodle2.h delete mode 100644 lib/oodle/oodle2base.h delete mode 100644 lib/oodle/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 30efb97..6e8ff1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,28 +118,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "bindgen" -version = "0.64.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn", - "which", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -261,15 +239,6 @@ dependencies = [ "jobserver", ] -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-expr" version = "0.11.0" @@ -294,17 +263,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "clang-sys" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" -dependencies = [ - "glob", - "libc", - "libloading", -] - [[package]] name = "clap" version = "4.1.7" @@ -717,7 +675,7 @@ dependencies = [ "druid", "dtmt-shared", "futures", - "oodle", + "oodle-sys", "sdk", "serde", "serde_sjson", @@ -744,7 +702,7 @@ dependencies = [ "glob", "libloading", "nanorand", - "oodle", + "oodle-sys", "path-clean", "pin-project-lite", "promptly", @@ -784,12 +742,6 @@ dependencies = [ "wio", ] -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - [[package]] name = "endian-type" version = "0.1.2" @@ -1434,12 +1386,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.139" @@ -1651,11 +1597,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] -name = "oodle" +name = "oodle-sys" version = "0.1.0" dependencies = [ - "bindgen", - "color-eyre", + "libloading", + "thiserror", "tracing", ] @@ -1765,12 +1711,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "pest" version = "2.5.5" @@ -2104,7 +2044,7 @@ dependencies = [ "libloading", "luajit2-sys", "nanorand", - "oodle", + "oodle-sys", "pin-project-lite", "serde", "serde_sjson", @@ -2198,12 +2138,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shlex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -2746,17 +2680,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - [[package]] name = "winapi" version = "0.3.9" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 6d95e32..6516bcc 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -13,7 +13,7 @@ confy = "0.5.1" druid = { git = "https://github.com/linebender/druid.git", features = ["im", "serde"] } dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } futures = "0.3.25" -oodle = { path = "../../lib/oodle", version = "*" } +oodle-sys = { path = "../../lib/oodle-sys", version = "*" } sdk = { path = "../../lib/sdk", version = "*" } serde_sjson = { path = "../../lib/serde_sjson", version = "*" } serde = { version = "1.0.152", features = ["derive", "rc"] } diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index a89dde5..abdce49 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -50,6 +50,10 @@ fn main() -> Result<()> { let (log_tx, log_rx) = tokio::sync::mpsc::unbounded_channel(); util::log::create_tracing_subscriber(log_tx); + unsafe { + oodle_sys::init(matches.get_one::("oodle")); + } + let config = util::config::read_config(&default_config_path, &matches) .wrap_err("failed to read config file")?; diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index ee5d4c4..9067ae8 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -15,7 +15,7 @@ futures-util = "0.3.24" glob = "0.3.0" libloading = "0.7.4" nanorand = "0.7.0" -oodle = { path = "../../lib/oodle", version = "*" } +oodle-sys = { path = "../../lib/oodle-sys", version = "*" } pin-project-lite = "0.2.9" promptly = "0.3.1" sdk = { path = "../../lib/sdk", version = "*" } diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index f4a92bf..c72a115 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -253,6 +253,10 @@ pub(crate) async fn read_project_config(dir: Option) -> Result Result<()> { + unsafe { + oodle_sys::init(matches.get_one::("oodle")); + } + let cfg = read_project_config(matches.get_one::("directory").cloned()).await?; let game_dir = matches diff --git a/crates/dtmt/src/cmd/bundle/mod.rs b/crates/dtmt/src/cmd/bundle/mod.rs index 0e7c9f7..2a18a02 100644 --- a/crates/dtmt/src/cmd/bundle/mod.rs +++ b/crates/dtmt/src/cmd/bundle/mod.rs @@ -18,6 +18,10 @@ pub(crate) fn command_definition() -> Command { #[tracing::instrument(skip_all)] pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { + unsafe { + oodle_sys::init(matches.get_one::("oodle")); + } + match matches.subcommand() { Some(("decompress", sub_matches)) => decompress::run(ctx, sub_matches).await, Some(("extract", sub_matches)) => extract::run(ctx, sub_matches).await, diff --git a/lib/oodle-sys/.gitignore b/lib/oodle-sys/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/lib/oodle-sys/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/lib/oodle/Cargo.toml b/lib/oodle-sys/Cargo.toml similarity index 69% rename from lib/oodle/Cargo.toml rename to lib/oodle-sys/Cargo.toml index 3283592..539427d 100644 --- a/lib/oodle/Cargo.toml +++ b/lib/oodle-sys/Cargo.toml @@ -1,13 +1,11 @@ [package] -name = "oodle" +name = "oodle-sys" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -color-eyre = "0.6.2" +libloading = "0.7.4" +thiserror = "1.0.38" tracing = "0.1.37" - -[build-dependencies] -bindgen = "0.64.0" diff --git a/lib/oodle-sys/src/lib.rs b/lib/oodle-sys/src/lib.rs new file mode 100644 index 0000000..8346c5f --- /dev/null +++ b/lib/oodle-sys/src/lib.rs @@ -0,0 +1,77 @@ +#![feature(c_size_t)] +#![feature(once_cell)] + +use std::ffi::OsStr; +use std::sync::OnceLock; + +mod library; +mod types; + +pub use library::Library; +pub use library::CHUNK_SIZE; +pub use types::*; + +#[derive(thiserror::Error, Debug)] +pub enum OodleError { + #[error("{0}")] + Oodle(String), + #[error(transparent)] + Library(#[from] libloading::Error), +} + +type Result = std::result::Result; + +static LIB: OnceLock = OnceLock::new(); + +/// Initialize the global library handle that this module's +/// functions operate on. +/// +/// # Safety +/// +/// The safety concerns as described by [`libloading::Library::new`] apply. +pub unsafe fn init>(name: Option

) { + let lib = match name { + Some(name) => Library::with_name(name), + None => Library::new(), + }; + + let lib = lib.expect("Failed to load library."); + if LIB.set(lib).is_err() { + panic!("Library was already initialized. Did you call `init` twice?"); + } +} + +fn get() -> Result<&'static Library> { + match LIB.get() { + Some(lib) => Ok(lib), + None => { + let err = OodleError::Oodle(String::from("Library has not been initialized, yet.")); + Err(err) + } + } +} + +pub fn decompress( + data: I, + fuzz_safe: OodleLZ_FuzzSafe, + check_crc: OodleLZ_CheckCRC, +) -> Result> +where + I: AsRef<[u8]>, +{ + let lib = get()?; + lib.decompress(data, fuzz_safe, check_crc) +} + +pub fn compress(data: I) -> Result> +where + I: AsRef<[u8]>, +{ + let lib = get()?; + lib.compress(data) +} + +pub fn get_decode_buffer_size(raw_size: usize, corruption_possible: bool) -> Result { + let lib = get()?; + lib.get_decode_buffer_size(raw_size, corruption_possible) +} diff --git a/lib/oodle-sys/src/library.rs b/lib/oodle-sys/src/library.rs new file mode 100644 index 0000000..ef773e4 --- /dev/null +++ b/lib/oodle-sys/src/library.rs @@ -0,0 +1,154 @@ +use std::{ffi::OsStr, ptr}; + +use libloading::Symbol; + +use super::Result; +use crate::{types::*, OodleError}; + +// Hardcoded chunk size of Bitsquid's bundle compression +pub const CHUNK_SIZE: usize = 512 * 1024; +pub const COMPRESSOR: OodleLZ_Compressor = OodleLZ_Compressor::Kraken; +pub const LEVEL: OodleLZ_CompressionLevel = OodleLZ_CompressionLevel::Optimal2; + +#[cfg(target_os = "windows")] +const OODLE_LIB_NAME: &str = "oo2core_8_win64"; + +#[cfg(target_os = "linux")] +const OODLE_LIB_NAME: &str = "liboo2corelinux64.so"; + +pub struct Library { + inner: libloading::Library, +} + +impl Library { + /// Load the Oodle library by its default name. + /// + /// The default name is platform-specific: + /// - Windows: `oo2core_8_win64` + /// - Linux: `liboo2corelinux64.so` + /// + /// # Safety + /// + /// The safety concerns as described by [`libloading::Library::new`] apply. + pub unsafe fn new() -> Result { + Self::with_name(OODLE_LIB_NAME) + } + + /// Load the Oodle library by the given name or path. + /// + /// See [`libloading::Library::new`] for how the `name` parameter is handled. + /// + /// # Safety + /// + /// The safety concerns as described by [`libloading::Library::new`] apply. + pub unsafe fn with_name>(name: P) -> Result { + let inner = libloading::Library::new(name)?; + Ok(Self { inner }) + } + + #[tracing::instrument(skip(self, data))] + pub fn decompress( + &self, + data: I, + fuzz_safe: OodleLZ_FuzzSafe, + check_crc: OodleLZ_CheckCRC, + ) -> Result> + where + I: AsRef<[u8]>, + { + let data = data.as_ref(); + let mut out = vec![0; CHUNK_SIZE]; + + let verbosity = if tracing::enabled!(tracing::Level::INFO) { + OodleLZ_Verbosity::Minimal + } else if tracing::enabled!(tracing::Level::DEBUG) { + OodleLZ_Verbosity::Some + } else if tracing::enabled!(tracing::Level::TRACE) { + OodleLZ_Verbosity::Lots + } else { + OodleLZ_Verbosity::None + }; + + let ret = unsafe { + let decompress: Symbol = self.inner.get(b"OodleLZ_Decompress\0")?; + + decompress( + data.as_ptr() as *const _, + data.len(), + out.as_mut_ptr() as *mut _, + out.len(), + fuzz_safe, + check_crc, + verbosity, + ptr::null_mut(), + 0, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + 0, + OodleLZ_Decode_ThreadPhase::UNTHREADED, + ) + }; + + if ret == 0 { + let err = OodleError::Oodle(String::from("Decompression failed.")); + return Err(err); + } + + Ok(out) + } + + #[tracing::instrument(name = "Oodle::compress", skip(self, data))] + pub fn compress(&self, data: I) -> Result> + where + I: AsRef<[u8]>, + { + let mut raw = Vec::from(data.as_ref()); + raw.resize(CHUNK_SIZE, 0); + + // TODO: Query oodle for buffer size + let mut out = vec![0u8; CHUNK_SIZE]; + + let ret = unsafe { + let compress: Symbol = self.inner.get(b"OodleLZ_Compress\0")?; + + compress( + COMPRESSOR, + raw.as_ptr() as *const _, + raw.len(), + out.as_mut_ptr() as *mut _, + LEVEL, + ptr::null_mut(), + 0, + ptr::null_mut(), + ptr::null_mut(), + 0, + ) + }; + + tracing::debug!(compressed_size = ret, "Compressed chunk"); + + if ret == 0 { + let err = OodleError::Oodle(String::from("Compression failed.")); + return Err(err); + } + + out.resize(ret as usize, 0); + + Ok(out) + } + + pub fn get_decode_buffer_size( + &self, + raw_size: usize, + corruption_possible: bool, + ) -> Result { + unsafe { + let f: Symbol = + self.inner.get(b"OodleLZ_GetDecodeBufferSize\0")?; + + let size = f(COMPRESSOR, raw_size, corruption_possible); + Ok(size) + } + } +} diff --git a/lib/oodle-sys/src/types.rs b/lib/oodle-sys/src/types.rs new file mode 100644 index 0000000..5d306f8 --- /dev/null +++ b/lib/oodle-sys/src/types.rs @@ -0,0 +1,197 @@ +#![allow(dead_code)] +use core::ffi::{c_char, c_int, c_size_t, c_ulonglong, c_void}; + +// Type definitions taken from Unreal Engine's `oodle2.h` + +#[repr(C)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug)] +pub enum OodleLZ_FuzzSafe { + No = 0, + Yes = 1, +} + +impl From for OodleLZ_FuzzSafe { + fn from(value: bool) -> Self { + if value { + Self::Yes + } else { + Self::No + } + } +} + +#[repr(C)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug)] +pub enum OodleLZ_CheckCRC { + No = 0, + Yes = 1, + Force32 = 0x40000000, +} + +impl From for OodleLZ_CheckCRC { + fn from(value: bool) -> Self { + if value { + Self::Yes + } else { + Self::No + } + } +} + +#[repr(C)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug)] +pub enum OodleLZ_Verbosity { + None = 0, + Minimal = 1, + Some = 2, + Lots = 3, + Force32 = 0x40000000, +} + +#[repr(C)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug)] +pub enum OodleLZ_Decode_ThreadPhase { + Phase1 = 1, + Phase2 = 2, + PhaseAll = 3, +} + +impl OodleLZ_Decode_ThreadPhase { + pub const UNTHREADED: Self = OodleLZ_Decode_ThreadPhase::PhaseAll; +} + +#[repr(C)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug)] +pub enum OodleLZ_Compressor { + Invalid = -1, + // None = memcpy, pass through uncompressed bytes + None = 3, + + // NEW COMPRESSORS: + // Fast decompression and high compression ratios, amazing! + Kraken = 8, + // Leviathan = Kraken's big brother with higher compression, slightly slower decompression. + Leviathan = 13, + // Mermaid is between Kraken & Selkie - crazy fast, still decent compression. + Mermaid = 9, + // Selkie is a super-fast relative of Mermaid. For maximum decode speed. + Selkie = 11, + // Hydra, the many-headed beast = Leviathan, Kraken, Mermaid, or Selkie (see $OodleLZ_About_Hydra) + Hydra = 12, + BitKnit = 10, + // DEPRECATED but still supported + Lzb16 = 4, + Lzna = 7, + Lzh = 0, + Lzhlw = 1, + Lznib = 2, + Lzblw = 5, + Lza = 6, + Count = 14, + Force32 = 0x40000000, +} + +#[repr(C)] +#[allow(non_camel_case_types)] +#[derive(Clone, Copy, Debug)] +pub enum OodleLZ_CompressionLevel { + // don't compress, just copy raw bytes + None = 0, + // super fast mode, lower compression ratio + SuperFast = 1, + // fastest LZ mode with still decent compression ratio + VeryFast = 2, + // fast - good for daily use + Fast = 3, + // standard medium speed LZ mode + Normal = 4, + // optimal parse level 1 (faster optimal encoder) + Optimal1 = 5, + // optimal parse level 2 (recommended baseline optimal encoder) + Optimal2 = 6, + // optimal parse level 3 (slower optimal encoder) + Optimal3 = 7, + // optimal parse level 4 (very slow optimal encoder) + Optimal4 = 8, + // optimal parse level 5 (don't care about encode speed, maximum compression) + Optimal5 = 9, + // faster than SuperFast, less compression + HyperFast1 = -1, + // faster than HyperFast1, less compression + HyperFast2 = -2, + // faster than HyperFast2, less compression + HyperFast3 = -3, + // fastest, less compression + HyperFast4 = -4, + Force32 = 0x40000000, +} + +impl OodleLZ_CompressionLevel { + // alias hyperfast base level + pub const HYPERFAST: Self = OodleLZ_CompressionLevel::HyperFast1; + // alias optimal standard level + pub const OPTIMAL: Self = OodleLZ_CompressionLevel::Optimal2; + // maximum compression level + pub const MAX: Self = OodleLZ_CompressionLevel::Optimal5; + // fastest compression level + pub const MIN: Self = OodleLZ_CompressionLevel::HyperFast4; + pub const INVALID: Self = OodleLZ_CompressionLevel::Force32; +} + +#[allow(non_camel_case_types)] +pub type t_fp_OodleCore_Plugin_Printf = + extern "C" fn(level: c_int, file: *const c_char, line: c_int, fmt: *const c_char); + +#[allow(non_camel_case_types)] +pub type OodleLZ_Decompress = extern "C" fn( + compressed_buffer: *const c_void, + compressed_length: c_size_t, + raw_buffer: *mut c_void, + raw_length: c_size_t, + fuzz_safe: OodleLZ_FuzzSafe, + check_crc: OodleLZ_CheckCRC, + verbosity: OodleLZ_Verbosity, + decBufBase: *mut c_void, + decBufSize: c_size_t, + callback: *const c_void, + callback_user_data: *const c_void, + decoder_memory: *mut c_void, + decoder_memory_size: c_size_t, + thread_phase: OodleLZ_Decode_ThreadPhase, +) -> c_ulonglong; + +#[allow(non_camel_case_types)] +pub type OodleLZ_Compress = extern "C" fn( + compressor: OodleLZ_Compressor, + raw_buffer: *const c_void, + raw_len: c_size_t, + compressed_buffer: *mut c_void, + level: OodleLZ_CompressionLevel, + options: *const c_void, + dictionary_base: c_size_t, + lrm: *const c_void, + scratch_memory: *mut c_void, + scratch_size: c_size_t, +) -> c_ulonglong; + +#[allow(non_camel_case_types)] +pub type OodleLZ_GetDecodeBufferSize = extern "C" fn( + compressor: OodleLZ_Compressor, + raw_size: c_size_t, + corruption_possible: bool, +) -> c_size_t; + +#[allow(non_camel_case_types)] +pub type OodleCore_Plugins_SetPrintf = + extern "C" fn(f: t_fp_OodleCore_Plugin_Printf) -> t_fp_OodleCore_Plugin_Printf; + +#[allow(non_camel_case_types)] +pub type OodleCore_Plugin_Printf_Verbose = t_fp_OodleCore_Plugin_Printf; + +#[allow(non_camel_case_types)] +pub type OodleCore_Plugin_Printf_Default = t_fp_OodleCore_Plugin_Printf; diff --git a/lib/oodle/build.rs b/lib/oodle/build.rs deleted file mode 100644 index 2a48078..0000000 --- a/lib/oodle/build.rs +++ /dev/null @@ -1,44 +0,0 @@ -extern crate bindgen; - -use std::env; -use std::path::PathBuf; - -fn main() { - // Tell cargo to look for shared libraries in the specified directory - // println!("cargo:rustc-link-search=/path/to/lib"); - - // Tell cargo to tell rustc to link the system bzip2 - // shared library. - if cfg!(target_os = "windows") { - println!("cargo:rustc-link-lib=oo2core_8_win64"); - } else { - println!("cargo:rustc-link-lib=oo2corelinux64"); - } - - // Tell cargo to invalidate the built crate whenever the wrapper changes - println!("cargo:rerun-if-changed=oodle2.h"); - - // The bindgen::Builder is the main entry point - // to bindgen, and lets you build up options for - // the resulting bindings. - let bindings = bindgen::Builder::default() - // The input header we would like to generate - // bindings for. - .header("oodle2base.h") - .header("oodle2.h") - .blocklist_file("stdint.h") - .blocklist_file("stdlib.h") - // Tell cargo to invalidate the built crate whenever any of the - // included header files changed. - .parse_callbacks(Box::new(bindgen::CargoCallbacks)) - // Finish the builder and generate the bindings. - .generate() - // Unwrap the Result and panic on failure. - .expect("Unable to generate bindings"); - - // Write the bindings to the $OUT_DIR/bindings.rs file. - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - bindings - .write_to_file(out_path.join("bindings.rs")) - .expect("Couldn't write bindings!"); -} diff --git a/lib/oodle/oodle2.h b/lib/oodle/oodle2.h deleted file mode 100644 index ffe4152..0000000 --- a/lib/oodle/oodle2.h +++ /dev/null @@ -1,1643 +0,0 @@ - -//=================================================== -// Oodle2 Core header -// (C) Copyright 1994-2021 Epic Games Tools LLC -//=================================================== - -#ifndef __OODLE2_H_INCLUDED__ -#define __OODLE2_H_INCLUDED__ - -#ifndef OODLE2_PUBLIC_HEADER -#define OODLE2_PUBLIC_HEADER 1 -#endif - -#ifndef __OODLE2BASE_H_INCLUDED__ -#include "oodle2base.h" -#endif - -#ifdef _MSC_VER -#pragma pack(push, Oodle, 8) - -#pragma warning(push) -#pragma warning(disable : 4127) // conditional is constant -#endif - -// header version : -// the DLL is incompatible when MAJOR is bumped -// MINOR is for internal revs and bug fixes that don't affect API compatibility -#define OODLE2_VERSION_MAJOR 9 -#define OODLE2_VERSION_MINOR 5 - -// OodleVersion string is 1 . MAJOR . MINOR -// don't make it from macros cuz the doc tool has to parse the string literal - -#define OodleVersion "2.9.5" /* -*/ - -//----------------------------------------------------- -// OodleLZ - -#if 0 -#define OODLE_ALLOW_DEPRECATED_COMPRESSORS /* If you need to encode with the deprecated compressors, define this before including oodle2.h - - You may still decode with them without defining this. -*/ -#endif - -// Default verbosity selection of 0 will not even log when it sees corruption -typedef enum OodleLZ_Verbosity -{ - OodleLZ_Verbosity_None = 0, - OodleLZ_Verbosity_Minimal = 1, - OodleLZ_Verbosity_Some = 2, - OodleLZ_Verbosity_Lots = 3, - OodleLZ_Verbosity_Force32 = 0x40000000 -} OodleLZ_Verbosity; -/* Verbosity of LZ functions - LZ functions print information to the function set by $OodleCore_Plugins_SetPrintf - or $OodleXLog_Printf if using OodleX. -*/ - -OO_COMPILER_ASSERT( sizeof(OodleLZ_Verbosity) == 4 ); - -typedef enum OodleLZ_Compressor -{ - OodleLZ_Compressor_Invalid = -1, - OodleLZ_Compressor_None = 3, // None = memcpy, pass through uncompressed bytes - - // NEW COMPRESSORS : - OodleLZ_Compressor_Kraken = 8, // Fast decompression and high compression ratios, amazing! - OodleLZ_Compressor_Leviathan = 13,// Leviathan = Kraken's big brother with higher compression, slightly slower decompression. - OodleLZ_Compressor_Mermaid = 9, // Mermaid is between Kraken & Selkie - crazy fast, still decent compression. - OodleLZ_Compressor_Selkie = 11, // Selkie is a super-fast relative of Mermaid. For maximum decode speed. - OodleLZ_Compressor_Hydra = 12, // Hydra, the many-headed beast = Leviathan, Kraken, Mermaid, or Selkie (see $OodleLZ_About_Hydra) - -#ifdef OODLE_ALLOW_DEPRECATED_COMPRESSORS - OodleLZ_Compressor_BitKnit = 10, // no longer supported as of Oodle 2.9.0 - OodleLZ_Compressor_LZB16 = 4, // DEPRECATED but still supported - OodleLZ_Compressor_LZNA = 7, // no longer supported as of Oodle 2.9.0 - OodleLZ_Compressor_LZH = 0, // no longer supported as of Oodle 2.9.0 - OodleLZ_Compressor_LZHLW = 1, // no longer supported as of Oodle 2.9.0 - OodleLZ_Compressor_LZNIB = 2, // no longer supported as of Oodle 2.9.0 - OodleLZ_Compressor_LZBLW = 5, // no longer supported as of Oodle 2.9.0 - OodleLZ_Compressor_LZA = 6, // no longer supported as of Oodle 2.9.0 -#endif - - OodleLZ_Compressor_Count = 14, - OodleLZ_Compressor_Force32 = 0x40000000 -} OodleLZ_Compressor; -/* Selection of compression algorithm. - - Each compressor provides a different balance of speed vs compression ratio. - - New Oodle users should only use the new sea monster family of compressors. - - The OODLE_ALLOW_DEPRECATED_COMPRESSORS set of compressors is no longer supported - as of Oodle 2.9.0 ; see $Oodle_FAQ_deprecated_compressors - - The sea monsters are all fuzz safe and use whole-block quantum (not the 16k quantum) - ($OodleLZ_Compressor_UsesWholeBlockQuantum) - - If you need to encode the deprecated compressors, define $OODLE_ALLOW_DEPRECATED_COMPRESSORS before - including oodle2.h - - See $Oodle_FAQ_WhichLZ for a quick FAQ on which compressor to use - - See $OodleLZ_About for discussion of how to choose a compressor. -*/ - -OO_COMPILER_ASSERT( sizeof(OodleLZ_Compressor) == 4 ); - -typedef enum OodleLZ_PackedRawOverlap -{ - OodleLZ_PackedRawOverlap_No = 0, - OodleLZ_PackedRawOverlap_Yes = 1, - OodleLZ_PackedRawOverlap_Force32 = 0x40000000 -} OodleLZ_PackedRawOverlap; -/* Bool enum -*/ - -typedef enum OodleLZ_CheckCRC -{ - OodleLZ_CheckCRC_No = 0, - OodleLZ_CheckCRC_Yes = 1, - OodleLZ_CheckCRC_Force32 = 0x40000000 -} OodleLZ_CheckCRC; -/* Bool enum for the LZ decoder - should it check CRC before decoding or not? - - NOTE : the CRC's in the LZH decompress checks are the CRC's of the *compressed* bytes. This allows checking the CRc - prior to decompression, so corrupted data cannot be fed to the compressor. - - To use OodleLZ_CheckCRC_Yes, the compressed data must have been made with $(OodleLZ_CompressOptions:sendQuantumCRCs) set to true. - - If you want a CRC of the raw bytes, there is one optionally stored in the $OodleLZ_SeekTable and can be confirmed with - $OodleLZ_CheckSeekTableCRCs -*/ - - -typedef enum OodleLZ_Profile -{ - OodleLZ_Profile_Main=0, // Main profile (all current features allowed) - OodleLZ_Profile_Reduced=1, // Reduced profile (Kraken only, limited feature set) - OodleLZ_Profile_Force32 = 0x40000000 -} OodleLZ_Profile; -/* Decode profile to target */ - -// Not flagged for idoc and done using a #define since it's internal (testing) use only -#define OodleLZ_Profile_Internal_Custom ((OodleLZ_Profile)100) - -OO_COMPILER_ASSERT( sizeof(OodleLZ_Profile) == 4 ); - -typedef enum OodleDecompressCallbackRet -{ - OodleDecompressCallbackRet_Continue=0, - OodleDecompressCallbackRet_Cancel=1, - OodleDecompressCallbackRet_Invalid=2, - OodleDecompressCallbackRet_Force32 = 0x40000000 -} OodleDecompressCallbackRet; -/* Return value for $OodleDecompressCallback - return OodleDecompressCallbackRet_Cancel to abort the in-progress decompression -*/ - -OODEFFUNC typedef OodleDecompressCallbackRet (OODLE_CALLBACK OodleDecompressCallback)(void * userdata, const OO_U8 * rawBuf,OO_SINTa rawLen,const OO_U8 * compBuf,OO_SINTa compBufferSize , OO_SINTa rawDone, OO_SINTa compUsed); -/* User-provided callback for decompression - - $:userdata the data you passed for _pcbData_ - $:rawBuf the decompressed buffer - $:rawLen the total decompressed length - $:compBuf the compressed buffer - $:compBufferSize the total compressed length - $:rawDone number of bytes in rawBuf decompressed so far - $:compUsed number of bytes in compBuf consumed so far - - OodleDecompressCallback is called incrementally during decompression. -*/ - -typedef enum OodleLZ_CompressionLevel -{ - OodleLZ_CompressionLevel_None=0, // don't compress, just copy raw bytes - OodleLZ_CompressionLevel_SuperFast=1, // super fast mode, lower compression ratio - OodleLZ_CompressionLevel_VeryFast=2, // fastest LZ mode with still decent compression ratio - OodleLZ_CompressionLevel_Fast=3, // fast - good for daily use - OodleLZ_CompressionLevel_Normal=4, // standard medium speed LZ mode - - OodleLZ_CompressionLevel_Optimal1=5, // optimal parse level 1 (faster optimal encoder) - OodleLZ_CompressionLevel_Optimal2=6, // optimal parse level 2 (recommended baseline optimal encoder) - OodleLZ_CompressionLevel_Optimal3=7, // optimal parse level 3 (slower optimal encoder) - OodleLZ_CompressionLevel_Optimal4=8, // optimal parse level 4 (very slow optimal encoder) - OodleLZ_CompressionLevel_Optimal5=9, // optimal parse level 5 (don't care about encode speed, maximum compression) - - OodleLZ_CompressionLevel_HyperFast1=-1, // faster than SuperFast, less compression - OodleLZ_CompressionLevel_HyperFast2=-2, // faster than HyperFast1, less compression - OodleLZ_CompressionLevel_HyperFast3=-3, // faster than HyperFast2, less compression - OodleLZ_CompressionLevel_HyperFast4=-4, // fastest, less compression - - // aliases : - OodleLZ_CompressionLevel_HyperFast=OodleLZ_CompressionLevel_HyperFast1, // alias hyperfast base level - OodleLZ_CompressionLevel_Optimal = OodleLZ_CompressionLevel_Optimal2, // alias optimal standard level - OodleLZ_CompressionLevel_Max = OodleLZ_CompressionLevel_Optimal5, // maximum compression level - OodleLZ_CompressionLevel_Min = OodleLZ_CompressionLevel_HyperFast4, // fastest compression level - - OodleLZ_CompressionLevel_Force32 = 0x40000000, - OodleLZ_CompressionLevel_Invalid = OodleLZ_CompressionLevel_Force32 -} OodleLZ_CompressionLevel; -/* Selection of compression encoder complexity - - Higher numerical value of CompressionLevel = slower compression, but smaller compressed data. - - The compressed stream is always decodable with the same decompressors. - CompressionLevel controls the amount of work the encoder does to find the best compressed bit stream. - CompressionLevel does not primary affect decode speed, it trades off encode speed for compressed bit stream quality. - - I recommend starting with OodleLZ_CompressionLevel_Normal, then try up or down if you want - faster encoding or smaller output files. - - The Optimal levels are good for distribution when you compress rarely and decompress often; - they provide very high compression ratios but are slow to encode. Optimal2 is the recommended level - to start with of the optimal levels. - Optimal4 and 5 are not recommended for common use, they are very slow and provide the maximum compression ratio, - but the gain over Optimal3 is usually small. - - The HyperFast levels have negative numeric CompressionLevel values. - They are faster than SuperFast for when you're encoder CPU time constrained or want - something closer to symmetric compression vs. decompression time. - The HyperFast levels are currently only available in Kraken, Mermaid & Selkie. - Higher levels of HyperFast are faster to encode, eg. HyperFast4 is the fastest. - - The CompressionLevel does not affect decode speed much. Higher compression level does not mean - slower to decode. To trade off decode speed vs ratio, use _spaceSpeedTradeoffBytes_ in $OodleLZ_CompressOptions - -*/ - -OO_COMPILER_ASSERT( sizeof(OodleLZ_CompressionLevel) == 4 ); - -typedef enum OodleLZ_Jobify -{ - OodleLZ_Jobify_Default=0, // Use compressor default for level of internal job usage - OodleLZ_Jobify_Disable=1, // Don't use jobs at all - OodleLZ_Jobify_Normal=2, // Try to balance parallelism with increased memory usage - OodleLZ_Jobify_Aggressive=3, // Maximize parallelism even when doing so requires large amounts of memory - OodleLZ_Jobify_Count=4, - - OodleLZ_Jobify_Force32 = 0x40000000, -} OodleLZ_Jobify; -/* Controls the amount of internal threading in $OodleLZ_Compress calls - - Once you install a pluggable job system via $OodleCore_Plugins_SetJobSystem, Oodle can internally break - heavy-weight compression tasks into smaller jobs that can run in parallel. This can speed up - compression of large blocks of data at Optimal1 and higher levels substantially. - - The trade-off is that running more jobs concurrently rather than sequentially can greatly increase - memory requirements when there are multiple outstanding memory-intensive jobs. - - OodleLZ_Jobify_Default lets the compressor decide; typically compressors will default to "Normal" - when a pluggable job system has been installed, and "Disable" otherwise. - - OodleLZ_Jobify_Disable disables use of internal jobs entirely; all compression work is done on - the calling thread. This minimizes the amount of memory used, and is also appropriate when you're - getting parallelism in other ways, e.g. by running OodleLZ_Compress on many threads yourself. - - OodleLZ_Jobify_Normal uses jobs to increase compressor parallelism and speeds up compression of - large blocks of data, but avoids handing out many concurrent jobs for tasks that are memory-intensive. - - OodleLZ_Jobify_Aggressive will use concurrent jobs even for highly memory-intensive tasks. This - can speed up things further, but at a potentially significant increase in the amount of memory used - by Oodle. - -*/ - -#define OODLELZ_LOCALDICTIONARYSIZE_MAX (1<<30) /* Maximum value of maxLocalDictionarySize in OodleLZ_CompressOptions -*/ - -#define OODLELZ_SPACESPEEDTRADEOFFBYTES_DEFAULT (256) /* Default value of spaceSpeedTradeoffBytes in OodleLZ_CompressOptions - Changes how the encoder makes decisions in the bit stream - Higher spaceSpeedTradeoffBytes favors decode speed more (larger compressed files) - Lower spaceSpeedTradeoffBytes favors smaller compressed files (slower decoder) - Goes in a power of 2 scale; so try 64,128 and 512,1024 - (OODLELZ_SPACESPEEDTRADEOFFBYTES_DEFAULT/2) or (OODLELZ_SPACESPEEDTRADEOFFBYTES_DEFAULT*2) -*/ - - -typedef OOSTRUCT OodleLZ_CompressOptions -{ - OO_U32 unused_was_verbosity; // unused ; was verbosity (set to zero) - OO_S32 minMatchLen; // minimum match length ; cannot be used to reduce a compressor's default MML, but can be higher. On some types of data, a large MML (6 or 8) is a space-speed win. - OO_BOOL seekChunkReset; // whether chunks should be independent, for seeking and parallelism - OO_S32 seekChunkLen; // length of independent seek chunks (if seekChunkReset) ; must be a power of 2 and >= $OODLELZ_BLOCK_LEN ; you can use $OodleLZ_MakeSeekChunkLen - OodleLZ_Profile profile; // decoder profile to target (set to zero) - OO_S32 dictionarySize; // sets a maximum offset for matches, if lower than the maximum the format supports. <= 0 means infinite (use whole buffer). Often power of 2 but doesn't have to be. - OO_S32 spaceSpeedTradeoffBytes; // this is a number of bytes; I must gain at least this many bytes of compressed size to accept a speed-decreasing decision - OO_S32 unused_was_maxHuffmansPerChunk; // unused ; was maxHuffmansPerChunk - OO_BOOL sendQuantumCRCs; // should the encoder send a CRC of each compressed quantum, for integrity checks; this is necessary if you want to use OodleLZ_CheckCRC_Yes on decode - OO_S32 maxLocalDictionarySize; // (Optimals) size of local dictionary before needing a long range matcher. This does not set a window size for the decoder; it's useful to limit memory use and time taken in the encoder. maxLocalDictionarySize must be a power of 2. Must be <= OODLELZ_LOCALDICTIONARYSIZE_MAX - OO_BOOL makeLongRangeMatcher; // (Optimals) should the encoder find matches beyond maxLocalDictionarySize using an LRM - OO_S32 matchTableSizeLog2; //(non-Optimals) when variable, sets the size of the match finder structure (often a hash table) ; use 0 for the compressor's default - - OodleLZ_Jobify jobify; // controls internal job usage by compressors - void * jobifyUserPtr; // user pointer passed through to RunJob and WaitJob callbacks - - OO_S32 farMatchMinLen; // far matches must be at least this len - OO_S32 farMatchOffsetLog2; // if not zero, the log2 of an offset that must meet farMatchMinLen - - OO_U32 reserved[4]; // reserved space for adding more options; zero these! -} OodleLZ_CompressOptions; -/* Options for the compressor - - Typically filled by calling $OodleLZ_CompressOptions_GetDefault , then individual options may be modified, like : - - OodleLZ_CompressOptions my_options = *OodleLZ_CompressOptions_GetDefault() - - To ensure you have set up the options correctly, call $OodleLZ_CompressOptions_Validate. - - _unused_was_verbosity_ : place holder, set to zero - - _minMatchLen_ : rarely useful. Default value of 0 means let the compressor decide. On some types of data, - bumping this up to 4,6, or 8 can improve decode speed with little effect on compression ratio. Most of the - Oodle compressors use a default MML of 4 at levels below 7, and MML 3 at levels >= 7. If you want to keep MML 4 - at the higher levels, set _minMatchLen_ here to 4. _minMatchLen_ cannot be used to reduce the base MML of the compressor, only to increase it. - - _seekChunkReset_ must be true if you want the decode to be able to run "Wide", with pieces that can be - decoded independently (not keeping previous pieces in memory for match references). - - _seekChunkLen_ : length of independent seek chunks (if seekChunkReset) ; must be a power of 2 and >= $OODLELZ_BLOCK_LEN ; you can use $OodleLZ_MakeSeekChunkLen - - _profile_ : tells the encoder to target alternate bitstream profile. Default value of zero for normal use. - - _dictionarySize_ : limits the encoder to partial buffer access for matches. Can be useful for decoding incrementally - without keeping the entire output buffer in memory. - - _spaceSpeedTradeoffBytes_ is a way to trade off compression ratio for decode speed. If you make it smaller, - you get more compression ratio and slower decodes. It's the number of bytes that a decision must save to - be worth a slower decode. Default is 256 (OODLELZ_SPACESPEEDTRADEOFFBYTES_DEFAULT). So that means the encoder must be able to save >= 256 bytes to - accept something that will slow down decoding (like adding another Huffman table). The typical range is - 64-1024. - - Lower _spaceSpeedTradeoffBytes_ = more compression, slower decode - Higher _spaceSpeedTradeoffBytes_ = less compression, faster decode - - _spaceSpeedTradeoffBytes_ is the primary parameter for controlling Hydra. The default value of 256 will make - Hydra decodes that are just a little bit faster than Kraken. You get Kraken speeds around 200, and Mermaid - speeds around 1200. - - At the extreme, a _spaceSpeedTradeoffBytes_ of zero would mean all you care about is compression ratio, not decode - speed, you want the encoder to make the smallest possible output. (you cannot actually set zero, as zero values - always mean "use default" in this struct; you never really want zero anyway) - Generally _spaceSpeedTradeoffBytes_ below 16 provides diminishing gains in size with pointless decode speed loss. - - _spaceSpeedTradeoffBytes_ is on sort of powers of 2 scale, so you might want to experiment with 32,64,128,256,512 - - _spaceSpeedTradeoffBytes_ outside the range [16 - 2048] is not recommended. - - _unused_was_maxHuffmansPerChunk_ : place holder, set to zero - - _sendQuantumCRCs_ : send hashes of the compressed data to verify in the decoder; not recommended, if you need data - verification, use your own system outside of Oodle. DEPRECATED, not recommended. For backwards compatibility only. - - _maxLocalDictionarySize_ : only applies to optimal parsers at level >= Optimal2. This limits the encoder memory use. - Making it larger = more compression, higher memory use. Matches within maxLocalDictionarySize are found exactly, - outside the maxLocalDictionarySize window an approximate long range matcher is used. - - _makeLongRangeMatcher_ : whether an LRM should be used to find matches outside the _maxLocalDictionarySize_ window - (Optimal levels only) - - _matchTableSizeLog2_ : for non-optimal levels (level <= Normal), controls the hash table size. Making this very - small can sometimes boost encoder speed. For the very fastest encoding, use the SuperFast level and change - _matchTableSizeLog2_ to 12 or 13. - - _matchTableSizeLog2_ should usually be left zero to use the encoder's default - - _matchTableSizeLog2_ allows you to limit memory use of the non-Optimal encoder levels. Memory use is roughly - ( 1 MB + 4 << matchTableSizeLog2 ) - - _jobify_ tells compressors how to use internal jobs for compression tasks. Jobs can be run in parallel using the - job system plugins set with $OodleCore_Plugins_SetJobSystem. Not all compressors or compression level support - jobs, but the slower ones generally do. The default value of jobify is to use a thread system if one is installed. - - _farMatchMinLen_ and _farMatchOffsetLog2_ can be used to tune the encoded stream for a known cache size on the - decoding hardware. If set, then offsets with log2 greater or each to _farMatchOffsetLog2_ must have a minimum - length of _farMatchMinLen_. For example to target a machine with a 2 MB cache, set _farMatchOffsetLog2_ to 21, - and _farMatchMinLen_ to something large, like 16 or 20. - - Without _farMatchMinLen_ and _farMatchOffsetLog2_ set, the Oodle encoders tune for a blend of cache sizes that works - well on most machines. _dictionarySize_ can also be used to tune for cache size, but cuts off all matches - beyond a certain distance. That may be more appropriate when you don't want to go out of cache at all. - _farMatchMinLen_ can only be used to make the standard blend target more restrictive; it can reduce the target cache size - but can't make it larger (or it can raise min match len outside cache but can't make it shorter). - - For help on setting up OodleLZ_CompressOptions contact support at oodle@radgametools.com - - NOTE : fields you do not set should always be zero initialized. In particular the _reserved_ fields should be zeroed. - Zero always means "use default" and is a future-portable initialization value. - - If you set fields to zero to mean "use default" you can call $OodleLZ_CompressOptions_Validate to change them - to default values. This is done automatically internally if you don't do it explicitly. - -*/ - -typedef enum OodleLZ_Decode_ThreadPhase -{ - OodleLZ_Decode_ThreadPhase1 = 1, - OodleLZ_Decode_ThreadPhase2 = 2, - OodleLZ_Decode_ThreadPhaseAll = 3, - OodleLZ_Decode_Unthreaded = OodleLZ_Decode_ThreadPhaseAll -} OodleLZ_Decode_ThreadPhase; -/* ThreadPhase for threaded Oodle decode - - Check $OodleLZ_Compressor_CanDecodeThreadPhased - (currently only used by Kraken) - - See $OodleLZ_About_ThreadPhasedDecode - -*/ - -typedef enum OodleLZ_FuzzSafe -{ - OodleLZ_FuzzSafe_No = 0, - OodleLZ_FuzzSafe_Yes = 1 -} OodleLZ_FuzzSafe; -/* OodleLZ_FuzzSafe (deprecated) - - About fuzz safety: - - Fuzz Safe decodes will not crash on corrupt data. They may or may not return failure, and produce garbage output. - - Fuzz safe decodes will not read out of bounds. They won't put data on the stack or previously in memory - into the output buffer. - - As of Oodle 2.9.0 all compressors supported are fuzzsafe, so OodleLZ_FuzzSafe_Yes should always be used and this - enum is deprecated. - -*/ - -#define OODLELZ_BLOCK_LEN (1<<18) /* The number of raw bytes per "seek chunk" - Seek chunks can be decompressed independently if $(OodleLZ_CompressOptions:seekChunkReset) is set. -*/ - -#define OODLELZ_BLOCK_MAXIMUM_EXPANSION (2) -#define OODLELZ_BLOCK_MAX_COMPLEN (OODLELZ_BLOCK_LEN+OODLELZ_BLOCK_MAXIMUM_EXPANSION) /* Maximum expansion per $OODLELZ_BLOCK_LEN is 1 byte. - Note that the compressed buffer must be allocated bigger than this (use $OodleLZ_GetCompressedBufferSizeNeeded) -*/ - -#define OODLELZ_QUANTUM_LEN (1<<14) /* Minimum decompression quantum (for old legacy codecs only) - - Deprecated. - - The new sea monster family of compressors use a whole block quantum (OODLELZ_BLOCK_LEN). - Check $OodleLZ_Compressor_UsesWholeBlockQuantum -*/ - -// 5 byte expansion per-quantum with CRC's -#define OODLELZ_QUANTUM_MAXIMUM_EXPANSION (5) - -#define OODLELZ_QUANTUM_MAX_COMPLEN (OODLELZ_QUANTUM_LEN+OODLELZ_QUANTUM_MAXIMUM_EXPANSION) - -#define OODLELZ_SEEKCHUNKLEN_MIN OODLELZ_BLOCK_LEN -#define OODLELZ_SEEKCHUNKLEN_MAX (1<<29) // half GB - -typedef OOSTRUCT OodleLZ_DecodeSome_Out -{ - OO_S32 decodedCount; // number of uncompressed bytes decoded - OO_S32 compBufUsed; // number of compressed bytes consumed - - - OO_S32 curQuantumRawLen; // tells you the current quantum size. you must have at least this much room available in the output buffer to be able to decode anything. - OO_S32 curQuantumCompLen; // if you didn't pass in enough data, nothing will decode (decodedCount will be 0), and this will tell you how much is needed -} OodleLZ_DecodeSome_Out; -/* Output value of $OodleLZDecoder_DecodeSome -*/ - -//--------------------------------------------- - -//======================================================= - -typedef OOSTRUCT OodleLZ_SeekTable -{ - OodleLZ_Compressor compressor; // which compressor was used - OO_BOOL seekChunksIndependent; // are the seek chunks independent, or must they be decompressed in sequence - - OO_S64 totalRawLen; // total uncompressed data lenth - OO_S64 totalCompLen; // sum of seekChunkCompLens - - OO_S32 numSeekChunks; // derived from rawLen & seekChunkLen - OO_S32 seekChunkLen; // multiple of OODLELZ_BLOCK_LEN - - OO_U32 * seekChunkCompLens; // array of compressed lengths of seek chunks - OO_U32 * rawCRCs; // crc of the raw bytes of the chunk (optional; NULL unless $OodleLZSeekTable_Flags_MakeRawCRCs was specified) -} OodleLZ_SeekTable; - -typedef enum OodleLZSeekTable_Flags -{ - OodleLZSeekTable_Flags_None = 0, // default - OodleLZSeekTable_Flags_MakeRawCRCs = 1, // make the _rawCRCs_ member of $OodleLZ_SeekTable - OodleLZSeekTable_Flags_Force32 = 0x40000000 -} OodleLZSeekTable_Flags; - -//===================================================== - - -typedef OOSTRUCT OodleConfigValues -{ - OO_S32 m_OodleLZ_LW_LRM_step; // LZHLW LRM : bytes between LRM entries - OO_S32 m_OodleLZ_LW_LRM_hashLength; // LZHLW LRM : bytes hashed for each LRM entries - OO_S32 m_OodleLZ_LW_LRM_jumpbits; // LZHLW LRM : bits of hash used for jump table - - OO_S32 m_OodleLZ_Decoder_Max_Stack_Size; // if OodleLZ_Decompress needs to allocator a Decoder object, and it's smaller than this size, it's put on the stack instead of the heap - OO_S32 m_OodleLZ_Small_Buffer_LZ_Fallback_Size_Unused; // deprecated - OO_S32 m_OodleLZ_BackwardsCompatible_MajorVersion; // if you need to encode streams that can be read with an older version of Oodle, set this to the Oodle2 MAJOR version number that you need compatibility with. eg to be compatible with oodle 2.7.3 you would put 7 here - - OO_U32 m_oodle_header_version; // = OODLE_HEADER_VERSION - -} OodleConfigValues; -/* OodleConfigValues - - Struct of user-settable low level config values. See $Oodle_SetConfigValues. - - May have different defaults per platform. -*/ - -OOFUNC1 void OOFUNC2 Oodle_GetConfigValues(OodleConfigValues * ptr); -/* Get $OodleConfigValues - - $:ptr filled with OodleConfigValues - - Gets the current $OodleConfigValues. - - May be different per platform. -*/ - -OOFUNC1 void OOFUNC2 Oodle_SetConfigValues(const OodleConfigValues * ptr); -/* Set $OodleConfigValues - - $:ptr your desired OodleConfigValues - - Sets the global $OodleConfigValues from your struct. - - You should call $Oodle_GetConfigValues to fill the struct, then change the values you - want to change, then call $Oodle_SetConfigValues. - - This should generally be done before doing anything with Oodle (eg. even before OodleX_Init). - Changing OodleConfigValues after Oodle has started has undefined effects. -*/ - -typedef enum Oodle_UsageWarnings -{ - Oodle_UsageWarnings_Enabled = 0, - Oodle_UsageWarnings_Disabled = 1, - Oodle_UsageWarnings_Force32 = 0x40000000 -} Oodle_UsageWarnings; -/* Whether Oodle usage warnings are enable or disabled. */ - -OOFUNC1 void OOFUNC2 Oodle_SetUsageWarnings(Oodle_UsageWarnings state); -/* Enables or disables Oodle usage warnings. - - $:state whether usage warnings should be enabled or disabled. - - Usage warnings are enabled by default and try to be low-noise, but in case you want to - disable them, this is how. - - This should generally be done once at startup. Setting this state while there are Oodle - calls running on other threads has undefined results. -*/ - -// function pointers to mallocs needed : - -OODEFFUNC typedef void * (OODLE_CALLBACK t_fp_OodleCore_Plugin_MallocAligned)( OO_SINTa bytes, OO_S32 alignment); -/* Function pointer type for OodleMallocAligned - - $:bytes number of bytes to allocate - $:alignment required alignment of returned pointer - $:return pointer to memory allocated (must not be NULL) - - _alignment_ will always be a power of two - - _alignment_ will always be >= $OODLE_MALLOC_MINIMUM_ALIGNMENT - -*/ - -OODEFFUNC typedef void (OODLE_CALLBACK t_fp_OodleCore_Plugin_Free)( void * ptr ); -/* Function pointer type for OodleFree - - $:return pointer to memory to free - -*/ - -OOFUNC1 void OOFUNC2 OodleCore_Plugins_SetAllocators( - t_fp_OodleCore_Plugin_MallocAligned * fp_OodleMallocAligned, - t_fp_OodleCore_Plugin_Free * fp_OodleFree); -/* Set the function pointers for allocation needed by Oodle2 Core - - If these are not set, the default implementation on most platforms uses the C stdlib. - On Microsoft platforms the default implementation uses HeapAlloc. - - These must not be changed once they are set! Set them once then don't change them. - - NOTE: if you are using Oodle Ext, do NOT call this. OodleX_Init will install an allocator for Oodle Core. Do not mix your own allocator with the OodleX allocator. See $OodleXAPI_Malloc. - - If you want to ensure that Oodle is not doing any allocations, you can call OodleCore_Plugins_SetAllocators(NULL,NULL); - If you do that, then any time Oodle needs to allocate memory internally, it will stop the process. - It is STRONGLY not recommended that you ship that way. You can verify that Oodle is not allocating, but then leave some - fallback allocator installed when you actually ship just in case. - - Also note that on many consoles the standard allocation practices may not - leave much heap memory for the C stdlib malloc. In this case Oodle may fail to allocate. - -*/ - -OODEFFUNC typedef OO_U64 (OODLE_CALLBACK t_fp_OodleCore_Plugin_RunJob)( t_fp_Oodle_Job * fp_job, void * job_data , OO_U64 * dependencies, int num_dependencies, void * user_ptr ); -/* Function pointer type for OodleCore_Plugins_SetJobSystem - - $:dependencies array of handles of other pending jobs. All guaranteed to be nonzero. - $:num_dependencies number of dependencies. Guaranteed to be no more than OODLE_JOB_MAX_DEPENDENCIES. - $:user_ptr is passed through from the OodleLZ_CompressOptions. - $:return handle to the async job, or 0 if it was run synchronously - - RunJob will call fp_job(job_data) - - it may be done on a thread, or it may run the function synchronously and return 0, indicating the job is already done. - The returned OO_U64 is a handle passed to WaitJob, unless it is 0, in which case WaitJob won't get called. - - fp_job should not run until all the dependencies are done. This function should not delete the dependencies. - - RunJob must be callable from within an Oodle Job, i.e. jobs may spawn their own sub-jobs directly. - However, the matching WaitJob calls will only ever occur on the thread that called the - internally threaded Oodle API function. - - See $Oodle_About_Job_Threading_Plugins -*/ - -OODEFFUNC typedef void (OODLE_CALLBACK t_fp_OodleCore_Plugin_WaitJob)( OO_U64 job_handle, void * user_ptr ); -/* Function pointer type for OodleCore_Plugins_SetJobSystem - - $:job_handle a job handle returned from RunJob. Never 0. - $:user_ptr is passed through from the OodleLZ_CompressOptions. - - Waits until the job specified by job_handle is done and cleans up any associated resources. Oodle - will call WaitJob exactly once for every RunJob call that didn't return 0. - - If job_handle was already completed, this should clean it up without waiting. - - A handle value should not be reused by another RunJob until WaitJob has been done with that value. - - WaitJob will not be called from running jobs. It will be only be called from the original thread that - invoked Oodle. If you are running Oodle from a worker thread, ensure that that thread is allowed to wait - on other job threads. - - See $Oodle_About_Job_Threading_Plugins -*/ - -OOFUNC1 void OOFUNC2 OodleCore_Plugins_SetJobSystem( - t_fp_OodleCore_Plugin_RunJob * fp_RunJob, - t_fp_OodleCore_Plugin_WaitJob * fp_WaitJob); -/* DEPRECATED use OodleCore_Plugins_SetJobSystemAndCount instead - - See $OodleCore_Plugins_SetJobSystemAndCount -*/ - - -OOFUNC1 void OOFUNC2 OodleCore_Plugins_SetJobSystemAndCount( - t_fp_OodleCore_Plugin_RunJob * fp_RunJob, - t_fp_OodleCore_Plugin_WaitJob * fp_WaitJob, - int target_parallelism); -/* Set the function pointers for async job system needed by Oodle2 Core - - $:fp_RunJob pointer to RunJob function - $:fp_WaitJob pointer to WaitJob function - $:target_parallelism goal of number of jobs to run simultaneously - - If these are not set, the default implementation runs jobs synchronously on the calling thread. - - These must not be changed once they are set! Set them once then don't change them. - - _target_parallelism_ allows you to tell Oodle how many Jobs it should try to keep in flight at once. - Depending on the operation it may not be able to split work into this many jobs (so fewer will be used), - but it will not exceed this count. - - For Oodle Data LZ work, typically _target_parallelism_ is usually best at the number of hardware cores - not including hyper threads). - - For Oodle Texture BCN encoding work, _target_parallelism_ is usually best as the full number of hyper cores. - - In some cases you may wish to reduce _target_parallelism_ by 1 or 2 cores to leave some of the CPU free for - other work. - - For example on a CPU with 16 cores and 32 hardware threads, for LZ work you might set _target_parallelism_ to 15 - when calling OodleCorePlugins. For BC7 encoding you might set _target_parallelism_ to 30 when calling OodleTexPlugins. - - NOTE : if you are using Oodle Ext, do NOT call this. OodleX_Init will install a job system for Oodle Core. - Note OodleX only installs automatically to Oodle Core, not Net or Tex. See example_jobify.cpp for manual - plugin. - - Replaces deprecated $OodleCore_Plugins_SetJobSystem - - See $Oodle_About_Job_Threading_Plugins -*/ - -// the main func pointer for log : -OODEFFUNC typedef void (OODLE_CALLBACK t_fp_OodleCore_Plugin_Printf)(int verboseLevel,const char * file,int line,const char * fmt,...); -/* Function pointer to Oodle Core printf - - $:verboseLevel verbosity of the message; 0-2 ; lower = more important - $:file C file that sent the message - $:line C line that sent the message - $:fmt vararg printf format string - - The logging function installed here must parse varargs like printf. - - _verboseLevel_ may be used to omit verbose messages. -*/ - -OOFUNC1 t_fp_OodleCore_Plugin_Printf * OOFUNC2 OodleCore_Plugins_SetPrintf(t_fp_OodleCore_Plugin_Printf * fp_rrRawPrintf); -/* Install the callback used by Oodle Core for logging - - $:fp_rrRawPrintf function pointer to your log function; may be NULL to disable all logging - $:return returns the previous function pointer - - Use this function to install your own printf for Oodle Core. - - The default implementation in debug builds, if you install nothing, uses the C stdio printf for logging. - On Microsoft platforms, it uses OutputDebugString and not stdio. - - To disable all logging, call OodleCore_Plugins_SetPrintf(NULL) - - WARNING : this function is NOT thread safe! It should be done only once and done in a place where the caller can guarantee thread safety. - - In the debug build of Oodle, you can install OodleCore_Plugin_Printf_Verbose to get more verbose logging - -*/ - -OODEFFUNC typedef OO_BOOL (OODLE_CALLBACK t_fp_OodleCore_Plugin_DisplayAssertion)(const char * file,const int line,const char * function,const char * message); -/* Function pointer to Oodle Core assert callback - - $:file C file that triggered the assert - $:line C line that triggered the assert - $:function C function that triggered the assert (may be NULL) - $:message assert message - $:return true to break execution at the assertion site, false to continue - - This callback is called by Oodle Core when it detects an assertion condition. - - This will only happen in debug builds. - - -*/ - -OOFUNC1 t_fp_OodleCore_Plugin_DisplayAssertion * OOFUNC2 OodleCore_Plugins_SetAssertion(t_fp_OodleCore_Plugin_DisplayAssertion * fp_rrDisplayAssertion); -/* Install the callback used by Oodle Core for asserts - - $:fp_rrDisplayAssertion function pointer to your assert display function - $:return returns the previous function pointer - - Use this function to install your own display for Oodle Core assertions. - This will only happen in debug builds. - - The default implementation in debug builds, if you install nothing, uses the C stderr printf for logging, - except on Microsoft platforms where it uses OutputDebugString. - - WARNING : this function is NOT thread safe! It should be done only once and done in a place where the caller can guarantee thread safety. - -*/ - -//============================================================= - - -OOFUNC1 void * OOFUNC2 OodleCore_Plugin_MallocAligned_Default(OO_SINTa size,OO_S32 alignment); -OOFUNC1 void OOFUNC2 OodleCore_Plugin_Free_Default(void * ptr); -OOFUNC1 void OOFUNC2 OodleCore_Plugin_Printf_Default(int verboseLevel,const char * file,int line,const char * fmt,...); -OOFUNC1 void OOFUNC2 OodleCore_Plugin_Printf_Verbose(int verboseLevel,const char * file,int line,const char * fmt,...); -OOFUNC1 OO_BOOL OOFUNC2 OodleCore_Plugin_DisplayAssertion_Default(const char * file,const int line,const char * function,const char * message); -OOFUNC1 OO_U64 OOFUNC2 OodleCore_Plugin_RunJob_Default( t_fp_Oodle_Job * fp_job, void * job_data, OO_U64 * dependencies, int num_dependencies, void * user_ptr ); -OOFUNC1 void OOFUNC2 OodleCore_Plugin_WaitJob_Default( OO_U64 job_handle, void * user_ptr ); - -//============================================================= - -//---------------------------------------------- -// OodleLZ - -#define OODLELZ_FAILED (0) /* Return value of OodleLZ_Decompress on failure -*/ - -//======================================================= - -OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_Compress(OodleLZ_Compressor compressor, - const void * rawBuf,OO_SINTa rawLen,void * compBuf, - OodleLZ_CompressionLevel level, - const OodleLZ_CompressOptions * pOptions OODEFAULT(NULL), - const void * dictionaryBase OODEFAULT(NULL), - const void * lrm OODEFAULT(NULL), - void * scratchMem OODEFAULT(NULL), - OO_SINTa scratchSize OODEFAULT(0) ); -/* Compress some data from memory to memory, synchronously, with OodleLZ - - $:compressor which OodleLZ variant to use in compression - $:rawBuf raw data to compress - $:rawLen number of bytes in rawBuf to compress - $:compBuf pointer to write compressed data to ; should be at least $OodleLZ_GetCompressedBufferSizeNeeded - $:level OodleLZ_CompressionLevel controls how much CPU effort is put into maximizing compression - $:pOptions (optional) options; if NULL, $OodleLZ_CompressOptions_GetDefault is used - $:dictionaryBase (optional) if not NULL, provides preceding data to prime the dictionary; must be contiguous with rawBuf, the data between the pointers _dictionaryBase_ and _rawBuf_ is used as the preconditioning data. The exact same precondition must be passed to encoder and decoder. - $:lrm (optional) long range matcher - $:scratchMem (optional) pointer to scratch memory - $:scratchSize (optional) size of scratch memory (see $OodleLZ_GetCompressScratchMemBound) - $:return size of compressed data written, or $OODLELZ_FAILED for failure - - Performs synchronous memory to memory LZ compression. - - In tools, you should generally use $OodleXLZ_Compress_AsyncAndWait instead to get parallelism. (in the Oodle2 Ext lib) - - You can compress a large buffer in several calls by setting _dictionaryBase_ to the start - of the buffer, and then making _rawBuf_ and _rawLen_ select portions of that buffer. As long - as _rawLen_ is a multiple of $OODLELZ_BLOCK_LEN , the compressed chunks can simply be - concatenated together. - - If _scratchMem_ is provided, it will be used for the compressor's scratch memory needs before OodleMalloc is - called. If the scratch is big enough, no malloc will be done. If the scratch is not big enough, the compress - will not fail, instead OodleMalloc will be used. OodleMalloc should not return null. There is currently no way - to make compress fail cleanly due to using too much memory, it must either succeed or abort the process. - - If _scratchSize_ is at least $OodleLZ_GetCompressScratchMemBound , additional allocations will not be needed. - - See $OodleLZ_About for tips on setting the compression options. - - If _dictionaryBase_ is provided, the backup distance from _rawBuf_ must be a multiple of $OODLELZ_BLOCK_LEN - - If $(OodleLZ_CompressOptions:seekChunkReset) is enabled, and _dictionaryBase_ is not NULL or _rawBuf_ , then the - seek chunk boundaries are relative to _dictionaryBase_, not to _rawBuf_. - -*/ - -// Decompress returns raw (decompressed) len received -// Decompress returns 0 (OODLELZ_FAILED) if it detects corruption -OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_Decompress(const void * compBuf,OO_SINTa compBufSize,void * rawBuf,OO_SINTa rawLen, - OodleLZ_FuzzSafe fuzzSafe OODEFAULT(OodleLZ_FuzzSafe_Yes), - OodleLZ_CheckCRC checkCRC OODEFAULT(OodleLZ_CheckCRC_No), - OodleLZ_Verbosity verbosity OODEFAULT(OodleLZ_Verbosity_None), - void * decBufBase OODEFAULT(NULL), - OO_SINTa decBufSize OODEFAULT(0), - OodleDecompressCallback * fpCallback OODEFAULT(NULL), - void * callbackUserData OODEFAULT(NULL), - void * decoderMemory OODEFAULT(NULL), - OO_SINTa decoderMemorySize OODEFAULT(0), - OodleLZ_Decode_ThreadPhase threadPhase OODEFAULT(OodleLZ_Decode_Unthreaded) - ); -/* Decompress a some data from memory to memory, synchronously. - - $:compBuf pointer to compressed data - $:compBufSize number of compressed bytes available (must be greater or equal to the number consumed) - $:rawBuf pointer to output uncompressed data into - $:rawLen number of uncompressed bytes to output - $:fuzzSafe (optional) should the decode fail if it contains non-fuzz safe codecs? - $:checkCRC (optional) if data could be corrupted and you want to know about it, pass OodleLZ_CheckCRC_Yes - $:verbosity (optional) if not OodleLZ_Verbosity_None, logs some info - $:decBufBase (optional) if not NULL, provides preceding data to prime the dictionary; must be contiguous with rawBuf, the data between the pointers _dictionaryBase_ and _rawBuf_ is used as the preconditioning data. The exact same precondition must be passed to encoder and decoder. The decBufBase must be a reset point. - $:decBufSize (optional) size of decode buffer starting at decBufBase, if 0, _rawLen_ is assumed - $:fpCallback (optional) OodleDecompressCallback to call incrementally as decode proceeds - $:callbackUserData (optional) passed as userData to fpCallback - $:decoderMemory (optional) pre-allocated memory for the Decoder, of size _decoderMemorySize_ - $:decoderMemorySize (optional) size of the buffer at _decoderMemory_; must be at least $OodleLZDecoder_MemorySizeNeeded bytes to be used - $:threadPhase (optional) for threaded decode; see $OodleLZ_About_ThreadPhasedDecode (default OodleLZ_Decode_Unthreaded) - $:return the number of decompressed bytes output, $OODLELZ_FAILED (0) if none can be decompressed - - Decodes data encoded with any $OodleLZ_Compressor. - - Note : _rawLen_ must be the actual number of bytes to output, the same as the number that were encoded with the corresponding - OodleLZ_Compress size. You must store this somewhere in your own header and pass it in to this call. _compBufSize_ does NOT - need to be the exact number of compressed bytes, is the number of bytes available in the buffer, it must be greater or equal to - the actual compressed length. - - Note that the new compressors (Kraken,Mermaid,Selkie,BitKnit) are all fuzz safe and you can use OodleLZ_FuzzSafe_Yes - with them and no padding of the decode target buffer. - - If checkCRC is OodleLZ_CheckCRC_Yes, then corrupt data will be detected and the decode aborted. - If checkCRC is OodleLZ_CheckCRC_No, then corruption might result in invalid data, but no detection of any error (garbage in, garbage out). - - If corruption is possible, _fuzzSafe_ is No and _checkCRC_ is OodleLZ_CheckCRC_No, $OodleLZ_GetDecodeBufferSize must be used to allocate - _rawBuf_ large enough to prevent overrun. - - $OodleLZ_GetDecodeBufferSize should always be used to ensure _rawBuf_ is large enough, even when corruption is not - possible (when fuzzSafe is No). - - _compBuf_ and _rawBuf_ are allowed to overlap for "in place" decoding, but then _rawBuf_ must be allocated to - the size given by $OodleLZ_GetInPlaceDecodeBufferSize , and the compressed data must be at the end of that buffer. - - An easy way to take the next step to parallel decoding is with $OodleXLZ_Decompress_MakeSeekTable_Wide_Async (in the Oodle2 Ext lib) - - NOTE : the return value is the *total* number of decompressed bytes output so far. If rawBuf is > decBufBase, that means - the initial inset of (rawBuf - decBufBase) is included! (eg. you won't just get _rawLen_) - - If _decBufBase_ is provided, the backup distance from _rawBuf_ must be a multiple of $OODLELZ_BLOCK_LEN - - About fuzz safety: - - OodleLZ_Decompress is guaranteed not to crash even if the data is corrupted when _fuzzSafe_ is set to OodleLZ_FuzzSafe_Yes. - When _fuzzSafe_ is Yes, the target buffer (_rawBuf_ and _rawLen_) will never be overrun. Note that corrupted data might not - be detected (the return value might indicate success). - - Fuzz Safe decodes will not crash on corrupt data. They may or may not return failure, and produce garbage output. - - Fuzz safe decodes will not read out of bounds. They won't put data on the stack or previously in memory - into the output buffer. - - Fuzz safe decodes will not output more than the uncompressed size. (eg. the output buffer does not need to - be padded like OodleLZ_GetDecodeBufferSize) - - If you ask for a fuzz safe decode and the compressor doesn't satisfy OodleLZ_Compressor_CanDecodeFuzzSafe - then it will return failure. - - The _fuzzSafe_ argument should always be OodleLZ_FuzzSafe_Yes as of Oodle 2.9.0 ; older compressors did not - support fuzz safety but they now all do. - - Use of OodleLZ_FuzzSafe_No is deprecated. - -*/ - - -//------------------------------------------- -// Incremental Decoder functions : - -struct _OodleLZDecoder; -typedef struct _OodleLZDecoder OodleLZDecoder; -/* Opaque type for OodleLZDecoder - - See $OodleLZDecoder_Create -*/ - - -OOFUNC1 OodleLZDecoder * OOFUNC2 OodleLZDecoder_Create(OodleLZ_Compressor compressor,OO_S64 rawLen,void * memory, OO_SINTa memorySize); -/* Create a OodleLZDecoder - - $:compressor the type of data you will decode; use $OodleLZ_Compressor_Invalid if unknown - $:rawLen total raw bytes of the decode - $:memory (optional) provide memory for the OodleLZDecoder object (not the window) - $:memorySize (optional) if memory is provided, this is its size in bytes - $:return the OodleLZDecoder - - If memory is provided, it must be of size $OodleLZDecoder_MemorySizeNeeded. If it is NULL it will be - allocated with the malloc specified by $OodleAPI_OodleCore_Plugins. - - Free with $OodleLZDecoder_Destroy. You should Destroy even if you passed in the memory. - - Providing _compressor_ lets the OodleLZDecoder be the minimum size needed for that type of data. - If you pass $OodleLZ_Compressor_Invalid, then any type of data may be decoded, and the Decoder is allocated - large enought to handle any of them. - - If you are going to pass rawLen to OodleLZDecoder_Reset , then you can pass 0 to rawLen here. - - See $OodleLZDecoder_DecodeSome for more. -*/ - -OOFUNC1 OO_S32 OOFUNC2 OodleLZDecoder_MemorySizeNeeded(OodleLZ_Compressor compressor OODEFAULT(OodleLZ_Compressor_Invalid), OO_SINTa rawLen OODEFAULT(-1)); -/* If you want to provide the memory needed by $OodleLZDecoder_Create , this tells you how big it must be. - - $:compressor the type of data you will decode; use $OodleLZ_Compressor_Invalid if unknown - $:rawLen should almost always be -1, which supports any size of raw data decompression - $:return bytes to allocate or reserve, 0 for failure - - NOTE : using $OodleLZ_Compressor_Invalid lets you decode any time of compressed data. - It requests as much memory as the largest compressor. This may be a *lot* more than your data needs; - try to use the correct compressor type. - - If _rawLen_ is -1 (default) then the Decoder object created can be used on any length of raw data - decompression. If _rawLen_ is specified here, then you can only use it to decode data shorter than - the length you specified here. This use case is very rare, contact support for details. -*/ - -OOFUNC1 OO_S32 OOFUNC2 OodleLZ_ThreadPhased_BlockDecoderMemorySizeNeeded(void); -/* Returns the size of the decoder needed for ThreadPhased decode - - For use with $OodleLZ_Decode_ThreadPhase - See $OodleLZ_About_ThreadPhasedDecode -*/ - -OOFUNC1 void OOFUNC2 OodleLZDecoder_Destroy(OodleLZDecoder * decoder); -/* Pairs with $OodleLZDecoder_Create - - You should always call Destroy even if you provided the memory for $OodleLZDecoder_Create -*/ - -// Reset decoder - can reset to the start of any OODLELZ_BLOCK_LEN chunk -OOFUNC1 OO_BOOL OOFUNC2 OodleLZDecoder_Reset(OodleLZDecoder * decoder, OO_SINTa decPos, OO_SINTa decLen OODEFAULT(0)); -/* Reset an OodleLZDecoder to restart at given pos - - $:decoder the OodleLZDecoder, made by $OodleLZDecoder_Create - $:decPos position to reset to; must be a multiple of OODLELZ_BLOCK_LEN - $:decLen (optional) if not zero, change the length of the data we expect to decode - $:return true for success - - If you are seeking in a packed stream, you must seek to a seek chunk reset point, as was made at compress time. - - That is, $(OodleLZ_CompressOptions:seekChunkReset) must have been true, and - _decPos_ must be a multiple of $(OodleLZ_CompressOptions:seekChunkLen) that was used at compress time. - - You can use $OodleLZ_GetChunkCompressor to verify that you are at a valid - independent chunk start point. - -*/ - -// returns false if corruption detected -OOFUNC1 OO_BOOL OOFUNC2 OodleLZDecoder_DecodeSome( - OodleLZDecoder * decoder, - OodleLZ_DecodeSome_Out * out, - - // the decode sliding window : we output here & read from this for matches - void * decBuf, - OO_SINTa decBufPos, - OO_SINTa decBufferSize, // decBufferSize should be the result of OodleLZDecoder_MakeDecodeBufferSize() - OO_SINTa decBufAvail, // usually Size - Pos, but maybe less if you have pending IO flushes - - // compressed data : - const void * compPtr, - OO_SINTa compAvail, - - OodleLZ_FuzzSafe fuzzSafe OODEFAULT(OodleLZ_FuzzSafe_No), - OodleLZ_CheckCRC checkCRC OODEFAULT(OodleLZ_CheckCRC_No), - OodleLZ_Verbosity verbosity OODEFAULT(OodleLZ_Verbosity_None), - OodleLZ_Decode_ThreadPhase threadPhase OODEFAULT(OodleLZ_Decode_Unthreaded) - - ); -/* Incremental decode some LZ compressed data - - $:decoder the OodleLZDecoder, made by $OodleLZDecoder_Create - $:out filled with results - $:decBuf the decode buffer (window) - $:decBufPos the current position in the buffer - $:decBufferSize size of decBuf ; this must be either equal to the total decompressed size (_rawLen_ passed to $OodleLZDecoder_Create) or the result of $OodleLZDecoder_MakeValidCircularWindowSize - $:decBufAvail the number of bytes available after decBufPos in decBuf ; usually (decBufferSize - decBufPos), but can be less - $:compPtr pointer to compressed data to read - $:compAvail number of compressed bytes available at compPtr - $:fuzzSafe (optional) should the decode be fuzz safe - $:checkCRC (optional) if data could be corrupted and you want to know about it, pass OodleLZ_CheckCRC_Yes - $:verbosity (optional) if not OodleLZ_Verbosity_None, logs some info - $:threadPhase (optional) for threaded decode; see $OodleLZ_About_ThreadPhasedDecode (default OodleLZ_Decode_Unthreaded) - $:return true if success, false if invalid arguments or data is encountered - - Decodes data encoded with an OodleLZ compressor. - - Decodes an integer number of quanta; quanta are $OODLELZ_QUANTUM_LEN uncompressed bytes. - - _decBuf_ can either be a circular window or the whole _rawLen_ array. - In either case, _decBufPos_ should be in the range [0,_decBufferSize_). - If _decBuf_ is a circular window, then _decBufferSize_ should come from $OodleLZDecoder_MakeValidCircularWindowSize. - - (circular windows are deprecated as of 2.9.0) - - NOTE : all the new LZ codecs (Kraken, etc.) do not do circular windows. They can do sliding windows, see lz_test_11 in $example_lz. - They should always have decBufferSize = total raw size, even if the decode buffer is smaller than that. - - NOTE : insufficient data provided (with _compAvail_ > 0 but not enough to decode a quantum) is a *success* case - (return value of true), even though nothing is decoded. A return of false always indicates a non-recoverable error. - - If _decBufAvail_ or _compAvail_ is insufficient for any decompression, the "curQuantum" fields of $OodleLZ_DecodeSome_Out - will tell you how much you must provide to proceed. That is, if enough compressed bytes are provided to get a quantum header, but not enough to decode a quantum, this - function returns true and fills out the $OodleLZ_DecodeSome_Out structure with the size of the quantum. - - See $OodleLZ_Decompress about fuzz safety. - - NOTE : DecodeSome expect to decode either one full quantum (of len $OODLELZ_QUANTUM_LEN) or up to the length of the total buffer specified in the -call to $OodleLZDecoder_Create or $OodleLZDecoder_Reset. That total buffer length -must match what was use during compression (or be a seek-chunk portion thereof). -That is, you cannot decompress partial streams in intervals smaller than -$OODLELZ_QUANTUM_LEN except for the final partial quantum at the end of the stream. - -*/ - -// pass in how much you want to alloc and it will tell you a valid size as close that as possible -// the main use is just to call OodleLZDecoder_MakeDecodeBufferSize(0) to get the min size; the min size is a good size -OOFUNC1 OO_S32 OOFUNC2 OodleLZDecoder_MakeValidCircularWindowSize(OodleLZ_Compressor compressor,OO_S32 minWindowSize OODEFAULT(0)); -/* Get a valid "Window" size for an LZ - - $:compressor which compressor you will be decoding - $:minWindowSize (optional) minimum size of the window - - NOTE: circular windows are deprecated as of 2.9.0 - - Most common usage is OodleLZDecoder_MakeValidCircularWindowSize(0) to get the minimum window size. - - Only compressors which pass $OodleLZ_Compressor_CanDecodeInCircularWindow can be decoded in a circular window. - - WARNING : this is NOT the size to malloc the window! you need to call $OodleLZ_GetDecodeBufferSize() and - pass in the window size to get the malloc size. -*/ - -//======================================================= - -//======================================================= -// remember if you want to IO the SeekEntries you need to make them endian-independent -// see WriteOOZHeader for example - -#define OODLELZ_SEEKPOINTCOUNT_DEFAULT 16 - -OOFUNC1 OO_S32 OOFUNC2 OodleLZ_MakeSeekChunkLen(OO_S64 rawLen, OO_S32 desiredSeekPointCount); -/* Compute a valid seekChunkLen - - $:rawLen total length of uncompressed data - $:desiredSeekPointCount desired number of seek chunks - $:return a valid seekChunkLen for use in $OodleLZ_CreateSeekTable - - Returns a seekChunkLen which is close to (rawLen/desiredSeekPointCount) but is a power of two multiple of $OODLELZ_BLOCK_LEN - - _desiredSeekPointCount_ = 16 is good for parallel decompression. - (OODLELZ_SEEKPOINTCOUNT_DEFAULT) -*/ - -OOFUNC1 OO_S32 OOFUNC2 OodleLZ_GetNumSeekChunks(OO_S64 rawLen, OO_S32 seekChunkLen); -/* Compute the number of seek chunks - - $:rawLen total length of uncompressed data - $:seekChunkLen the length of a seek chunk (eg from $OodleLZ_MakeSeekChunkLen) - $:return the number of seek chunks - - returns (rawLen+seekChunkLen-1)/seekChunkLen -*/ - -OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_GetSeekTableMemorySizeNeeded(OO_S32 numSeekChunks,OodleLZSeekTable_Flags flags); -/* Tells you the size in bytes to allocate the seekTable before calling $OodleLZ_FillSeekTable - - $:numSeekChunks number of seek chunks (eg from $OodleLZ_GetNumSeekChunks) - $:flags options that will be passed to $OodleLZ_CreateSeekTable - $:return size in bytes of memory needed for seek table - - If you wish to provide the memory for the seek table yourself, you may call this to get the required size, - allocate the memory, and then simply point a $OodleLZ_SeekTable at your memory. - Then use $OodleLZ_FillSeekTable to fill it out. - - Do NOT use sizeof(OodleLZ_SeekTable) ! -*/ - -OOFUNC1 OO_BOOL OOFUNC2 OodleLZ_FillSeekTable(OodleLZ_SeekTable * pTable,OodleLZSeekTable_Flags flags,OO_S32 seekChunkLen,const void * rawBuf, OO_SINTa rawLen,const void * compBuf,OO_SINTa compLen); -/* scan compressed LZ stream to fill the seek table - - $:pTable pointer to table to be filled - $:flags options - $:seekChunkLen the length of a seek chunk (eg from $OodleLZ_MakeSeekChunkLen) - $:rawBuf (optional) uncompressed buffer; used to compute the _rawCRCs_ member of $OodleLZ_SeekTable - $:rawLen size of rawBuf - $:compBuf compressed buffer - $:compLen size of compBuf - $:return true for success - - _pTable_ must be able to hold at least $OodleLZ_GetSeekTableMemorySizeNeeded - - _seekChunkLen_ must be a multiple of $OODLELZ_BLOCK_LEN. - _seekChunkLen_ must match what was in CompressOptions when the buffer was made, or any integer multiple thereof. -*/ - - -OOFUNC1 OodleLZ_SeekTable * OOFUNC2 OodleLZ_CreateSeekTable(OodleLZSeekTable_Flags flags,OO_S32 seekChunkLen,const void * rawBuf, OO_SINTa rawLen,const void * compBuf,OO_SINTa compLen); -/* allocate a table, then scan compressed LZ stream to fill the seek table - - $:flags options - $:seekChunkLen the length of a seek chunk (eg from $OodleLZ_MakeSeekChunkLen) - $:rawBuf (optional) uncompressed buffer; used to compute the _rawCRCs_ member of $OodleLZ_SeekTable - $:rawLen size of rawBuf - $:compBuf compressed buffer - $:compLen size of compBuf - $:return pointer to table if succeeded, null if failed - - Same as $OodleLZ_FillSeekTable , but allocates the memory for you. Use $OodleLZ_FreeSeekTable to free. - - _seekChunkLen_ must be a multiple of $OODLELZ_BLOCK_LEN. - _seekChunkLen_ must match what was in CompressOptions when the buffer was made, or any integer multiple thereof. - -*/ - -OOFUNC1 void OOFUNC2 OodleLZ_FreeSeekTable(OodleLZ_SeekTable * pTable); -/* Frees a table allocated by $OodleLZ_CreateSeekTable -*/ - -OOFUNC1 OO_BOOL OOFUNC2 OodleLZ_CheckSeekTableCRCs(const void * rawBuf,OO_SINTa rawLen, const OodleLZ_SeekTable * seekTable); -/* Check the CRC's in seekTable vs rawBuf - - $:rawBuf uncompressed buffer - $:rawLen size of rawBuf - $:seekTable result of $OodleLZ_CreateSeekTable - $:return true if the CRC's check out - - Note that $OodleLZ_Decompress option of $OodleLZ_CheckCRC checks the CRC of *compressed* data, - this call checks the CRC of the *raw* (uncompressed) data. - - OodleLZ data contains a CRC of the compressed data if it was made with $(OodleLZ_CompressOptions:sendQuantumCRCs). - The SeekTable contains a CRC of the raw data if it was made with $OodleLZSeekTable_Flags_MakeRawCRCs. - - Checking the CRC of compressed data is faster, but does not verify that the decompress succeeded. -*/ - -OOFUNC1 OO_S32 OOFUNC2 OodleLZ_FindSeekEntry( OO_S64 rawPos, const OodleLZ_SeekTable * seekTable); -/* Find the seek entry that contains a raw position - - $:rawPos uncompressed position to look for - $:seekTable result of $OodleLZ_CreateSeekTable - $:return a seek entry index - - returns the index of the chunk that contains _rawPos_ -*/ - -OOFUNC1 OO_S64 OOFUNC2 OodleLZ_GetSeekEntryPackedPos( OO_S32 seekI , const OodleLZ_SeekTable * seekTable ); -/* Get the compressed position of a seek entry - - $:seekI seek entry index , in [0,numSeekEntries) - $:seekTable result of $OodleLZ_CreateSeekTable - $:return compressed buffer position of the start of this seek entry - - -*/ - -//============================================================= - -OOFUNC1 const char * OOFUNC2 OodleLZ_CompressionLevel_GetName(OodleLZ_CompressionLevel compressSelect); -/* Provides a string naming a $OodleLZ_CompressionLevel compressSelect -*/ - -OOFUNC1 const char * OOFUNC2 OodleLZ_Compressor_GetName(OodleLZ_Compressor compressor); -/* Provides a string naming a $OodleLZ_Compressor compressor -*/ - -OOFUNC1 const char * OOFUNC2 OodleLZ_Jobify_GetName(OodleLZ_Jobify jobify); -/* Provides a string naming a $OodleLZ_Jobify enum -*/ - -OOFUNC1 const OodleLZ_CompressOptions * OOFUNC2 OodleLZ_CompressOptions_GetDefault( - OodleLZ_Compressor compressor OODEFAULT(OodleLZ_Compressor_Invalid), - OodleLZ_CompressionLevel lzLevel OODEFAULT(OodleLZ_CompressionLevel_Normal)); -/* Provides a pointer to default compression options - - $:compressor deprecated, ignored - $:lzLevel deprecated, ignored - - Use to fill your own $OodleLZ_CompressOptions then change individual fields. - -*/ - -// after you fiddle with options, call this to ensure they are allowed -OOFUNC1 void OOFUNC2 OodleLZ_CompressOptions_Validate(OodleLZ_CompressOptions * pOptions); -/* Clamps the values in _pOptions_ to be in valid range - -*/ - -// inline functions for compressor property queries -OODEFSTART - -OO_BOOL OodleLZ_Compressor_UsesWholeBlockQuantum(OodleLZ_Compressor compressor); -/* OodleLZ_Compressor properties helper. - - Tells you if this compressor is "whole block quantum" ; must decode in steps of - $OODLELZ_BLOCK_LEN , not $OODLELZ_QUANTUM_LEN like others. -*/ -OO_BOOL OodleLZ_Compressor_UsesLargeWindow(OodleLZ_Compressor compressor); -/* OodleLZ_Compressor properties helper. - - Tells you if this compressor is "LargeWindow" or not, meaning it can benefit from - a Long-Range-Matcher and windows larger than $OODLELZ_BLOCK_LEN -*/ -OO_BOOL OodleLZ_Compressor_CanDecodeInCircularWindow(OodleLZ_Compressor compressor); -/* OodleLZ_Compressor properties helper. - - Tells you if this compressor can be decoded using a fixed size circular window. - deprecated as of 2.9.0 -*/ -OO_BOOL OodleLZ_Compressor_CanDecodeThreadPhased(OodleLZ_Compressor compressor); -/* OodleLZ_Compressor properties helper. - - Tells you if this compressor can be used with the $OodleLZ_Decode_ThreadPhase. - - See $OodleLZ_About_ThreadPhasedDecode -*/ -OO_BOOL OodleLZ_Compressor_CanDecodeInPlace(OodleLZ_Compressor compressor); -/* OodleLZ_Compressor properties helper. - - Tells you if this compressor can be used with "in-place" decoding. - - This is now always true (all compressors support in-place decoding). The function is left - for backward compatibility. - - All compressors in the future will support in-place, you don't need to check this property. - -*/ -OO_BOOL OodleLZ_Compressor_MustDecodeWithoutResets(OodleLZ_Compressor compressor); -/* OodleLZ_Compressor properties helper. - - Tells you if this compressor must decode contiguous ranges of buffer with the same Decoder. - - That is, most of the compressors can be Reset and restart on any block, not just seek blocks, - as long as the correct window data is provided. That is, if this returns false then the only - state required across a non-reset block is the dictionary of previously decoded data. - - But if OodleLZ_Compressor_MustDecodeWithoutResets returns true, then you cannot do that, - because the Decoder object must carry state across blocks (except reset blocks). - - This does not apply to seek points - you can always reset and restart decompression at a seek point. -*/ -OO_BOOL OodleLZ_Compressor_CanDecodeFuzzSafe(OodleLZ_Compressor compressor); -/* OodleLZ_Compressor properties helper. - - Tells you if this compressor is "fuzz safe" which means it can accept corrupted data - and won't crash or overrun any buffers. -*/ - -OO_BOOL OodleLZ_Compressor_RespectsDictionarySize(OodleLZ_Compressor compressor); -/* OodleLZ_Compressor properties helper. - - Tells you if this compressor obeys $(OodleLZ_CompressOptions:dictionarySize) which limits - match references to a finite bound. (eg. for sliding window decompression). - - All the new codecs do (Kraken,Mermaid,Selkie,Leviathan). Some old codecs don't. -*/ -//===================================================================== - -#define OODLELZ_COMPRESSOR_MASK(c) (((OO_U32)1)<<((OO_S32)(c))) -// OODLELZ_COMPRESSOR_BOOLBIT : extract a value of 1 or 0 so it maps to "bool" -#define OODLELZ_COMPRESSOR_BOOLBIT(s,c) (((s)>>(OO_S32)(c))&1) - -OOINLINEFUNC OO_BOOL OodleLZ_Compressor_IsNewLZFamily(OodleLZ_Compressor compressor) -{ - const OO_U32 set = - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Kraken) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Leviathan) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Mermaid) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Selkie) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Hydra); - return OODLELZ_COMPRESSOR_BOOLBIT(set,compressor); -} - -OOINLINEFUNC OO_BOOL OodleLZ_Compressor_CanDecodeFuzzSafe(OodleLZ_Compressor compressor) -{ - #ifdef OODLE_ALLOW_DEPRECATED_COMPRESSORS - const OO_U32 set = - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_None) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Kraken) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Leviathan) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Mermaid) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Selkie) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Hydra) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_BitKnit) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_LZB16); - return OODLELZ_COMPRESSOR_BOOLBIT(set,compressor); - #else - // all new compressors are fuzz safe - return compressor != OodleLZ_Compressor_Invalid; - #endif -} - -OOINLINEFUNC OO_BOOL OodleLZ_Compressor_RespectsDictionarySize(OodleLZ_Compressor compressor) -{ - #ifdef OODLE_ALLOW_DEPRECATED_COMPRESSORS - const OO_U32 set = - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_None) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Kraken) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Leviathan) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Mermaid) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Selkie) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Hydra) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_LZNA) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_BitKnit); - return OODLELZ_COMPRESSOR_BOOLBIT(set,compressor); - #else - // all new compressors respect dictionarySize - return compressor != OodleLZ_Compressor_Invalid; - #endif -} - -OOINLINEFUNC OO_BOOL OodleLZ_Compressor_UsesWholeBlockQuantum(OodleLZ_Compressor compressor) -{ - return OodleLZ_Compressor_IsNewLZFamily(compressor); -} - -OOINLINEFUNC OO_BOOL OodleLZ_Compressor_CanDecodeThreadPhased(OodleLZ_Compressor compressor) -{ - return OodleLZ_Compressor_IsNewLZFamily(compressor); -} - -OOINLINEFUNC OO_BOOL OodleLZ_Compressor_CanDecodeInPlace(OodleLZ_Compressor compressor) -{ - // all compressors can now decode in place : - return compressor != OodleLZ_Compressor_Invalid; -} - -OOINLINEFUNC OO_BOOL OodleLZ_Compressor_CanDecodeInCircularWindow(OodleLZ_Compressor compressor) -{ - #ifdef OODLE_ALLOW_DEPRECATED_COMPRESSORS - const OO_U32 set = - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_LZH) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_LZB16); - #else - const OO_U32 set = 0; - #endif - - return OODLELZ_COMPRESSOR_BOOLBIT(set,compressor); -} - -OOINLINEFUNC OO_BOOL OodleLZ_Compressor_UsesLargeWindow(OodleLZ_Compressor compressor) -{ - // all but LZH and LZB16 now are large window - return ! OodleLZ_Compressor_CanDecodeInCircularWindow(compressor); -} - -OOINLINEFUNC OO_BOOL OodleLZ_Compressor_MustDecodeWithoutResets(OodleLZ_Compressor compressor) -{ - #ifdef OODLE_ALLOW_DEPRECATED_COMPRESSORS - const OO_U32 set = - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_BitKnit) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_LZA) | - OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_LZNA); - #else - const OO_U32 set = 0; - #endif - - return OODLELZ_COMPRESSOR_BOOLBIT(set,compressor); -} - -OODEFEND - -//======================================================= - - -#define OODLELZ_SCRATCH_MEM_NO_BOUND (-1) /* Scratch mem size when bound is unknown. - Installed allocator may be used no matter how much scratch mem you provide. -*/ - -OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_GetCompressScratchMemBound( - OodleLZ_Compressor compressor, - OodleLZ_CompressionLevel level, - OO_SINTa rawLen, - const OodleLZ_CompressOptions * pOptions OODEFAULT(NULL) - ); -/* Return the maximum amount of scratch mem that will be needed by OodleLZ_Compress - - $:compressor which OodleLZ variant to use in compression - $:level OodleLZ_CompressionLevel controls how much CPU effort is put into maximizing compression - $:rawLen maximum number of bytes you will compress (plus dictionary backup) - $:pOptions (optional) options; if NULL, $OodleLZ_CompressOptions_GetDefault is used - - If you pass scratch mem to $OodleLZ_Compress of this size, it is gauranteed to do no allocations. - (normally if it runs out of scratch mem, it falls back to the installed allocator) - - For _rawLen_ pass at least the maximum size you will ever encode. If your data is divided into chunks, - pass the chunk size. If you will encode full buffers of unbounded size, pass -1. - - The options must be the same as when you call $OodleLZ_Compress - - Some options and levels may not have simple finite bounds. Then $OODLELZ_SCRATCH_MEM_NO_BOUND is returned - and the call to $OodleLZ_Compress may use the allocator even if infinite scratch memory is provided. - Currently this applies to all the Optimal levels. - - When OODLELZ_SCRATCH_MEM_NO_BOUND is returned, you can still pass in scratch mem which will be used before - going to the plugin allocator. - -*/ - -// get maximum expanded size for compBuf alloc : -// (note this is actually larger than the maximum compressed stream, it includes trash padding) -OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_GetCompressedBufferSizeNeeded(OodleLZ_Compressor compressor,OO_SINTa rawSize); -/* Return the size you must malloc the compressed buffer - - $:compressor compressor used; OodleLZ_Compressor_Invalid to make it enough for any compressor - $:rawSize uncompressed size you will compress into this buffer - - The _compBuf_ passed to $OodleLZ_Compress must be allocated at least this big. - - note this is actually larger than the maximum size of a compressed stream, it includes overrun padding. - -*/ - -// decBuf needs to be a little larger than rawLen, -// this will tell you exactly how much : -OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_GetDecodeBufferSize(OodleLZ_Compressor compressor,OO_SINTa rawSize,OO_BOOL corruptionPossible); -/* Get the size you must malloc the decode (raw) buffer - - $:compressor compressor used; OodleLZ_Compressor_Invalid to make it enough for any compressor - $:rawSize uncompressed (raw) size without padding - $:corruptionPossible true if it is possible for the decoder to get corrupted data - $:return size of buffer to malloc; slightly larger than rawSize if padding is needed - - As of Oodle 2.9.0 this function is deprecated. For all new codecs you can just use the size of the - uncompressed data for the decode buffer size (_rawSize_), no padding is needed. - - Note that LZB16 is still supported in 2.9.0 but does require padding when used in a circular - window (which is deprecated). - - This padding is necessary for the older compressors when FuzzSafe_No is used. The old compressors - and FuzzSafe_No are no longer supported. - - If _corruptionPossible_ is true, a slightly larger buffer size is returned. - - If _corruptionPossible_ is false, then you must ensure that the decoder does not get corrupted data, - either by passing $OodleLZ_CheckCRC_Yes , or by your own mechanism. - - Note about possible overrun in LZ decoding (applies to the old non-fuzz-safe compressors) : - as long as the compresseddata is not corrupted, - and you decode either the entire compressed buffer, or an integer number of "seek chunks" ($OODLELZ_BLOCK_LEN), - then there will be no overrun. So you can decode LZ data in place and it won't stomp any following bytes. - If those conditions are not true (eg. decoding only part of a larger compressed stream, decoding - around a circular window, decoding data that may be corrupted), then there may be some limited amount of - overrun on decode, as returned by $OodleLZ_GetDecodeBufferSize. - - -*/ - -// OodleLZ_GetInPlaceDecodeBufferSize : -// after compressing, ask how big the in-place buffer needs to be -OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_GetInPlaceDecodeBufferSize(OodleLZ_Compressor compressor,OO_SINTa compLen, OO_SINTa rawLen); -/* Get the size of buffer needed for "in place" decode - - $:compressor compressor used; OodleLZ_Compressor_Invalid to make it enough for any compressor - $:compLen compressed data length - $:rawLen decompressed data length - $:return size of buffer needed for "in place" decode ; slighly larger than rawLen - - To do an "in place" decode, allocate a buffer of this size (or larger). Read the compressed data into the end of - the buffer, and decompress to the front of the buffer. The size returned here guarantees that the writes to the - front of the buffer don't conflict with the reads from the end. - - If _compressor_ is one of the new codecs (Kraken,Mermaid,Selkie,Leviathan), the padding for in place decodes can be - very small indeed. It is assumed you will be passing FuzzSafe_Yes to the decompress call. - - If _compLen_ is unknown, you want an in place buffer size that can accomodate any compressed data, then - pass compLen = 0. - - See $OodleLZ_Decompress for more. -*/ - -// GetCompressedStepForRawStep is at OODLELZ_QUANTUM_LEN granularity -// returns how many packed bytes to step to get the desired raw count step -OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_GetCompressedStepForRawStep( - const void * compPtr, OO_SINTa compAvail, - OO_SINTa startRawPos, OO_SINTa rawSeekBytes, - OO_SINTa * pEndRawPos OODEFAULT(NULL), - OO_BOOL * pIndependent OODEFAULT(NULL) ); -/* How many bytes to step a compressed pointer to advance a certain uncompressed amount - - $:compPtr current compressed pointer - $:compAvail compressed bytes available at compPtr - $:startRawPos initial raw pos (corresponding to compPtr) - $:rawSeekBytes the desired step in raw bytes, must be a multiple of $OODLELZ_QUANTUM_LEN or $OODLELZ_BLOCK_LEN - $:pEndRawPos (optional) filled with the end raw pos actually reached - $:pIndependent (optional) filled with a bool that is true if the current chunk is independent from previous - $:return the number of compressed bytes to step - - You should try to use GetCompressedStepForRawStep only at block granularity - both _startRawPos_ and - _rawSeekBytes_ should be multiples of OODLELZ_BLOCK_LEN (except at the end of the stream). As long as you - do that, then *pEndRawPos will = startRawPos + rawSeekBytes. - - You can use it at quantum granularity (OODLELZ_QUANTUM_LEN), but there are some caveats. You cannot step - quanta inside uncompressed blocks, only in normal LZ blocks. If you try to seek quanta inside an uncompressed - block, you will get *pEndRawPos = the end of the block. - - You can only resume seeking from *pEndRawPos . - - returns 0 for valid not-enough-data case - returns -1 for error - - If _compAvail_ is not the whole compressed buffer, then the returned step may be less than the amount you requested. - eg. if the compressed data in _compAvail_ does not contain enough data to make a step of _rawSeekBytes_ a smaller - step will be taken. - NOTE : *can* return comp step > comp avail ! - - -*/ - -OOFUNC1 OodleLZ_Compressor OOFUNC2 OodleLZ_GetAllChunksCompressor(const void * compBuf,OO_SINTa compBufSize, - OO_SINTa rawLen); -/* ask who compressed all chunks in this buf chunk - - $:compBuf pointer to compressed data; must be the start of compressed buffer, or a step of $OODLELZ_BLOCK_LEN raw bytes - $:compBufSize size of _compBuf_ - $:rawLen rawlen of data in _compBuf_ - $:return the $OodleLZ_Compressor used to encode this chunk - - returns a simple compressor (for example OodleLZ_Compressor_Kraken) if that was used on all chunks - - returns OodleLZ_Compressor_Hydra if different NewLZ encoders were used (for example Kraken+Mermaid) - - returns OodleLZ_Compressor_Count if a heterogenous mix of compressors was used (not just NewLZ) - - returns OodleLZ_Compressor_Invalid on error - - note this is only for this chunk - later chunks may have different compressors (eg. with Hydra) - if you compressed all chunks the same it's up to you to store that info in your header - - returns OodleLZ_Compressor_Invalid if _compBufSize_ is too small or any chunk is corrupt -*/ - -OOFUNC1 OodleLZ_Compressor OOFUNC2 OodleLZ_GetFirstChunkCompressor(const void * compChunkPtr, - OO_SINTa compBufAvail, - OO_BOOL * pIndependent); -/* ask who compressed this chunk - - $:compChunkPtr pointer to compressed data; must be the start of compressed buffer, or a step of $OODLELZ_BLOCK_LEN raw bytes - $:compBufAvail number of bytes at _compChunkPtr_ available to read - $:pIndependent (optional) filled with a bool for whether this chunk is independent of predecessors - $:return the $OodleLZ_Compressor used to encode this chunk - - note this is only for this chunk - later chunks may have different compressors (eg. with Hydra) - if you compressed all chunks the same it's up to you to store that info in your header - - Use $OodleLZ_GetAllChunksCompressor for data that might be mixed compressors. - - This replaces the deprecated function $OodleLZ_GetChunkCompressor - - returns OodleLZ_Compressor_Invalid if _compBufAvail_ is too small or the chunk is corrupt -*/ - -OOFUNC1 OodleLZ_Compressor OOFUNC2 OodleLZ_GetChunkCompressor(const void * compChunkPtr, - OO_SINTa compBufAvail, - OO_BOOL * pIndependent); -/* Deprecated entry point for backwards compatibility - - Use $OodleLZ_GetFirstChunkCompressor or $OodleLZ_GetAllChunksCompressor - -*/ - -//======================================================= - -#define OODLE_HEADER_VERSION ((46<<24)|(OODLE2_VERSION_MAJOR<<16)|(OODLE2_VERSION_MINOR<<8)|(OO_U32)sizeof(OodleLZ_SeekTable)) /* OODLE_HEADER_VERSION is used to ensure the Oodle header matches the lib. Don't copy the value of this macro, it will change when - the header is rev'ed. - - This is what you pass to $OodleX_Init or $Oodle_CheckVersion -*/ - -OOFUNC1 OO_BOOL OOFUNC2 Oodle_CheckVersion(OO_U32 oodle_header_version, OO_U32 * pOodleLibVersion OODEFAULT(NULL)); -/* Check the Oodle lib version against the header you are compiling with - - $:oodle_header_version pass $OODLE_HEADER_VERSION here - $:pOodleLibVersion (optional) filled with the Oodle lib version - $:return false if $OODLE_HEADER_VERSION is not compatible with this lib - - If you use the Oodle2 Ext lib,, $OodleX_Init does it for you. But if you want to check that you have a - compatible lib before trying to Init, then use this. -*/ - -OOFUNC1 void OOFUNC2 Oodle_LogHeader(void); -/* Log the Oodle version & copyright - - Uses the log set with $OodleCore_Plugins_SetPrintf -*/ - -// define old names so they still compile : -#define OODLECORE_PLUGIN_JOB_MAX_DEPENDENCIES OODLE_JOB_MAX_DEPENDENCIES -#define t_fp_OodleCore_Plugin_Job t_fp_Oodle_Job - -#ifdef _MSC_VER -#pragma warning(pop) -#pragma pack(pop, Oodle) -#endif - -#endif // __OODLE2_H_INCLUDED__ diff --git a/lib/oodle/oodle2base.h b/lib/oodle/oodle2base.h deleted file mode 100644 index 05f73f3..0000000 --- a/lib/oodle/oodle2base.h +++ /dev/null @@ -1,167 +0,0 @@ - -//=================================================== -// Oodle2 Base header -// (C) Copyright 1994-2021 Epic Games Tools LLC -//=================================================== - -#ifndef __OODLE2BASE_H_INCLUDED__ -#define __OODLE2BASE_H_INCLUDED__ - -#ifndef OODLE2BASE_PUBLIC_HEADER -#define OODLE2BASE_PUBLIC_HEADER 1 -#endif - -#ifdef _MSC_VER -#pragma pack(push, Oodle, 8) - -#pragma warning(push) -#pragma warning(disable : 4127) // conditional is constant -#endif - -#ifndef OODLE_BASE_TYPES_H -#define OODLE_BASE_TYPES_H - -#include - -#define OOCOPYRIGHT "Copyright (C) 1994-2021, Epic Games Tools LLC" - -// Typedefs -typedef int8_t OO_S8; -typedef uint8_t OO_U8; -typedef int16_t OO_S16; -typedef uint16_t OO_U16; -typedef int32_t OO_S32; -typedef uint32_t OO_U32; -typedef int64_t OO_S64; -typedef uint64_t OO_U64; -typedef float OO_F32; -typedef double OO_F64; -typedef intptr_t OO_SINTa; -typedef uintptr_t OO_UINTa; -typedef int32_t OO_BOOL; - -// Struct packing handling and inlining -#if defined(__GNUC__) || defined(__clang__) - #define OOSTRUCT struct __attribute__((__packed__)) - #define OOINLINEFUNC inline -#elif defined(_MSC_VER) - // on VC++, we use pragmas for the struct packing - #define OOSTRUCT struct - #define OOINLINEFUNC __inline -#endif - -// Linkage stuff -#if defined(_WIN32) - #define OOLINK __stdcall - #define OOEXPLINK __stdcall -#else - #define OOLINK - #define OOEXPLINK -#endif - -// C++ name demangaling -#ifdef __cplusplus - #define OODEFFUNC extern "C" - #define OODEFSTART extern "C" { - #define OODEFEND } - #define OODEFAULT( val ) =val -#else - #define OODEFFUNC - #define OODEFSTART - #define OODEFEND - #define OODEFAULT( val ) -#endif - -// ======================================================== -// Exported function declarations -#define OOEXPFUNC OODEFFUNC - -//=========================================================================== -// OO_STRING_JOIN joins strings in the preprocessor and works with LINESTRING -#define OO_STRING_JOIN(arg1, arg2) OO_STRING_JOIN_DELAY(arg1, arg2) -#define OO_STRING_JOIN_DELAY(arg1, arg2) OO_STRING_JOIN_IMMEDIATE(arg1, arg2) -#define OO_STRING_JOIN_IMMEDIATE(arg1, arg2) arg1 ## arg2 - -//=========================================================================== -// OO_NUMBERNAME is a macro to make a name unique, so that you can use it to declare -// variable names and they won't conflict with each other -// using __LINE__ is broken in MSVC with /ZI , but __COUNTER__ is an MSVC extension that works - -#ifdef _MSC_VER - #define OO_NUMBERNAME(name) OO_STRING_JOIN(name,__COUNTER__) -#else - #define OO_NUMBERNAME(name) OO_STRING_JOIN(name,__LINE__) -#endif - -//=================================================================== -// simple compiler assert -// this happens at declaration time, so if it's inside a function in a C file, drop {} around it -#ifndef OO_COMPILER_ASSERT - #if defined(__clang__) - #define OO_COMPILER_ASSERT_UNUSED __attribute__((unused)) // hides warnings when compiler_asserts are in a local scope - #else - #define OO_COMPILER_ASSERT_UNUSED - #endif - - #define OO_COMPILER_ASSERT(exp) typedef char OO_NUMBERNAME(_dummy_array) [ (exp) ? 1 : -1 ] OO_COMPILER_ASSERT_UNUSED -#endif - - -#endif - - - -// Oodle2 base header - -#ifndef OODLE2_PUBLIC_CORE_DEFINES -#define OODLE2_PUBLIC_CORE_DEFINES 1 - -#define OOFUNC1 OOEXPFUNC -#define OOFUNC2 OOEXPLINK -#define OOFUNCSTART -#define OODLE_CALLBACK OOLINK - -// Check build flags - #if defined(OODLE_BUILDING_LIB) || defined(OODLE_BUILDING_DLL) - #error Should not see OODLE_BUILDING set for users of oodle.h - #endif - -#ifndef NULL -#define NULL (0) -#endif - -// OODLE_MALLOC_MINIMUM_ALIGNMENT is 8 in 32-bit, 16 in 64-bit -#define OODLE_MALLOC_MINIMUM_ALIGNMENT ((OO_SINTa)(2*sizeof(void *))) - -typedef void (OODLE_CALLBACK t_OodleFPVoidVoid)(void); -/* void-void callback func pointer - takes void, returns void -*/ - -typedef void (OODLE_CALLBACK t_OodleFPVoidVoidStar)(void *); -/* void-void-star callback func pointer - takes void pointer, returns void -*/ - -#define OODLE_JOB_MAX_DEPENDENCIES (4) /* Maximum number of dependencies Oodle will ever pass to a RunJob callback -*/ - -#define OODLE_JOB_NULL_HANDLE (0) /* Value 0 of Jobify handles is reserved to mean none -* Wait(OODLE_JOB_NULL_HANDLE) is a nop -* if RunJob returns OODLE_JOB_NULL_HANDLE it means the job -* was run synchronously and no wait is required -*/ - -#define t_fp_Oodle_Job t_OodleFPVoidVoidStar /* Job function pointer for Plugin Jobify system - - takes void pointer returns void -*/ - -#endif // OODLE2_PUBLIC_CORE_DEFINES - -#ifdef _MSC_VER -#pragma warning(pop) -#pragma pack(pop, Oodle) -#endif - -#endif // __OODLE2BASE_H_INCLUDED__ diff --git a/lib/oodle/src/lib.rs b/lib/oodle/src/lib.rs deleted file mode 100644 index 76b1d16..0000000 --- a/lib/oodle/src/lib.rs +++ /dev/null @@ -1,145 +0,0 @@ -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] - -use std::ptr; - -use color_eyre::{eyre, Result}; - -#[allow(dead_code)] -mod bindings { - include!(concat!(env!("OUT_DIR"), "/bindings.rs")); -} - -// Hardcoded chunk size of Bitsquid's bundle compression -pub const CHUNK_SIZE: usize = 512 * 1024; -pub const COMPRESSOR: bindings::OodleLZ_Compressor = - bindings::OodleLZ_Compressor_OodleLZ_Compressor_Kraken; -pub const LEVEL: bindings::OodleLZ_CompressionLevel = - bindings::OodleLZ_CompressionLevel_OodleLZ_CompressionLevel_Optimal2; - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum OodleLZ_FuzzSafe { - Yes, - No, -} - -impl From for bindings::OodleLZ_FuzzSafe { - fn from(value: OodleLZ_FuzzSafe) -> Self { - match value { - OodleLZ_FuzzSafe::Yes => bindings::OodleLZ_FuzzSafe_OodleLZ_FuzzSafe_Yes, - OodleLZ_FuzzSafe::No => bindings::OodleLZ_FuzzSafe_OodleLZ_FuzzSafe_No, - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum OodleLZ_CheckCRC { - Yes, - No, -} - -impl From for bindings::OodleLZ_CheckCRC { - fn from(value: OodleLZ_CheckCRC) -> Self { - match value { - OodleLZ_CheckCRC::Yes => bindings::OodleLZ_CheckCRC_OodleLZ_CheckCRC_Yes, - OodleLZ_CheckCRC::No => bindings::OodleLZ_CheckCRC_OodleLZ_CheckCRC_No, - } - } -} - -#[tracing::instrument(skip(data))] -pub fn decompress( - data: I, - fuzz_safe: OodleLZ_FuzzSafe, - check_crc: OodleLZ_CheckCRC, -) -> Result> -where - I: AsRef<[u8]>, -{ - let data = data.as_ref(); - let mut out = vec![0; CHUNK_SIZE]; - - let verbosity = if tracing::enabled!(tracing::Level::INFO) { - bindings::OodleLZ_Verbosity_OodleLZ_Verbosity_Minimal - } else if tracing::enabled!(tracing::Level::DEBUG) { - bindings::OodleLZ_Verbosity_OodleLZ_Verbosity_Some - } else if tracing::enabled!(tracing::Level::TRACE) { - bindings::OodleLZ_Verbosity_OodleLZ_Verbosity_Lots - } else { - bindings::OodleLZ_Verbosity_OodleLZ_Verbosity_None - }; - - let ret = unsafe { - bindings::OodleLZ_Decompress( - data.as_ptr() as *const _, - data.len() as isize, - out.as_mut_ptr() as *mut _, - out.len() as isize, - fuzz_safe.into(), - check_crc.into(), - verbosity, - ptr::null_mut(), - 0, - None, - ptr::null_mut(), - ptr::null_mut(), - 0, - bindings::OodleLZ_Decode_ThreadPhase_OodleLZ_Decode_Unthreaded, - ) - }; - - if ret == 0 { - eyre::bail!("Decompression failed"); - } - - Ok(out) -} - -#[tracing::instrument(skip(data))] -pub fn compress(data: I) -> Result> -where - I: AsRef<[u8]>, -{ - let mut raw = Vec::from(data.as_ref()); - raw.resize(CHUNK_SIZE, 0); - - // TODO: Query oodle for buffer size - let mut out = vec![0u8; CHUNK_SIZE]; - - let ret = unsafe { - bindings::OodleLZ_Compress( - COMPRESSOR, - raw.as_ptr() as *const _, - raw.len() as isize, - out.as_mut_ptr() as *mut _, - LEVEL, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - 0, - ) - }; - - tracing::debug!(compressed_size = ret, "Compressed chunk"); - - if ret == 0 { - eyre::bail!("Compression failed"); - } - - out.resize(ret as usize, 0); - - Ok(out) -} - -pub fn get_decode_buffer_size(raw_size: usize, corruption_possible: bool) -> Result { - let size = unsafe { - bindings::OodleLZ_GetDecodeBufferSize( - COMPRESSOR, - raw_size as isize, - if corruption_possible { 1 } else { 0 }, - ) - }; - Ok(size as usize) -} diff --git a/lib/sdk/Cargo.toml b/lib/sdk/Cargo.toml index d735f36..d85b26f 100644 --- a/lib/sdk/Cargo.toml +++ b/lib/sdk/Cargo.toml @@ -17,7 +17,7 @@ nanorand = "0.7.0" pin-project-lite = "0.2.9" serde = { version = "1.0.147", features = ["derive"] } serde_sjson = { path = "../../lib/serde_sjson", version = "*" } -oodle = { path = "../../lib/oodle", version = "*" } +oodle-sys = { path = "../../lib/oodle-sys", version = "*" } tokio = { version = "1.21.2", features = ["rt-multi-thread", "fs", "process", "macros", "tracing", "io-util", "io-std"] } tokio-stream = { version = "0.1.11", features = ["fs", "io-util"] } tracing = { version = "0.1.37", features = ["async-await"] } diff --git a/lib/sdk/src/bundle/mod.rs b/lib/sdk/src/bundle/mod.rs index 1c08530..8b8d7c5 100644 --- a/lib/sdk/src/bundle/mod.rs +++ b/lib/sdk/src/bundle/mod.rs @@ -4,7 +4,7 @@ use std::path::Path; use color_eyre::eyre::{self, Context, Result}; use color_eyre::{Help, Report, SectionExt}; -use oodle::{OodleLZ_CheckCRC, OodleLZ_FuzzSafe, CHUNK_SIZE}; +use oodle_sys::{OodleLZ_CheckCRC, OodleLZ_FuzzSafe, CHUNK_SIZE}; use crate::binary::sync::*; use crate::bundle::file::Properties; @@ -159,7 +159,7 @@ impl Bundle { decompressed.append(&mut compressed_buffer); } else { // TODO: Optimize to not reallocate? - let mut raw_buffer = oodle::decompress( + let mut raw_buffer = oodle_sys::decompress( &compressed_buffer, OodleLZ_FuzzSafe::No, OodleLZ_CheckCRC::No, @@ -257,7 +257,7 @@ impl Bundle { let mut chunk_sizes = Vec::with_capacity(num_chunks); for chunk in chunks { - let compressed = oodle::compress(chunk)?; + let compressed = oodle_sys::compress(chunk)?; tracing::trace!( raw_chunk_size = chunk.len(), compressed_chunk_size = compressed.len() @@ -359,7 +359,7 @@ where r.read_exact(&mut compressed_buffer)?; // TODO: Optimize to not reallocate? - let mut raw_buffer = oodle::decompress( + let mut raw_buffer = oodle_sys::decompress( &compressed_buffer, OodleLZ_FuzzSafe::No, OodleLZ_CheckCRC::No, From 86757089b42e2d268f35d564e54f04ba50fd9089 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 2 Mar 2023 15:11:50 +0100 Subject: [PATCH 030/335] chore: Update LuaJIT --- luajit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/luajit b/luajit index f0e865d..505e2c0 160000 --- a/luajit +++ b/luajit @@ -1 +1 @@ -Subproject commit f0e865dd4861520258299d0f2a56491bd9d602e1 +Subproject commit 505e2c03de35e2718eef0d2d3660712e06dadf1f From 905bbf001822b7aa2adc6a578abcc72b5e38ee71 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 2 Mar 2023 16:17:40 +0100 Subject: [PATCH 031/335] Revert "fix: Remove obsolete CLI parameter" This reverts commit 22ea4d75a1468e611318c8f1b087000c4e1fdb7f. --- crates/dtmm/src/main.rs | 6 ++++++ crates/dtmt/src/cmd/build.rs | 6 ++++++ crates/dtmt/src/cmd/bundle/mod.rs | 8 +++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index abdce49..fdce5ab 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -37,6 +37,12 @@ fn main() -> Result<()> { tracing::trace!(default_config_path = %default_config_path.display()); let matches = command!() + .arg(Arg::new("oodle").long("oodle").help( + "The oodle library to load. This may either be:\n\ + - A library name that will be searched for in the system's default paths.\n\ + - A file path relative to the current working directory.\n\ + - An absolute file path.", + )) .arg( Arg::new("config") .long("config") diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index c72a115..f0f6b41 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -32,6 +32,12 @@ pub(crate) fn command_definition() -> Command { If omitted, dtmt will search from the current working directory upward.", ), ) + .arg(Arg::new("oodle").long("oodle").help( + "The oodle library to load. This may either be:\n\ + - A library name that will be searched for in the system's default paths.\n\ + - A file path relative to the current working directory.\n\ + - An absolute file path.", + )) .arg( Arg::new("out") .long("out") diff --git a/crates/dtmt/src/cmd/bundle/mod.rs b/crates/dtmt/src/cmd/bundle/mod.rs index 2a18a02..03ab3f5 100644 --- a/crates/dtmt/src/cmd/bundle/mod.rs +++ b/crates/dtmt/src/cmd/bundle/mod.rs @@ -1,4 +1,4 @@ -use clap::{ArgMatches, Command}; +use clap::{Arg, ArgMatches, Command}; use color_eyre::eyre::Result; mod decompress; @@ -10,6 +10,12 @@ pub(crate) fn command_definition() -> Command { Command::new("bundle") .subcommand_required(true) .about("Manipulate the game's bundle files") + .arg(Arg::new("oodle").long("oodle").help( + "The oodle library to load. This may either be:\n\ + - A library name that will be searched for in the system's default paths.\n\ + - A file path relative to the current working directory.\n\ + - An absolute file path.", + )) .subcommand(decompress::command_definition()) .subcommand(extract::command_definition()) .subcommand(inject::command_definition()) From dcaefa0a8a4385c55a8270109f3bc8a57d2f49c3 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 2 Mar 2023 20:33:55 +0100 Subject: [PATCH 032/335] fix(dtmm): Fix importing archives on Windows The path separators in zip files are OS-specific. Fix #43. --- crates/dtmm/src/controller/app.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index d8cb619..b9903cf 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -50,10 +50,18 @@ pub(crate) async fn import_mod(state: State, info: FileInfo) -> Result tracing::info!("Importing mod {}", dir_name); + let names: Vec<_> = archive.file_names().map(|s| s.to_string()).collect(); + let mod_cfg: ModConfig = { + let name = names + .iter() + .find(|name| name.ends_with("dtmt.cfg")) + .ok_or_else(|| eyre::eyre!("archive does not contain mod config"))?; + let mut f = archive - .by_name(&format!("{}/{}", dir_name, "dtmt.cfg")) + .by_name(name) .wrap_err("failed to read mod config from archive")?; + let mut buf = Vec::with_capacity(f.size() as usize); f.read_to_end(&mut buf) .wrap_err("failed to read mod config from archive")?; @@ -66,8 +74,13 @@ pub(crate) async fn import_mod(state: State, info: FileInfo) -> Result tracing::debug!(?mod_cfg); let files: HashMap> = { + let name = names + .iter() + .find(|name| name.ends_with("files.sjson")) + .ok_or_else(|| eyre::eyre!("archive does not contain file index"))?; + let mut f = archive - .by_name(&format!("{}/{}", dir_name, "files.sjson")) + .by_name(name) .wrap_err("failed to read file index from archive")?; let mut buf = Vec::with_capacity(f.size() as usize); f.read_to_end(&mut buf) From 6cf4bb42c69618def6d4b2cee2bcaa7927235794 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 2 Mar 2023 22:16:54 +0100 Subject: [PATCH 033/335] fix(dtmm): Prevent opening an empty console host Fixes #44. --- crates/dtmm/src/main.rs | 1 + crates/dtmt/src/main.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index fdce5ab..b1d87b9 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -1,5 +1,6 @@ #![recursion_limit = "256"] #![feature(let_chains)] +#![windows_subsystem = "windows"] use std::path::PathBuf; use std::sync::Arc; diff --git a/crates/dtmt/src/main.rs b/crates/dtmt/src/main.rs index d4df73c..15c6758 100644 --- a/crates/dtmt/src/main.rs +++ b/crates/dtmt/src/main.rs @@ -1,5 +1,6 @@ #![feature(io_error_more)] #![feature(let_chains)] +#![windows_subsystem = "console"] use std::path::PathBuf; use std::sync::Arc; From bb5a2ecf64f00b3b107b024729707aebeb39ffe7 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 3 Mar 2023 14:30:56 +0100 Subject: [PATCH 034/335] refactor(dtmm): Extract inlined variables --- crates/dtmm/src/ui/window/main.rs | 50 ++++++++++++++----------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index a0ccaa2..25c1001 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -31,43 +31,39 @@ pub(crate) fn new() -> WindowDesc { } fn build_top_bar() -> impl Widget { + let mods_button = Button::new("Mods") + .on_click(|_ctx, state: &mut State, _env| state.current_view = View::Mods); + + let settings_button = Button::new("Settings").on_click(|_ctx, state: &mut State, _env| { + state.current_view = View::Settings; + }); + + let deploy_button = Button::new("Deploy Mods") + .on_click(|ctx, _state: &mut State, _env| { + ctx.submit_command(ACTION_START_DEPLOY); + }) + .disabled_if(|data, _| data.is_deployment_in_progress || data.is_reset_in_progress); + + let reset_button = Button::new("Reset Game") + .on_click(|ctx, _state: &mut State, _env| { + ctx.submit_command(ACTION_START_RESET_DEPLOYMENT); + }) + .disabled_if(|data, _| data.is_deployment_in_progress || data.is_reset_in_progress); + Flex::row() .must_fill_main_axis(true) .main_axis_alignment(MainAxisAlignment::SpaceBetween) .with_child( Flex::row() - .with_child( - Button::new("Mods") - .on_click(|_ctx, state: &mut State, _env| state.current_view = View::Mods), - ) + .with_child(mods_button) .with_default_spacer() - .with_child( - Button::new("Settings").on_click(|_ctx, state: &mut State, _env| { - state.current_view = View::Settings; - }), - ), + .with_child(settings_button), ) .with_child( Flex::row() - .with_child( - Button::new("Deploy Mods") - .on_click(|ctx, _state: &mut State, _env| { - ctx.submit_command(ACTION_START_DEPLOY); - }) - .disabled_if(|data, _| { - data.is_deployment_in_progress || data.is_reset_in_progress - }), - ) + .with_child(deploy_button) .with_default_spacer() - .with_child( - Button::new("Reset Game") - .on_click(|ctx, _state: &mut State, _env| { - ctx.submit_command(ACTION_START_RESET_DEPLOYMENT); - }) - .disabled_if(|data, _| { - data.is_deployment_in_progress || data.is_reset_in_progress - }), - ), + .with_child(reset_button), ) .padding(theme::TOP_BAR_INSETS) .background(theme::TOP_BAR_BACKGROUND_COLOR) From 3252e66a3ff09f2438a03abce1f32ca64b4d3959 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 3 Mar 2023 14:35:35 +0100 Subject: [PATCH 035/335] feat(dtmm): Add indicator when a deployment is necessary Closes #32. --- crates/dtmm/src/state/data.rs | 2 ++ crates/dtmm/src/state/delegate.rs | 7 +++++++ crates/dtmm/src/ui/widget/controller.rs | 26 +++++++++++++++---------- crates/dtmm/src/ui/window/main.rs | 17 ++++++++++++---- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index c8dc3aa..2045ddc 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -78,6 +78,7 @@ pub(crate) struct State { pub current_view: View, pub mods: Vector, pub selected_mod_index: Option, + pub dirty: bool, pub is_deployment_in_progress: bool, pub is_reset_in_progress: bool, pub is_save_in_progress: bool, @@ -106,6 +107,7 @@ impl State { current_view: View::default(), mods: Vector::new(), selected_mod_index: None, + dirty: false, is_deployment_in_progress: false, is_reset_in_progress: false, is_save_in_progress: false, diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index 08d17b0..689332c 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -33,6 +33,8 @@ pub(crate) const ACTION_START_SAVE_SETTINGS: Selector = pub(crate) const ACTION_FINISH_SAVE_SETTINGS: Selector = Selector::new("dtmm.action.finish-save-settings"); +pub(crate) const ACTION_SET_DIRTY: Selector = Selector::new("dtmm.action.set-dirty"); + pub(crate) enum AsyncAction { DeployMods(State), ResetDeployment(State), @@ -81,6 +83,7 @@ impl AppDelegate for Delegate { } cmd if cmd.is(ACTION_FINISH_DEPLOY) => { state.is_deployment_in_progress = false; + state.dirty = false; Handled::Yes } cmd if cmd.is(ACTION_START_RESET_DEPLOYMENT) => { @@ -226,6 +229,10 @@ impl AppDelegate for Delegate { Handled::Yes } + cmd if cmd.is(ACTION_SET_DIRTY) => { + state.dirty = true; + Handled::Yes + } cmd => { if cfg!(debug_assertions) { tracing::warn!("Unknown command: {:?}", cmd); diff --git a/crates/dtmm/src/ui/widget/controller.rs b/crates/dtmm/src/ui/widget/controller.rs index ce18d5b..1bd7f6a 100644 --- a/crates/dtmm/src/ui/widget/controller.rs +++ b/crates/dtmm/src/ui/widget/controller.rs @@ -1,7 +1,7 @@ use druid::widget::{Button, Controller, Scroll}; use druid::{Data, Env, Event, EventCtx, Rect, UpdateCtx, Widget}; -use crate::state::{State, ACTION_START_SAVE_SETTINGS}; +use crate::state::{State, ACTION_SET_DIRTY, ACTION_START_SAVE_SETTINGS}; pub struct DisabledButtonController; @@ -57,11 +57,16 @@ impl> Controller> for AutoScrollController } } -/// A controller that submits the command to save settings every time its widget's -/// data changes. -pub struct SaveSettingsController; +macro_rules! compare_state_fields { + ($old:ident, $new:ident, $($field:ident),+) => { + $($old.$field != $new.$field) || + + } +} -impl> Controller for SaveSettingsController { +/// A controller that tracks state changes for certain fields and submits commands to handle them. +pub struct DirtyStateController; + +impl> Controller for DirtyStateController { fn update( &mut self, child: &mut W, @@ -70,13 +75,14 @@ impl> Controller for SaveSettingsController { data: &State, env: &Env, ) { - // Only filter for the values that actually go into the settings file. - if old_data.mods != data.mods - || old_data.game_dir != data.game_dir - || old_data.data_dir != data.data_dir - { + if compare_state_fields!(old_data, data, mods, game_dir, data_dir) { ctx.submit_command(ACTION_START_SAVE_SETTINGS); } + + if compare_state_fields!(old_data, data, mods, game_dir) { + ctx.submit_command(ACTION_SET_DIRTY); + } + child.update(ctx, old_data, data, env) } } diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 25c1001..3b3ef00 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -15,7 +15,7 @@ use crate::state::{ ACTION_START_RESET_DEPLOYMENT, }; use crate::ui::theme; -use crate::ui::widget::controller::{AutoScrollController, SaveSettingsController}; +use crate::ui::widget::controller::{AutoScrollController, DirtyStateController}; use crate::ui::widget::PathBufFormatter; const TITLE: &str = "Darktide Mod Manager"; @@ -38,11 +38,20 @@ fn build_top_bar() -> impl Widget { state.current_view = View::Settings; }); - let deploy_button = Button::new("Deploy Mods") + let deploy_button = { + Button::dynamic(|state: &State, _| { + let mut s = String::new(); + if state.dirty { + s.push_str("! "); + } + s.push_str("Deploy Mods"); + s + }) .on_click(|ctx, _state: &mut State, _env| { ctx.submit_command(ACTION_START_DEPLOY); }) - .disabled_if(|data, _| data.is_deployment_in_progress || data.is_reset_in_progress); + .disabled_if(|data, _| data.is_deployment_in_progress || data.is_reset_in_progress) + }; let reset_button = Button::new("Reset Game") .on_click(|ctx, _state: &mut State, _env| { @@ -308,5 +317,5 @@ fn build_window() -> impl Widget { .with_child(build_top_bar()) .with_flex_child(build_main(), 1.0) .with_child(build_log_view()) - .controller(SaveSettingsController) + .controller(DirtyStateController) } From 4a1e88987cc8580d746db2758e2e9e3e5be24adb Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 3 Mar 2023 10:58:35 +0100 Subject: [PATCH 036/335] fix: Force unix path separators for engine values The engine, and therefore the SDK, too, use unix path separators. However, on Windows, `PathBuf` automatically produces backslashes. Fix #46. --- CHANGELOG.adoc | 4 ++++ Cargo.lock | 9 +++++++++ crates/dtmm/Cargo.toml | 1 + crates/dtmm/src/controller/game.rs | 9 +++++---- crates/dtmt/Cargo.toml | 1 + crates/dtmt/src/cmd/build.rs | 5 +++-- crates/dtmt/src/cmd/package.rs | 5 +++-- lib/sdk/Cargo.toml | 1 + lib/sdk/src/filetype/package.rs | 3 ++- 9 files changed, 29 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index e32ff71..8d148d8 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -7,6 +7,10 @@ - dtmt: split `build` into `build` and `package` - dtmt: implement deploying built bundles +=== Fixed + +- all: force unix path separators for engine values + == 2023-03-01 === Added diff --git a/Cargo.lock b/Cargo.lock index 6e8ff1d..af5a5f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -676,6 +676,7 @@ dependencies = [ "dtmt-shared", "futures", "oodle-sys", + "path-slash", "sdk", "serde", "serde_sjson", @@ -704,6 +705,7 @@ dependencies = [ "nanorand", "oodle-sys", "path-clean", + "path-slash", "pin-project-lite", "promptly", "sdk", @@ -1699,6 +1701,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" +[[package]] +name = "path-slash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" + [[package]] name = "pbkdf2" version = "0.11.0" @@ -2045,6 +2053,7 @@ dependencies = [ "luajit2-sys", "nanorand", "oodle-sys", + "path-slash", "pin-project-lite", "serde", "serde_sjson", diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 6516bcc..4719234 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -23,3 +23,4 @@ tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } zip = "0.6.4" tokio-stream = { version = "0.1.12", features = ["fs"] } +path-slash = "0.2.1" diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 508e84a..a9db26b 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -8,6 +8,7 @@ use color_eyre::eyre::Context; use color_eyre::{eyre, Help, Result}; use futures::stream; use futures::StreamExt; +use path_slash::PathBufExt; use sdk::filetype::lua; use sdk::filetype::package::Package; use sdk::murmur::Murmur64; @@ -153,22 +154,22 @@ fn build_mod_data_lua(state: Arc) -> String { lua.push_str(" new_mod(\""); lua.push_str(&mod_info.id); lua.push_str("\", {\n mod_script = \""); - lua.push_str(&resources.init.to_string_lossy()); + lua.push_str(&resources.init.to_slash_lossy()); if let Some(data) = resources.data.as_ref() { lua.push_str("\",\n mod_data = \""); - lua.push_str(&data.to_string_lossy()); + lua.push_str(&data.to_slash_lossy()); } if let Some(localization) = &resources.localization { lua.push_str("\",\n mod_localization = \""); - lua.push_str(&localization.to_string_lossy()); + lua.push_str(&localization.to_slash_lossy()); } lua.push_str("\",\n })\n"); } else { lua.push_str(" return dofile(\""); - lua.push_str(&resources.init.to_string_lossy()); + lua.push_str(&resources.init.to_slash_lossy()); lua.push_str("\")\n"); } diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index 9067ae8..8b210e5 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -29,6 +29,7 @@ tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tracing = { version = "0.1.37", features = ["async-await"] } zip = "0.6.3" path-clean = "1.0.1" +path-slash = "0.2.1" [dev-dependencies] tempfile = "3.3.0" diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index f0f6b41..a49c9a1 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -9,6 +9,7 @@ use color_eyre::{Help, Report}; use dtmt_shared::ModConfig; use futures::future::try_join_all; use futures::StreamExt; +use path_slash::PathExt; use sdk::filetype::package::Package; use sdk::murmur::IdString64; use sdk::{Bundle, BundleFile}; @@ -134,7 +135,7 @@ where path.set_extension(""); BundleFile::from_sjson( - path.to_string_lossy().to_string(), + path.to_slash_lossy().to_string(), file_type, sjson, root.as_ref(), @@ -176,7 +177,7 @@ where .await .wrap_err_with(|| format!("failed to read file {}", path.display()))?; - let pkg_name = package.to_string_lossy().to_string(); + let pkg_name = package.to_slash_lossy().to_string(); let pkg = Package::from_sjson(sjson, pkg_name.clone(), root) .await .wrap_err_with(|| format!("invalid package file {}", &pkg_name))?; diff --git a/crates/dtmt/src/cmd/package.rs b/crates/dtmt/src/cmd/package.rs index 44184f6..2a64a9f 100644 --- a/crates/dtmt/src/cmd/package.rs +++ b/crates/dtmt/src/cmd/package.rs @@ -5,6 +5,7 @@ use std::path::PathBuf; use clap::{value_parser, Arg, ArgMatches, Command}; use color_eyre::eyre::{Context, Result}; use color_eyre::Help; +use path_slash::PathBufExt; use tokio::fs::{self, DirEntry}; use tokio_stream::wrappers::ReadDirStream; use tokio_stream::StreamExt; @@ -88,7 +89,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> .await .wrap_err_with(|| format!("failed to read mod config at {}", path.display()))?; - zip.start_file(name.to_string_lossy(), Default::default())?; + zip.start_file(name.to_slash_lossy(), Default::default())?; zip.write_all(&data)?; } @@ -111,7 +112,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> let (name, data) = res?; let name = base_path.join(name); - zip.start_file(name.to_string_lossy(), Default::default())?; + zip.start_file(name.to_slash_lossy(), Default::default())?; zip.write_all(&data)?; } }; diff --git a/lib/sdk/Cargo.toml b/lib/sdk/Cargo.toml index d85b26f..8df7524 100644 --- a/lib/sdk/Cargo.toml +++ b/lib/sdk/Cargo.toml @@ -24,3 +24,4 @@ tracing = { version = "0.1.37", features = ["async-await"] } tracing-error = "0.2.0" luajit2-sys = "0.0.2" async-recursion = "1.0.2" +path-slash = "0.2.1" diff --git a/lib/sdk/src/filetype/package.rs b/lib/sdk/src/filetype/package.rs index 338cc8e..9545677 100644 --- a/lib/sdk/src/filetype/package.rs +++ b/lib/sdk/src/filetype/package.rs @@ -7,6 +7,7 @@ use std::str::FromStr; use async_recursion::async_recursion; use color_eyre::eyre::{self, Context}; use color_eyre::Result; +use path_slash::PathBufExt; use tokio::fs; use crate::binary::sync::{ReadExt, WriteExt}; @@ -258,7 +259,7 @@ impl Package { for path in paths.iter() { w.write_u64(t.hash().into())?; - let hash = Murmur64::hash(path.to_string_lossy().as_bytes()); + let hash = Murmur64::hash(path.to_slash_lossy().as_bytes()); w.write_u64(hash.into())?; } } From fb88388acf1f2312455186fc58013e08bcb1bdec Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 6 Mar 2023 09:20:57 +0100 Subject: [PATCH 037/335] fix(dtmt): Fix extracting files with non-flattened file names Fixes #51. --- CHANGELOG.adoc | 1 + crates/dtmt/src/cmd/bundle/extract.rs | 58 ++++++++++++++++----------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 8d148d8..63d56a4 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -10,6 +10,7 @@ === Fixed - all: force unix path separators for engine values +- dtmt: fix extracing files with non-flattened file names == 2023-03-01 diff --git a/crates/dtmt/src/cmd/bundle/extract.rs b/crates/dtmt/src/cmd/bundle/extract.rs index 3524f5b..35dee15 100644 --- a/crates/dtmt/src/cmd/bundle/extract.rs +++ b/crates/dtmt/src/cmd/bundle/extract.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use clap::{value_parser, Arg, ArgAction, ArgMatches, Command}; use color_eyre::eyre::{self, Context, Result}; -use color_eyre::{Help, Report, SectionExt}; +use color_eyre::{Help, Report}; use futures::future::try_join_all; use futures::StreamExt; use glob::Pattern; @@ -33,7 +33,7 @@ pub(crate) fn command_definition() -> Command { .value_parser(value_parser!(PathBuf)) .help( "Path to the bundle(s) to read. If this points to a directory instead \ - of a file, all files in that directory will be checked.", + of a file, all files in that directory will be checked.", ), ) .arg( @@ -311,14 +311,25 @@ where path.push(name); if options.dry_run { - tracing::info!(path = %path.display(), "Writing file"); + tracing::info!("Dry Run: Writing file '{}'", path.display()); } else { - tracing::debug!(path = %path.display(), "Writing file"); + tracing::info!("Writing file '{}'", path.display()); tasks.push(tokio::spawn(async move { - fs::write(&path, file.data()) - .await - .wrap_err("failed to write extracted file to disc") - .with_section(|| path.display().to_string().header("Path")) + if let Some(parent) = path.parent() { + fs::create_dir_all(&parent).await.wrap_err_with(|| { + format!( + "failed to create parent directories '{}'", + parent.display() + ) + })?; + } + + fs::write(&path, file.data()).await.wrap_err_with(|| { + format!( + "failed to write extracted file to disc: '{}'", + path.display() + ) + }) })); } } @@ -342,9 +353,9 @@ where path.push(name); if options.dry_run { - tracing::info!(path = %path.display(), "Writing file"); + tracing::info!("Dry Run: Writing file '{}'", path.display()); } else { - tracing::debug!(path = %path.display(), "Writing file"); + tracing::info!("Writing file '{}'", path.display()); tasks.push(tokio::spawn(async move { let parent = match path.parent() { Some(parent) => parent, @@ -356,17 +367,19 @@ where } }; - fs::create_dir_all(parent) - .await - .wrap_err("failed to create parent directory") - .with_section(|| { - parent.display().to_string().header("Path") - })?; + fs::create_dir_all(parent).await.wrap_err_with(|| { + format!( + "failed to create parent directory: '{}'", + parent.display() + ) + })?; - fs::write(&path, file.data()) - .await - .wrap_err("failed to write extracted file to disc") - .with_section(|| path.display().to_string().header("Path")) + fs::write(&path, file.data()).await.wrap_err_with(|| { + format!( + "failed to write extracted file to disc: '{}'", + path.display() + ) + }) })); } } @@ -374,10 +387,7 @@ where } } Err(err) => { - let err = err - .wrap_err("Failed to decompile") - .with_section(|| name.header("File")); - + let err = err.wrap_err(format!("Failed to decompile file {}", name)); tracing::error!("{:?}", err); } }; From 014d89242612d345ec1ed6c2acf70a4578147a2d Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 6 Mar 2023 11:07:17 +0100 Subject: [PATCH 038/335] chore: Update crates --- Cargo.lock | 121 ++++++++++++++++++++++------------------------------- 1 file changed, 51 insertions(+), 70 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af5a5f6..d3c2b3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -265,9 +265,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.7" +version = "4.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3061d6db6d8fcbbd4b05e057f2acace52e64e96b498c08c2d7a4e65addd340" +checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" dependencies = [ "bitflags", "clap_derive", @@ -282,9 +282,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.1.7" +version = "4.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d122164198950ba84a918270a3bb3f7ededd25e15f7451673d986f55bd2667" +checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" dependencies = [ "heck", "proc-macro-error", @@ -494,9 +494,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if", ] @@ -513,9 +513,9 @@ dependencies = [ [[package]] name = "csv-async" -version = "1.2.5" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c652d4c48e4dc80b26fadd169c02fb6053d9f57507ddd3e6b8706e7d0242235e" +checksum = "71933d3f2d0481d5111cb2817b15b6961961458ec58adf8008194e6c850046f4" dependencies = [ "bstr", "cfg-if", @@ -602,8 +602,8 @@ dependencies = [ [[package]] name = "druid" -version = "0.8.2" -source = "git+https://github.com/linebender/druid.git#5fa4ce51ed3d74640388de6385f135c50d346c8d" +version = "0.8.3" +source = "git+https://github.com/linebender/druid.git#c02452ddeebc527992e8f112f434f23ce24c934d" dependencies = [ "console_error_panic_hook", "druid-derive", @@ -625,7 +625,7 @@ dependencies = [ [[package]] name = "druid-derive" version = "0.5.0" -source = "git+https://github.com/linebender/druid.git#5fa4ce51ed3d74640388de6385f135c50d346c8d" +source = "git+https://github.com/linebender/druid.git#c02452ddeebc527992e8f112f434f23ce24c934d" dependencies = [ "proc-macro2", "quote", @@ -634,8 +634,8 @@ dependencies = [ [[package]] name = "druid-shell" -version = "0.8.0" -source = "git+https://github.com/linebender/druid.git#5fa4ce51ed3d74640388de6385f135c50d346c8d" +version = "0.8.3" +source = "git+https://github.com/linebender/druid.git#c02452ddeebc527992e8f112f434f23ce24c934d" dependencies = [ "anyhow", "bitflags", @@ -813,11 +813,11 @@ dependencies = [ [[package]] name = "field-offset" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" +checksum = "a3cf3a800ff6e860c863ca6d4b16fd999db8b752819c1606884047b73e468535" dependencies = [ - "memoffset", + "memoffset 0.8.0", "rustc_version", ] @@ -1341,15 +1341,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "jobserver" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" dependencies = [ "libc", ] @@ -1374,9 +1374,9 @@ dependencies = [ [[package]] name = "kurbo" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e119590a03caff1f7a582e8ee8c2164ddcc975791701188132fd1d1b518d3871" +checksum = "db8c31eaef73f18e0d938785e01ab471ec73e3f90c3389e84335ade689ba953b" dependencies = [ "arrayvec", "serde", @@ -1469,6 +1469,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1521,7 +1530,7 @@ dependencies = [ "cc", "cfg-if", "libc", - "memoffset", + "memoffset 0.6.5", ] [[package]] @@ -1719,16 +1728,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "pest" -version = "2.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028accff104c4e513bad663bbcd2ad7cfd5304144404c31ed0a77ac103d00660" -dependencies = [ - "thiserror", - "ucd-trie", -] - [[package]] name = "piet" version = "0.6.2" @@ -1979,18 +1978,18 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_version" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] [[package]] name = "rustix" -version = "0.36.8" +version = "0.36.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" dependencies = [ "bitflags", "errno", @@ -2026,9 +2025,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "scopeguard" @@ -2071,21 +2070,9 @@ checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" [[package]] name = "semver" -version = "0.11.0" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "serde" @@ -2256,18 +2243,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" dependencies = [ "proc-macro2", "quote", @@ -2324,9 +2311,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.25.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" dependencies = [ "autocfg", "bytes", @@ -2338,7 +2325,7 @@ dependencies = [ "signal-hook-registry", "tokio-macros", "tracing", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -2487,12 +2474,6 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" -[[package]] -name = "ucd-trie" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - [[package]] name = "unic-bidi" version = "0.9.0" @@ -2573,9 +2554,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-segmentation" @@ -2803,9 +2784,9 @@ checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winnow" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faf09497b8f8b5ac5d3bb4d05c0a99be20f26fd3d5f2db7b0716e946d5103658" +checksum = "c95fb4ff192527911dd18eb138ac30908e7165b8944e528b6af93aa4c842d345" dependencies = [ "memchr", ] From ba9c190a9632f5a394562507751058ebea87ba9c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 6 Mar 2023 15:24:14 +0100 Subject: [PATCH 039/335] fix(dtmm): Fix change detection for mod info --- CHANGELOG.adoc | 1 + crates/dtmm/src/controller/app.rs | 9 +++++---- crates/dtmm/src/controller/worker.rs | 2 +- crates/dtmm/src/state/data.rs | 22 ++++++++-------------- crates/dtmm/src/state/delegate.rs | 12 ++++++------ crates/dtmm/src/state/lens.rs | 12 +++++++----- crates/dtmm/src/ui/window/main.rs | 28 +++++++++++++++++----------- crates/dtmm/src/util/config.rs | 2 ++ 8 files changed, 47 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index e32ff71..c25777f 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -6,6 +6,7 @@ - dtmt: split `build` into `build` and `package` - dtmt: implement deploying built bundles +- dtmm: indicate when a deployment is necessary == 2023-03-01 diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index b9903cf..aba2442 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use std::io::{Cursor, ErrorKind, Read}; use std::path::Path; +use std::sync::Arc; use color_eyre::eyre::{self, Context}; use color_eyre::{Help, Result}; @@ -107,7 +108,7 @@ pub(crate) async fn import_mod(state: State, info: FileInfo) -> Result let packages = files .into_iter() - .map(|(name, files)| PackageInfo::new(name, files.into_iter().collect())) + .map(|(name, files)| Arc::new(PackageInfo::new(name, files.into_iter().collect()))) .collect(); let info = ModInfo::new(mod_cfg, packages); @@ -171,14 +172,14 @@ async fn read_mod_dir_entry(res: Result) -> Result { let packages = files .into_iter() - .map(|(name, files)| PackageInfo::new(name, files.into_iter().collect())) + .map(|(name, files)| Arc::new(PackageInfo::new(name, files.into_iter().collect()))) .collect(); let info = ModInfo::new(cfg, packages); Ok(info) } #[tracing::instrument(skip(mod_order))] -pub(crate) fn load_mods<'a, P, S>(mod_dir: P, mod_order: S) -> Result> +pub(crate) fn load_mods<'a, P, S>(mod_dir: P, mod_order: S) -> Result>> where S: Iterator, P: AsRef + std::fmt::Debug, @@ -214,7 +215,7 @@ where .filter_map(|entry| { if let Some(mut info) = mods.remove(&entry.id) { info.enabled = entry.enabled; - Some(info) + Some(Arc::new(info)) } else { None } diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index 80abf53..49785b4 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -41,7 +41,7 @@ async fn handle_action( .await .submit_command( ACTION_FINISH_ADD_MOD, - SingleUse::new(mod_info), + SingleUse::new(Arc::new(mod_info)), Target::Auto, ) .expect("failed to send command"); diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 2045ddc..65156a3 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -17,7 +17,7 @@ impl Default for View { } } -#[derive(Clone, Data, Debug)] +#[derive(Clone, Data, Debug, PartialEq)] pub struct PackageInfo { pub name: String, pub files: Vector, @@ -29,14 +29,14 @@ impl PackageInfo { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub(crate) struct ModResourceInfo { pub init: PathBuf, pub data: Option, pub localization: Option, } -#[derive(Clone, Data, Debug, Lens)] +#[derive(Clone, Data, Debug, Lens, PartialEq)] pub(crate) struct ModInfo { pub id: String, pub name: String, @@ -44,14 +44,14 @@ pub(crate) struct ModInfo { pub enabled: bool, #[lens(ignore)] #[data(ignore)] - pub packages: Vector, + pub packages: Vector>, #[lens(ignore)] #[data(ignore)] pub resources: ModResourceInfo, } impl ModInfo { - pub fn new(cfg: ModConfig, packages: Vector) -> Self { + pub fn new(cfg: ModConfig, packages: Vector>) -> Self { Self { id: cfg.id, name: cfg.name, @@ -67,16 +67,10 @@ impl ModInfo { } } -impl PartialEq for ModInfo { - fn eq(&self, other: &Self) -> bool { - self.name.eq(&other.name) - } -} - #[derive(Clone, Data, Lens)] pub(crate) struct State { pub current_view: View, - pub mods: Vector, + pub mods: Vector>, pub selected_mod_index: Option, pub dirty: bool, pub is_deployment_in_progress: bool, @@ -123,8 +117,8 @@ impl State { self.selected_mod_index = Some(index); } - pub fn add_mod(&mut self, info: ModInfo) { - if let Some(pos) = self.mods.index_of(&info) { + pub fn add_mod(&mut self, info: Arc) { + if let Some(pos) = self.mods.iter().position(|i| i.id == info.id) { self.mods.set(pos, info); self.selected_mod_index = Some(pos); } else { diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index 689332c..6b05b96 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use druid::{ AppDelegate, Command, DelegateCtx, Env, FileInfo, Handled, Selector, SingleUse, Target, }; @@ -9,9 +11,9 @@ pub(crate) const ACTION_SELECT_MOD: Selector = Selector::new("dtmm.action pub(crate) const ACTION_SELECTED_MOD_UP: Selector = Selector::new("dtmm.action.selected-mod-up"); pub(crate) const ACTION_SELECTED_MOD_DOWN: Selector = Selector::new("dtmm.action.selected-mod-down"); -pub(crate) const ACTION_START_DELETE_SELECTED_MOD: Selector> = +pub(crate) const ACTION_START_DELETE_SELECTED_MOD: Selector>> = Selector::new("dtmm.action.srart-delete-selected-mod"); -pub(crate) const ACTION_FINISH_DELETE_SELECTED_MOD: Selector> = +pub(crate) const ACTION_FINISH_DELETE_SELECTED_MOD: Selector>> = Selector::new("dtmm.action.finish-delete-selected-mod"); pub(crate) const ACTION_START_DEPLOY: Selector = Selector::new("dtmm.action.start-deploy"); @@ -23,7 +25,7 @@ pub(crate) const ACTION_FINISH_RESET_DEPLOYMENT: Selector = Selector::new("dtmm.action.finish-reset-deployment"); pub(crate) const ACTION_ADD_MOD: Selector = Selector::new("dtmm.action.add-mod"); -pub(crate) const ACTION_FINISH_ADD_MOD: Selector> = +pub(crate) const ACTION_FINISH_ADD_MOD: Selector>> = Selector::new("dtmm.action.finish-add-mod"); pub(crate) const ACTION_LOG: Selector> = Selector::new("dtmm.action.log"); @@ -39,7 +41,7 @@ pub(crate) enum AsyncAction { DeployMods(State), ResetDeployment(State), AddMod((State, FileInfo)), - DeleteMod((State, ModInfo)), + DeleteMod((State, Arc)), SaveSettings(State), } @@ -168,7 +170,6 @@ impl AppDelegate for Delegate { }; state.mods.remove(index); - // ctx.submit_command(ACTION_START_SAVE_SETTINGS); Handled::Yes } @@ -191,7 +192,6 @@ impl AppDelegate for Delegate { .expect("command type matched but didn't contain the expected value"); if let Some(info) = info.take() { state.add_mod(info); - // ctx.submit_command(ACTION_START_SAVE_SETTINGS); } Handled::Yes } diff --git a/crates/dtmm/src/state/lens.rs b/crates/dtmm/src/state/lens.rs index 6c457a4..1156f52 100644 --- a/crates/dtmm/src/state/lens.rs +++ b/crates/dtmm/src/state/lens.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use druid::im::Vector; use druid::{Data, Lens}; @@ -5,9 +7,9 @@ use super::{ModInfo, State}; pub(crate) struct SelectedModLens; -impl Lens> for SelectedModLens { +impl Lens>> for SelectedModLens { #[tracing::instrument(name = "SelectedModLens::with", skip_all)] - fn with) -> V>(&self, data: &State, f: F) -> V { + fn with>) -> V>(&self, data: &State, f: F) -> V { let info = data .selected_mod_index .and_then(|i| data.mods.get(i).cloned()); @@ -16,16 +18,16 @@ impl Lens> for SelectedModLens { } #[tracing::instrument(name = "SelectedModLens::with_mut", skip_all)] - fn with_mut) -> V>(&self, data: &mut State, f: F) -> V { + fn with_mut>) -> V>(&self, data: &mut State, f: F) -> V { match data.selected_mod_index { Some(i) => { let mut info = data.mods.get_mut(i).cloned(); let ret = f(&mut info); - if let Some(info) = info { + if let Some(new) = info { // TODO: Figure out a way to check for equality and // only update when needed - data.mods.set(i, info); + data.mods.set(i, new); } else { data.selected_mod_index = None; } diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 3b3ef00..d500610 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use druid::im::Vector; use druid::lens; use druid::widget::{ @@ -82,9 +84,11 @@ fn build_top_bar() -> impl Widget { fn build_mod_list() -> impl Widget { let list = List::new(|| { - let checkbox = - Checkbox::new("").lens(lens!((usize, ModInfo, bool), 1).then(ModInfo::enabled)); - let name = Label::raw().lens(lens!((usize, ModInfo, bool), 1).then(ModInfo::name)); + let checkbox = Checkbox::new("") + .lens(lens!((usize, Arc, bool), 1).then(ModInfo::enabled.in_arc())); + + let name = + Label::raw().lens(lens!((usize, Arc, bool), 1).then(ModInfo::name.in_arc())); Flex::row() .must_fill_main_axis(true) @@ -114,8 +118,10 @@ fn build_mod_list() -> impl Widget { .collect::>() }, |state, infos| { - infos.into_iter().for_each(|(i, info, _)| { - state.mods.set(i, info); + infos.into_iter().for_each(|(i, new, _)| { + if state.mods.get(i).cloned() != Some(new.clone()) { + state.mods.set(i, new); + } }); }, )); @@ -147,12 +153,12 @@ fn build_mod_details_buttons() -> impl Widget { .on_click(|_ctx, enabled: &mut bool, _env| { *enabled = !(*enabled); }) - .lens(ModInfo::enabled) + .lens(ModInfo::enabled.in_arc()) }, // TODO: Gray out || Button::new("Enable Mod"), ) - .disabled_if(|info: &Option, _env: &druid::Env| info.is_none()) + .disabled_if(|info: &Option>, _env: &druid::Env| info.is_none()) .lens(State::selected_mod); let button_add_mod = Button::new("Add Mod").on_click(|ctx, _state: &mut State, _env| { @@ -167,14 +173,14 @@ fn build_mod_details_buttons() -> impl Widget { }); let button_delete_mod = Button::new("Delete Mod") - .on_click(|ctx, data: &mut Option, _env| { + .on_click(|ctx, data: &mut Option>, _env| { if let Some(info) = data { ctx.submit_command( ACTION_START_DELETE_SELECTED_MOD.with(SingleUse::new(info.clone())), ); } }) - .disabled_if(|info: &Option, _env: &druid::Env| info.is_none()) + .disabled_if(|info: &Option>, _env: &druid::Env| info.is_none()) .lens(State::selected_mod); Flex::column() @@ -208,10 +214,10 @@ fn build_mod_details_info() -> impl Widget { // Force the label to take up the entire details' pane width, // so that we can center-align it. .expand_width() - .lens(ModInfo::name); + .lens(ModInfo::name.in_arc()); let description = Label::raw() .with_line_break_mode(LineBreaking::WordWrap) - .lens(ModInfo::description); + .lens(ModInfo::description.in_arc()); Flex::column() .cross_axis_alignment(CrossAxisAlignment::Start) diff --git a/crates/dtmm/src/util/config.rs b/crates/dtmm/src/util/config.rs index 86812b3..2c942ea 100644 --- a/crates/dtmm/src/util/config.rs +++ b/crates/dtmm/src/util/config.rs @@ -1,5 +1,6 @@ use std::io::ErrorKind; use std::path::PathBuf; +use std::sync::Arc; use std::{fs, path::Path}; use clap::{parser::ValueSource, ArgMatches}; @@ -38,6 +39,7 @@ impl<'a> From<&'a State> for ConfigSerialize<'a> { mod_order: state .mods .iter() + .map(Arc::as_ref) .map(LoadOrderEntrySerialize::from) .collect(), } From 61dbbcf2d9fc971a460473aff4b7149048258d32 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 3 Mar 2023 13:28:52 +0100 Subject: [PATCH 040/335] feat(dtmm): Write deployment info to disk Ref: #35. Fixes #29. --- Cargo.lock | 1 + crates/dtmm/Cargo.toml | 1 + crates/dtmm/src/controller/game.rs | 105 ++++++++++++++++++++++++++--- 3 files changed, 99 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3c2b3b..60358e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -680,6 +680,7 @@ dependencies = [ "sdk", "serde", "serde_sjson", + "time", "tokio", "tokio-stream", "tracing", diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 4719234..a54177b 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -24,3 +24,4 @@ tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } zip = "0.6.4" tokio-stream = { version = "0.1.12", features = ["fs"] } path-slash = "0.2.1" +time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] } diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index a9db26b..fa359de 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -15,6 +15,8 @@ use sdk::murmur::Murmur64; use sdk::{ Bundle, BundleDatabase, BundleFile, BundleFileType, BundleFileVariant, FromBinary, ToBinary, }; +use serde::{Deserialize, Serialize}; +use time::OffsetDateTime; use tokio::fs; use tokio::io::AsyncWriteExt; use tracing::Instrument; @@ -28,6 +30,13 @@ const BUNDLE_DATABASE_NAME: &str = "bundle_database.data"; const MOD_BOOT_SCRIPT: &str = "scripts/mod_main"; const MOD_DATA_SCRIPT: &str = "scripts/mods/mod_data"; const SETTINGS_FILE_PATH: &str = "application_settings/settings_common.ini"; +const DEPLOYMENT_DATA_PATH: &str = "dtmm-deployment.sjson"; + +#[derive(Serialize, Deserialize)] +struct DeploymentData { + bundles: Vec, + timestamp: OffsetDateTime, +} #[tracing::instrument] async fn read_file_with_backup

(path: P) -> Result> @@ -449,8 +458,11 @@ async fn patch_boot_bundle(state: Arc) -> Result> { Ok(bundles) } -#[tracing::instrument(skip_all, fields(bundles = bundles.len()))] -async fn patch_bundle_database(state: Arc, bundles: Vec) -> Result<()> { +#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] +async fn patch_bundle_database(state: Arc, bundles: B) -> Result<()> +where + B: AsRef<[Bundle]>, +{ let bundle_dir = Arc::new(state.game_dir.join("bundle")); let database_path = bundle_dir.join(BUNDLE_DATABASE_NAME); @@ -464,9 +476,9 @@ async fn patch_bundle_database(state: Arc, bundles: Vec) -> Resul db }; - for bundle in bundles { + for bundle in bundles.as_ref() { tracing::trace!("Adding '{}' to bundle database", bundle.name().display()); - db.add_bundle(&bundle); + db.add_bundle(bundle); } { @@ -484,6 +496,29 @@ async fn patch_bundle_database(state: Arc, bundles: Vec) -> Resul Ok(()) } +#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] +async fn write_deployment_data(state: Arc, bundles: B) -> Result<()> +where + B: AsRef<[Bundle]>, +{ + let info = DeploymentData { + timestamp: OffsetDateTime::now_utc(), + bundles: bundles + .as_ref() + .iter() + .map(|bundle| format!("{:x}", bundle.name().to_murmur64())) + .collect(), + }; + let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); + let data = serde_sjson::to_string(&info).wrap_err("failed to serizalie deployment data")?; + + fs::write(&path, &data) + .await + .wrap_err_with(|| format!("failed to write deployment data to '{}'", path.display()))?; + + Ok(()) +} + #[tracing::instrument(skip_all, fields( game_dir = %state.game_dir.display(), mods = state.mods.len() @@ -522,10 +557,15 @@ pub(crate) async fn deploy_mods(state: State) -> Result<()> { .wrap_err("failed to patch game settings")?; tracing::info!("Patching bundle database"); - patch_bundle_database(state.clone(), bundles) + patch_bundle_database(state.clone(), &bundles) .await .wrap_err("failed to patch bundle database")?; + tracing::info!("Writing deployment data"); + write_deployment_data(state.clone(), &bundles) + .await + .wrap_err("failed to write deployment data")?; + tracing::info!("Finished deploying mods"); Ok(()) } @@ -538,6 +578,40 @@ pub(crate) async fn reset_mod_deployment(state: State) -> Result<()> { tracing::info!("Resetting mod deployment in {}", bundle_dir.display()); + tracing::debug!("Reading mod deployment"); + + let info: DeploymentData = { + let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); + let data = match fs::read(&path).await { + Ok(data) => data, + Err(err) if err.kind() == ErrorKind::NotFound => { + tracing::info!("No deployment to reset"); + return Ok(()); + } + Err(err) => { + return Err(err).wrap_err_with(|| { + format!("failed to read deployment info at '{}'", path.display()) + }); + } + }; + + let data = String::from_utf8(data).wrap_err("invalid UTF8 in deployment data")?; + + serde_sjson::from_str(&data).wrap_err("invalid SJSON in deployment data")? + }; + + for name in info.bundles { + let path = bundle_dir.join(name); + + match fs::remove_file(&path).await { + Ok(_) => {} + Err(err) if err.kind() == ErrorKind::NotFound => {} + Err(err) => { + tracing::error!("Failed to remove '{}': {:?}", path.display(), err); + } + }; + } + for p in paths { let path = bundle_dir.join(p); let backup = bundle_dir.join(&format!("{}.bak", p)); @@ -555,9 +629,13 @@ pub(crate) async fn reset_mod_deployment(state: State) -> Result<()> { tracing::debug!("Deleting backup: {}", backup.display()); - fs::remove_file(&backup) - .await - .wrap_err_with(|| format!("failed to remove '{}'", backup.display())) + match fs::remove_file(&backup).await { + Ok(_) => Ok(()), + Err(err) if err.kind() == ErrorKind::NotFound => Ok(()), + Err(err) => { + Err(err).wrap_err_with(|| format!("failed to remove '{}'", backup.display())) + } + } } .await; @@ -570,6 +648,17 @@ pub(crate) async fn reset_mod_deployment(state: State) -> Result<()> { } } + { + let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); + if let Err(err) = fs::remove_file(&path).await { + tracing::error!( + "Failed to remove deployment data '{}': {:?}", + path.display(), + err + ); + } + } + tracing::info!("Reset finished"); Ok(()) From 14406ecae955d10a5f96e7a24562be9569b1f77e Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sat, 4 Mar 2023 09:29:55 +0100 Subject: [PATCH 041/335] fix(dtmm): Use a well-known format to serialize timestamps --- crates/dtmm/src/controller/game.rs | 1 + lib/serde_sjson | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index fa359de..cbb1b02 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -35,6 +35,7 @@ const DEPLOYMENT_DATA_PATH: &str = "dtmm-deployment.sjson"; #[derive(Serialize, Deserialize)] struct DeploymentData { bundles: Vec, + #[serde(with = "time::serde::iso8601")] timestamp: OffsetDateTime, } diff --git a/lib/serde_sjson b/lib/serde_sjson index e94218d..81213f7 160000 --- a/lib/serde_sjson +++ b/lib/serde_sjson @@ -1 +1 @@ -Subproject commit e94218d8f52a51529c83af33a99cc17f66caae2e +Subproject commit 81213f792767ddf1e7be60b066c87f7b137ca0a7 From 3a85fdeb16463c3204fdc37b44b079897d703fed Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 5 Mar 2023 20:27:39 +0100 Subject: [PATCH 042/335] feat(dtmm): Locate Steam installation --- .gitmodules | 3 + Cargo.lock | 230 ++++++++++++++++++++++++++++++++++++- crates/dtmm/src/main.rs | 4 +- lib/dtmt-shared/Cargo.toml | 2 + lib/dtmt-shared/src/lib.rs | 41 ++++++- lib/steamlocate-rs | 1 + 6 files changed, 276 insertions(+), 5 deletions(-) create mode 160000 lib/steamlocate-rs diff --git a/.gitmodules b/.gitmodules index a56154d..5741e15 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "lib/serde_sjson"] path = lib/serde_sjson url = git@git.sclu1034.dev:lucas/serde_sjson.git +[submodule "lib/steamlocate-rs"] + path = lib/steamlocate-rs + url = git@github.com:sclu1034/steamlocate-rs.git diff --git a/Cargo.lock b/Cargo.lock index 60358e3..69eeed1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -483,6 +483,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + [[package]] name = "crc32fast" version = "1.3.2" @@ -557,6 +572,15 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -726,7 +750,9 @@ dependencies = [ name = "dtmt-shared" version = "0.1.0" dependencies = [ + "color-eyre", "serde", + "steamlocate", "time", "tracing", "tracing-error", @@ -751,6 +777,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +[[package]] +name = "enum_primitive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" +dependencies = [ + "num-traits 0.1.43", +] + [[package]] name = "errno" version = "0.2.8" @@ -1373,6 +1408,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "keyvalues-parser" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d990301996c856ea07a84bc291e76f1273db52683663efc05c8d355976897e5" +dependencies = [ + "pest", + "pest_derive", + "thiserror", +] + [[package]] name = "kurbo" version = "0.9.1" @@ -1534,6 +1580,12 @@ dependencies = [ "memoffset 0.6.5", ] +[[package]] +name = "nom" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce" + [[package]] name = "nom" version = "7.1.3" @@ -1552,7 +1604,7 @@ checksum = "b1e299bf5ea7b212e811e71174c5d1a5d065c4c0ad0c8691ecb1f97e3e66025e" dependencies = [ "bytecount", "memchr", - "nom", + "nom 7.1.3", ] [[package]] @@ -1565,6 +1617,91 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits 0.2.15", +] + +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg", + "num-integer", + "num-traits 0.2.15", +] + +[[package]] +name = "num-complex" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" +dependencies = [ + "num-traits 0.2.15", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits 0.2.15", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits 0.2.15", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits 0.2.15", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.15", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.15.0" @@ -1729,6 +1866,50 @@ dependencies = [ "sha2", ] +[[package]] +name = "pest" +version = "2.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cbd939b234e95d72bc393d51788aec68aeeb5d51e748ca08ff3aad58cb722f7" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a81186863f3d0a27340815be8f2078dd8050b14cd71913db9fbda795e5f707d7" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a1ef20bf3193c15ac345acb32e26b3dc3223aff4d77ae4fc5359567683796b" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3b284b1f13a20dc5ebc90aff59a51b8d7137c221131b52a7260c08cbc1cc80" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + [[package]] name = "piet" version = "0.6.2" @@ -2099,7 +2280,7 @@ dependencies = [ name = "serde_sjson" version = "0.2.4" dependencies = [ - "nom", + "nom 7.1.3", "nom_locate", "serde", ] @@ -2169,6 +2350,42 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "steamid-ng" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb049f8faa2cba570c5366dbaf88ee5849725b16edb771848639fac92e33673" +dependencies = [ + "enum_primitive", + "lazy_static", + "num", + "regex", + "serde", + "serde_derive", + "thiserror", +] + +[[package]] +name = "steamlocate" +version = "1.1.1" +dependencies = [ + "crc", + "dirs", + "keyvalues-parser", + "steamid-ng", + "steamy-vdf", + "winreg", +] + +[[package]] +name = "steamy-vdf" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533127ad49314bfe71c3d3fd36b3ebac3d24f40618092e70e1cfe8362c7fac79" +dependencies = [ + "nom 1.2.4", +] + [[package]] name = "str-buf" version = "1.0.6" @@ -2792,6 +3009,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + [[package]] name = "wio" version = "0.2.2" diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index b1d87b9..667fc07 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -64,10 +64,12 @@ fn main() -> Result<()> { let config = util::config::read_config(&default_config_path, &matches) .wrap_err("failed to read config file")?; + let game_info = dtmt_shared::collect_game_info()?; + let initial_state = { let mut state = State::new( config.path, - config.game_dir.unwrap_or_default(), + config.game_dir.unwrap_or(game_info.path), config.data_dir.unwrap_or_default(), ); state.mods = load_mods(state.get_mod_dir(), config.mod_order.iter()) diff --git a/lib/dtmt-shared/Cargo.toml b/lib/dtmt-shared/Cargo.toml index 0f8ed63..eb9591d 100644 --- a/lib/dtmt-shared/Cargo.toml +++ b/lib/dtmt-shared/Cargo.toml @@ -6,7 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +color-eyre = "0.6.2" serde = "1.0.152" +steamlocate = { path = "../../lib/steamlocate-rs", version = "*" } time = { version = "0.3.19", features = ["formatting", "local-offset", "macros"] } tracing = "0.1.37" tracing-error = "0.2.0" diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index 3c8690d..46e7950 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -1,8 +1,13 @@ -mod log; - use std::path::PathBuf; +use color_eyre::eyre; +use color_eyre::Result; + +mod log; + pub use log::*; +use steamlocate::SteamDir; +use time::OffsetDateTime; #[derive(Clone, Debug, Default, serde::Deserialize)] pub struct ModConfigResources { @@ -26,3 +31,35 @@ pub struct ModConfig { #[serde(default)] pub depends: Vec, } + +pub const STEAMAPP_ID: u32 = 1361210; + +pub struct GameInfo { + pub path: PathBuf, + pub last_updated: OffsetDateTime, +} + +pub fn collect_game_info() -> Result { + let mut dir = if let Some(dir) = SteamDir::locate() { + dir + } else { + eyre::bail!("Failed to locate Steam installation") + }; + + let found = dir + .app(&STEAMAPP_ID) + .and_then(|app| app.vdf.get("LastUpdated").map(|v| (app.path.clone(), v))); + + let Some((path, last_updated)) = found else { + eyre::bail!("Failed to find game installation"); + }; + + let Some(last_updated) = last_updated + .as_value() + .and_then(|v| v.to::()) + .and_then(|v| OffsetDateTime::from_unix_timestamp(v).ok()) else { + eyre::bail!("Couldn't read 'LastUpdate'."); + }; + + Ok(GameInfo { path, last_updated }) +} diff --git a/lib/steamlocate-rs b/lib/steamlocate-rs new file mode 160000 index 0000000..4d6898f --- /dev/null +++ b/lib/steamlocate-rs @@ -0,0 +1 @@ +Subproject commit 4d6898f632e20ea15d47b0b071daa4f3fa6c9574 From 2d48b96dc15cb0d731df1d9071cd967995449a98 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 5 Mar 2023 21:23:12 +0100 Subject: [PATCH 043/335] feat(dtmm): Check Steam update before deployment Closes #35. --- CHANGELOG.adoc | 1 + crates/dtmm/src/controller/app.rs | 13 ++-------- crates/dtmm/src/controller/game.rs | 41 ++++++++++++++++++++++++++++-- crates/dtmm/src/controller/mod.rs | 23 +++++++++++++++++ crates/dtmm/src/main.rs | 8 +++--- lib/dtmt-shared/src/lib.rs | 1 + 6 files changed, 69 insertions(+), 18 deletions(-) create mode 100644 crates/dtmm/src/controller/mod.rs diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index dd0306f..d58a20f 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -7,6 +7,7 @@ - dtmt: split `build` into `build` and `package` - dtmt: implement deploying built bundles - dtmm: indicate when a deployment is necessary +- dtmm: check for Steam game update before deployment === Fixed diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index aba2442..c2f5962 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -8,7 +8,6 @@ use color_eyre::{Help, Result}; use druid::im::Vector; use druid::FileInfo; use dtmt_shared::ModConfig; -use serde::Deserialize; use tokio::fs::{self, DirEntry}; use tokio::runtime::Runtime; use tokio_stream::wrappers::ReadDirStream; @@ -18,6 +17,8 @@ use zip::ZipArchive; use crate::state::{ModInfo, PackageInfo, State}; use crate::util::config::{ConfigSerialize, LoadOrderEntry}; +use super::read_sjson_file; + #[tracing::instrument(skip(state))] pub(crate) async fn import_mod(state: State, info: FileInfo) -> Result { let data = fs::read(&info.path) @@ -144,16 +145,6 @@ pub(crate) async fn save_settings(state: State) -> Result<()> { }) } -async fn read_sjson_file(path: P) -> Result -where - T: for<'a> Deserialize<'a>, - P: AsRef + std::fmt::Debug, -{ - let buf = fs::read(path).await.wrap_err("failed to read file")?; - let data = String::from_utf8(buf).wrap_err("invalid UTF8")?; - serde_sjson::from_str(&data).wrap_err("failed to deserialize") -} - #[tracing::instrument(skip_all,fields( name = ?res.as_ref().map(|entry| entry.file_name()) ))] diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index cbb1b02..f504b3a 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use std::sync::Arc; use color_eyre::eyre::Context; -use color_eyre::{eyre, Help, Result}; +use color_eyre::{eyre, Help, Report, Result}; use futures::stream; use futures::StreamExt; use path_slash::PathBufExt; @@ -21,6 +21,7 @@ use tokio::fs; use tokio::io::AsyncWriteExt; use tracing::Instrument; +use super::read_sjson_file; use crate::state::{PackageInfo, State}; const MOD_BUNDLE_NAME: &str = "packages/mods"; @@ -32,7 +33,7 @@ const MOD_DATA_SCRIPT: &str = "scripts/mods/mod_data"; const SETTINGS_FILE_PATH: &str = "application_settings/settings_common.ini"; const DEPLOYMENT_DATA_PATH: &str = "dtmm-deployment.sjson"; -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] struct DeploymentData { bundles: Vec, #[serde(with = "time::serde::iso8601")] @@ -535,6 +536,42 @@ pub(crate) async fn deploy_mods(state: State) -> Result<()> { } } + let (game_info, deployment_info) = tokio::try_join!( + async { + tokio::task::spawn_blocking(dtmt_shared::collect_game_info) + .await + .map_err(Report::new) + }, + async { + let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); + match read_sjson_file::<_, DeploymentData>(path) + .await + { + Ok(data) => Ok(Some(data)), + Err(err) => { + if let Some(err) = err.downcast_ref::() && err.kind() == ErrorKind::NotFound { + Ok(None) + } else { + Err(err).wrap_err("failed to read deployment data") + } + } + } + } + ) + .wrap_err("failed to gather deployment information")?; + + let game_info = game_info.wrap_err("failed to collect Steam info")?; + + tracing::debug!(?game_info, ?deployment_info); + + if deployment_info + .as_ref() + .map(|i| game_info.last_updated > i.timestamp) + .unwrap_or(false) + { + eyre::bail!("Game was updated since last mod deployment. Please reset first."); + } + tracing::info!( "Deploying {} mods to {}", state.mods.len(), diff --git a/crates/dtmm/src/controller/mod.rs b/crates/dtmm/src/controller/mod.rs new file mode 100644 index 0000000..f7f250d --- /dev/null +++ b/crates/dtmm/src/controller/mod.rs @@ -0,0 +1,23 @@ +use std::path::Path; + +use color_eyre::{eyre::Context, Result}; +use serde::Deserialize; +use tokio::fs; + +pub mod app; +pub mod game; +pub mod worker; + +#[tracing::instrument] +async fn read_sjson_file(path: P) -> Result +where + T: for<'a> Deserialize<'a>, + P: AsRef + std::fmt::Debug, +{ + let path = path.as_ref(); + let buf = fs::read(path) + .await + .wrap_err_with(|| format!("failed to read file '{}'", path.display()))?; + let data = String::from_utf8(buf).wrap_err("invalid UTF8")?; + serde_sjson::from_str(&data).wrap_err("failed to deserialize SJSON") +} diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 667fc07..21b9f37 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -17,11 +17,7 @@ use crate::controller::app::load_mods; use crate::controller::worker::work_thread; use crate::state::{Delegate, State}; -mod controller { - pub mod app; - pub mod game; - pub mod worker; -} +mod controller; mod state; mod util { pub mod config; @@ -66,6 +62,8 @@ fn main() -> Result<()> { let game_info = dtmt_shared::collect_game_info()?; + tracing::debug!(?config, ?game_info); + let initial_state = { let mut state = State::new( config.path, diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index 46e7950..fa8c407 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -34,6 +34,7 @@ pub struct ModConfig { pub const STEAMAPP_ID: u32 = 1361210; +#[derive(Debug)] pub struct GameInfo { pub path: PathBuf, pub last_updated: OffsetDateTime, From 18760d7760af2843ee4fc3e248868f79baa772cd Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 6 Mar 2023 09:55:50 +0100 Subject: [PATCH 044/335] feat(dtmm): Remove unused bundles from previous deployment Fixes #29. --- CHANGELOG.adoc | 1 + crates/dtmm/src/controller/game.rs | 31 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index d58a20f..48bc35f 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -8,6 +8,7 @@ - dtmt: implement deploying built bundles - dtmm: indicate when a deployment is necessary - dtmm: check for Steam game update before deployment +- dtmm: remove unused bundles from previous deployment === Fixed diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index f504b3a..c339407 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -589,6 +589,37 @@ pub(crate) async fn deploy_mods(state: State) -> Result<()> { .wrap_err("failed to patch boot bundle")?; bundles.append(&mut more_bundles); + if let Some(info) = &deployment_info { + let bundle_dir = Arc::new(state.game_dir.join("bundle")); + let tasks = info + .bundles + .iter() + .cloned() + .map(|v| (v, bundle_dir.clone())) + .filter_map(|(file_name, bundle_dir)| { + let contains = bundles.iter().any(|b2| { + let name = b2.name().to_murmur64().to_string(); + file_name == name + }); + + if !contains { + let task = async move { + let path = bundle_dir.join(file_name); + if let Err(err) = fs::remove_file(&path).await.wrap_err_with(|| { + format!("failed to remove unused bundle '{}'", path.display()) + }) { + tracing::error!("{:?}", err); + } + }; + Some(task) + } else { + None + } + }); + + futures::future::join_all(tasks).await; + } + tracing::info!("Patch game settings"); patch_game_settings(state.clone()) .await From 53188f8c3003a0e0e3a3972664abf4125805f6ff Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 7 Mar 2023 19:45:47 +0100 Subject: [PATCH 045/335] chore(dtmm): Improve debug logging --- crates/dtmm/src/controller/game.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index c339407..a69691c 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -574,7 +574,7 @@ pub(crate) async fn deploy_mods(state: State) -> Result<()> { tracing::info!( "Deploying {} mods to {}", - state.mods.len(), + state.mods.iter().filter(|i| i.enabled).count(), state.game_dir.join("bundle").display() ); @@ -604,7 +604,10 @@ pub(crate) async fn deploy_mods(state: State) -> Result<()> { if !contains { let task = async move { - let path = bundle_dir.join(file_name); + let path = bundle_dir.join(&file_name); + + tracing::debug!("Removing unused bundle '{}'", file_name); + if let Err(err) = fs::remove_file(&path).await.wrap_err_with(|| { format!("failed to remove unused bundle '{}'", path.display()) }) { From 762cf03aa8899e712c3de392eaca73d6a1e883a6 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 8 Mar 2023 11:36:57 +0100 Subject: [PATCH 046/335] fix(dtmm): Strip ANSI from error message in log view Until they are implemented to color the text, they only hinder legibility. --- Cargo.lock | 45 ++++++++++++++++++++++++++++++++++++- crates/dtmm/Cargo.toml | 1 + crates/dtmm/src/util/log.rs | 3 ++- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 69eeed1..2709c2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,12 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "arrayvec" version = "0.7.2" @@ -704,6 +710,7 @@ dependencies = [ "sdk", "serde", "serde_sjson", + "strip-ansi-escapes", "time", "tokio", "tokio-stream", @@ -1425,7 +1432,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db8c31eaef73f18e0d938785e01ab471ec73e3f90c3389e84335ade689ba953b" dependencies = [ - "arrayvec", + "arrayvec 0.7.2", "serde", ] @@ -2401,6 +2408,15 @@ dependencies = [ "regex", ] +[[package]] +name = "strip-ansi-escapes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8" +dependencies = [ + "vte", +] + [[package]] name = "strsim" version = "0.10.0" @@ -2692,6 +2708,12 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + [[package]] name = "unic-bidi" version = "0.9.0" @@ -2818,6 +2840,27 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vte" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" +dependencies = [ + "arrayvec 0.5.2", + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index a54177b..261a994 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -25,3 +25,4 @@ zip = "0.6.4" tokio-stream = { version = "0.1.12", features = ["fs"] } path-slash = "0.2.1" time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] } +strip-ansi-escapes = "0.1.1" diff --git a/crates/dtmm/src/util/log.rs b/crates/dtmm/src/util/log.rs index e6a019e..1b379dc 100644 --- a/crates/dtmm/src/util/log.rs +++ b/crates/dtmm/src/util/log.rs @@ -20,7 +20,8 @@ impl ChannelWriter { impl std::io::Write for ChannelWriter { fn write(&mut self, buf: &[u8]) -> std::io::Result { let tx = self.tx.clone(); - let string = String::from_utf8_lossy(buf).to_string(); + let stripped = strip_ansi_escapes::strip(buf)?; + let string = String::from_utf8_lossy(&stripped).to_string(); // The `send` errors when the receiving end has closed. // But there's not much we can do at that point, so we just ignore it. From 658d996315b10470660bcc78828704cf4a95bcc0 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 8 Mar 2023 20:06:07 +0100 Subject: [PATCH 047/335] feat(dtmm): Implement error dialog Closes #37. --- CHANGELOG.adoc | 1 + Cargo.lock | 1 + crates/dtmm/Cargo.toml | 1 + crates/dtmm/src/controller/app.rs | 16 ++-- crates/dtmm/src/controller/game.rs | 22 +++--- crates/dtmm/src/controller/worker.rs | 61 ++++++++++----- crates/dtmm/src/state/data.rs | 9 ++- crates/dtmm/src/state/delegate.rs | 107 +++++++++++++++++++++++---- crates/dtmm/src/ui/mod.rs | 1 + crates/dtmm/src/ui/window/dialog.rs | 36 +++++++++ crates/dtmm/src/ui/window/main.rs | 18 ++++- crates/dtmm/src/util/config.rs | 6 +- 12 files changed, 221 insertions(+), 58 deletions(-) create mode 100644 crates/dtmm/src/ui/window/dialog.rs diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 48bc35f..f61467b 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -9,6 +9,7 @@ - dtmm: indicate when a deployment is necessary - dtmm: check for Steam game update before deployment - dtmm: remove unused bundles from previous deployment +- dtmm: show dialog for critical errors === Fixed diff --git a/Cargo.lock b/Cargo.lock index 2709c2c..5f2a35a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -705,6 +705,7 @@ dependencies = [ "druid", "dtmt-shared", "futures", + "lazy_static", "oodle-sys", "path-slash", "sdk", diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 261a994..5e11a9b 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -26,3 +26,4 @@ tokio-stream = { version = "0.1.12", features = ["fs"] } path-slash = "0.2.1" time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] } strip-ansi-escapes = "0.1.1" +lazy_static = "1.4.0" diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index c2f5962..3a8adc5 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -14,13 +14,13 @@ use tokio_stream::wrappers::ReadDirStream; use tokio_stream::StreamExt; use zip::ZipArchive; -use crate::state::{ModInfo, PackageInfo, State}; +use crate::state::{ActionState, ModInfo, PackageInfo}; use crate::util::config::{ConfigSerialize, LoadOrderEntry}; use super::read_sjson_file; #[tracing::instrument(skip(state))] -pub(crate) async fn import_mod(state: State, info: FileInfo) -> Result { +pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result { let data = fs::read(&info.path) .await .wrap_err_with(|| format!("failed to read file {}", info.path.display()))?; @@ -95,16 +95,16 @@ pub(crate) async fn import_mod(state: State, info: FileInfo) -> Result tracing::trace!(?files); - let mod_dir = state.get_mod_dir(); + let mod_dir = state.mod_dir; tracing::trace!("Creating mods directory {}", mod_dir.display()); - fs::create_dir_all(&mod_dir) + fs::create_dir_all(Arc::as_ref(&mod_dir)) .await .wrap_err_with(|| format!("failed to create data directory {}", mod_dir.display()))?; tracing::trace!("Extracting mod archive to {}", mod_dir.display()); archive - .extract(&mod_dir) + .extract(Arc::as_ref(&mod_dir)) .wrap_err_with(|| format!("failed to extract archive to {}", mod_dir.display()))?; let packages = files @@ -117,8 +117,8 @@ pub(crate) async fn import_mod(state: State, info: FileInfo) -> Result } #[tracing::instrument(skip(state))] -pub(crate) async fn delete_mod(state: State, info: &ModInfo) -> Result<()> { - let mod_dir = state.get_mod_dir().join(&info.id); +pub(crate) async fn delete_mod(state: ActionState, info: &ModInfo) -> Result<()> { + let mod_dir = state.mod_dir.join(&info.id); fs::remove_dir_all(&mod_dir) .await .wrap_err_with(|| format!("failed to remove directory {}", mod_dir.display()))?; @@ -127,7 +127,7 @@ pub(crate) async fn delete_mod(state: State, info: &ModInfo) -> Result<()> { } #[tracing::instrument(skip(state))] -pub(crate) async fn save_settings(state: State) -> Result<()> { +pub(crate) async fn save_settings(state: ActionState) -> Result<()> { let cfg = ConfigSerialize::from(&state); tracing::info!("Saving settings to '{}'", state.config_path.display()); diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index a69691c..9260908 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -22,7 +22,7 @@ use tokio::io::AsyncWriteExt; use tracing::Instrument; use super::read_sjson_file; -use crate::state::{PackageInfo, State}; +use crate::state::{ActionState, PackageInfo}; const MOD_BUNDLE_NAME: &str = "packages/mods"; const BOOT_BUNDLE_NAME: &str = "packages/boot"; @@ -100,7 +100,7 @@ where } #[tracing::instrument(skip_all)] -async fn patch_game_settings(state: Arc) -> Result<()> { +async fn patch_game_settings(state: Arc) -> Result<()> { let settings_path = state.game_dir.join("bundle").join(SETTINGS_FILE_PATH); let settings = read_file_with_backup(&settings_path) @@ -146,7 +146,7 @@ fn make_package(info: &PackageInfo) -> Result { Ok(pkg) } -fn build_mod_data_lua(state: Arc) -> String { +fn build_mod_data_lua(state: Arc) -> String { let mut lua = String::from("return {\n"); // DMF is handled explicitely by the loading procedures, as it actually drives most of that @@ -203,7 +203,7 @@ fn build_mod_data_lua(state: Arc) -> String { } #[tracing::instrument(skip_all)] -async fn build_bundles(state: Arc) -> Result> { +async fn build_bundles(state: Arc) -> Result> { let mut mod_bundle = Bundle::new(MOD_BUNDLE_NAME.to_string()); let mut tasks = Vec::new(); @@ -227,7 +227,7 @@ async fn build_bundles(state: Arc) -> Result> { let span = tracing::trace_span!("building mod packages", name = mod_info.name); let _enter = span.enter(); - let mod_dir = state.get_mod_dir().join(&mod_info.id); + let mod_dir = state.mod_dir.join(&mod_info.id); for pkg_info in &mod_info.packages { let span = tracing::trace_span!("building package", name = pkg_info.name); let _enter = span.enter(); @@ -320,7 +320,7 @@ async fn build_bundles(state: Arc) -> Result> { } #[tracing::instrument(skip_all)] -async fn patch_boot_bundle(state: Arc) -> Result> { +async fn patch_boot_bundle(state: Arc) -> Result> { let bundle_dir = Arc::new(state.game_dir.join("bundle")); let bundle_path = bundle_dir.join(format!("{:x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes()))); @@ -381,7 +381,7 @@ async fn patch_boot_bundle(state: Arc) -> Result> { let bundle_name = Murmur64::hash(&pkg_info.name) .to_string() .to_ascii_lowercase(); - let src = state.get_mod_dir().join(&mod_info.id).join(&bundle_name); + let src = state.mod_dir.join(&mod_info.id).join(&bundle_name); { let bin = fs::read(&src) @@ -461,7 +461,7 @@ async fn patch_boot_bundle(state: Arc) -> Result> { } #[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] -async fn patch_bundle_database(state: Arc, bundles: B) -> Result<()> +async fn patch_bundle_database(state: Arc, bundles: B) -> Result<()> where B: AsRef<[Bundle]>, { @@ -499,7 +499,7 @@ where } #[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] -async fn write_deployment_data(state: Arc, bundles: B) -> Result<()> +async fn write_deployment_data(state: Arc, bundles: B) -> Result<()> where B: AsRef<[Bundle]>, { @@ -525,7 +525,7 @@ where game_dir = %state.game_dir.display(), mods = state.mods.len() ))] -pub(crate) async fn deploy_mods(state: State) -> Result<()> { +pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { let state = Arc::new(state); { @@ -643,7 +643,7 @@ pub(crate) async fn deploy_mods(state: State) -> Result<()> { } #[tracing::instrument(skip(state))] -pub(crate) async fn reset_mod_deployment(state: State) -> Result<()> { +pub(crate) async fn reset_mod_deployment(state: ActionState) -> Result<()> { let boot_bundle_path = format!("{:016x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes())); let paths = [BUNDLE_DATABASE_NAME, &boot_bundle_path, SETTINGS_FILE_PATH]; let bundle_dir = state.game_dir.join("bundle"); diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index 49785b4..294af0e 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -1,5 +1,8 @@ use std::sync::Arc; +use color_eyre::eyre::Context; +use color_eyre::Help; +use color_eyre::Report; use color_eyre::Result; use druid::{ExtEventSink, SingleUse, Target}; use tokio::runtime::Runtime; @@ -10,11 +13,19 @@ use crate::controller::app::*; use crate::controller::game::*; use crate::state::AsyncAction; use crate::state::ACTION_FINISH_SAVE_SETTINGS; +use crate::state::ACTION_SHOW_ERROR_DIALOG; use crate::state::{ ACTION_FINISH_ADD_MOD, ACTION_FINISH_DELETE_SELECTED_MOD, ACTION_FINISH_DEPLOY, ACTION_FINISH_RESET_DEPLOYMENT, ACTION_LOG, }; +async fn send_error(sink: Arc>, err: Report) { + sink.write() + .await + .submit_command(ACTION_SHOW_ERROR_DIALOG, SingleUse::new(err), Target::Auto) + .expect("failed to send command"); +} + async fn handle_action( event_sink: Arc>, action_queue: Arc>>, @@ -23,8 +34,9 @@ async fn handle_action( let event_sink = event_sink.clone(); match action { AsyncAction::DeployMods(state) => tokio::spawn(async move { - if let Err(err) = deploy_mods(state).await { - tracing::error!("Failed to deploy mods: {:?}", err); + if let Err(err) = deploy_mods(state).await.wrap_err("failed to deploy mods") { + tracing::error!("{:?}", err); + send_error(event_sink.clone(), err).await; } event_sink @@ -33,8 +45,11 @@ async fn handle_action( .submit_command(ACTION_FINISH_DEPLOY, (), Target::Auto) .expect("failed to send command"); }), - AsyncAction::AddMod((state, info)) => tokio::spawn(async move { - match import_mod(state, info).await { + AsyncAction::AddMod(state, info) => tokio::spawn(async move { + match import_mod(state, info) + .await + .wrap_err("failed to import mod") + { Ok(mod_info) => { event_sink .write() @@ -47,18 +62,22 @@ async fn handle_action( .expect("failed to send command"); } Err(err) => { - tracing::error!("Failed to import mod: {:?}", err); + tracing::error!("{:?}", err); + send_error(event_sink.clone(), err).await; } } }), - AsyncAction::DeleteMod((state, info)) => tokio::spawn(async move { - if let Err(err) = delete_mod(state, &info).await { - tracing::error!( - "Failed to delete mod files. \ - You might want to clean up the data directory manually. \ - Reason: {:?}", - err - ); + AsyncAction::DeleteMod(state, info) => tokio::spawn(async move { + let mod_dir = state.mod_dir.join(&info.id); + if let Err(err) = delete_mod(state, &info) + .await + .wrap_err("failed to delete mod files") + .with_suggestion(|| { + format!("Clean the folder '{}' manually", mod_dir.display()) + }) + { + tracing::error!("{:?}", err); + send_error(event_sink.clone(), err).await; } event_sink @@ -72,8 +91,12 @@ async fn handle_action( .expect("failed to send command"); }), AsyncAction::ResetDeployment(state) => tokio::spawn(async move { - if let Err(err) = reset_mod_deployment(state).await { - tracing::error!("Failed to reset mod deployment: {:?}", err); + if let Err(err) = reset_mod_deployment(state) + .await + .wrap_err("failed to reset mod deployment") + { + tracing::error!("{:?}", err); + send_error(event_sink.clone(), err).await; } event_sink @@ -83,8 +106,12 @@ async fn handle_action( .expect("failed to send command"); }), AsyncAction::SaveSettings(state) => tokio::spawn(async move { - if let Err(err) = save_settings(state).await { - tracing::error!("Failed to save settings: {:?}", err); + if let Err(err) = save_settings(state) + .await + .wrap_err("failed to save settings") + { + tracing::error!("{:?}", err); + send_error(event_sink.clone(), err).await; } event_sink diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 65156a3..8021840 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -1,6 +1,9 @@ use std::{path::PathBuf, sync::Arc}; -use druid::{im::Vector, Data, Lens}; +use druid::{ + im::{HashMap, Vector}, + Data, Lens, WindowHandle, WindowId, +}; use dtmt_shared::ModConfig; use super::SelectedModLens; @@ -86,6 +89,9 @@ pub(crate) struct State { pub config_path: Arc, #[lens(ignore)] #[data(ignore)] + pub windows: HashMap, + #[lens(ignore)] + #[data(ignore)] pub ctx: Arc, } @@ -110,6 +116,7 @@ impl State { game_dir: Arc::new(game_dir), data_dir: Arc::new(data_dir), log: Arc::new(String::new()), + windows: HashMap::new(), } } diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index 6b05b96..506cbf8 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -1,10 +1,14 @@ -use std::sync::Arc; +use std::{path::PathBuf, sync::Arc}; +use color_eyre::Report; use druid::{ - AppDelegate, Command, DelegateCtx, Env, FileInfo, Handled, Selector, SingleUse, Target, + im::Vector, AppDelegate, Command, DelegateCtx, Env, FileInfo, Handled, Selector, SingleUse, + Target, WindowHandle, WindowId, }; use tokio::sync::mpsc::UnboundedSender; +use crate::ui::window; + use super::{ModInfo, State}; pub(crate) const ACTION_SELECT_MOD: Selector = Selector::new("dtmm.action.select-mod"); @@ -37,12 +41,42 @@ pub(crate) const ACTION_FINISH_SAVE_SETTINGS: Selector = pub(crate) const ACTION_SET_DIRTY: Selector = Selector::new("dtmm.action.set-dirty"); +pub(crate) const ACTION_SHOW_ERROR_DIALOG: Selector> = + Selector::new("dtmm.action.show-error-dialog"); + +pub(crate) const ACTION_SET_WINDOW_HANDLE: Selector> = + Selector::new("dtmm.action.set-window-handle"); + +// A sub-selection of `State`'s fields that are required in `AsyncAction`s and that are +// `Send + Sync` +pub(crate) struct ActionState { + pub mods: Vector>, + pub game_dir: Arc, + pub data_dir: Arc, + pub mod_dir: Arc, + pub config_path: Arc, + pub ctx: Arc, +} + +impl From for ActionState { + fn from(state: State) -> Self { + Self { + mods: state.mods, + game_dir: state.game_dir, + mod_dir: Arc::new(state.data_dir.join("mods")), + data_dir: state.data_dir, + config_path: state.config_path, + ctx: state.ctx, + } + } +} + pub(crate) enum AsyncAction { - DeployMods(State), - ResetDeployment(State), - AddMod((State, FileInfo)), - DeleteMod((State, Arc)), - SaveSettings(State), + DeployMods(ActionState), + ResetDeployment(ActionState), + AddMod(ActionState, FileInfo), + DeleteMod(ActionState, Arc), + SaveSettings(ActionState), } pub(crate) struct Delegate { @@ -73,7 +107,7 @@ impl AppDelegate for Delegate { cmd if cmd.is(ACTION_START_DEPLOY) => { if self .sender - .send(AsyncAction::DeployMods(state.clone())) + .send(AsyncAction::DeployMods(state.clone().into())) .is_ok() { state.is_deployment_in_progress = true; @@ -91,7 +125,7 @@ impl AppDelegate for Delegate { cmd if cmd.is(ACTION_START_RESET_DEPLOYMENT) => { if self .sender - .send(AsyncAction::ResetDeployment(state.clone())) + .send(AsyncAction::ResetDeployment(state.clone().into())) .is_ok() { state.is_reset_in_progress = true; @@ -147,11 +181,12 @@ impl AppDelegate for Delegate { cmd if cmd.is(ACTION_START_DELETE_SELECTED_MOD) => { let info = cmd .get(ACTION_START_DELETE_SELECTED_MOD) - .and_then(|info| info.take()) + .and_then(SingleUse::take) .expect("command type matched but didn't contain the expected value"); + if self .sender - .send(AsyncAction::DeleteMod((state.clone(), info))) + .send(AsyncAction::DeleteMod(state.clone().into(), info)) .is_err() { tracing::error!("Failed to queue action to deploy mods"); @@ -162,8 +197,9 @@ impl AppDelegate for Delegate { cmd if cmd.is(ACTION_FINISH_DELETE_SELECTED_MOD) => { let info = cmd .get(ACTION_FINISH_DELETE_SELECTED_MOD) - .and_then(|info| info.take()) + .and_then(SingleUse::take) .expect("command type matched but didn't contain the expected value"); + let found = state.mods.iter().enumerate().find(|(_, i)| i.id == info.id); let Some((index, _)) = found else { return Handled::No; @@ -177,9 +213,10 @@ impl AppDelegate for Delegate { let info = cmd .get(ACTION_ADD_MOD) .expect("command type matched but didn't contain the expected value"); + if self .sender - .send(AsyncAction::AddMod((state.clone(), info.clone()))) + .send(AsyncAction::AddMod(state.clone().into(), info.clone())) .is_err() { tracing::error!("Failed to queue action to add mod"); @@ -190,9 +227,11 @@ impl AppDelegate for Delegate { let info = cmd .get(ACTION_FINISH_ADD_MOD) .expect("command type matched but didn't contain the expected value"); + if let Some(info) = info.take() { state.add_mod(info); } + Handled::Yes } cmd if cmd.is(ACTION_LOG) => { @@ -209,7 +248,7 @@ impl AppDelegate for Delegate { state.is_next_save_pending = true; } else if self .sender - .send(AsyncAction::SaveSettings(state.clone())) + .send(AsyncAction::SaveSettings(state.clone().into())) .is_ok() { state.is_save_in_progress = true; @@ -233,6 +272,31 @@ impl AppDelegate for Delegate { state.dirty = true; Handled::Yes } + cmd if cmd.is(ACTION_SHOW_ERROR_DIALOG) => { + let err = cmd + .get(ACTION_SHOW_ERROR_DIALOG) + .and_then(SingleUse::take) + .expect("command type matched but didn't contain the expected value"); + + let window = state + .windows + .get(&window::main::WINDOW_ID) + .expect("root window does not exist"); + + let dialog = window::dialog::error::(err, window.clone()); + ctx.new_window(dialog); + + Handled::Yes + } + cmd if cmd.is(ACTION_SET_WINDOW_HANDLE) => { + let (id, handle) = cmd + .get(ACTION_SET_WINDOW_HANDLE) + .and_then(SingleUse::take) + .expect("command type matched but didn't contain the expected value"); + + state.windows.insert(id, handle); + Handled::Yes + } cmd => { if cfg!(debug_assertions) { tracing::warn!("Unknown command: {:?}", cmd); @@ -241,4 +305,19 @@ impl AppDelegate for Delegate { } } } + + fn window_added( + &mut self, + id: WindowId, + handle: WindowHandle, + data: &mut State, + _: &Env, + _: &mut DelegateCtx, + ) { + data.windows.insert(id, handle); + } + + fn window_removed(&mut self, id: WindowId, data: &mut State, _: &Env, _: &mut DelegateCtx) { + data.windows.remove(&id); + } } diff --git a/crates/dtmm/src/ui/mod.rs b/crates/dtmm/src/ui/mod.rs index cf8554f..12f66c9 100644 --- a/crates/dtmm/src/ui/mod.rs +++ b/crates/dtmm/src/ui/mod.rs @@ -1,5 +1,6 @@ pub mod theme; pub mod widget; pub mod window { + pub mod dialog; pub mod main; } diff --git a/crates/dtmm/src/ui/window/dialog.rs b/crates/dtmm/src/ui/window/dialog.rs new file mode 100644 index 0000000..3b6802a --- /dev/null +++ b/crates/dtmm/src/ui/window/dialog.rs @@ -0,0 +1,36 @@ +use color_eyre::Report; +use druid::widget::{Button, CrossAxisAlignment, Flex, Label, LineBreaking, MainAxisAlignment}; +use druid::{Data, WidgetExt, WindowDesc, WindowHandle, WindowLevel, WindowSizePolicy}; + +const ERROR_DIALOG_SIZE: (f64, f64) = (750., 400.); + +pub fn error(err: Report, parent: WindowHandle) -> WindowDesc { + let msg = format!("A critical error ocurred: {:?}", err); + let stripped = + strip_ansi_escapes::strip(msg.as_bytes()).expect("failed to strip ANSI in error"); + let msg = String::from_utf8_lossy(&stripped); + + let text = Label::new(msg.to_string()).with_line_break_mode(LineBreaking::WordWrap); + + let button = Button::new("Ok") + .on_click(|ctx, _, _| { + ctx.window().close(); + }) + .align_right(); + + let widget = Flex::column() + .main_axis_alignment(MainAxisAlignment::SpaceBetween) + .cross_axis_alignment(CrossAxisAlignment::End) + .with_child(text) + .with_spacer(20.) + .with_child(button) + .padding(10.); + + WindowDesc::new(widget) + .title("Error") + .with_min_size(ERROR_DIALOG_SIZE) + .resizable(false) + .window_size_policy(WindowSizePolicy::Content) + .set_always_on_top(true) + .set_level(WindowLevel::Modal(parent)) +} diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index d500610..f48b5c3 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -1,25 +1,30 @@ use std::sync::Arc; use druid::im::Vector; -use druid::lens; use druid::widget::{ Button, Checkbox, CrossAxisAlignment, Flex, Label, LineBreaking, List, MainAxisAlignment, Maybe, Scroll, SizedBox, Split, TextBox, ViewSwitcher, }; +use druid::{lens, LifeCycleCtx}; use druid::{ Color, FileDialogOptions, FileSpec, FontDescriptor, FontFamily, Key, LensExt, SingleUse, - TextAlignment, Widget, WidgetExt, WindowDesc, + TextAlignment, Widget, WidgetExt, WindowDesc, WindowId, }; +use lazy_static::lazy_static; use crate::state::{ ModInfo, State, View, ACTION_ADD_MOD, ACTION_SELECTED_MOD_DOWN, ACTION_SELECTED_MOD_UP, - ACTION_SELECT_MOD, ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY, - ACTION_START_RESET_DEPLOYMENT, + ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE, ACTION_START_DELETE_SELECTED_MOD, + ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT, }; use crate::ui::theme; use crate::ui::widget::controller::{AutoScrollController, DirtyStateController}; use crate::ui::widget::PathBufFormatter; +lazy_static! { + pub static ref WINDOW_ID: WindowId = WindowId::next(); +} + const TITLE: &str = "Darktide Mod Manager"; const WINDOW_SIZE: (f64, f64) = (1080., 720.); const MOD_DETAILS_MIN_WIDTH: f64 = 325.; @@ -324,4 +329,9 @@ fn build_window() -> impl Widget { .with_flex_child(build_main(), 1.0) .with_child(build_log_view()) .controller(DirtyStateController) + .on_added(|_, ctx: &mut LifeCycleCtx, _, _| { + ctx.submit_command( + ACTION_SET_WINDOW_HANDLE.with(SingleUse::new((*WINDOW_ID, ctx.window().clone()))), + ); + }) } diff --git a/crates/dtmm/src/util/config.rs b/crates/dtmm/src/util/config.rs index 2c942ea..41fef73 100644 --- a/crates/dtmm/src/util/config.rs +++ b/crates/dtmm/src/util/config.rs @@ -7,7 +7,7 @@ use clap::{parser::ValueSource, ArgMatches}; use color_eyre::{eyre::Context, Result}; use serde::{Deserialize, Serialize}; -use crate::state::{ModInfo, State}; +use crate::state::{ActionState, ModInfo}; #[derive(Clone, Debug, Serialize)] pub(crate) struct LoadOrderEntrySerialize<'a> { @@ -31,8 +31,8 @@ pub(crate) struct ConfigSerialize<'a> { mod_order: Vec>, } -impl<'a> From<&'a State> for ConfigSerialize<'a> { - fn from(state: &'a State) -> Self { +impl<'a> From<&'a ActionState> for ConfigSerialize<'a> { + fn from(state: &'a ActionState) -> Self { Self { game_dir: &state.game_dir, data_dir: &state.data_dir, From a8db19cf9faad94814f3f3f2620a954f56d7202a Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 8 Mar 2023 20:34:45 +0100 Subject: [PATCH 048/335] refactor: Capitalize error messages --- crates/dtmm/src/controller/app.rs | 38 ++++++------ crates/dtmm/src/controller/game.rs | 90 ++++++++++++++-------------- crates/dtmm/src/controller/mod.rs | 6 +- crates/dtmm/src/controller/worker.rs | 10 ++-- crates/dtmm/src/main.rs | 4 +- crates/dtmm/src/util/config.rs | 12 ++-- crates/dtmt/src/cmd/build.rs | 32 +++++----- crates/dtmt/src/cmd/bundle/inject.rs | 8 +-- crates/dtmt/src/cmd/bundle/list.rs | 2 +- crates/dtmt/src/cmd/dictionary.rs | 2 +- crates/dtmt/src/cmd/new.rs | 6 +- crates/dtmt/src/cmd/package.rs | 10 ++-- crates/dtmt/src/main.rs | 4 +- lib/sdk/src/bundle/file.rs | 12 ++-- lib/sdk/src/bundle/mod.rs | 4 +- lib/sdk/src/filetype/lua.rs | 2 +- lib/sdk/src/filetype/package.rs | 4 +- lib/sdk/src/murmur/mod.rs | 4 +- 18 files changed, 125 insertions(+), 125 deletions(-) diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index 3a8adc5..ce8f2a2 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -23,10 +23,10 @@ use super::read_sjson_file; pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result { let data = fs::read(&info.path) .await - .wrap_err_with(|| format!("failed to read file {}", info.path.display()))?; + .wrap_err_with(|| format!("Failed to read file {}", info.path.display()))?; let data = Cursor::new(data); - let mut archive = ZipArchive::new(data).wrap_err("failed to open ZIP archive")?; + let mut archive = ZipArchive::new(data).wrap_err("Failed to open ZIP archive")?; if tracing::enabled!(tracing::Level::DEBUG) { let names = archive.file_names().fold(String::new(), |mut s, name| { @@ -38,7 +38,7 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result Result Result Result<()> let mod_dir = state.mod_dir.join(&info.id); fs::remove_dir_all(&mod_dir) .await - .wrap_err_with(|| format!("failed to remove directory {}", mod_dir.display()))?; + .wrap_err_with(|| format!("Failed to remove directory {}", mod_dir.display()))?; Ok(()) } @@ -133,7 +133,7 @@ pub(crate) async fn save_settings(state: ActionState) -> Result<()> { tracing::info!("Saving settings to '{}'", state.config_path.display()); tracing::debug!(?cfg); - let data = serde_sjson::to_string(&cfg).wrap_err("failed to serialize config")?; + let data = serde_sjson::to_string(&cfg).wrap_err("Failed to serialize config")?; fs::write(state.config_path.as_ref(), &data) .await @@ -155,11 +155,11 @@ async fn read_mod_dir_entry(res: Result) -> Result { let cfg: ModConfig = read_sjson_file(&config_path) .await - .wrap_err_with(|| format!("failed to read mod config '{}'", config_path.display()))?; + .wrap_err_with(|| format!("Failed to read mod config '{}'", config_path.display()))?; let files: HashMap> = read_sjson_file(&index_path) .await - .wrap_err_with(|| format!("failed to read file index '{}'", index_path.display()))?; + .wrap_err_with(|| format!("Failed to read file index '{}'", index_path.display()))?; let packages = files .into_iter() @@ -186,12 +186,12 @@ where } Err(err) => { return Err(err) - .wrap_err_with(|| format!("failed to open directory '{}'", mod_dir.display())); + .wrap_err_with(|| format!("Failed to open directory '{}'", mod_dir.display())); } }; let stream = ReadDirStream::new(read_dir) - .map(|res| res.wrap_err("failed to read dir entry")) + .map(|res| res.wrap_err("Failed to read dir entry")) .then(read_mod_dir_entry); tokio::pin!(stream); diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 9260908..62502c4 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -74,7 +74,7 @@ where ); fs::copy(path, &backup_path).await.wrap_err_with(|| { format!( - "failed to back up {} '{}' to '{}'", + "Failed to back up {} '{}' to '{}'", file_name, path.display(), backup_path.display() @@ -83,13 +83,13 @@ where tracing::debug!("Reading {} from original '{}'", file_name, path.display()); fs::read(path).await.wrap_err_with(|| { - format!("failed to read {} file: {}", file_name, path.display()) + format!("Failed to read {} file: {}", file_name, path.display()) })? } Err(err) => { return Err(err).wrap_err_with(|| { format!( - "failed to read {} from backup '{}'", + "Failed to read {} from backup '{}'", file_name, backup_path.display() ) @@ -105,12 +105,12 @@ async fn patch_game_settings(state: Arc) -> Result<()> { let settings = read_file_with_backup(&settings_path) .await - .wrap_err("failed to read settings.ini")?; - let settings = String::from_utf8(settings).wrap_err("settings.ini is not valid UTF-8")?; + .wrap_err("Failed to read settings.ini")?; + let settings = String::from_utf8(settings).wrap_err("Settings.ini is not valid UTF-8")?; let mut f = fs::File::create(&settings_path) .await - .wrap_err_with(|| format!("failed to open {}", settings_path.display()))?; + .wrap_err_with(|| format!("Failed to open {}", settings_path.display()))?; let Some(i) = settings.find("boot_script =") else { eyre::bail!("couldn't find 'boot_script' field"); @@ -138,7 +138,7 @@ fn make_package(info: &PackageInfo) -> Result { .next() .ok_or_else(|| eyre::eyre!("missing file extension")) .and_then(BundleFileType::from_str) - .wrap_err("invalid file name in package info")?; + .wrap_err("Invalid file name in package info")?; let name: String = it.collect(); pkg.add_file(file_type, name); } @@ -216,9 +216,9 @@ async fn build_bundles(state: Arc) -> Result> { let _enter = span.enter(); let lua = build_mod_data_lua(state.clone()); - let lua = CString::new(lua).wrap_err("failed to build CString from mod data Lua string")?; + let lua = CString::new(lua).wrap_err("Failed to build CString from mod data Lua string")?; let file = - lua::compile(MOD_DATA_SCRIPT, &lua).wrap_err("failed to compile mod data Lua file")?; + lua::compile(MOD_DATA_SCRIPT, &lua).wrap_err("Failed to compile mod data Lua file")?; mod_bundle.add_file(file); } @@ -232,11 +232,11 @@ async fn build_bundles(state: Arc) -> Result> { let span = tracing::trace_span!("building package", name = pkg_info.name); let _enter = span.enter(); - let pkg = make_package(pkg_info).wrap_err("failed to make package")?; + let pkg = make_package(pkg_info).wrap_err("Failed to make package")?; let mut variant = BundleFileVariant::new(); let bin = pkg .to_binary() - .wrap_err("failed to serialize package to binary")?; + .wrap_err("Failed to serialize package to binary")?; variant.set_data(bin); let mut file = BundleFile::new(pkg_info.name.clone(), BundleFileType::Package); file.add_variant(variant); @@ -260,11 +260,11 @@ async fn build_bundles(state: Arc) -> Result> { let task = async move { let bundle = { let bin = fs::read(&src).await.wrap_err_with(|| { - format!("failed to read bundle file '{}'", src.display()) + format!("Failed to read bundle file '{}'", src.display()) })?; let name = Bundle::get_name_from_path(&ctx, &src); Bundle::from_binary(&ctx, name, bin) - .wrap_err_with(|| format!("failed to parse bundle '{}'", src.display()))? + .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))? }; tracing::debug!( @@ -283,7 +283,7 @@ async fn build_bundles(state: Arc) -> Result> { let _ = fs::remove_file(&dest).await; fs::copy(&src, &dest).await.wrap_err_with(|| { format!( - "failed to copy bundle {pkg_name} for mod {mod_name}. src: {}, dest: {}", + "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", src.display(), dest.display() ) @@ -311,7 +311,7 @@ async fn build_bundles(state: Arc) -> Result> { tracing::trace!("Writing mod bundle to '{}'", path.display()); fs::write(&path, mod_bundle.to_binary()?) .await - .wrap_err_with(|| format!("failed to write bundle to '{}'", path.display()))?; + .wrap_err_with(|| format!("Failed to write bundle to '{}'", path.display()))?; } bundles.push(mod_bundle); @@ -329,14 +329,14 @@ async fn patch_boot_bundle(state: Arc) -> Result> { let mut boot_bundle = async { let bin = read_file_with_backup(&bundle_path) .await - .wrap_err("failed to read boot bundle")?; + .wrap_err("Failed to read boot bundle")?; Bundle::from_binary(&state.ctx, BOOT_BUNDLE_NAME.to_string(), bin) - .wrap_err("failed to parse boot bundle") + .wrap_err("Failed to parse boot bundle") } .instrument(tracing::trace_span!("read boot bundle")) .await - .wrap_err_with(|| format!("failed to read bundle '{}'", BOOT_BUNDLE_NAME))?; + .wrap_err_with(|| format!("Failed to read bundle '{}'", BOOT_BUNDLE_NAME))?; { tracing::trace!("Adding mod package file to boot bundle"); @@ -386,11 +386,11 @@ async fn patch_boot_bundle(state: Arc) -> Result> { { let bin = fs::read(&src) .await - .wrap_err_with(|| format!("failed to read bundle file '{}'", src.display()))?; + .wrap_err_with(|| format!("Failed to read bundle file '{}'", src.display()))?; let name = Bundle::get_name_from_path(&state.ctx, &src); let dml_bundle = Bundle::from_binary(&state.ctx, name, bin) - .wrap_err_with(|| format!("failed to parse bundle '{}'", src.display()))?; + .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))?; bundles.push(dml_bundle); }; @@ -416,14 +416,14 @@ async fn patch_boot_bundle(state: Arc) -> Result> { let _ = fs::remove_file(&dest).await; fs::copy(&src, &dest).await.wrap_err_with(|| { format!( - "failed to copy bundle {pkg_name} for mod {mod_name}. src: {}, dest: {}", + "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", src.display(), dest.display() ) })?; } - let pkg = make_package(pkg_info).wrap_err("failed to create package file for dml")?; + let pkg = make_package(pkg_info).wrap_err("Failed to create package file for dml")?; variant.set_data(pkg.to_binary()?); let mut f = BundleFile::new(DML_BUNDLE_NAME.to_string(), BundleFileType::Package); @@ -437,9 +437,9 @@ async fn patch_boot_bundle(state: Arc) -> Result> { let _enter = span.enter(); let lua = include_str!("../../assets/mod_main.lua"); - let lua = CString::new(lua).wrap_err("failed to build CString from mod main Lua string")?; + let lua = CString::new(lua).wrap_err("Failed to build CString from mod main Lua string")?; let file = - lua::compile(MOD_BOOT_SCRIPT, &lua).wrap_err("failed to compile mod main Lua file")?; + lua::compile(MOD_BOOT_SCRIPT, &lua).wrap_err("Failed to compile mod main Lua file")?; boot_bundle.add_file(file); } @@ -447,10 +447,10 @@ async fn patch_boot_bundle(state: Arc) -> Result> { async { let bin = boot_bundle .to_binary() - .wrap_err("failed to serialize boot bundle")?; + .wrap_err("Failed to serialize boot bundle")?; fs::write(&bundle_path, bin) .await - .wrap_err_with(|| format!("failed to write main bundle: {}", bundle_path.display())) + .wrap_err_with(|| format!("Failed to write main bundle: {}", bundle_path.display())) } .instrument(tracing::trace_span!("write boot bundle")) .await?; @@ -471,9 +471,9 @@ where let mut db = { let bin = read_file_with_backup(&database_path) .await - .wrap_err("failed to read bundle database")?; + .wrap_err("Failed to read bundle database")?; let mut r = Cursor::new(bin); - let db = BundleDatabase::from_binary(&mut r).wrap_err("failed to parse bundle database")?; + let db = BundleDatabase::from_binary(&mut r).wrap_err("Failed to parse bundle database")?; tracing::trace!("Finished parsing bundle database"); db }; @@ -486,7 +486,7 @@ where { let bin = db .to_binary() - .wrap_err("failed to serialize bundle database")?; + .wrap_err("Failed to serialize bundle database")?; fs::write(&database_path, bin).await.wrap_err_with(|| { format!( "failed to write bundle database to '{}'", @@ -512,11 +512,11 @@ where .collect(), }; let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); - let data = serde_sjson::to_string(&info).wrap_err("failed to serizalie deployment data")?; + let data = serde_sjson::to_string(&info).wrap_err("Failed to serizalie deployment data")?; fs::write(&path, &data) .await - .wrap_err_with(|| format!("failed to write deployment data to '{}'", path.display()))?; + .wrap_err_with(|| format!("Failed to write deployment data to '{}'", path.display()))?; Ok(()) } @@ -552,15 +552,15 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { if let Some(err) = err.downcast_ref::() && err.kind() == ErrorKind::NotFound { Ok(None) } else { - Err(err).wrap_err("failed to read deployment data") + Err(err).wrap_err("Failed to read deployment data") } } } } ) - .wrap_err("failed to gather deployment information")?; + .wrap_err("Failed to gather deployment information")?; - let game_info = game_info.wrap_err("failed to collect Steam info")?; + let game_info = game_info.wrap_err("Failed to collect Steam info")?; tracing::debug!(?game_info, ?deployment_info); @@ -581,12 +581,12 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { tracing::info!("Build mod bundles"); let mut bundles = build_bundles(state.clone()) .await - .wrap_err("failed to build mod bundles")?; + .wrap_err("Failed to build mod bundles")?; tracing::info!("Patch boot bundle"); let mut more_bundles = patch_boot_bundle(state.clone()) .await - .wrap_err("failed to patch boot bundle")?; + .wrap_err("Failed to patch boot bundle")?; bundles.append(&mut more_bundles); if let Some(info) = &deployment_info { @@ -609,7 +609,7 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { tracing::debug!("Removing unused bundle '{}'", file_name); if let Err(err) = fs::remove_file(&path).await.wrap_err_with(|| { - format!("failed to remove unused bundle '{}'", path.display()) + format!("Failed to remove unused bundle '{}'", path.display()) }) { tracing::error!("{:?}", err); } @@ -626,17 +626,17 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { tracing::info!("Patch game settings"); patch_game_settings(state.clone()) .await - .wrap_err("failed to patch game settings")?; + .wrap_err("Failed to patch game settings")?; tracing::info!("Patching bundle database"); patch_bundle_database(state.clone(), &bundles) .await - .wrap_err("failed to patch bundle database")?; + .wrap_err("Failed to patch bundle database")?; tracing::info!("Writing deployment data"); write_deployment_data(state.clone(), &bundles) .await - .wrap_err("failed to write deployment data")?; + .wrap_err("Failed to write deployment data")?; tracing::info!("Finished deploying mods"); Ok(()) @@ -662,14 +662,14 @@ pub(crate) async fn reset_mod_deployment(state: ActionState) -> Result<()> { } Err(err) => { return Err(err).wrap_err_with(|| { - format!("failed to read deployment info at '{}'", path.display()) + format!("Failed to read deployment info at '{}'", path.display()) }); } }; - let data = String::from_utf8(data).wrap_err("invalid UTF8 in deployment data")?; + let data = String::from_utf8(data).wrap_err("Invalid UTF8 in deployment data")?; - serde_sjson::from_str(&data).wrap_err("invalid SJSON in deployment data")? + serde_sjson::from_str(&data).wrap_err("Invalid SJSON in deployment data")? }; for name in info.bundles { @@ -697,7 +697,7 @@ pub(crate) async fn reset_mod_deployment(state: ActionState) -> Result<()> { fs::copy(&backup, &path) .await - .wrap_err_with(|| format!("failed to copy from '{}'", backup.display()))?; + .wrap_err_with(|| format!("Failed to copy from '{}'", backup.display()))?; tracing::debug!("Deleting backup: {}", backup.display()); @@ -705,7 +705,7 @@ pub(crate) async fn reset_mod_deployment(state: ActionState) -> Result<()> { Ok(_) => Ok(()), Err(err) if err.kind() == ErrorKind::NotFound => Ok(()), Err(err) => { - Err(err).wrap_err_with(|| format!("failed to remove '{}'", backup.display())) + Err(err).wrap_err_with(|| format!("Failed to remove '{}'", backup.display())) } } } diff --git a/crates/dtmm/src/controller/mod.rs b/crates/dtmm/src/controller/mod.rs index f7f250d..eacc3ef 100644 --- a/crates/dtmm/src/controller/mod.rs +++ b/crates/dtmm/src/controller/mod.rs @@ -17,7 +17,7 @@ where let path = path.as_ref(); let buf = fs::read(path) .await - .wrap_err_with(|| format!("failed to read file '{}'", path.display()))?; - let data = String::from_utf8(buf).wrap_err("invalid UTF8")?; - serde_sjson::from_str(&data).wrap_err("failed to deserialize SJSON") + .wrap_err_with(|| format!("Failed to read file '{}'", path.display()))?; + let data = String::from_utf8(buf).wrap_err("Invalid UTF8")?; + serde_sjson::from_str(&data).wrap_err("Failed to deserialize SJSON") } diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index 294af0e..48e9c1e 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -34,7 +34,7 @@ async fn handle_action( let event_sink = event_sink.clone(); match action { AsyncAction::DeployMods(state) => tokio::spawn(async move { - if let Err(err) = deploy_mods(state).await.wrap_err("failed to deploy mods") { + if let Err(err) = deploy_mods(state).await.wrap_err("Failed to deploy mods") { tracing::error!("{:?}", err); send_error(event_sink.clone(), err).await; } @@ -48,7 +48,7 @@ async fn handle_action( AsyncAction::AddMod(state, info) => tokio::spawn(async move { match import_mod(state, info) .await - .wrap_err("failed to import mod") + .wrap_err("Failed to import mod") { Ok(mod_info) => { event_sink @@ -71,7 +71,7 @@ async fn handle_action( let mod_dir = state.mod_dir.join(&info.id); if let Err(err) = delete_mod(state, &info) .await - .wrap_err("failed to delete mod files") + .wrap_err("Failed to delete mod files") .with_suggestion(|| { format!("Clean the folder '{}' manually", mod_dir.display()) }) @@ -93,7 +93,7 @@ async fn handle_action( AsyncAction::ResetDeployment(state) => tokio::spawn(async move { if let Err(err) = reset_mod_deployment(state) .await - .wrap_err("failed to reset mod deployment") + .wrap_err("Failed to reset mod deployment") { tracing::error!("{:?}", err); send_error(event_sink.clone(), err).await; @@ -108,7 +108,7 @@ async fn handle_action( AsyncAction::SaveSettings(state) => tokio::spawn(async move { if let Err(err) = save_settings(state) .await - .wrap_err("failed to save settings") + .wrap_err("Failed to save settings") { tracing::error!("{:?}", err); send_error(event_sink.clone(), err).await; diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 21b9f37..4a6b153 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -58,7 +58,7 @@ fn main() -> Result<()> { } let config = util::config::read_config(&default_config_path, &matches) - .wrap_err("failed to read config file")?; + .wrap_err("Failed to read config file")?; let game_info = dtmt_shared::collect_game_info()?; @@ -71,7 +71,7 @@ fn main() -> Result<()> { config.data_dir.unwrap_or_default(), ); state.mods = load_mods(state.get_mod_dir(), config.mod_order.iter()) - .wrap_err("failed to load mods")?; + .wrap_err("Failed to load mods")?; state }; diff --git a/crates/dtmm/src/util/config.rs b/crates/dtmm/src/util/config.rs index 41fef73..bf2a72b 100644 --- a/crates/dtmm/src/util/config.rs +++ b/crates/dtmm/src/util/config.rs @@ -113,10 +113,10 @@ where match fs::read(path) { Ok(data) => { let data = String::from_utf8(data).wrap_err_with(|| { - format!("config file {} contains invalid UTF-8", path.display()) + format!("Config file '{}' contains invalid UTF-8", path.display()) })?; let mut cfg: Config = serde_sjson::from_str(&data) - .wrap_err_with(|| format!("invalid config file {}", path.display()))?; + .wrap_err_with(|| format!("Invalid config file {}", path.display()))?; cfg.path = path.clone(); Ok(cfg) @@ -124,7 +124,7 @@ where Err(err) if err.kind() == ErrorKind::NotFound => { if matches.value_source("config") != Some(ValueSource::DefaultValue) { return Err(err) - .wrap_err_with(|| format!("failed to read config file {}", path.display()))?; + .wrap_err_with(|| format!("Failed to read config file {}", path.display()))?; } { @@ -132,7 +132,7 @@ where .parent() .expect("a file path always has a parent directory"); fs::create_dir_all(parent).wrap_err_with(|| { - format!("failed to create directories {}", parent.display()) + format!("Failed to create directories {}", parent.display()) })?; } @@ -145,7 +145,7 @@ where { let data = serde_sjson::to_string(&config) - .wrap_err("failed to serialize default config value")?; + .wrap_err("Failed to serialize default config value")?; fs::write(&config.path, data).wrap_err_with(|| { format!( "failed to write default config to {}", @@ -157,7 +157,7 @@ where Ok(config) } Err(err) => { - Err(err).wrap_err_with(|| format!("failed to read config file {}", path.display())) + Err(err).wrap_err_with(|| format!("Failed to read config file {}", path.display())) } } } diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index a49c9a1..e47d1ea 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -66,7 +66,7 @@ async fn find_project_config(dir: Option) -> Result { let (path, mut file) = if let Some(path) = dir { let file = File::open(&path.join(PROJECT_CONFIG_NAME)) .await - .wrap_err_with(|| format!("failed to open file: {}", path.display())) + .wrap_err_with(|| format!("Failed to open file: {}", path.display())) .with_suggestion(|| { format!( "Make sure the file at '{}' exists and is readable", @@ -90,7 +90,7 @@ async fn find_project_config(dir: Option) -> Result { } Err(err) => { let err = Report::new(err) - .wrap_err(format!("failed to open file: {}", path.display())); + .wrap_err(format!("Failed to open file: {}", path.display())); return Err(err); } } @@ -100,10 +100,10 @@ async fn find_project_config(dir: Option) -> Result { let mut buf = String::new(); file.read_to_string(&mut buf) .await - .wrap_err("invalid UTF-8")?; + .wrap_err("Invalid UTF-8")?; let mut cfg: ModConfig = - serde_sjson::from_str(&buf).wrap_err("failed to deserialize mod config")?; + serde_sjson::from_str(&buf).wrap_err("Failed to deserialize mod config")?; cfg.dir = path; Ok(cfg) } @@ -175,18 +175,18 @@ where path.set_extension("package"); let sjson = fs::read_to_string(&path) .await - .wrap_err_with(|| format!("failed to read file {}", path.display()))?; + .wrap_err_with(|| format!("Failed to read file {}", path.display()))?; let pkg_name = package.to_slash_lossy().to_string(); let pkg = Package::from_sjson(sjson, pkg_name.clone(), root) .await - .wrap_err_with(|| format!("invalid package file {}", &pkg_name))?; + .wrap_err_with(|| format!("Invalid package file {}", &pkg_name))?; compile_package_files(&pkg, root) .await - .wrap_err("failed to compile package") + .wrap_err("Failed to compile package") .and_then(|files| compile_bundle(pkg_name, files)) - .wrap_err("failed to build bundle") + .wrap_err("Failed to build bundle") } fn normalize_file_path>(path: P) -> Result { @@ -211,7 +211,7 @@ pub(crate) async fn read_project_config(dir: Option) -> Result) -> Result) -> Result Result<()> fs::create_dir_all(out_path) .await - .wrap_err_with(|| format!("failed to create output directory '{}'", out_path.display()))?; + .wrap_err_with(|| format!("Failed to create output directory '{}'", out_path.display()))?; let file_map = Arc::new(Mutex::new(FileIndexMap::new())); @@ -335,7 +335,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> ); fs::write(&path, &data) .await - .wrap_err_with(|| format!("failed to write bundle to '{}'", path.display()))?; + .wrap_err_with(|| format!("Failed to write bundle to '{}'", path.display()))?; if let Some(game_dir) = game_dir.as_ref() { let path = game_dir.join(&name); @@ -347,7 +347,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> ); fs::write(&path, &data) .await - .wrap_err_with(|| format!("failed to write bundle to '{}'", path.display()))?; + .wrap_err_with(|| format!("Failed to write bundle to '{}'", path.display()))?; } Ok(()) @@ -355,7 +355,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> try_join_all(tasks) .await - .wrap_err("failed to build mod bundles")?; + .wrap_err("Failed to build mod bundles")?; { let file_map = file_map.lock().await; @@ -363,7 +363,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> let path = out_path.join("files.sjson"); fs::write(&path, data) .await - .wrap_err_with(|| format!("failed to write file index to '{}'", path.display()))?; + .wrap_err_with(|| format!("Failed to write file index to '{}'", path.display()))?; } tracing::info!("Compiled bundles written to '{}'", out_path.display()); diff --git a/crates/dtmt/src/cmd/bundle/inject.rs b/crates/dtmt/src/cmd/bundle/inject.rs index 9c47686..2b86691 100644 --- a/crates/dtmt/src/cmd/bundle/inject.rs +++ b/crates/dtmt/src/cmd/bundle/inject.rs @@ -61,7 +61,7 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { if let Some(name) = matches.get_one::("replace") { let mut file = File::open(&file_path) .await - .wrap_err_with(|| format!("failed to open '{}'", file_path.display()))?; + .wrap_err_with(|| format!("Failed to open '{}'", file_path.display()))?; if let Some(variant) = bundle .files_mut() @@ -72,7 +72,7 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { let mut data = Vec::new(); file.read_to_end(&mut data) .await - .wrap_err("failed to read input file")?; + .wrap_err("Failed to read input file")?; variant.set_data(data); } else { let err = eyre::eyre!("No file '{}' in this bundle.", name) @@ -99,11 +99,11 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { let out_path = matches.get_one::("output").unwrap_or(bundle_path); let data = bundle .to_binary() - .wrap_err("failed to write changed bundle to output")?; + .wrap_err("Failed to write changed bundle to output")?; fs::write(out_path, &data) .await - .wrap_err("failed to write data to output file")?; + .wrap_err("Failed to write data to output file")?; Ok(()) } else { diff --git a/crates/dtmt/src/cmd/bundle/list.rs b/crates/dtmt/src/cmd/bundle/list.rs index b985ad2..dd72ad2 100644 --- a/crates/dtmt/src/cmd/bundle/list.rs +++ b/crates/dtmt/src/cmd/bundle/list.rs @@ -98,7 +98,7 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { async move { if let Err(err) = print_bundle_contents(&ctx, &p, fmt) .await - .wrap_err_with(|| format!("failed to list contents of bundle {}", p.display())) + .wrap_err_with(|| format!("Failed to list contents of bundle {}", p.display())) { tracing::error!("{err:?}"); } diff --git a/crates/dtmt/src/cmd/dictionary.rs b/crates/dtmt/src/cmd/dictionary.rs index 20ec1fc..0400519 100644 --- a/crates/dtmt/src/cmd/dictionary.rs +++ b/crates/dtmt/src/cmd/dictionary.rs @@ -122,7 +122,7 @@ pub(crate) async fn run(mut ctx: sdk::Context, matches: &ArgMatches) -> Result<( .expect("required argument not found"); u64::from_str_radix(s, 16) - .wrap_err("failed to parse argument as hexadecimal string")? + .wrap_err("Failed to parse argument as hexadecimal string")? }; let groups = sub_matches diff --git a/crates/dtmt/src/cmd/new.rs b/crates/dtmt/src/cmd/new.rs index 187706c..77fa649 100644 --- a/crates/dtmt/src/cmd/new.rs +++ b/crates/dtmt/src/cmd/new.rs @@ -86,7 +86,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> let root = if let Some(dir) = matches.get_one::("root") { if dir == "." { std::env::current_dir() - .wrap_err("the current working dir is invalid") + .wrap_err("The current working dir is invalid") .with_suggestion(|| "Change to a different directory.")? } else { PathBuf::from(dir) @@ -142,13 +142,13 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> .recursive(true) .create(&dir) .await - .wrap_err_with(|| format!("failed to create directory {}", dir.display()))?; + .wrap_err_with(|| format!("Failed to create directory {}", dir.display()))?; tracing::trace!("Writing file {}", path.display()); fs::write(&path, content.as_bytes()) .await - .wrap_err_with(|| format!("failed to write content to path {}", path.display())) + .wrap_err_with(|| format!("Failed to write content to path {}", path.display())) }); futures::stream::iter(templates) diff --git a/crates/dtmt/src/cmd/package.rs b/crates/dtmt/src/cmd/package.rs index 2a64a9f..eab826c 100644 --- a/crates/dtmt/src/cmd/package.rs +++ b/crates/dtmt/src/cmd/package.rs @@ -52,7 +52,7 @@ async fn process_dir_entry(res: Result) -> Result<(OsString, Vec)> let data = fs::read(&path) .await - .wrap_err_with(|| format!("failed to read '{}'", path.display()))?; + .wrap_err_with(|| format!("Failed to read '{}'", path.display()))?; Ok((entry.file_name(), data)) } @@ -87,7 +87,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> let data = fs::read(&path) .await - .wrap_err_with(|| format!("failed to read mod config at {}", path.display()))?; + .wrap_err_with(|| format!("Failed to read mod config at {}", path.display()))?; zip.start_file(name.to_slash_lossy(), Default::default())?; zip.write_all(&data)?; @@ -101,10 +101,10 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> ); let read_dir = fs::read_dir(&path) .await - .wrap_err_with(|| format!("failed to read directory '{}'", path.display()))?; + .wrap_err_with(|| format!("Failed to read directory '{}'", path.display()))?; let stream = ReadDirStream::new(read_dir) - .map(|res| res.wrap_err("failed to read dir entry")) + .map(|res| res.wrap_err("Failed to read dir entry")) .then(process_dir_entry); tokio::pin!(stream); @@ -121,7 +121,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> fs::write(&dest, data.into_inner()) .await - .wrap_err_with(|| format!("failed to write mod archive to '{}'", dest.display())) + .wrap_err_with(|| format!("Failed to write mod archive to '{}'", dest.display())) .with_suggestion(|| "Make sure that parent directories exist.".to_string())?; tracing::info!("Mod archive written to {}", dest.display()); diff --git a/crates/dtmt/src/main.rs b/crates/dtmt/src/main.rs index 15c6758..8c7b40f 100644 --- a/crates/dtmt/src/main.rs +++ b/crates/dtmt/src/main.rs @@ -75,7 +75,7 @@ async fn main() -> Result<()> { tokio::spawn(async move { let res = File::open(&path) .await - .wrap_err_with(|| format!("failed to open dictionary file: {}", path.display())); + .wrap_err_with(|| format!("Failed to open dictionary file: {}", path.display())); let f = match res { Ok(f) => f, @@ -102,7 +102,7 @@ async fn main() -> Result<()> { tokio::spawn(async move { let conf = tokio::task::spawn_blocking(|| { confy::load::(clap::crate_name!(), None) - .wrap_err("failed to load global configuration") + .wrap_err("Failed to load global configuration") }) .await; diff --git a/lib/sdk/src/bundle/file.rs b/lib/sdk/src/bundle/file.rs index 872b48b..9881660 100644 --- a/lib/sdk/src/bundle/file.rs +++ b/lib/sdk/src/bundle/file.rs @@ -548,7 +548,7 @@ impl BundleFile { let _enter = span.enter(); let header = BundleFileVariant::read_header(r) - .wrap_err_with(|| format!("failed to read header {i}"))?; + .wrap_err_with(|| format!("Failed to read header {i}"))?; // TODO: Figure out how `header.unknown_1` correlates to `properties::DATA` // if props.contains(Properties::DATA) { @@ -572,18 +572,18 @@ impl BundleFile { let data = vec![]; let s = r .read_string_len(header.size) - .wrap_err("failed to read data file name")?; + .wrap_err("Failed to read data file name")?; (data, Some(s)) } else { let mut data = vec![0; header.size]; r.read_exact(&mut data) - .wrap_err_with(|| format!("failed to read file {i}"))?; + .wrap_err_with(|| format!("Failed to read file {i}"))?; let data_file_name = if header.len_data_file_name > 0 { let s = r .read_string_len(header.len_data_file_name) - .wrap_err("failed to read data file name")?; + .wrap_err("Failed to read data file name")?; Some(s) } else { None @@ -662,7 +662,7 @@ impl BundleFile { match file_type { BundleFileType::Lua => { let sjson = - CString::new(sjson.as_ref()).wrap_err("failed to build CString from SJSON")?; + CString::new(sjson.as_ref()).wrap_err("Failed to build CString from SJSON")?; lua::compile(name, sjson) } BundleFileType::Unknown(_) => { @@ -784,7 +784,7 @@ impl BundleFile { } }; - let res = res.wrap_err_with(|| format!("failed to decompile file {name}")); + let res = res.wrap_err_with(|| format!("Failed to decompile file {name}")); match res { Ok(files) => files, Err(err) => { diff --git a/lib/sdk/src/bundle/mod.rs b/lib/sdk/src/bundle/mod.rs index 8b8d7c5..af87d29 100644 --- a/lib/sdk/src/bundle/mod.rs +++ b/lib/sdk/src/bundle/mod.rs @@ -164,7 +164,7 @@ impl Bundle { OodleLZ_FuzzSafe::No, OodleLZ_CheckCRC::No, ) - .wrap_err_with(|| format!("failed to decompress chunk {chunk_index}"))?; + .wrap_err_with(|| format!("Failed to decompress chunk {chunk_index}"))?; if unpacked_size_tracked < CHUNK_SIZE { raw_buffer.resize(unpacked_size_tracked, 0); @@ -192,7 +192,7 @@ impl Bundle { let _enter = span.enter(); let file = BundleFile::from_reader(ctx, &mut r, *props) - .wrap_err_with(|| format!("failed to read file {i}"))?; + .wrap_err_with(|| format!("Failed to read file {i}"))?; files.push(file); } diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index 68b95e3..720988f 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -38,7 +38,7 @@ where lua::lua_setglobal(state, b"code\0".as_ptr() as _); let name = CString::new(name.as_bytes()) - .wrap_err_with(|| format!("cannot convert name into CString: {}", name))?; + .wrap_err_with(|| format!("Cannot convert name into CString: {}", name))?; lua::lua_pushstring(state, name.as_ptr() as _); lua::lua_setglobal(state, b"name\0".as_ptr() as _); diff --git a/lib/sdk/src/filetype/package.rs b/lib/sdk/src/filetype/package.rs index 9545677..1f50f81 100644 --- a/lib/sdk/src/filetype/package.rs +++ b/lib/sdk/src/filetype/package.rs @@ -148,7 +148,7 @@ impl Package { None } else { let t = BundleFileType::from_str(ty) - .wrap_err("invalid file type in package definition")?; + .wrap_err("Invalid file type in package definition")?; Some(t) }; @@ -200,7 +200,7 @@ impl Package { } } - serde_sjson::to_string(&map).wrap_err("failed to serialize Package to SJSON") + serde_sjson::to_string(&map).wrap_err("Failed to serialize Package to SJSON") } #[tracing::instrument("Package::from_binary", skip(binary, ctx), fields(binary_len = binary.as_ref().len()))] diff --git a/lib/sdk/src/murmur/mod.rs b/lib/sdk/src/murmur/mod.rs index 7ede170..a2a9ef3 100644 --- a/lib/sdk/src/murmur/mod.rs +++ b/lib/sdk/src/murmur/mod.rs @@ -56,7 +56,7 @@ impl TryFrom<&str> for Murmur64 { fn try_from(value: &str) -> Result { u64::from_str_radix(value, 16) .map(Self) - .wrap_err_with(|| format!("failed to convert value to Murmur64: {value}")) + .wrap_err_with(|| format!("Failed to convert value to Murmur64: {value}")) } } @@ -164,7 +164,7 @@ impl TryFrom<&str> for Murmur32 { fn try_from(value: &str) -> Result { u32::from_str_radix(value, 16) .map(Self) - .wrap_err_with(|| format!("failed to convert value to Murmur32: {value}")) + .wrap_err_with(|| format!("Failed to convert value to Murmur32: {value}")) } } From 308613d5fc48cfec6a40ea5b1b7632ab753f45f0 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 8 Mar 2023 21:27:29 +0100 Subject: [PATCH 049/335] feat(dtmt): Improve error messages when compiling Lua Fixes #58. --- crates/dtmm/src/controller/game.rs | 5 +---- crates/dtmt/src/cmd/build.rs | 27 +++++++++---------------- lib/sdk/src/bundle/file.rs | 8 ++------ lib/sdk/src/filetype/lua.rs | 32 +++++++++++++++++++++++------- 4 files changed, 37 insertions(+), 35 deletions(-) diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 62502c4..9a95be6 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -1,4 +1,3 @@ -use std::ffi::CString; use std::io::{Cursor, ErrorKind}; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -216,7 +215,6 @@ async fn build_bundles(state: Arc) -> Result> { let _enter = span.enter(); let lua = build_mod_data_lua(state.clone()); - let lua = CString::new(lua).wrap_err("Failed to build CString from mod data Lua string")?; let file = lua::compile(MOD_DATA_SCRIPT, &lua).wrap_err("Failed to compile mod data Lua file")?; @@ -437,9 +435,8 @@ async fn patch_boot_bundle(state: Arc) -> Result> { let _enter = span.enter(); let lua = include_str!("../../assets/mod_main.lua"); - let lua = CString::new(lua).wrap_err("Failed to build CString from mod main Lua string")?; let file = - lua::compile(MOD_BOOT_SCRIPT, &lua).wrap_err("Failed to compile mod main Lua file")?; + lua::compile(MOD_BOOT_SCRIPT, lua).wrap_err("Failed to compile mod main Lua file")?; boot_bundle.add_file(file); } diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index e47d1ea..523dccd 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -151,17 +151,6 @@ where results.into_iter().collect() } -#[tracing::instrument(skip_all, fields(files = files.len()))] -fn compile_bundle(name: String, files: Vec) -> Result { - let mut bundle = Bundle::new(name); - - for file in files { - bundle.add_file(file); - } - - Ok(bundle) -} - #[tracing::instrument] async fn build_package(package: P1, root: P2) -> Result where @@ -182,18 +171,20 @@ where .await .wrap_err_with(|| format!("Invalid package file {}", &pkg_name))?; - compile_package_files(&pkg, root) - .await - .wrap_err("Failed to compile package") - .and_then(|files| compile_bundle(pkg_name, files)) - .wrap_err("Failed to build bundle") + let files = compile_package_files(&pkg, root).await?; + let mut bundle = Bundle::new(pkg_name); + for file in files { + bundle.add_file(file); + } + + Ok(bundle) } fn normalize_file_path>(path: P) -> Result { let path = path.as_ref(); if path.is_absolute() || path.has_root() { - let err = eyre::eyre!("path is absolute: {}", path.display()); + let err = eyre::eyre!("Path is absolute: {}", path.display()); return Err(err).with_suggestion(|| "Specify a relative file path.".to_string()); } @@ -302,7 +293,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> let bundle = build_package(path, &cfg.dir).await.wrap_err_with(|| { format!( - "failed to build package {} in {}", + "Failed to build package '{}' at '{}'", path.display(), cfg.dir.display() ) diff --git a/lib/sdk/src/bundle/file.rs b/lib/sdk/src/bundle/file.rs index 9881660..ab59884 100644 --- a/lib/sdk/src/bundle/file.rs +++ b/lib/sdk/src/bundle/file.rs @@ -1,4 +1,3 @@ -use std::ffi::CString; use std::io::{Cursor, Read, Seek, Write}; use std::path::Path; @@ -660,11 +659,8 @@ impl BundleFile { S: AsRef, { match file_type { - BundleFileType::Lua => { - let sjson = - CString::new(sjson.as_ref()).wrap_err("Failed to build CString from SJSON")?; - lua::compile(name, sjson) - } + BundleFileType::Lua => lua::compile(name.clone(), sjson) + .wrap_err_with(|| format!("Failed to compile Lua file '{}'", name)), BundleFileType::Unknown(_) => { eyre::bail!("Unknown file type. Cannot compile from SJSON"); } diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index 720988f..9c53a1c 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -25,7 +25,7 @@ where pub fn compile(name: S, code: C) -> Result where S: Into, - C: AsRef, + C: AsRef, { let name = name.into(); let code = code.as_ref(); @@ -34,15 +34,33 @@ where let state = lua::luaL_newstate(); lua::luaL_openlibs(state); - lua::lua_pushstring(state, code.as_ptr() as _); - lua::lua_setglobal(state, b"code\0".as_ptr() as _); - let name = CString::new(name.as_bytes()) .wrap_err_with(|| format!("Cannot convert name into CString: {}", name))?; - lua::lua_pushstring(state, name.as_ptr() as _); - lua::lua_setglobal(state, b"name\0".as_ptr() as _); + match lua::luaL_loadbuffer( + state, + code.as_ptr() as _, + code.len() as _, + name.as_ptr() as _, + ) as u32 + { + lua::LUA_OK => {} + lua::LUA_ERRSYNTAX => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); - let run = b"return string.dump(loadstring(code, \"@\" .. name), false)\0"; + lua::lua_close(state); + + eyre::bail!("Invalid syntax: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to compile LuaJIT bytecode") + } + _ => unreachable!(), + } + lua::lua_setglobal(state, b"fn\0".as_ptr() as _); + + let run = b"return string.dump(fn, false)\0"; match lua::luaL_loadstring(state, run.as_ptr() as _) as u32 { lua::LUA_OK => {} lua::LUA_ERRSYNTAX => { From 6b168bad6b120a0d64f2b51dba202da9f170e29a Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 9 Mar 2023 11:34:54 +0100 Subject: [PATCH 050/335] fix(dtmm): Fix game directory check when there is no Steam Fixes #60. --- crates/dtmm/src/controller/game.rs | 25 ++++++++++++++--------- crates/dtmm/src/main.rs | 31 ++++++++++++++++++++--------- crates/dtmm/src/ui/window/dialog.rs | 1 + lib/dtmt-shared/src/lib.rs | 16 +++++++-------- 4 files changed, 47 insertions(+), 26 deletions(-) diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 9a95be6..c054688 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -533,7 +533,14 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { } } - let (game_info, deployment_info) = tokio::try_join!( + let (_, game_info, deployment_info) = tokio::try_join!( + async { + let path = state.game_dir.join("bundle"); + fs::metadata(&path) + .await + .wrap_err("Failed to open game bundle directory") + .with_suggestion(|| "Double-check 'Game Directory' in the Settings tab.") + }, async { tokio::task::spawn_blocking(dtmt_shared::collect_game_info) .await @@ -557,16 +564,16 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { ) .wrap_err("Failed to gather deployment information")?; - let game_info = game_info.wrap_err("Failed to collect Steam info")?; - tracing::debug!(?game_info, ?deployment_info); - if deployment_info - .as_ref() - .map(|i| game_info.last_updated > i.timestamp) - .unwrap_or(false) - { - eyre::bail!("Game was updated since last mod deployment. Please reset first."); + if let Some(game_info) = game_info { + if deployment_info + .as_ref() + .map(|i| game_info.last_updated > i.timestamp) + .unwrap_or(false) + { + eyre::bail!("Game was updated since last mod deployment. Please reset first."); + } } tracing::info!( diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 4a6b153..c3b8cb5 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -8,13 +8,17 @@ use std::sync::Arc; use clap::command; use clap::value_parser; use clap::Arg; +use color_eyre::eyre; use color_eyre::eyre::Context; use color_eyre::{Report, Result}; use druid::AppLauncher; +use druid::SingleUse; +use druid::Target; use tokio::sync::RwLock; use crate::controller::app::load_mods; use crate::controller::worker::work_thread; +use crate::state::ACTION_SHOW_ERROR_DIALOG; use crate::state::{Delegate, State}; mod controller; @@ -57,17 +61,32 @@ fn main() -> Result<()> { oodle_sys::init(matches.get_one::("oodle")); } + let (action_tx, action_rx) = tokio::sync::mpsc::unbounded_channel(); + let delegate = Delegate::new(action_tx); + + let launcher = AppLauncher::with_window(ui::window::main::new()).delegate(delegate); + + let event_sink = launcher.get_external_handle(); + let config = util::config::read_config(&default_config_path, &matches) .wrap_err("Failed to read config file")?; - - let game_info = dtmt_shared::collect_game_info()?; + let game_info = dtmt_shared::collect_game_info(); tracing::debug!(?config, ?game_info); + let game_dir = config.game_dir.or_else(|| game_info.map(|i| i.path)); + if game_dir.is_none() { + let err = + eyre::eyre!("No Game Directory set. Head to the 'Settings' tab to set it manually",); + event_sink + .submit_command(ACTION_SHOW_ERROR_DIALOG, SingleUse::new(err), Target::Auto) + .expect("failed to send command"); + } + let initial_state = { let mut state = State::new( config.path, - config.game_dir.unwrap_or(game_info.path), + game_dir.unwrap_or_default(), config.data_dir.unwrap_or_default(), ); state.mods = load_mods(state.get_mod_dir(), config.mod_order.iter()) @@ -75,12 +94,6 @@ fn main() -> Result<()> { state }; - let (action_tx, action_rx) = tokio::sync::mpsc::unbounded_channel(); - let delegate = Delegate::new(action_tx); - - let launcher = AppLauncher::with_window(ui::window::main::new()).delegate(delegate); - - let event_sink = launcher.get_external_handle(); std::thread::spawn(move || { let event_sink = Arc::new(RwLock::new(event_sink)); let action_rx = Arc::new(RwLock::new(action_rx)); diff --git a/crates/dtmm/src/ui/window/dialog.rs b/crates/dtmm/src/ui/window/dialog.rs index 3b6802a..64ce88d 100644 --- a/crates/dtmm/src/ui/window/dialog.rs +++ b/crates/dtmm/src/ui/window/dialog.rs @@ -20,6 +20,7 @@ pub fn error(err: Report, parent: WindowHandle) -> WindowDesc { let widget = Flex::column() .main_axis_alignment(MainAxisAlignment::SpaceBetween) + .must_fill_main_axis(true) .cross_axis_alignment(CrossAxisAlignment::End) .with_child(text) .with_spacer(20.) diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index fa8c407..f15e4e7 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -1,8 +1,5 @@ use std::path::PathBuf; -use color_eyre::eyre; -use color_eyre::Result; - mod log; pub use log::*; @@ -40,11 +37,12 @@ pub struct GameInfo { pub last_updated: OffsetDateTime, } -pub fn collect_game_info() -> Result { +pub fn collect_game_info() -> Option { let mut dir = if let Some(dir) = SteamDir::locate() { dir } else { - eyre::bail!("Failed to locate Steam installation") + tracing::debug!("Failed to locate Steam installation"); + return None; }; let found = dir @@ -52,15 +50,17 @@ pub fn collect_game_info() -> Result { .and_then(|app| app.vdf.get("LastUpdated").map(|v| (app.path.clone(), v))); let Some((path, last_updated)) = found else { - eyre::bail!("Failed to find game installation"); + tracing::debug!("Found Steam, but failed to find game installation"); + return None; }; let Some(last_updated) = last_updated .as_value() .and_then(|v| v.to::()) .and_then(|v| OffsetDateTime::from_unix_timestamp(v).ok()) else { - eyre::bail!("Couldn't read 'LastUpdate'."); + tracing::error!("Found Steam game, but couldn't read 'LastUpdate'."); + return None; }; - Ok(GameInfo { path, last_updated }) + Some(GameInfo { path, last_updated }) } From 4995190199d681ead794bb8b3764134682e041ed Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 9 Mar 2023 11:52:52 +0100 Subject: [PATCH 051/335] feat(dtmm): Check mod order Closes #13. --- CHANGELOG.adoc | 2 + crates/dtmm/src/controller/app.rs | 63 +++++++++++++++++++++++++++++- crates/dtmm/src/controller/game.rs | 11 ++---- crates/dtmm/src/state/data.rs | 32 +++++++++++++++ lib/dtmt-shared/src/lib.rs | 21 ++++++++-- 5 files changed, 117 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index f61467b..ecfed2b 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -10,6 +10,8 @@ - dtmm: check for Steam game update before deployment - dtmm: remove unused bundles from previous deployment - dtmm: show dialog for critical errors +- dtmm: check mod order before deployment +- dtmt: add mod dependencies to config === Fixed diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index ce8f2a2..c36e7f2 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -14,7 +14,7 @@ use tokio_stream::wrappers::ReadDirStream; use tokio_stream::StreamExt; use zip::ZipArchive; -use crate::state::{ActionState, ModInfo, PackageInfo}; +use crate::state::{ActionState, ModInfo, ModOrder, PackageInfo}; use crate::util::config::{ConfigSerialize, LoadOrderEntry}; use super::read_sjson_file; @@ -216,3 +216,64 @@ where Ok::<_, color_eyre::Report>(mods) }) } + +pub(crate) fn check_mod_order(state: &ActionState) -> Result<()> { + { + let first = state.mods.get(0); + if first.is_none() || !(first.unwrap().id == "dml" && first.unwrap().enabled) { + // TODO: Add a suggestion where to get it, once that's published + eyre::bail!("'Darktide Mod Loader' needs to be installed, enabled and at the top of the load order"); + } + } + + state + .mods + .iter() + .filter(|i| i.enabled) + .enumerate() + .for_each(|(i, info)| tracing::debug!(i, ?info)); + + for (i, mod_info) in state.mods.iter().filter(|i| i.enabled).enumerate() { + for dep in &mod_info.depends { + let dep_info = state.mods.iter().enumerate().find(|(_, m)| m.id == dep.id); + + match dep_info { + Some((_, dep_info)) if !dep_info.enabled => { + eyre::bail!( + "Dependency '{}' ({}) must be enabled.", + dep_info.name, + dep.id + ); + } + Some((j, dep_info)) if dep.order == ModOrder::Before && j >= i => { + eyre::bail!( + "Dependency '{}' ({}) must be loaded before '{}'", + dep_info.name, + dep.id, + mod_info.name + ); + } + Some((j, dep_info)) if dep.order == ModOrder::After && j <= i => { + eyre::bail!( + "Dependency '{}' ({}) must be loaded after '{}'", + dep_info.name, + dep.id, + mod_info.name + ); + } + None => { + eyre::bail!( + "Missing dependency '{}' for mod '{}'", + dep.id, + mod_info.name + ); + } + Some(_) => { + // All good + } + } + } + } + + Ok(()) +} diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index c054688..68f9269 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -21,6 +21,7 @@ use tokio::io::AsyncWriteExt; use tracing::Instrument; use super::read_sjson_file; +use crate::controller::app::check_mod_order; use crate::state::{ActionState, PackageInfo}; const MOD_BUNDLE_NAME: &str = "packages/mods"; @@ -525,14 +526,6 @@ where pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { let state = Arc::new(state); - { - let first = state.mods.get(0); - if first.is_none() || !(first.unwrap().id == "dml" && first.unwrap().enabled) { - // TODO: Add a suggestion where to get it, once that's published - eyre::bail!("'Darktide Mod Loader' needs to be installed, enabled and at the top of the load order"); - } - } - let (_, game_info, deployment_info) = tokio::try_join!( async { let path = state.game_dir.join("bundle"); @@ -576,6 +569,8 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { } } + check_mod_order(&state)?; + tracing::info!( "Deploying {} mods to {}", state.mods.iter().filter(|i| i.enabled).count(), diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 8021840..779b67c 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -39,6 +39,36 @@ pub(crate) struct ModResourceInfo { pub localization: Option, } +#[derive(Clone, Data, Debug, PartialEq)] +pub(crate) enum ModOrder { + Before, + After, +} + +#[derive(Clone, Data, Debug, PartialEq)] +pub(crate) struct ModDependency { + pub id: String, + pub order: ModOrder, +} + +impl From for ModDependency { + fn from(value: dtmt_shared::ModDependency) -> Self { + match value { + dtmt_shared::ModDependency::ID(id) => ModDependency { + id, + order: ModOrder::Before, + }, + dtmt_shared::ModDependency::Config { id, order } => ModDependency { + id, + order: match order { + dtmt_shared::ModOrder::Before => ModOrder::Before, + dtmt_shared::ModOrder::After => ModOrder::After, + }, + }, + } + } +} + #[derive(Clone, Data, Debug, Lens, PartialEq)] pub(crate) struct ModInfo { pub id: String, @@ -51,6 +81,7 @@ pub(crate) struct ModInfo { #[lens(ignore)] #[data(ignore)] pub resources: ModResourceInfo, + pub depends: Vector, } impl ModInfo { @@ -66,6 +97,7 @@ impl ModInfo { data: cfg.resources.data, localization: cfg.resources.localization, }, + depends: cfg.depends.into_iter().map(ModDependency::from).collect(), } } } diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index f15e4e7..17b8d92 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -3,10 +3,11 @@ use std::path::PathBuf; mod log; pub use log::*; +use serde::Deserialize; use steamlocate::SteamDir; use time::OffsetDateTime; -#[derive(Clone, Debug, Default, serde::Deserialize)] +#[derive(Clone, Debug, Default, Deserialize)] pub struct ModConfigResources { pub init: PathBuf, #[serde(default)] @@ -15,7 +16,21 @@ pub struct ModConfigResources { pub localization: Option, } -#[derive(Clone, Debug, Default, serde::Deserialize)] +#[derive(Clone, Debug, PartialEq, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ModOrder { + Before, + After, +} + +#[derive(Clone, Debug, PartialEq, Deserialize)] +#[serde(untagged)] +pub enum ModDependency { + ID(String), + Config { id: String, order: ModOrder }, +} + +#[derive(Clone, Debug, Default, Deserialize)] pub struct ModConfig { #[serde(skip)] pub dir: std::path::PathBuf, @@ -26,7 +41,7 @@ pub struct ModConfig { pub packages: Vec, pub resources: ModConfigResources, #[serde(default)] - pub depends: Vec, + pub depends: Vec, } pub const STEAMAPP_ID: u32 = 1361210; From 65c0974de20a0f64410424dc4fa85547db320b09 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 9 Mar 2023 14:29:44 +0100 Subject: [PATCH 052/335] feat(dtmm): Add additional details fields Ref: #15. --- crates/dtmm/src/state/data.rs | 12 ++++++++-- crates/dtmm/src/ui/window/main.rs | 37 ++++++++++++++++++++++++++++--- crates/dtmt/src/cmd/new.rs | 29 ++++++++++++++++++------ lib/dtmt-shared/src/lib.rs | 8 +++++-- 4 files changed, 72 insertions(+), 14 deletions(-) diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 779b67c..ae622f7 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -73,7 +73,11 @@ impl From for ModDependency { pub(crate) struct ModInfo { pub id: String, pub name: String, - pub description: Arc, + pub summary: Arc, + pub description: Option>, + pub categories: Vector, + pub author: Option, + pub version: String, pub enabled: bool, #[lens(ignore)] #[data(ignore)] @@ -89,9 +93,13 @@ impl ModInfo { Self { id: cfg.id, name: cfg.name, - description: Arc::new(cfg.description), + summary: Arc::new(cfg.summary), + description: cfg.description.map(Arc::new), + author: cfg.author, + version: cfg.version, enabled: false, packages, + categories: cfg.categories.into_iter().collect(), resources: ModResourceInfo { init: cfg.resources.init, data: cfg.resources.data, diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index f48b5c3..8fa7de0 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -220,16 +220,47 @@ fn build_mod_details_info() -> impl Widget { // so that we can center-align it. .expand_width() .lens(ModInfo::name.in_arc()); - let description = Label::raw() + let summary = Label::raw() .with_line_break_mode(LineBreaking::WordWrap) - .lens(ModInfo::description.in_arc()); + .lens(ModInfo::summary.in_arc()); + + // TODO: Image/icon? + + let version_line = Label::dynamic(|info: &Arc, _| { + if let Some(author) = &info.author { + format!("Version: {}, by {author}", info.version) + } else { + format!("Version: {}", info.version) + } + }); + + let categories = Label::dynamic(|info: &Arc, _| { + if info.categories.is_empty() { + String::from("Uncategorized") + } else { + info.categories.iter().enumerate().fold( + String::from("Category: "), + |mut s, (i, category)| { + if i > 0 { + s.push_str(", "); + } + s.push_str(category); + s + }, + ) + } + }); Flex::column() .cross_axis_alignment(CrossAxisAlignment::Start) .main_axis_alignment(MainAxisAlignment::Start) .with_child(name) .with_spacer(4.) - .with_child(description) + .with_child(summary) + .with_spacer(4.) + .with_child(version_line) + .with_spacer(4.) + .with_child(categories) }, Flex::column, ) diff --git a/crates/dtmt/src/cmd/new.rs b/crates/dtmt/src/cmd/new.rs index 77fa649..374cdb7 100644 --- a/crates/dtmt/src/cmd/new.rs +++ b/crates/dtmt/src/cmd/new.rs @@ -13,8 +13,27 @@ const TEMPLATES: [(&str, &str); 5] = [ "dtmt.cfg", r#"id = "{{id}}" name = "{{name}}" -description = "This is my new mod '{{name}}'!" version = "0.1.0" +// author = "" + +// A one- or two-line short description. +summary = "This is my new mod '{{name}}'!" +// description = "" + +// Can contain arbitrary strings. But to keep things consistent and useful, +// capitalize names and check existing mods for matching categories. +categories = [ + Misc + // UI + // QoL + // Tools +] + +// A list of mod IDs that this mod depends on. You can find +// those IDs by downloading the mod and extracting their `dtmt.cfg`. +depends = [ + DMF +] resources = { init = "scripts/mods/{{id}}/init" @@ -23,16 +42,12 @@ resources = { } packages = [ - "packages/{{id}}" -] - -depends = [ - "dmf" + "packages/mods/{{id}}" ] "#, ), ( - "packages/{{id}}.package", + "packages/mods/{{id}}.package", r#"lua = [ "scripts/mods/{{id}}/*" ] diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index 17b8d92..0bf3924 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -36,9 +36,13 @@ pub struct ModConfig { pub dir: std::path::PathBuf, pub id: String, pub name: String, - pub description: String, + pub summary: String, + pub description: Option, + pub author: Option, pub version: String, - pub packages: Vec, + #[serde(default)] + pub categories: Vec, + pub packages: Vec, pub resources: ModConfigResources, #[serde(default)] pub depends: Vec, From 978701bed8af9d5b65d2db8898c02e6c69ad6e78 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 9 Mar 2023 20:07:22 +0100 Subject: [PATCH 053/335] feat(dtmm): Implement mod logo Closes #15. --- Cargo.lock | 60 ++++++++++++++++- crates/dtmm/Cargo.toml | 2 +- crates/dtmm/src/controller/app.rs | 65 ++++++++++++++++-- crates/dtmm/src/state/data.rs | 12 +++- crates/dtmm/src/state/delegate.rs | 5 ++ crates/dtmm/src/ui/widget/controller.rs | 45 +++++++++++-- crates/dtmm/src/ui/window/main.rs | 29 ++++++-- crates/dtmt/Cargo.toml | 1 + crates/dtmt/src/cmd/build.rs | 35 +++++++++- crates/dtmt/src/cmd/new.rs | 1 + crates/dtmt/src/cmd/package.rs | 90 +++++++++++++++---------- lib/dtmt-shared/src/lib.rs | 3 +- 12 files changed, 289 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5f2a35a..a46c278 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,6 +178,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + [[package]] name = "byteorder" version = "1.4.3" @@ -399,6 +405,12 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "confy" version = "0.5.1" @@ -725,6 +737,7 @@ dependencies = [ name = "dtmt" version = "0.3.0" dependencies = [ + "async-recursion", "clap", "cli-table", "color-eyre", @@ -1314,6 +1327,21 @@ dependencies = [ "version_check", ] +[[package]] +name = "image" +version = "0.24.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "jpeg-decoder", + "num-rational 0.4.1", + "num-traits 0.2.15", + "png", +] + [[package]] name = "indenter" version = "0.3.3" @@ -1398,6 +1426,12 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" + [[package]] name = "js-sys" version = "0.3.61" @@ -1635,7 +1669,7 @@ dependencies = [ "num-complex", "num-integer", "num-iter", - "num-rational", + "num-rational 0.3.2", "num-traits 0.2.15", ] @@ -1692,6 +1726,17 @@ dependencies = [ "num-traits 0.2.15", ] +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits 0.2.15", +] + [[package]] name = "num-traits" version = "0.1.43" @@ -1924,6 +1969,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e381186490a3e2017a506d62b759ea8eaf4be14666b13ed53973e8ae193451b1" dependencies = [ + "image", "kurbo", "unic-bidi", ] @@ -2022,6 +2068,18 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "png" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" +dependencies = [ + "bitflags", + "crc32fast", + "flate2", + "miniz_oxide", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 5e11a9b..05db4e0 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -10,7 +10,7 @@ bitflags = "1.3.2" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } color-eyre = "0.6.2" confy = "0.5.1" -druid = { git = "https://github.com/linebender/druid.git", features = ["im", "serde"] } +druid = { git = "https://github.com/linebender/druid.git", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp"] } dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } futures = "0.3.25" oodle-sys = { path = "../../lib/oodle-sys", version = "*" } diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index c36e7f2..9aa5a6d 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -4,9 +4,9 @@ use std::path::Path; use std::sync::Arc; use color_eyre::eyre::{self, Context}; -use color_eyre::{Help, Result}; +use color_eyre::{Help, Report, Result}; use druid::im::Vector; -use druid::FileInfo; +use druid::{FileInfo, ImageBuf}; use dtmt_shared::ModConfig; use tokio::fs::{self, DirEntry}; use tokio::runtime::Runtime; @@ -95,6 +95,36 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result img, + Err(err) => { + let err = Report::msg(err.to_string()).wrap_err("Invalid image data"); + return Err(err).with_suggestion(|| { + "Supported formats are: PNG, JPEG, Bitmap and WebP".to_string() + }); + } + }; + + Some(img) + } else { + None + }; + let mod_dir = state.mod_dir; tracing::trace!("Creating mods directory {}", mod_dir.display()); @@ -111,7 +141,7 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result) -> Result { .await .wrap_err_with(|| format!("Failed to read file index '{}'", index_path.display()))?; + let image = if let Some(path) = &cfg.image { + let path = entry.path().join(path); + if let Ok(data) = fs::read(&path).await { + // Druid somehow doesn't return an error compatible with eyre, here. + // So we have to wrap through `Display` manually. + let img = match ImageBuf::from_data(&data) { + Ok(img) => img, + Err(err) => { + let err = Report::msg(err.to_string()); + return Err(err) + .wrap_err_with(|| { + format!("Failed to import image file '{}'", path.display()) + }) + .with_suggestion(|| { + "Supported formats are: PNG, JPEG, Bitmap and WebP".to_string() + }); + } + }; + + Some(img) + } else { + None + } + } else { + None + }; + let packages = files .into_iter() .map(|(name, files)| Arc::new(PackageInfo::new(name, files.into_iter().collect()))) .collect(); - let info = ModInfo::new(cfg, packages); + let info = ModInfo::new(cfg, packages, image); Ok(info) } diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index ae622f7..34ab92d 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -2,7 +2,7 @@ use std::{path::PathBuf, sync::Arc}; use druid::{ im::{HashMap, Vector}, - Data, Lens, WindowHandle, WindowId, + Data, ImageBuf, Lens, WindowHandle, WindowId, }; use dtmt_shared::ModConfig; @@ -69,7 +69,7 @@ impl From for ModDependency { } } -#[derive(Clone, Data, Debug, Lens, PartialEq)] +#[derive(Clone, Data, Debug, Lens)] pub(crate) struct ModInfo { pub id: String, pub name: String, @@ -77,6 +77,7 @@ pub(crate) struct ModInfo { pub description: Option>, pub categories: Vector, pub author: Option, + pub image: Option, pub version: String, pub enabled: bool, #[lens(ignore)] @@ -89,7 +90,11 @@ pub(crate) struct ModInfo { } impl ModInfo { - pub fn new(cfg: ModConfig, packages: Vector>) -> Self { + pub fn new( + cfg: ModConfig, + packages: Vector>, + image: Option, + ) -> Self { Self { id: cfg.id, name: cfg.name, @@ -99,6 +104,7 @@ impl ModInfo { version: cfg.version, enabled: false, packages, + image, categories: cfg.categories.into_iter().collect(), resources: ModResourceInfo { init: cfg.resources.init, diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index 506cbf8..73aa42d 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -259,6 +259,11 @@ impl AppDelegate for Delegate { Handled::Yes } cmd if cmd.is(ACTION_FINISH_SAVE_SETTINGS) => { + tracing::trace!( + in_progress = state.is_save_in_progress, + next_pending = state.is_next_save_pending, + "Finished saving settings", + ); state.is_save_in_progress = false; if state.is_next_save_pending { diff --git a/crates/dtmm/src/ui/widget/controller.rs b/crates/dtmm/src/ui/widget/controller.rs index 1bd7f6a..a7ff2f1 100644 --- a/crates/dtmm/src/ui/widget/controller.rs +++ b/crates/dtmm/src/ui/widget/controller.rs @@ -1,5 +1,7 @@ -use druid::widget::{Button, Controller, Scroll}; -use druid::{Data, Env, Event, EventCtx, Rect, UpdateCtx, Widget}; +use druid::widget::{Button, Controller, Image, Scroll}; +use druid::{ + Data, Env, Event, EventCtx, ImageBuf, LifeCycle, LifeCycleCtx, Rect, UpdateCtx, Widget, +}; use crate::state::{State, ACTION_SET_DIRTY, ACTION_START_SAVE_SETTINGS}; @@ -48,18 +50,19 @@ impl> Controller> for AutoScrollController data: &T, env: &Env, ) { + child.update(ctx, old_data, data, env); + if !ctx.is_disabled() { let size = child.child_size(); let end_region = Rect::new(size.width - 1., size.height - 1., size.width, size.height); child.scroll_to(ctx, end_region); } - child.update(ctx, old_data, data, env) } } macro_rules! compare_state_fields { ($old:ident, $new:ident, $($field:ident),+) => { - $($old.$field != $new.$field) || + + $(!Data::same(&$old.$field, &$new.$field)) || + } } @@ -86,3 +89,37 @@ impl> Controller for DirtyStateController { child.update(ctx, old_data, data, env) } } + +pub struct ImageLensController; + +impl Controller for ImageLensController { + fn lifecycle( + &mut self, + widget: &mut Image, + ctx: &mut LifeCycleCtx, + event: &LifeCycle, + data: &ImageBuf, + env: &Env, + ) { + if let LifeCycle::WidgetAdded = event { + widget.set_image_data(data.clone()); + } + + widget.lifecycle(ctx, event, data, env); + } + + fn update( + &mut self, + widget: &mut Image, + ctx: &mut UpdateCtx, + old_data: &ImageBuf, + data: &ImageBuf, + env: &Env, + ) { + if !Data::same(old_data, data) { + widget.set_image_data(data.clone()); + } + + widget.update(ctx, old_data, data, env); + } +} diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 8fa7de0..c8d19a8 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -2,10 +2,10 @@ use std::sync::Arc; use druid::im::Vector; use druid::widget::{ - Button, Checkbox, CrossAxisAlignment, Flex, Label, LineBreaking, List, MainAxisAlignment, - Maybe, Scroll, SizedBox, Split, TextBox, ViewSwitcher, + Button, Checkbox, CrossAxisAlignment, Flex, Image, Label, LineBreaking, List, + MainAxisAlignment, Maybe, Scroll, SizedBox, Split, TextBox, ViewSwitcher, }; -use druid::{lens, LifeCycleCtx}; +use druid::{lens, Data, ImageBuf, LifeCycleCtx}; use druid::{ Color, FileDialogOptions, FileSpec, FontDescriptor, FontFamily, Key, LensExt, SingleUse, TextAlignment, Widget, WidgetExt, WindowDesc, WindowId, @@ -18,7 +18,9 @@ use crate::state::{ ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT, }; use crate::ui::theme; -use crate::ui::widget::controller::{AutoScrollController, DirtyStateController}; +use crate::ui::widget::controller::{ + AutoScrollController, DirtyStateController, ImageLensController, +}; use crate::ui::widget::PathBufFormatter; lazy_static! { @@ -124,7 +126,7 @@ fn build_mod_list() -> impl Widget { }, |state, infos| { infos.into_iter().for_each(|(i, new, _)| { - if state.mods.get(i).cloned() != Some(new.clone()) { + if Data::same(&state.mods.get(i).cloned(), &Some(new.clone())) { state.mods.set(i, new); } }); @@ -251,7 +253,7 @@ fn build_mod_details_info() -> impl Widget { } }); - Flex::column() + let details = Flex::column() .cross_axis_alignment(CrossAxisAlignment::Start) .main_axis_alignment(MainAxisAlignment::Start) .with_child(name) @@ -261,10 +263,23 @@ fn build_mod_details_info() -> impl Widget { .with_child(version_line) .with_spacer(4.) .with_child(categories) + .padding((4., 4.)); + + let image = + Maybe::or_empty(|| Image::new(ImageBuf::empty()).controller(ImageLensController)) + .lens(ModInfo::image.in_arc()); + + Flex::column() + .main_axis_alignment(MainAxisAlignment::Start) + .must_fill_main_axis(true) + .cross_axis_alignment(CrossAxisAlignment::Start) + .with_child(image) + // .with_spacer(4.) + // .with_flex_child(details, 1.) + .with_child(details) }, Flex::column, ) - .padding((4., 4.)) .lens(State::selected_mod) } diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index 8b210e5..6ed04de 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -30,6 +30,7 @@ tracing = { version = "0.1.37", features = ["async-await"] } zip = "0.6.3" path-clean = "1.0.1" path-slash = "0.2.1" +async-recursion = "1.0.2" [dev-dependencies] tempfile = "3.3.0" diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index 523dccd..b8f23a8 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -201,6 +201,17 @@ fn normalize_file_path>(path: P) -> Result { pub(crate) async fn read_project_config(dir: Option) -> Result { let mut cfg = find_project_config(dir).await?; + if let Some(path) = cfg.image { + let path = normalize_file_path(path) + .wrap_err("Invalid config field 'image'") + .with_suggestion(|| { + "Specify a file path relative to and child path of the \ + directory where 'dtmt.cfg' is." + .to_string() + })?; + cfg.image = Some(path); + } + cfg.resources.init = normalize_file_path(cfg.resources.init) .wrap_err("Invalid config field 'resources.init'") .with_suggestion(|| { @@ -349,14 +360,36 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> .wrap_err("Failed to build mod bundles")?; { + let path = out_path.join("files.sjson"); + tracing::trace!(path = %path.display(), "Writing file index"); let file_map = file_map.lock().await; let data = serde_sjson::to_string(file_map.deref())?; - let path = out_path.join("files.sjson"); fs::write(&path, data) .await .wrap_err_with(|| format!("Failed to write file index to '{}'", path.display()))?; } + if let Some(img_path) = &cfg.image { + let path = cfg.dir.join(img_path); + let dest = out_path.join(img_path); + + tracing::trace!(src = %path.display(), dest = %dest.display(), "Copying image file"); + + if let Some(parent) = dest.parent() { + fs::create_dir_all(&parent) + .await + .wrap_err_with(|| format!("Failed to create directory '{}'", parent.display()))?; + } + + fs::copy(&path, &dest).await.wrap_err_with(|| { + format!( + "Failed to copy image from '{}' to '{}'", + path.display(), + dest.display() + ) + })?; + } + tracing::info!("Compiled bundles written to '{}'", out_path.display()); if let Some(game_dir) = game_dir.as_ref() { diff --git a/crates/dtmt/src/cmd/new.rs b/crates/dtmt/src/cmd/new.rs index 374cdb7..eb6a4f9 100644 --- a/crates/dtmt/src/cmd/new.rs +++ b/crates/dtmt/src/cmd/new.rs @@ -19,6 +19,7 @@ version = "0.1.0" // A one- or two-line short description. summary = "This is my new mod '{{name}}'!" // description = "" +// image = "assets/logo.png" // Can contain arbitrary strings. But to keep things consistent and useful, // capitalize names and check existing mods for matching categories. diff --git a/crates/dtmt/src/cmd/package.rs b/crates/dtmt/src/cmd/package.rs index eab826c..f4d990e 100644 --- a/crates/dtmt/src/cmd/package.rs +++ b/crates/dtmt/src/cmd/package.rs @@ -1,12 +1,13 @@ -use std::ffi::OsString; use std::io::{Cursor, Write}; use std::path::PathBuf; +use std::sync::Arc; use clap::{value_parser, Arg, ArgMatches, Command}; use color_eyre::eyre::{Context, Result}; use color_eyre::Help; use path_slash::PathBufExt; -use tokio::fs::{self, DirEntry}; +use tokio::fs; +use tokio::sync::Mutex; use tokio_stream::wrappers::ReadDirStream; use tokio_stream::StreamExt; use zip::ZipWriter; @@ -46,15 +47,45 @@ pub(crate) fn command_definition() -> Command { ) } -async fn process_dir_entry(res: Result) -> Result<(OsString, Vec)> { - let entry = res?; - let path = entry.path(); - - let data = fs::read(&path) +#[async_recursion::async_recursion] +async fn process_directory( + zip: Arc>>, + path: PathBuf, + prefix: PathBuf, +) -> Result<()> { + zip.lock() .await - .wrap_err_with(|| format!("Failed to read '{}'", path.display()))?; + .add_directory(prefix.to_slash_lossy(), Default::default())?; - Ok((entry.file_name(), data)) + let read_dir = fs::read_dir(&path) + .await + .wrap_err_with(|| format!("Failed to read directory '{}'", path.display()))?; + + let stream = ReadDirStream::new(read_dir).map(|res| res.wrap_err("Failed to read dir entry")); + tokio::pin!(stream); + + while let Some(res) = stream.next().await { + let entry = res?; + let in_path = entry.path(); + let out_path = prefix.join(entry.file_name()); + + let t = entry.file_type().await?; + + if t.is_file() || t.is_symlink() { + let data = fs::read(&in_path) + .await + .wrap_err_with(|| format!("Failed to read '{}'", in_path.display()))?; + { + let mut zip = zip.lock().await; + zip.start_file(out_path.to_slash_lossy(), Default::default())?; + zip.write_all(&data)?; + } + } else if t.is_dir() { + process_directory(zip.clone(), in_path, out_path).await?; + } + } + + Ok(()) } #[tracing::instrument(skip_all)] @@ -75,14 +106,23 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> }; let data = Cursor::new(Vec::new()); - let mut zip = ZipWriter::new(data); + let zip = ZipWriter::new(data); + let zip = Arc::new(Mutex::new(zip)); - zip.add_directory(&cfg.id, Default::default())?; + let path = cfg.dir.join( + matches + .get_one::("directory") + .expect("parameter has default value"), + ); - let base_path = PathBuf::from(cfg.id); + process_directory(zip.clone(), path, PathBuf::from(&cfg.id)) + .await + .wrap_err("Failed to add directory to archive")?; + + let mut zip = zip.lock().await; { - let name = base_path.join("dtmt.cfg"); + let name = PathBuf::from(&cfg.id).join("dtmt.cfg"); let path = cfg.dir.join("dtmt.cfg"); let data = fs::read(&path) @@ -93,30 +133,6 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> zip.write_all(&data)?; } - { - let path = cfg.dir.join( - matches - .get_one::("directory") - .expect("parameter has default value"), - ); - let read_dir = fs::read_dir(&path) - .await - .wrap_err_with(|| format!("Failed to read directory '{}'", path.display()))?; - - let stream = ReadDirStream::new(read_dir) - .map(|res| res.wrap_err("Failed to read dir entry")) - .then(process_dir_entry); - tokio::pin!(stream); - - while let Some(res) = stream.next().await { - let (name, data) = res?; - - let name = base_path.join(name); - zip.start_file(name.to_slash_lossy(), Default::default())?; - zip.write_all(&data)?; - } - }; - let data = zip.finish()?; fs::write(&dest, data.into_inner()) diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index 0bf3924..d556fdf 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -33,13 +33,14 @@ pub enum ModDependency { #[derive(Clone, Debug, Default, Deserialize)] pub struct ModConfig { #[serde(skip)] - pub dir: std::path::PathBuf, + pub dir: PathBuf, pub id: String, pub name: String, pub summary: String, pub description: Option, pub author: Option, pub version: String, + pub image: Option, #[serde(default)] pub categories: Vec, pub packages: Vec, From 8cf08e07381cf1342d8c64775d9e00a996a30815 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 9 Mar 2023 21:29:18 +0100 Subject: [PATCH 054/335] feat(dtmt): Implement watch command Closes #61. --- Cargo.lock | 110 +++++++++++++++ crates/dtmt/Cargo.toml | 1 + crates/dtmt/src/cmd/build.rs | 59 +++++--- crates/dtmt/src/cmd/package.rs | 76 ++++++----- crates/dtmt/src/cmd/watch.rs | 238 +++++++++++++++++++++++++++++++-- crates/dtmt/src/main.rs | 2 +- 6 files changed, 422 insertions(+), 64 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a46c278..d1b4bce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -525,6 +525,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.15" @@ -749,6 +759,7 @@ dependencies = [ "glob", "libloading", "nanorand", + "notify", "oodle-sys", "path-clean", "path-slash", @@ -878,6 +889,18 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "filetime" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys 0.45.0", +] + [[package]] name = "flate2" version = "1.0.25" @@ -949,6 +972,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "futures" version = "0.3.26" @@ -1358,6 +1390,26 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "instant" version = "0.1.12" @@ -1461,6 +1513,26 @@ dependencies = [ "thiserror", ] +[[package]] +name = "kqueue" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587" +dependencies = [ + "bitflags", + "libc", +] + [[package]] name = "kurbo" version = "0.9.1" @@ -1649,6 +1721,24 @@ dependencies = [ "nom 7.1.3", ] +[[package]] +name = "notify" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9" +dependencies = [ + "bitflags", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "mio", + "walkdir", + "windows-sys 0.42.0", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2277,6 +2367,15 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -2920,6 +3019,17 @@ dependencies = [ "quote", ] +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index 6ed04de..f89c27c 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -31,6 +31,7 @@ zip = "0.6.3" path-clean = "1.0.1" path-slash = "0.2.1" async-recursion = "1.0.2" +notify = "5.1.0" [dev-dependencies] tempfile = "3.3.0" diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index b8f23a8..9bdddf7 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -260,26 +260,16 @@ pub(crate) async fn read_project_config(dir: Option) -> Result Result<()> { - unsafe { - oodle_sys::init(matches.get_one::("oodle")); - } - - let cfg = read_project_config(matches.get_one::("directory").cloned()).await?; - - let game_dir = matches - .get_one::("deploy") - .map(|p| p.join("bundle")); - - let out_path = matches - .get_one::("out") - .expect("parameter should have default value"); - - tracing::debug!(?cfg, ?game_dir, ?out_path); - - let game_dir = Arc::new(game_dir); - let cfg = Arc::new(cfg); +pub(crate) async fn build( + cfg: &ModConfig, + out_path: P1, + game_dir: Arc>, +) -> Result<()> +where + P1: AsRef, + P2: AsRef, +{ + let out_path = out_path.as_ref(); fs::create_dir_all(out_path) .await @@ -340,7 +330,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> .wrap_err_with(|| format!("Failed to write bundle to '{}'", path.display()))?; if let Some(game_dir) = game_dir.as_ref() { - let path = game_dir.join(&name); + let path = game_dir.as_ref().join(&name); tracing::trace!( "Deploying bundle {} to '{}'", @@ -393,8 +383,33 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> tracing::info!("Compiled bundles written to '{}'", out_path.display()); if let Some(game_dir) = game_dir.as_ref() { - tracing::info!("Deployed bundles to '{}'", game_dir.display()); + tracing::info!("Deployed bundles to '{}'", game_dir.as_ref().display()); } Ok(()) } + +#[tracing::instrument(skip_all)] +pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { + unsafe { + oodle_sys::init(matches.get_one::("oodle")); + } + + let cfg = read_project_config(matches.get_one::("directory").cloned()).await?; + + let game_dir = matches + .get_one::("deploy") + .map(|p| p.join("bundle")); + + let out_path = matches + .get_one::("out") + .expect("parameter should have default value"); + + tracing::debug!(?cfg, ?game_dir, ?out_path); + + let game_dir = Arc::new(game_dir); + + build(&cfg, out_path, game_dir).await?; + + Ok(()) +} diff --git a/crates/dtmt/src/cmd/package.rs b/crates/dtmt/src/cmd/package.rs index f4d990e..e922b6e 100644 --- a/crates/dtmt/src/cmd/package.rs +++ b/crates/dtmt/src/cmd/package.rs @@ -1,11 +1,12 @@ use std::io::{Cursor, Write}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::Arc; use clap::{value_parser, Arg, ArgMatches, Command}; use color_eyre::eyre::{Context, Result}; use color_eyre::Help; -use path_slash::PathBufExt; +use dtmt_shared::ModConfig; +use path_slash::{PathBufExt, PathExt}; use tokio::fs; use tokio::sync::Mutex; use tokio_stream::wrappers::ReadDirStream; @@ -41,18 +42,28 @@ pub(crate) fn command_definition() -> Command { Arg::new("out") .long("out") .short('o') - .default_value(".") .value_parser(value_parser!(PathBuf)) - .help("The path to write the packaged file to. May be a directory or a file name."), + .help( + "The path to write the packaged file to. Will default to a file in the \ + current working directory", + ), ) } #[async_recursion::async_recursion] -async fn process_directory( +async fn process_directory( zip: Arc>>, - path: PathBuf, - prefix: PathBuf, -) -> Result<()> { + path: P1, + prefix: P2, +) -> Result<()> +where + P1: AsRef + std::marker::Send, + P2: AsRef + std::marker::Send, + W: std::io::Write + std::io::Seek + std::marker::Send, +{ + let path = path.as_ref(); + let prefix = prefix.as_ref(); + zip.lock() .await .add_directory(prefix.to_slash_lossy(), Default::default())?; @@ -88,33 +99,18 @@ async fn process_directory Result<()> { - let cfg = read_project_config(matches.get_one::("project").cloned()).await?; - - let dest = { - let mut path = matches - .get_one::("out") - .cloned() - .unwrap_or_else(|| PathBuf::from(".")); - - if path.extension().is_none() { - path.push(format!("{}.zip", cfg.id)) - } - - path - }; +pub(crate) async fn package(cfg: &ModConfig, path: P1, dest: P2) -> Result<()> +where + P1: AsRef, + P2: AsRef, +{ + let path = path.as_ref(); + let dest = dest.as_ref(); let data = Cursor::new(Vec::new()); let zip = ZipWriter::new(data); let zip = Arc::new(Mutex::new(zip)); - let path = cfg.dir.join( - matches - .get_one::("directory") - .expect("parameter has default value"), - ); - process_directory(zip.clone(), path, PathBuf::from(&cfg.id)) .await .wrap_err("Failed to add directory to archive")?; @@ -135,7 +131,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> let data = zip.finish()?; - fs::write(&dest, data.into_inner()) + fs::write(dest, data.into_inner()) .await .wrap_err_with(|| format!("Failed to write mod archive to '{}'", dest.display())) .with_suggestion(|| "Make sure that parent directories exist.".to_string())?; @@ -143,3 +139,21 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> tracing::info!("Mod archive written to {}", dest.display()); Ok(()) } + +#[tracing::instrument(skip_all)] +pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { + let cfg = read_project_config(matches.get_one::("project").cloned()).await?; + + let dest = matches + .get_one::("out") + .map(path_clean::clean) + .unwrap_or_else(|| PathBuf::from(format!("{}.zip", cfg.id))); + + let path = cfg.dir.join( + matches + .get_one::("directory") + .expect("parameter has default value"), + ); + + package(&cfg, path, dest).await +} diff --git a/crates/dtmt/src/cmd/watch.rs b/crates/dtmt/src/cmd/watch.rs index 508cef9..54e2274 100644 --- a/crates/dtmt/src/cmd/watch.rs +++ b/crates/dtmt/src/cmd/watch.rs @@ -1,24 +1,242 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use std::time::Duration; -use clap::{value_parser, Arg, ArgMatches, Command}; -use color_eyre::eyre::Result; +use clap::{value_parser, Arg, ArgAction, ArgMatches, Command}; +use color_eyre::eyre::{Context, Result}; +use dtmt_shared::ModConfig; +use notify::{Event, Watcher}; -pub(crate) fn _command_definition() -> Command { +use crate::cmd::build::{build, read_project_config}; + +use super::package::package; + +pub(crate) fn command_definition() -> Command { Command::new("watch") - .about("Re-build the given directory on file changes.") + .about("Watch for file system changes and re-build the mod archive.") + .arg( + Arg::new("debounce") + .default_value("150") + .value_parser(value_parser!(u64)) + .help( + "The delay to debounce events by. This avoids continously \ + rebuilding on rapid file changes, such as version control checkouts.", + ), + ) .arg( Arg::new("directory") .required(false) - .default_value(".") .value_parser(value_parser!(PathBuf)) .help( "The path to the project to build. \ - If omitted, the current working directory is used.", + If omitted, the current working directory is used.", + ), + ) + .arg(Arg::new("oodle").long("oodle").help( + "The oodle library to load. This may either be:\n\ + - A library name that will be searched for in the system's default paths.\n\ + - A file path relative to the current working directory.\n\ + - An absolute file path.", + )) + .arg( + Arg::new("out") + .long("out") + .short('o') + .default_value("out") + .value_parser(value_parser!(PathBuf)) + .help("The directory to write output files to."), + ) + .arg( + Arg::new("deploy") + .long("deploy") + .short('d') + .value_parser(value_parser!(PathBuf)) + .help( + "If the path to the game (without the trailing '/bundle') is specified, \ + deploy the newly built bundles. \ + This will not adjust the bundle database or package files, so if files are \ + added or removed, you will have to import into DTMM and re-deploy there.", + ), + ) + .arg( + Arg::new("archive") + .long("archive") + .short('a') + .value_parser(value_parser!(PathBuf)) + .help( + "The path to write the packaged file to. Will default to a file in the \ + current working directory", + ), + ) + .arg( + Arg::new("ignore") + .long("ignore") + .short('i') + .value_parser(value_parser!(PathBuf)) + .action(ArgAction::Append) + .help( + "A directory or file path to ignore. May be specified multiple times. \ + The values of 'out' and 'archive' are ignored automatically.", ), ) } -#[tracing::instrument(skip_all)] -pub(crate) async fn run(_ctx: sdk::Context, _matches: &ArgMatches) -> Result<()> { - unimplemented!() +async fn compile( + cfg: &ModConfig, + out_path: P1, + archive_path: P2, + game_dir: Arc>, +) -> Result<()> +where + P1: AsRef + std::marker::Copy, + P2: AsRef, + P3: AsRef, +{ + build(cfg, out_path, game_dir) + .await + .wrap_err("Failed to build bundles")?; + package(cfg, out_path, archive_path) + .await + .wrap_err("Failed to package bundles") +} + +#[tracing::instrument(skip_all)] +pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { + unsafe { + oodle_sys::init(matches.get_one::("oodle")); + } + + let cfg = read_project_config(matches.get_one::("directory").cloned()) + .await + .wrap_err("failed to load project config")?; + tracing::debug!(?cfg); + let cfg = Arc::new(cfg); + + let game_dir = matches + .get_one::("deploy") + .map(path_clean::clean) + .map(|p| if p.is_absolute() { p } else { cfg.dir.join(p) }) + .map(|p| p.join("bundle")); + + let out_path = matches + .get_one::("out") + .map(path_clean::clean) + .map(|p| if p.is_absolute() { p } else { cfg.dir.join(p) }) + .expect("parameter should have default value"); + + let archive_path = matches + .get_one::("archive") + .map(path_clean::clean) + .map(|p| if p.is_absolute() { p } else { cfg.dir.join(p) }) + .unwrap_or_else(|| cfg.dir.join(format!("{}.zip", cfg.id))); + + let ignored = { + let mut ignored: Vec<_> = matches + .get_many::("ignore") + .unwrap_or_default() + .map(path_clean::clean) + .map(|p| if p.is_absolute() { p } else { cfg.dir.join(p) }) + .collect(); + + ignored.push(out_path.clone()); + ignored.push(archive_path.clone()); + + ignored + }; + + if tracing::enabled!(tracing::Level::INFO) { + let list = ignored.iter().fold(String::new(), |mut s, p| { + s.push_str("\n - "); + s.push_str(&p.display().to_string()); + s + }); + + tracing::info!("Ignoring:{}", list); + } + + let game_dir = Arc::new(game_dir); + + let duration = + Duration::from_millis(matches.get_one::("debounce").copied().unwrap_or(150)); + let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel(); + + let mut watcher = notify::recommended_watcher(move |res: Result| { + let ignored = match &res { + Ok(evt) => evt.paths.iter().any(|p1| { + let p1 = path_clean::clean(p1); + ignored.iter().any(|p2| p1.starts_with(p2)) + }), + Err(_) => false, + }; + + tracing::trace!(?res, ignored, "Received file system event"); + + if !ignored { + if let Err(err) = tx.send(res) { + tracing::error!("Failed to send file system event: {:?}", err); + } + } + }) + .wrap_err("failed to create file system watcher")?; + + tracing::info!("Starting file watcher on '{}'", cfg.dir.display()); + + let path = cfg.dir.clone(); + watcher + .watch(&path, notify::RecursiveMode::Recursive) + .wrap_err_with(|| { + format!( + "failed to watch directory for file changes: {}", + path.display() + ) + })?; + + tracing::trace!("Starting debounce loop"); + + let mut dirty = false; + loop { + // While we could just always await on the timeout, splitting things like this + // optimizes the case when no events happen for a while. Rather than being woken every + // `duration` just to do nothing, this way we always wait for a new event first until + // we start the debounce timeouts. + if dirty { + match tokio::time::timeout(duration, rx.recv()).await { + // The error is the wanted case, as it signals that we haven't received an + // event within `duration`, which es what the debounce is supposed to wait for. + Err(_) => { + tracing::trace!("Received debounce timeout, running build"); + if let Err(err) = + compile(&cfg, &out_path, &archive_path, game_dir.clone()).await + { + tracing::error!("Failed to build mod archive: {:?}", err); + } + dirty = false; + } + Ok(None) => { + break; + } + // We received a value before the timeout, so we reset it + Ok(_) => { + tracing::trace!("Received value before timeout, resetting"); + } + } + } else { + match rx.recv().await { + Some(_) => { + tracing::trace!("Received event, starting debounce"); + dirty = true; + } + None => { + break; + } + } + } + } + + tracing::trace!("Event channel closed"); + if let Err(err) = compile(&cfg, &out_path, &archive_path, game_dir.clone()).await { + tracing::error!("Failed to build mod archive: {:?}", err); + } + + Ok(()) } diff --git a/crates/dtmt/src/main.rs b/crates/dtmt/src/main.rs index 8c7b40f..3e1fba2 100644 --- a/crates/dtmt/src/main.rs +++ b/crates/dtmt/src/main.rs @@ -55,7 +55,7 @@ async fn main() -> Result<()> { .subcommand(cmd::murmur::command_definition()) .subcommand(cmd::new::command_definition()) .subcommand(cmd::package::command_definition()) - // .subcommand(cmd::watch::command_definition()) + .subcommand(cmd::watch::command_definition()) .get_matches(); dtmt_shared::create_tracing_subscriber(); From 888c3e900a70e6047328adf413ba890092abfe1b Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 10 Mar 2023 11:36:45 +0100 Subject: [PATCH 055/335] chore: Update crates --- Cargo.lock | 30 +++++++++++++++--------------- lib/serde_sjson | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1b4bce..dfce5a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,9 +147,9 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] @@ -1443,9 +1443,9 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" dependencies = [ "libc", "windows-sys 0.45.0", @@ -1551,9 +1551,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" [[package]] name = "libloading" @@ -2423,18 +2423,18 @@ checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "8cdd151213925e7f1ab45a9bbfb129316bd00799784b174b7cc7bcd16961c49e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "4fc80d722935453bcafdc2c9a73cd6fac4dc1938f0346035d84bf99fa9e33217" dependencies = [ "proc-macro2", "quote", @@ -2443,7 +2443,7 @@ dependencies = [ [[package]] name = "serde_sjson" -version = "0.2.4" +version = "1.0.0" dependencies = [ "nom 7.1.3", "nom_locate", @@ -2976,9 +2976,9 @@ checksum = "14706d2a800ee8ff38c1d3edb873cd616971ea59eb7c0d046bb44ef59b06a1ae" [[package]] name = "utf8parse" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "valuable" @@ -3214,9 +3214,9 @@ checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winnow" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95fb4ff192527911dd18eb138ac30908e7165b8944e528b6af93aa4c842d345" +checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" dependencies = [ "memchr", ] diff --git a/lib/serde_sjson b/lib/serde_sjson index 81213f7..6e413b7 160000 --- a/lib/serde_sjson +++ b/lib/serde_sjson @@ -1 +1 @@ -Subproject commit 81213f792767ddf1e7be60b066c87f7b137ca0a7 +Subproject commit 6e413b7bf5fde09ca66bfcee1394443264c99ab1 From 6f8130828f4269c65d158b5a684202cf1320da93 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Mar 2023 20:27:36 +0100 Subject: [PATCH 056/335] chore: Name workspace members explicitly This makes switching between branches that introduce new crates easier. During switching, Git sometimes leaves directories in the tree, e.g. submodules, and those then produce issues when trying to compile in a branch that doesn't use that workspace member. --- Cargo.toml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index aef4708..6478737 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,14 @@ [workspace] resolver = "2" -members = ["crates/*", "lib/*"] +members = [ + "crates/dtmt", + "crates/dtmm", + "lib/dtmt-shared", + "lib/oodle-sys", + "lib/sdk", + "lib/serde_sjson", + "lib/steamlocate-rs", +] [profile.release] strip = "debuginfo" From acb1dbbe0f7bf5710ea0212650de6cbde92fe9e1 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Mar 2023 20:39:37 +0100 Subject: [PATCH 057/335] fix(dtmm): Fix the enabled checkbox in the mod list --- crates/dtmm/src/ui/window/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index c8d19a8..2506810 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -126,7 +126,7 @@ fn build_mod_list() -> impl Widget { }, |state, infos| { infos.into_iter().for_each(|(i, new, _)| { - if Data::same(&state.mods.get(i).cloned(), &Some(new.clone())) { + if !Data::same(&state.mods.get(i).cloned(), &Some(new.clone())) { state.mods.set(i, new); } }); From 705bc13c7b7684fc9993a70528ae22934596fc76 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Mar 2023 21:57:10 +0100 Subject: [PATCH 058/335] fix(dtmm): Fix mod title in details pane on Windows While it didn't work anyways, the center alignment somehow prevented the label from showing at all on Windows. --- crates/dtmm/src/ui/window/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 2506810..2b5d42f 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -8,7 +8,7 @@ use druid::widget::{ use druid::{lens, Data, ImageBuf, LifeCycleCtx}; use druid::{ Color, FileDialogOptions, FileSpec, FontDescriptor, FontFamily, Key, LensExt, SingleUse, - TextAlignment, Widget, WidgetExt, WindowDesc, WindowId, + Widget, WidgetExt, WindowDesc, WindowId, }; use lazy_static::lazy_static; @@ -216,7 +216,6 @@ fn build_mod_details_info() -> impl Widget { Maybe::new( || { let name = Label::raw() - .with_text_alignment(TextAlignment::Center) .with_text_size(24.) // Force the label to take up the entire details' pane width, // so that we can center-align it. From 4c33741b037e0c38fbe63507ade1c030606dfe00 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Mar 2023 17:25:09 +0100 Subject: [PATCH 059/335] feat(dtmm): Implement gruvbox dark theme --- .gitmodules | 3 + Cargo.lock | 312 +++++++++++++++++++++++++++- crates/dtmm/Cargo.toml | 3 +- crates/dtmm/assets/icons | 1 + crates/dtmm/src/main.rs | 5 +- crates/dtmm/src/ui/theme.rs | 4 - crates/dtmm/src/ui/theme/colors.rs | 99 +++++++++ crates/dtmm/src/ui/theme/icons.rs | 1 + crates/dtmm/src/ui/theme/keys.rs | 13 ++ crates/dtmm/src/ui/theme/mod.rs | 30 +++ crates/dtmm/src/ui/widget/button.rs | 113 ++++++++++ crates/dtmm/src/ui/widget/mod.rs | 1 + crates/dtmm/src/ui/window/main.rs | 88 ++++---- 13 files changed, 627 insertions(+), 46 deletions(-) create mode 160000 crates/dtmm/assets/icons delete mode 100644 crates/dtmm/src/ui/theme.rs create mode 100644 crates/dtmm/src/ui/theme/colors.rs create mode 100644 crates/dtmm/src/ui/theme/icons.rs create mode 100644 crates/dtmm/src/ui/theme/keys.rs create mode 100644 crates/dtmm/src/ui/theme/mod.rs create mode 100644 crates/dtmm/src/ui/widget/button.rs diff --git a/.gitmodules b/.gitmodules index 5741e15..1c9c3c2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/steamlocate-rs"] path = lib/steamlocate-rs url = git@github.com:sclu1034/steamlocate-rs.git +[submodule "crates/dtmm/assets/icons"] + path = crates/dtmm/assets/icons + url = https://github.com/tabler/tabler-icons diff --git a/Cargo.lock b/Cargo.lock index dfce5a3..a029489 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,12 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + [[package]] name = "arrayvec" version = "0.5.2" @@ -118,6 +124,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64ct" version = "1.6.0" @@ -411,6 +423,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colors-transform" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9226dbc05df4fb986f48d730b001532580883c4c06c5d1c213f4b34c1c157178" + [[package]] name = "confy" version = "0.5.1" @@ -580,6 +598,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "data-url" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" + [[package]] name = "digest" version = "0.10.6" @@ -666,11 +690,14 @@ dependencies = [ "fnv", "im", "instant", + "resvg", + "tiny-skia", "tracing", "tracing-subscriber", "tracing-wasm", "unic-langid", "unicode-segmentation", + "usvg", "xi-unicode", ] @@ -723,6 +750,7 @@ dependencies = [ "bitflags", "clap", "color-eyre", + "colors-transform", "confy", "druid", "dtmt-shared", @@ -911,6 +939,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + [[package]] name = "fluent-bundle" version = "0.15.2" @@ -951,6 +985,27 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "fontconfig-parser" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ab2e12762761366dcb876ab8b6e0cfa4797ddcd890575919f008b5ba655672a" +dependencies = [ + "roxmltree 0.18.0", +] + +[[package]] +name = "fontdb" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52186a39c335aa6f79fc0bf1c3cf854870b6ad4e50a7bb8a59b4ba1331f478a" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "ttf-parser", +] + [[package]] name = "foreign-types" version = "0.3.2" @@ -1150,6 +1205,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "gif" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.27.2" @@ -1533,6 +1598,15 @@ dependencies = [ "libc", ] +[[package]] +name = "kurbo" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449" +dependencies = [ + "arrayvec 0.7.2", +] + [[package]] name = "kurbo" version = "0.9.1" @@ -1621,6 +1695,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.6.5" @@ -2053,6 +2136,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "piet" version = "0.6.2" @@ -2060,7 +2149,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e381186490a3e2017a506d62b759ea8eaf4be14666b13ed53973e8ae193451b1" dependencies = [ "image", - "kurbo", + "kurbo 0.9.1", "unic-bidi", ] @@ -2256,6 +2345,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "rctree" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" + [[package]] name = "redox_syscall" version = "0.2.16" @@ -2302,6 +2397,51 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +[[package]] +name = "resvg" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea0337740f86c70141e7596d81c2e76c0cd3726dbcee053ac18d0ec45101f8e" +dependencies = [ + "gif", + "jpeg-decoder", + "log", + "pico-args", + "png", + "rgb", + "svgfilters", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "roxmltree" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9de9831a129b122e7e61f242db509fa9d0838008bf0b29bb0624669edfe48a" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "roxmltree" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f595a457b6b8c6cda66a48503e92ee8d19342f905948f29c383200ec9eb1d8" +dependencies = [ + "xmlparser", +] + [[package]] name = "rustc-demangle" version = "0.1.21" @@ -2337,6 +2477,22 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "rustybuzz" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab9e34ecf6900625412355a61bda0bd68099fe674de707c67e5e4aed2c05e489" +dependencies = [ + "bitflags", + "bytemuck", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-general-category", + "unicode-script", +] + [[package]] name = "rustyline" version = "9.1.2" @@ -2490,6 +2646,21 @@ dependencies = [ "libc", ] +[[package]] +name = "simplecss" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + [[package]] name = "sized-chunks" version = "0.6.5" @@ -2557,6 +2728,15 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" +[[package]] +name = "strict-num" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df65f20698aeed245efdde3628a6b559ea1239bbb871af1b6e3b58c413b2bd1" +dependencies = [ + "float-cmp", +] + [[package]] name = "string_template" version = "0.2.1" @@ -2587,6 +2767,25 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +[[package]] +name = "svgfilters" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "639abcebc15fdc2df179f37d6f5463d660c1c79cd552c12343a4600827a04bce" +dependencies = [ + "float-cmp", + "rgb", +] + +[[package]] +name = "svgtypes" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22975e8a2bac6a76bb54f898a6b18764633b00e780330f0b689f65afb3975564" +dependencies = [ + "siphasher", +] + [[package]] name = "syn" version = "1.0.109" @@ -2692,6 +2891,31 @@ dependencies = [ "time-core", ] +[[package]] +name = "tiny-skia" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfef3412c6975196fdfac41ef232f910be2bb37b9dd3313a49a1a6bc815a5bdb" +dependencies = [ + "arrayref", + "arrayvec 0.7.2", + "bytemuck", + "cfg-if", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b5edac058fc98f51c935daea4d805b695b38e2f151241cad125ade2a2ac20d" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tinystr" version = "0.7.1" @@ -2851,6 +3075,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ttf-parser" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" + [[package]] name = "type-map" version = "0.4.0" @@ -2950,24 +3180,86 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicode-bidi" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c" + +[[package]] +name = "unicode-bidi-mirroring" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" + +[[package]] +name = "unicode-ccc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" + +[[package]] +name = "unicode-general-category" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7" + [[package]] name = "unicode-ident" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +[[package]] +name = "unicode-script" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" + [[package]] name = "unicode-segmentation" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + [[package]] name = "unicode-width" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "usvg" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585bb2d87c8fd6041a479dea01479dcf9094e61b5f9af221606927e61a2bd939" +dependencies = [ + "base64", + "data-url", + "flate2", + "fontdb", + "kurbo 0.8.3", + "log", + "pico-args", + "rctree", + "roxmltree 0.15.1", + "rustybuzz", + "simplecss", + "siphasher", + "strict-num", + "svgtypes", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + [[package]] name = "utf16_lit" version = "2.0.2" @@ -3100,6 +3392,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + [[package]] name = "winapi" version = "0.3.9" @@ -3245,6 +3543,18 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" +[[package]] +name = "xmlparser" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" + +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + [[package]] name = "zip" version = "0.6.4" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 05db4e0..2265229 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -10,7 +10,7 @@ bitflags = "1.3.2" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } color-eyre = "0.6.2" confy = "0.5.1" -druid = { git = "https://github.com/linebender/druid.git", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp"] } +druid = { git = "https://github.com/linebender/druid.git", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] } dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } futures = "0.3.25" oodle-sys = { path = "../../lib/oodle-sys", version = "*" } @@ -27,3 +27,4 @@ path-slash = "0.2.1" time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] } strip-ansi-escapes = "0.1.1" lazy_static = "1.4.0" +colors-transform = "0.2.11" diff --git a/crates/dtmm/assets/icons b/crates/dtmm/assets/icons new file mode 160000 index 0000000..74838de --- /dev/null +++ b/crates/dtmm/assets/icons @@ -0,0 +1 @@ +Subproject commit 74838ded9980b6f134bb6f7edcf916cca4a2d97f diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index c3b8cb5..ce8c67f 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -20,6 +20,7 @@ use crate::controller::app::load_mods; use crate::controller::worker::work_thread; use crate::state::ACTION_SHOW_ERROR_DIALOG; use crate::state::{Delegate, State}; +use crate::ui::theme; mod controller; mod state; @@ -64,7 +65,9 @@ fn main() -> Result<()> { let (action_tx, action_rx) = tokio::sync::mpsc::unbounded_channel(); let delegate = Delegate::new(action_tx); - let launcher = AppLauncher::with_window(ui::window::main::new()).delegate(delegate); + let launcher = AppLauncher::with_window(ui::window::main::new()) + .delegate(delegate) + .configure_env(theme::set_theme_env); let event_sink = launcher.get_external_handle(); diff --git a/crates/dtmm/src/ui/theme.rs b/crates/dtmm/src/ui/theme.rs deleted file mode 100644 index 7658f3f..0000000 --- a/crates/dtmm/src/ui/theme.rs +++ /dev/null @@ -1,4 +0,0 @@ -use druid::{Color, Insets}; - -pub const TOP_BAR_BACKGROUND_COLOR: Color = Color::rgba8(255, 255, 255, 50); -pub const TOP_BAR_INSETS: Insets = Insets::uniform(5.0); diff --git a/crates/dtmm/src/ui/theme/colors.rs b/crates/dtmm/src/ui/theme/colors.rs new file mode 100644 index 0000000..cc86781 --- /dev/null +++ b/crates/dtmm/src/ui/theme/colors.rs @@ -0,0 +1,99 @@ +use colors_transform::Color as _; +use colors_transform::Rgb; +use druid::Color; + +pub use gruvbox_dark::*; + +macro_rules! make_color { + ($name:ident, $r:literal, $g:literal, $b:literal, $a:literal) => { + pub const $name: Color = Color::rgba8($r, $g, $b, $a); + }; + ($name:ident, $r:literal, $g:literal, $b:literal) => { + pub const $name: Color = Color::rgb8($r, $g, $b); + }; + ($name:ident, $col:expr) => { + pub const $name: Color = $col; + }; +} + +make_color!(TOP_BAR_BACKGROUND_COLOR, COLOR_BG1); + +#[allow(dead_code)] +pub mod gruvbox_dark { + use druid::Color; + + make_color!(COLOR_BG0_H, 0x1d, 0x20, 0x21); + make_color!(COLOR_BG0_S, 0x32, 0x20, 0x2f); + make_color!(COLOR_BG0, 0x28, 0x28, 0x28); + make_color!(COLOR_BG1, 0x3c, 0x38, 0x36); + make_color!(COLOR_BG2, 0x50, 0x49, 0x45); + make_color!(COLOR_BG3, 0x66, 0x5c, 0x54); + make_color!(COLOR_BG4, 0x7c, 0x6f, 0x64); + + make_color!(COLOR_FG0, 0xfb, 0xf1, 0xc7); + make_color!(COLOR_FG1, 0xeb, 0xdb, 0xb2); + make_color!(COLOR_FG2, 0xd5, 0xc4, 0xa1); + make_color!(COLOR_FG3, 0xbd, 0xae, 0x93); + make_color!(COLOR_FG4, 0xa8, 0x99, 0x84); + + make_color!(COLOR_BG, COLOR_BG0); + make_color!(COLOR_GRAY_LIGHT, 0x92, 0x83, 0x74); + + make_color!(COLOR_RED_DARK, 0xcc, 0x24, 0x1d); + make_color!(COLOR_RED_LIGHT, 0xfb, 0x49, 0x34); + + make_color!(COLOR_GREEN_DARK, 0x98, 0x97, 0x1a); + make_color!(COLOR_GREEN_LIGHT, 0xb8, 0xbb, 0x26); + + make_color!(COLOR_YELLOW_DARK, 0xd7, 0x99, 0x21); + make_color!(COLOR_YELLOW_LIGHT, 0xfa, 0xbd, 0x2f); + + make_color!(COLOR_BLUE_DARK, 0x45, 0x85, 0x88); + make_color!(COLOR_BLUE_LIGHT, 0x83, 0xa5, 0x98); + + make_color!(COLOR_PURPLE_DARK, 0xb1, 0x26, 0x86); + make_color!(COLOR_PURPLE_LIGHT, 0xd3, 0x86, 0x9b); + + make_color!(COLOR_AQUA_DARK, 0x68, 0x9d, 0x6a); + make_color!(COLOR_AQUA_LIGHT, 0x8e, 0xc0, 0x7c); + + make_color!(COLOR_GRAY_DARK, 0xa8, 0x99, 0x84); + make_color!(COLOR_FG, COLOR_FG1); + + make_color!(COLOR_ORANGE_DARK, 0xd6, 0x5d, 0x0e); + make_color!(COLOR_ORANGE_LIGHT, 0xfe, 0x80, 0x19); + + make_color!(COLOR_ACCENT, COLOR_RED_LIGHT); + make_color!(COLOR_ACCENT_FG, COLOR_BG0_H); +} + +pub trait ColorExt { + fn lighten(&self, fac: f32) -> Self; + fn darken(&self, fac: f32) -> Self; +} + +impl ColorExt for Color { + fn lighten(&self, fac: f32) -> Self { + let (r, g, b, a) = self.as_rgba(); + let rgb = Rgb::from(r as f32, g as f32, b as f32); + let rgb = rgb.lighten(fac); + Self::rgba( + rgb.get_red() as f64, + rgb.get_green() as f64, + rgb.get_blue() as f64, + a, + ) + } + + fn darken(&self, fac: f32) -> Self { + let (r, g, b, a) = self.as_rgba(); + let rgb = Rgb::from(r as f32, g as f32, b as f32); + let rgb = rgb.lighten(-1. * fac); + Self::rgba( + rgb.get_red() as f64, + rgb.get_green() as f64, + rgb.get_blue() as f64, + a, + ) + } +} diff --git a/crates/dtmm/src/ui/theme/icons.rs b/crates/dtmm/src/ui/theme/icons.rs new file mode 100644 index 0000000..a947871 --- /dev/null +++ b/crates/dtmm/src/ui/theme/icons.rs @@ -0,0 +1 @@ +pub static ALERT_CIRCLE: &str = include_str!("../../../assets/icons/icons/alert-circle.svg"); diff --git a/crates/dtmm/src/ui/theme/keys.rs b/crates/dtmm/src/ui/theme/keys.rs new file mode 100644 index 0000000..9d4120b --- /dev/null +++ b/crates/dtmm/src/ui/theme/keys.rs @@ -0,0 +1,13 @@ +use druid::{Color, Insets, Key}; + +pub const KEY_BUTTON_BG: Key = Key::new("dtmm.button.bg"); +pub const KEY_BUTTON_BG_HOT: Key = Key::new("dtmm.button.bg-hot"); +pub const KEY_BUTTON_BG_ACTIVE: Key = Key::new("dtmm.button.bg-active"); +pub const KEY_BUTTON_BG_DISABLED: Key = Key::new("dtmm.button.bg-disabled"); + +pub const KEY_BUTTON_FG: Key = Key::new("dtmm.button.fg"); +pub const KEY_BUTTON_FG_DISABLED: Key = Key::new("dtmm.button.fg-disabled"); + +pub const KEY_BUTTON_PADDING: Key = Key::new("dtmm.button.padding"); + +pub const KEY_MOD_LIST_ITEM_BG_COLOR: Key = Key::new("dtmm.mod-list.item.background-color"); diff --git a/crates/dtmm/src/ui/theme/mod.rs b/crates/dtmm/src/ui/theme/mod.rs new file mode 100644 index 0000000..45a7c3a --- /dev/null +++ b/crates/dtmm/src/ui/theme/mod.rs @@ -0,0 +1,30 @@ +use druid::{Env, Insets}; + +use crate::state::State; + +mod colors; +pub mod icons; +pub mod keys; + +pub use colors::*; + +pub const TOP_BAR_INSETS: Insets = Insets::uniform(5.0); +pub const DISABLED_ALPHA: f64 = 0.65; + +pub(crate) fn set_theme_env(env: &mut Env, _: &State) { + env.set(druid::theme::TEXT_COLOR, COLOR_FG); + env.set(druid::theme::BUTTON_BORDER_RADIUS, 2.); + env.set(keys::KEY_BUTTON_BG, COLOR_ACCENT); + env.set(keys::KEY_BUTTON_BG_HOT, COLOR_ACCENT.darken(0.03)); + env.set(keys::KEY_BUTTON_BG_ACTIVE, COLOR_ACCENT.darken(0.1)); + env.set( + keys::KEY_BUTTON_BG_DISABLED, + COLOR_ACCENT.with_alpha(DISABLED_ALPHA), + ); + env.set(keys::KEY_BUTTON_FG, COLOR_ACCENT_FG); + env.set( + keys::KEY_BUTTON_FG_DISABLED, + COLOR_ACCENT_FG.with_alpha(DISABLED_ALPHA), + ); + env.set(keys::KEY_BUTTON_PADDING, Insets::uniform_xy(8., 2.)); +} diff --git a/crates/dtmm/src/ui/widget/button.rs b/crates/dtmm/src/ui/widget/button.rs new file mode 100644 index 0000000..08e1dec --- /dev/null +++ b/crates/dtmm/src/ui/widget/button.rs @@ -0,0 +1,113 @@ +use druid::widget::prelude::*; +use druid::widget::{Click, ControllerHost, Label, LabelText}; +use druid::WidgetPod; +use druid::{Affine, WidgetExt}; + +use crate::ui::theme; + +pub struct Button { + inner: WidgetPod>>, + inner_size: Size, +} + +impl Button { + pub fn new(inner: impl Widget + 'static) -> Self { + let inner = inner.env_scope(|env, _| { + env.set( + druid::theme::TEXT_COLOR, + env.get(theme::keys::KEY_BUTTON_FG), + ); + env.set( + druid::theme::DISABLED_TEXT_COLOR, + env.get(theme::keys::KEY_BUTTON_FG_DISABLED), + ); + }); + let inner = WidgetPod::new(inner).boxed(); + Self { + inner, + inner_size: Size::ZERO, + } + } + + pub fn with_label(text: impl Into>) -> Self { + let inner = Label::new(text); + Self::new(inner) + } + + pub fn on_click( + self, + f: impl Fn(&mut EventCtx, &mut T, &Env) + 'static, + ) -> ControllerHost> { + ControllerHost::new(self, Click::new(f)) + } +} + +impl Widget for Button { + fn event(&mut self, ctx: &mut EventCtx, event: &Event, _: &mut T, _: &Env) { + match event { + Event::MouseDown(_) if !ctx.is_disabled() => { + ctx.set_active(true); + ctx.request_paint(); + } + Event::MouseUp(_) => { + if ctx.is_active() && !ctx.is_disabled() { + ctx.request_paint(); + } + ctx.set_active(false); + } + _ => {} + } + } + + fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) { + if let LifeCycle::HotChanged(_) | LifeCycle::DisabledChanged(_) = event { + ctx.request_paint(); + } + self.inner.lifecycle(ctx, event, data, env); + } + + fn update(&mut self, ctx: &mut UpdateCtx, _: &T, data: &T, env: &Env) { + self.inner.update(ctx, data, env); + } + + fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size { + bc.debug_check("Button"); + + let padding = env.get(theme::keys::KEY_BUTTON_PADDING).size(); + let inner_bc = bc.shrink(padding).loosen(); + + self.inner_size = self.inner.layout(ctx, &inner_bc, data, env); + + bc.constrain(Size::new( + self.inner_size.width + padding.width, + self.inner_size.height + padding.height, + )) + } + + fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) { + let size = ctx.size(); + + let bg_color = if ctx.is_disabled() { + env.get(theme::keys::KEY_BUTTON_BG_DISABLED) + } else if ctx.is_hot() { + env.get(theme::keys::KEY_BUTTON_BG_HOT) + } else if ctx.is_active() { + env.get(theme::keys::KEY_BUTTON_BG_ACTIVE) + } else { + env.get(theme::keys::KEY_BUTTON_BG) + }; + + ctx.fill( + size.to_rect() + .to_rounded_rect(env.get(druid::theme::BUTTON_BORDER_RADIUS)), + &bg_color, + ); + + let inner_pos = (size.to_vec2() - self.inner_size.to_vec2()) / 2.; + + ctx.with_save(|ctx| { + ctx.transform(Affine::translate(inner_pos)); + self.inner.paint(ctx, data, env); + }); + } +} diff --git a/crates/dtmm/src/ui/widget/mod.rs b/crates/dtmm/src/ui/widget/mod.rs index ebb634e..3c8c08c 100644 --- a/crates/dtmm/src/ui/widget/mod.rs +++ b/crates/dtmm/src/ui/widget/mod.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use druid::text::Formatter; use druid::{Data, Widget}; +pub mod button; pub mod controller; pub trait ExtraWidgetExt: Widget + Sized + 'static {} diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 2b5d42f..4a72f02 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -1,14 +1,15 @@ +use std::str::FromStr; use std::sync::Arc; use druid::im::Vector; use druid::widget::{ - Button, Checkbox, CrossAxisAlignment, Flex, Image, Label, LineBreaking, List, - MainAxisAlignment, Maybe, Scroll, SizedBox, Split, TextBox, ViewSwitcher, + Checkbox, CrossAxisAlignment, Either, Flex, Image, Label, LineBreaking, List, + MainAxisAlignment, Maybe, Scroll, SizedBox, Split, Svg, SvgData, TextBox, ViewSwitcher, }; use druid::{lens, Data, ImageBuf, LifeCycleCtx}; use druid::{ - Color, FileDialogOptions, FileSpec, FontDescriptor, FontFamily, Key, LensExt, SingleUse, - Widget, WidgetExt, WindowDesc, WindowId, + Color, FileDialogOptions, FileSpec, FontDescriptor, FontFamily, LensExt, SingleUse, Widget, + WidgetExt, WindowDesc, WindowId, }; use lazy_static::lazy_static; @@ -18,6 +19,7 @@ use crate::state::{ ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT, }; use crate::ui::theme; +use crate::ui::widget::button::Button; use crate::ui::widget::controller::{ AutoScrollController, DirtyStateController, ImageLensController, }; @@ -31,8 +33,6 @@ const TITLE: &str = "Darktide Mod Manager"; const WINDOW_SIZE: (f64, f64) = (1080., 720.); const MOD_DETAILS_MIN_WIDTH: f64 = 325.; -const KEY_MOD_LIST_ITEM_BG_COLOR: Key = Key::new("dtmm.mod-list.item.background-color"); - pub(crate) fn new() -> WindowDesc { WindowDesc::new(build_window()) .title(TITLE) @@ -40,29 +40,34 @@ pub(crate) fn new() -> WindowDesc { } fn build_top_bar() -> impl Widget { - let mods_button = Button::new("Mods") + let mods_button = Button::with_label("Mods") .on_click(|_ctx, state: &mut State, _env| state.current_view = View::Mods); - let settings_button = Button::new("Settings").on_click(|_ctx, state: &mut State, _env| { - state.current_view = View::Settings; - }); + let settings_button = + Button::with_label("Settings").on_click(|_ctx, state: &mut State, _env| { + state.current_view = View::Settings; + }); let deploy_button = { - Button::dynamic(|state: &State, _| { - let mut s = String::new(); - if state.dirty { - s.push_str("! "); - } - s.push_str("Deploy Mods"); - s - }) - .on_click(|ctx, _state: &mut State, _env| { - ctx.submit_command(ACTION_START_DEPLOY); - }) - .disabled_if(|data, _| data.is_deployment_in_progress || data.is_reset_in_progress) + let icon = Svg::new(SvgData::from_str(theme::icons::ALERT_CIRCLE).expect("invalid SVG")) + .fix_height(druid::theme::TEXT_SIZE_NORMAL); + + let inner = Either::new( + |state: &State, _| state.dirty, + Flex::row() + .with_child(icon) + .with_spacer(1.) + .with_child(Label::new("Deploy Mods")), + Label::new("Deploy Mods"), + ); + Button::new(inner) + .on_click(|ctx, _state: &mut State, _env| { + ctx.submit_command(ACTION_START_DEPLOY); + }) + .disabled_if(|data, _| data.is_deployment_in_progress || data.is_reset_in_progress) }; - let reset_button = Button::new("Reset Game") + let reset_button = Button::with_label("Reset Game") .on_click(|ctx, _state: &mut State, _env| { ctx.submit_command(ACTION_START_RESET_DEPLOYMENT); }) @@ -102,15 +107,19 @@ fn build_mod_list() -> impl Widget { .with_child(checkbox) .with_child(name) .padding((5.0, 4.0)) - .background(KEY_MOD_LIST_ITEM_BG_COLOR) + .background(theme::keys::KEY_MOD_LIST_ITEM_BG_COLOR) .on_click(|ctx, (i, _, _), _env| ctx.submit_command(ACTION_SELECT_MOD.with(*i))) .env_scope(|env, (i, _, selected)| { if *selected { - env.set(KEY_MOD_LIST_ITEM_BG_COLOR, Color::NAVY); - } else if (i % 2) == 1 { - env.set(KEY_MOD_LIST_ITEM_BG_COLOR, Color::WHITE.with_alpha(0.05)); + env.set(theme::keys::KEY_MOD_LIST_ITEM_BG_COLOR, theme::COLOR_ACCENT); + env.set(druid::theme::TEXT_COLOR, theme::COLOR_ACCENT_FG); } else { - env.set(KEY_MOD_LIST_ITEM_BG_COLOR, Color::TRANSPARENT); + env.set(druid::theme::TEXT_COLOR, theme::COLOR_FG); + if (i % 2) == 1 { + env.set(theme::keys::KEY_MOD_LIST_ITEM_BG_COLOR, theme::COLOR_BG1); + } else { + env.set(theme::keys::KEY_MOD_LIST_ITEM_BG_COLOR, Color::TRANSPARENT); + } } }) }); @@ -140,35 +149,36 @@ fn build_mod_list() -> impl Widget { } fn build_mod_details_buttons() -> impl Widget { - let button_move_up = Button::new("Move Up") + let button_move_up = Button::with_label("Move Up") .on_click(|ctx, _state, _env| ctx.submit_command(ACTION_SELECTED_MOD_UP)) .disabled_if(|state: &State, _env: &druid::Env| !state.can_move_mod_up()); - let button_move_down = Button::new("Move Down") + let button_move_down = Button::with_label("Move Down") .on_click(|ctx, _state, _env| ctx.submit_command(ACTION_SELECTED_MOD_DOWN)) .disabled_if(|state: &State, _env: &druid::Env| !state.can_move_mod_down()); let button_toggle_mod = Maybe::new( || { - Button::dynamic(|enabled, _env| { + let inner = Label::dynamic(|enabled, _env| { if *enabled { "Disable Mod".into() } else { "Enable Mod".into() } - }) - .on_click(|_ctx, enabled: &mut bool, _env| { - *enabled = !(*enabled); - }) - .lens(ModInfo::enabled.in_arc()) + }); + Button::new(inner) + .on_click(|_ctx, enabled: &mut bool, _env| { + *enabled = !(*enabled); + }) + .lens(ModInfo::enabled.in_arc()) }, // TODO: Gray out - || Button::new("Enable Mod"), + || Button::with_label("Enable Mod"), ) .disabled_if(|info: &Option>, _env: &druid::Env| info.is_none()) .lens(State::selected_mod); - let button_add_mod = Button::new("Add Mod").on_click(|ctx, _state: &mut State, _env| { + let button_add_mod = Button::with_label("Add Mod").on_click(|ctx, _state: &mut State, _env| { let zip = FileSpec::new("Zip file", &["zip"]); let opts = FileDialogOptions::new() .allowed_types(vec![zip]) @@ -179,7 +189,7 @@ fn build_mod_details_buttons() -> impl Widget { ctx.submit_command(druid::commands::SHOW_OPEN_PANEL.with(opts)) }); - let button_delete_mod = Button::new("Delete Mod") + let button_delete_mod = Button::with_label("Delete Mod") .on_click(|ctx, data: &mut Option>, _env| { if let Some(info) = data { ctx.submit_command( From c38909db22d163d9a8a1539286de560c2d63a9a6 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 Mar 2023 16:24:08 +0100 Subject: [PATCH 060/335] feat(dtmm): Add section borders This implements a new container widget that allows separate widths and colors for each border side. --- crates/dtmm/src/ui/theme/mod.rs | 3 + crates/dtmm/src/ui/widget/border.rs | 197 ++++++++++++++++++++++++++++ crates/dtmm/src/ui/widget/mod.rs | 1 + crates/dtmm/src/ui/window/main.rs | 42 ++++-- 4 files changed, 234 insertions(+), 9 deletions(-) create mode 100644 crates/dtmm/src/ui/widget/border.rs diff --git a/crates/dtmm/src/ui/theme/mod.rs b/crates/dtmm/src/ui/theme/mod.rs index 45a7c3a..7f93524 100644 --- a/crates/dtmm/src/ui/theme/mod.rs +++ b/crates/dtmm/src/ui/theme/mod.rs @@ -13,7 +13,10 @@ pub const DISABLED_ALPHA: f64 = 0.65; pub(crate) fn set_theme_env(env: &mut Env, _: &State) { env.set(druid::theme::TEXT_COLOR, COLOR_FG); + env.set(druid::theme::SCROLLBAR_COLOR, COLOR_FG); + env.set(druid::theme::BORDER_LIGHT, COLOR_FG); env.set(druid::theme::BUTTON_BORDER_RADIUS, 2.); + env.set(keys::KEY_BUTTON_BG, COLOR_ACCENT); env.set(keys::KEY_BUTTON_BG_HOT, COLOR_ACCENT.darken(0.03)); env.set(keys::KEY_BUTTON_BG_ACTIVE, COLOR_ACCENT.darken(0.1)); diff --git a/crates/dtmm/src/ui/widget/border.rs b/crates/dtmm/src/ui/widget/border.rs new file mode 100644 index 0000000..2ca7cdb --- /dev/null +++ b/crates/dtmm/src/ui/widget/border.rs @@ -0,0 +1,197 @@ +use druid::kurbo::Line; +use druid::widget::prelude::*; +use druid::{Color, KeyOrValue, Point, WidgetPod}; + +pub struct Border { + inner: WidgetPod>>, + color: BorderColor, + width: BorderWidths, + // corner_radius: KeyOrValue, +} + +impl Border { + pub fn new(inner: impl Widget + 'static) -> Self { + let inner = WidgetPod::new(inner).boxed(); + Self { + inner, + color: Color::TRANSPARENT.into(), + width: 0f64.into(), + } + } + + pub fn set_color(&mut self, color: impl Into>) { + self.color = BorderColor::Uniform(color.into()); + } + + pub fn with_color(mut self, color: impl Into>) -> Self { + self.set_color(color); + self + } + + pub fn set_bottom_border(&mut self, width: impl Into>) { + self.width.bottom = width.into(); + } + + pub fn with_bottom_border(mut self, width: impl Into>) -> Self { + self.set_bottom_border(width); + self + } + + pub fn set_top_border(&mut self, width: impl Into>) { + self.width.top = width.into(); + } + + pub fn with_top_border(mut self, width: impl Into>) -> Self { + self.set_top_border(width); + self + } +} + +impl Widget for Border { + fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) { + self.inner.event(ctx, event, data, env) + } + + fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) { + self.inner.lifecycle(ctx, event, data, env); + } + + fn update(&mut self, ctx: &mut UpdateCtx, _: &T, data: &T, env: &Env) { + self.inner.update(ctx, data, env); + } + + fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size { + bc.debug_check("Border"); + + let (left, top, right, bottom) = self.width.resolve(env); + + let inner_bc = bc.shrink((left + right, top + bottom)); + let inner_size = self.inner.layout(ctx, &inner_bc, data, env); + + let origin = Point::new(left, top); + self.inner.set_origin(ctx, origin); + + let size = Size::new( + inner_size.width + left + right, + inner_size.height + top + bottom, + ); + + let insets = self.inner.compute_parent_paint_insets(size); + ctx.set_paint_insets(insets); + + let baseline_offset = self.inner.baseline_offset(); + if baseline_offset > 0. { + ctx.set_baseline_offset(baseline_offset + bottom); + } + + size + } + + fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) { + let size = ctx.size(); + let (left, top, right, bottom) = self.width.resolve(env); + let (col_left, col_top, col_right, col_bottom) = self.color.resolve(env); + + self.inner.paint(ctx, data, env); + + // There's probably a more elegant way to create the various `Line`s, but this works for now. + // The important bit is to move each line inwards by half each side's border width. Otherwise + // it would draw hald of the border outside of the widget's boundary. + + if left > 0. { + ctx.stroke( + Line::new((left / 2., top / 2.), (left / 2., size.height)), + &col_left, + left, + ); + } + + if top > 0. { + ctx.stroke( + Line::new((left / 2., top / 2.), (size.width - (right / 2.), top / 2.)), + &col_top, + top, + ); + } + + if right > 0. { + ctx.stroke( + Line::new( + (size.width - (right / 2.), top / 2.), + (size.width - (right / 2.), size.height - (bottom / 2.)), + ), + &col_right, + right, + ); + } + + if bottom > 0. { + ctx.stroke( + Line::new( + (left / 2., size.height - (bottom / 2.)), + (size.width - (right / 2.), size.height - (bottom / 2.)), + ), + &col_bottom, + bottom, + ); + } + } +} + +#[derive(Clone, Debug)] +pub enum BorderColor { + Uniform(KeyOrValue), + // Individual { + // left: KeyOrValue, + // top: KeyOrValue, + // right: KeyOrValue, + // bottom: KeyOrValue, + // }, +} + +impl BorderColor { + pub fn resolve(&self, env: &Env) -> (Color, Color, Color, Color) { + match self { + Self::Uniform(val) => { + let color = val.resolve(env); + (color, color, color, color) + } + } + } +} + +impl From for BorderColor { + fn from(value: Color) -> Self { + Self::Uniform(value.into()) + } +} + +#[derive(Clone, Debug)] +pub struct BorderWidths { + pub left: KeyOrValue, + pub top: KeyOrValue, + pub right: KeyOrValue, + pub bottom: KeyOrValue, +} + +impl From for BorderWidths { + fn from(value: f64) -> Self { + Self { + left: value.into(), + top: value.into(), + right: value.into(), + bottom: value.into(), + } + } +} + +impl BorderWidths { + pub fn resolve(&self, env: &Env) -> (f64, f64, f64, f64) { + ( + self.left.resolve(env), + self.top.resolve(env), + self.right.resolve(env), + self.bottom.resolve(env), + ) + } +} diff --git a/crates/dtmm/src/ui/widget/mod.rs b/crates/dtmm/src/ui/widget/mod.rs index 3c8c08c..05c91c7 100644 --- a/crates/dtmm/src/ui/widget/mod.rs +++ b/crates/dtmm/src/ui/widget/mod.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use druid::text::Formatter; use druid::{Data, Widget}; +pub mod border; pub mod button; pub mod controller; diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 4a72f02..8fe62d4 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -18,7 +18,8 @@ use crate::state::{ ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE, ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT, }; -use crate::ui::theme; +use crate::ui::theme::{self, ColorExt}; +use crate::ui::widget::border::Border; use crate::ui::widget::button::Button; use crate::ui::widget::controller::{ AutoScrollController, DirtyStateController, ImageLensController, @@ -73,7 +74,7 @@ fn build_top_bar() -> impl Widget { }) .disabled_if(|data, _| data.is_deployment_in_progress || data.is_reset_in_progress); - Flex::row() + let bar = Flex::row() .must_fill_main_axis(true) .main_axis_alignment(MainAxisAlignment::SpaceBetween) .with_child( @@ -89,14 +90,29 @@ fn build_top_bar() -> impl Widget { .with_child(reset_button), ) .padding(theme::TOP_BAR_INSETS) - .background(theme::TOP_BAR_BACKGROUND_COLOR) - // TODO: Add bottom border. Need a custom widget for that, as the built-in only provides - // uniform borders on all sides + .background(theme::TOP_BAR_BACKGROUND_COLOR); + + Border::new(bar) + .with_color(theme::COLOR_FG2) + .with_bottom_border(1.) } fn build_mod_list() -> impl Widget { let list = List::new(|| { let checkbox = Checkbox::new("") + .env_scope(|env, selected| { + env.set(druid::theme::BORDER_DARK, theme::COLOR_BG3); + env.set(druid::theme::BORDER_LIGHT, theme::COLOR_BG3); + env.set(druid::theme::TEXT_COLOR, theme::COLOR_ACCENT_FG); + + if *selected { + env.set(druid::theme::BACKGROUND_DARK, theme::COLOR_ACCENT); + env.set(druid::theme::BACKGROUND_LIGHT, theme::COLOR_ACCENT); + } else { + env.set(druid::theme::BACKGROUND_DARK, Color::TRANSPARENT); + env.set(druid::theme::BACKGROUND_LIGHT, Color::TRANSPARENT); + } + }) .lens(lens!((usize, Arc, bool), 1).then(ModInfo::enabled.in_arc())); let name = @@ -112,13 +128,17 @@ fn build_mod_list() -> impl Widget { .env_scope(|env, (i, _, selected)| { if *selected { env.set(theme::keys::KEY_MOD_LIST_ITEM_BG_COLOR, theme::COLOR_ACCENT); - env.set(druid::theme::TEXT_COLOR, theme::COLOR_ACCENT_FG); + env.set( + druid::theme::TEXT_COLOR, + theme::COLOR_ACCENT_FG.darken(0.05), + ); } else { env.set(druid::theme::TEXT_COLOR, theme::COLOR_FG); + if (i % 2) == 1 { env.set(theme::keys::KEY_MOD_LIST_ITEM_BG_COLOR, theme::COLOR_BG1); } else { - env.set(theme::keys::KEY_MOD_LIST_ITEM_BG_COLOR, Color::TRANSPARENT); + env.set(theme::keys::KEY_MOD_LIST_ITEM_BG_COLOR, theme::COLOR_BG); } } }) @@ -298,7 +318,7 @@ fn build_mod_details() -> impl Widget { .cross_axis_alignment(CrossAxisAlignment::Start) .main_axis_alignment(MainAxisAlignment::SpaceBetween) .with_flex_child(build_mod_details_info(), 1.0) - .with_child(build_mod_details_buttons().padding(4.)) + .with_child(build_mod_details_buttons().padding((4., 4., 4., 8.))) } fn build_view_mods() -> impl Widget { @@ -373,7 +393,11 @@ fn build_log_view() -> impl Widget { .vertical() .controller(AutoScrollController); - SizedBox::new(label).expand_width().height(128.0) + let inner = Border::new(label) + .with_color(theme::COLOR_FG2) + .with_top_border(1.); + + SizedBox::new(inner).expand_width().height(128.0) } fn build_window() -> impl Widget { From 01f1e1724af29d944d9d13686ec2a01b0d9ff2ec Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 Mar 2023 16:45:13 +0100 Subject: [PATCH 061/335] feat(dtmm): Adjust icon spacing --- crates/dtmm/src/ui/window/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 8fe62d4..d4244fe 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -57,7 +57,7 @@ fn build_top_bar() -> impl Widget { |state: &State, _| state.dirty, Flex::row() .with_child(icon) - .with_spacer(1.) + .with_spacer(3.) .with_child(Label::new("Deploy Mods")), Label::new("Deploy Mods"), ); From 13d36c4947358d8e613d4b05701bd2dd66cee367 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 6 Mar 2023 13:44:07 +0100 Subject: [PATCH 062/335] feat(dtmm): Add Nexus API key setting --- crates/dtmm/src/main.rs | 1 + crates/dtmm/src/state/data.rs | 9 ++++++++- crates/dtmm/src/state/delegate.rs | 2 ++ crates/dtmm/src/ui/widget/controller.rs | 2 +- crates/dtmm/src/ui/window/main.rs | 12 +++++++++++- crates/dtmm/src/util/config.rs | 4 ++++ 6 files changed, 27 insertions(+), 3 deletions(-) diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index ce8c67f..de8ca8d 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -91,6 +91,7 @@ fn main() -> Result<()> { config.path, game_dir.unwrap_or_default(), config.data_dir.unwrap_or_default(), + config.nexus_api_key.unwrap_or_default(), ); state.mods = load_mods(state.get_mod_dir(), config.mod_order.iter()) .wrap_err("Failed to load mods")?; diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 34ab92d..09e3a97 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -128,6 +128,7 @@ pub(crate) struct State { pub is_next_save_pending: bool, pub game_dir: Arc, pub data_dir: Arc, + pub nexus_api_key: Arc, pub log: Arc, #[lens(ignore)] @@ -145,7 +146,12 @@ impl State { #[allow(non_upper_case_globals)] pub const selected_mod: SelectedModLens = SelectedModLens; - pub fn new(config_path: PathBuf, game_dir: PathBuf, data_dir: PathBuf) -> Self { + pub fn new( + config_path: PathBuf, + game_dir: PathBuf, + data_dir: PathBuf, + nexus_api_key: String, + ) -> Self { let ctx = sdk::Context::new(); Self { @@ -161,6 +167,7 @@ impl State { config_path: Arc::new(config_path), game_dir: Arc::new(game_dir), data_dir: Arc::new(data_dir), + nexus_api_key: Arc::new(nexus_api_key), log: Arc::new(String::new()), windows: HashMap::new(), } diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index 73aa42d..1dbe2a2 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -56,6 +56,7 @@ pub(crate) struct ActionState { pub mod_dir: Arc, pub config_path: Arc, pub ctx: Arc, + pub nexus_api_key: Arc, } impl From for ActionState { @@ -67,6 +68,7 @@ impl From for ActionState { data_dir: state.data_dir, config_path: state.config_path, ctx: state.ctx, + nexus_api_key: state.nexus_api_key, } } } diff --git a/crates/dtmm/src/ui/widget/controller.rs b/crates/dtmm/src/ui/widget/controller.rs index a7ff2f1..45170d4 100644 --- a/crates/dtmm/src/ui/widget/controller.rs +++ b/crates/dtmm/src/ui/widget/controller.rs @@ -78,7 +78,7 @@ impl> Controller for DirtyStateController { data: &State, env: &Env, ) { - if compare_state_fields!(old_data, data, mods, game_dir, data_dir) { + if compare_state_fields!(old_data, data, mods, game_dir, data_dir, nexus_api_key) { ctx.submit_command(ACTION_START_SAVE_SETTINGS); } diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index d4244fe..67ab0e5 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -359,12 +359,22 @@ fn build_view_settings() -> impl Widget { ) .expand_width(); + let nexus_apy_key_setting = Flex::row() + .must_fill_main_axis(true) + .main_axis_alignment(MainAxisAlignment::Start) + .with_child(Label::new("Nexus API Key:")) + .with_default_spacer() + .with_flex_child(TextBox::new().expand_width().lens(State::nexus_api_key), 1.) + .expand_width(); + let content = Flex::column() .must_fill_main_axis(true) .cross_axis_alignment(CrossAxisAlignment::Start) .with_child(data_dir_setting) .with_default_spacer() - .with_child(game_dir_setting); + .with_child(game_dir_setting) + .with_default_spacer() + .with_child(nexus_apy_key_setting); SizedBox::new(content) .width(800.) diff --git a/crates/dtmm/src/util/config.rs b/crates/dtmm/src/util/config.rs index bf2a72b..c2f2045 100644 --- a/crates/dtmm/src/util/config.rs +++ b/crates/dtmm/src/util/config.rs @@ -28,6 +28,7 @@ impl<'a> From<&'a ModInfo> for LoadOrderEntrySerialize<'a> { pub(crate) struct ConfigSerialize<'a> { game_dir: &'a Path, data_dir: &'a Path, + nexus_api_key: &'a String, mod_order: Vec>, } @@ -36,6 +37,7 @@ impl<'a> From<&'a ActionState> for ConfigSerialize<'a> { Self { game_dir: &state.game_dir, data_dir: &state.data_dir, + nexus_api_key: &state.nexus_api_key, mod_order: state .mods .iter() @@ -58,6 +60,7 @@ pub(crate) struct Config { pub path: PathBuf, pub data_dir: Option, pub game_dir: Option, + pub nexus_api_key: Option, #[serde(default)] pub mod_order: Vec, } @@ -140,6 +143,7 @@ where path: default_path, data_dir: Some(get_default_data_dir()), game_dir: None, + nexus_api_key: None, mod_order: Vec::new(), }; From 5ca1ca350627105581e562fad5fb5ea2f0492663 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 7 Mar 2023 11:40:15 +0100 Subject: [PATCH 063/335] feat(nexusmods): Start API implementation --- lib/nexusmods/Cargo.toml | 19 ++++++ lib/nexusmods/src/lib.rs | 132 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 lib/nexusmods/Cargo.toml create mode 100644 lib/nexusmods/src/lib.rs diff --git a/lib/nexusmods/Cargo.toml b/lib/nexusmods/Cargo.toml new file mode 100644 index 0000000..93dbd78 --- /dev/null +++ b/lib/nexusmods/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "nexusmods" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lazy_static = "1.4.0" +reqwest = { version = "0.11.14" } +serde = { version = "1.0.152", features = ["derive"] } +serde_json = "1.0.94" +thiserror = "1.0.39" +time = { version = "0.3.20", features = ["serde"] } +tracing = "0.1.37" +url = "2.3.1" + +[dev-dependencies] +tokio = { version = "1.26.0", features = ["rt", "macros"] } diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs new file mode 100644 index 0000000..c8ea2a6 --- /dev/null +++ b/lib/nexusmods/src/lib.rs @@ -0,0 +1,132 @@ +use std::convert::Infallible; + +use lazy_static::lazy_static; +use reqwest::header::{HeaderMap, HeaderValue, InvalidHeaderValue}; +use reqwest::{Client, Url}; +use serde::ser::SerializeTuple; +use serde::{Deserialize, Serialize}; +use thiserror::Error; +use time::OffsetDateTime; + +// TODO: Add OS information +const USER_AGENT: &str = concat!("DTMM/", env!("CARGO_PKG_VERSION")); +const GAME_ID: &str = "warhammer40kdarktide"; + +lazy_static! { + static ref BASE_URL: Url = Url::parse("https://api.nexusmods.com/v1/").unwrap(); + static ref BASE_URL_GAME: Url = + Url::parse("https://api.nexusmods.com/v1/games/warhammer40kdarktide/").unwrap(); +} + +#[derive(Error, Debug)] +pub enum Error { + #[error("HTTP error: {0:?}")] + HTTP(#[from] reqwest::Error), + #[error("invalid URL: {0:?}")] + URLParseError(#[from] url::ParseError), + #[error("failed to deserialize '{error}': {json}")] + Deserialize { + json: String, + error: serde_json::Error, + }, + #[error(transparent)] + InvalidHeaderValue(#[from] InvalidHeaderValue), + #[error("this error cannot happen")] + Infallible(#[from] Infallible), +} + +pub type Result = std::result::Result; + +#[derive(Clone, Debug, Deserialize)] +pub struct UpdateInfo { + pub mod_id: u64, + #[serde(with = "time::serde::timestamp")] + pub latest_file_update: OffsetDateTime, + #[serde(with = "time::serde::timestamp")] + pub latest_mod_activity: OffsetDateTime, +} + +#[derive(Copy, Clone, Debug)] +pub enum UpdatePeriod { + Day, + Week, + Month, +} + +impl Default for UpdatePeriod { + fn default() -> Self { + Self::Week + } +} + +impl Serialize for UpdatePeriod { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + let mut tup = serializer.serialize_tuple(2)?; + tup.serialize_element("period")?; + tup.serialize_element(match self { + Self::Day => "1d", + Self::Week => "1w", + Self::Month => "1m", + })?; + tup.end() + } +} + +pub struct Api { + client: Client, +} + +impl Api { + pub fn new(key: String) -> Result { + let mut headers = HeaderMap::new(); + headers.insert("accept", HeaderValue::from_static("application/json")); + headers.insert("apikey", HeaderValue::from_str(&key)?); + + let client = Client::builder() + .user_agent(USER_AGENT) + .default_headers(headers) + .build()?; + + Ok(Self { client }) + } + + #[tracing::instrument(skip(self))] + pub async fn mods_updated(&self, period: UpdatePeriod) -> Result> { + let url = BASE_URL_GAME.join("mods/updated.json")?; + + let res = self + .client + .get(url) + .query(&[period]) + .send() + .await? + .error_for_status()?; + + tracing::trace!(?res); + let json = res.text().await?; + + serde_json::from_str(&json).map_err(|error| Error::Deserialize { json, error }) + } +} + +#[cfg(test)] +mod test { + use crate::Api; + + fn make_api() -> Api { + let key = std::env::var("NEXUSMODS_API_KEY").expect("'NEXUSMODS_API_KEY' env var missing"); + Api::new(key).expect("failed to build API client") + } + + #[tokio::test] + async fn mods_updated() { + let client = make_api(); + client + .mods_updated(Default::default()) + .await + .expect("failed to query 'mods_updated'"); + } +} From 2452f9b4aba2b1b8c4edca4d5b401d5fd727c634 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 7 Mar 2023 14:44:41 +0100 Subject: [PATCH 064/335] feat(nexusmods): Implement additional endpoints --- lib/nexusmods/Cargo.toml | 2 +- lib/nexusmods/src/lib.rs | 106 ++++++++++++++++++------------------- lib/nexusmods/src/types.rs | 102 +++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+), 56 deletions(-) create mode 100644 lib/nexusmods/src/types.rs diff --git a/lib/nexusmods/Cargo.toml b/lib/nexusmods/Cargo.toml index 93dbd78..613aa7e 100644 --- a/lib/nexusmods/Cargo.toml +++ b/lib/nexusmods/Cargo.toml @@ -13,7 +13,7 @@ serde_json = "1.0.94" thiserror = "1.0.39" time = { version = "0.3.20", features = ["serde"] } tracing = "0.1.37" -url = "2.3.1" +url = { version = "2.3.1", features = ["serde"] } [dev-dependencies] tokio = { version = "1.26.0", features = ["rt", "macros"] } diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index c8ea2a6..3f3fb5e 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -2,15 +2,15 @@ use std::convert::Infallible; use lazy_static::lazy_static; use reqwest::header::{HeaderMap, HeaderValue, InvalidHeaderValue}; -use reqwest::{Client, Url}; -use serde::ser::SerializeTuple; -use serde::{Deserialize, Serialize}; +use reqwest::{Client, RequestBuilder, Url}; +use serde::Deserialize; use thiserror::Error; -use time::OffsetDateTime; + +mod types; +pub use types::*; // TODO: Add OS information const USER_AGENT: &str = concat!("DTMM/", env!("CARGO_PKG_VERSION")); -const GAME_ID: &str = "warhammer40kdarktide"; lazy_static! { static ref BASE_URL: Url = Url::parse("https://api.nexusmods.com/v1/").unwrap(); @@ -37,44 +37,6 @@ pub enum Error { pub type Result = std::result::Result; -#[derive(Clone, Debug, Deserialize)] -pub struct UpdateInfo { - pub mod_id: u64, - #[serde(with = "time::serde::timestamp")] - pub latest_file_update: OffsetDateTime, - #[serde(with = "time::serde::timestamp")] - pub latest_mod_activity: OffsetDateTime, -} - -#[derive(Copy, Clone, Debug)] -pub enum UpdatePeriod { - Day, - Week, - Month, -} - -impl Default for UpdatePeriod { - fn default() -> Self { - Self::Week - } -} - -impl Serialize for UpdatePeriod { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::Serializer, - { - let mut tup = serializer.serialize_tuple(2)?; - tup.serialize_element("period")?; - tup.serialize_element(match self { - Self::Day => "1d", - Self::Week => "1w", - Self::Month => "1m", - })?; - tup.end() - } -} - pub struct Api { client: Client, } @@ -93,22 +55,37 @@ impl Api { Ok(Self { client }) } + #[tracing::instrument(skip(self))] + async fn send(&self, req: RequestBuilder) -> Result + where + T: for<'a> Deserialize<'a>, + { + let res = req.send().await?.error_for_status()?; + tracing::trace!(?res); + + let json = res.text().await?; + serde_json::from_str(&json).map_err(|error| Error::Deserialize { json, error }) + } + + #[tracing::instrument(skip(self))] + pub async fn user_validate(&self) -> Result { + let url = BASE_URL.join("users/validate.json")?; + let req = self.client.get(url); + self.send(req).await + } + #[tracing::instrument(skip(self))] pub async fn mods_updated(&self, period: UpdatePeriod) -> Result> { let url = BASE_URL_GAME.join("mods/updated.json")?; + let req = self.client.get(url).query(&[period]); + self.send(req).await + } - let res = self - .client - .get(url) - .query(&[period]) - .send() - .await? - .error_for_status()?; - - tracing::trace!(?res); - let json = res.text().await?; - - serde_json::from_str(&json).map_err(|error| Error::Deserialize { json, error }) + #[tracing::instrument(skip(self))] + pub async fn mods_id(&self, id: u64) -> Result { + let url = BASE_URL_GAME.join(&format!("mods/{}.json", id))?; + let req = self.client.get(url); + self.send(req).await } } @@ -129,4 +106,23 @@ mod test { .await .expect("failed to query 'mods_updated'"); } + + #[tokio::test] + async fn user_validate() { + let client = make_api(); + client + .user_validate() + .await + .expect("failed to query 'user_validate'"); + } + + #[tokio::test] + async fn mods_id() { + let client = make_api(); + let dmf_id = 8; + client + .mods_id(dmf_id) + .await + .expect("failed to query 'mods_id'"); + } } diff --git a/lib/nexusmods/src/types.rs b/lib/nexusmods/src/types.rs new file mode 100644 index 0000000..fade6c1 --- /dev/null +++ b/lib/nexusmods/src/types.rs @@ -0,0 +1,102 @@ +use reqwest::Url; +use serde::ser::SerializeTuple; +use serde::{Deserialize, Serialize}; +use time::OffsetDateTime; + +#[derive(Debug, Deserialize)] +pub struct User { + pub user_id: u64, + pub name: String, + pub profile_url: Url, + // pub is_premium: bool, + // pub is_supporter: bool, + // pub email: String, +} + +#[derive(Copy, Clone, Debug, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ModStatus { + Published, +} + +#[derive(Copy, Clone, Debug, Deserialize)] +pub enum EndorseStatus { + Undecided, +} + +#[derive(Debug, Deserialize)] +pub struct ModEndorsement { + pub endorse_status: EndorseStatus, + #[serde(with = "time::serde::timestamp::option")] + pub timestamp: Option, + pub version: Option, +} + +#[derive(Debug, Deserialize)] +pub struct Mod { + pub name: String, + pub description: String, + pub summary: String, + pub picture_url: Url, + pub uid: u64, + pub mod_id: u64, + pub category_id: u64, + pub version: String, + #[serde(with = "time::serde::timestamp")] + pub created_timestamp: OffsetDateTime, + // created_time: OffsetDateTime, + #[serde(with = "time::serde::timestamp")] + pub updated_timestamp: OffsetDateTime, + // updated_time: OffsetDateTime, + pub author: String, + pub uploaded_by: String, + pub uploaded_users_profile_url: Url, + pub status: ModStatus, + pub available: bool, + pub endorsement: ModEndorsement, + // pub mod_downloads: u64, + // pub mod_unique_downloads: u64, + // pub game_id: u64, + // pub allow_rating: bool, + // pub domain_name: String, + // pub endorsement_count: u64, + // pub contains_adult_content: bool, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct UpdateInfo { + pub mod_id: u64, + #[serde(with = "time::serde::timestamp")] + pub latest_file_update: OffsetDateTime, + #[serde(with = "time::serde::timestamp")] + pub latest_mod_activity: OffsetDateTime, +} + +#[derive(Copy, Clone, Debug)] +pub enum UpdatePeriod { + Day, + Week, + Month, +} + +impl Default for UpdatePeriod { + fn default() -> Self { + Self::Week + } +} + +impl Serialize for UpdatePeriod { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::Serializer, + { + let mut tup = serializer.serialize_tuple(2)?; + tup.serialize_element("period")?; + tup.serialize_element(match self { + Self::Day => "1d", + Self::Week => "1w", + Self::Month => "1m", + })?; + tup.end() + } +} From d4d1d52f456176681da2f532c35fe3c36c2cfa59 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 13 Mar 2023 13:11:28 +0100 Subject: [PATCH 065/335] feat(nexusmods): Implement parsing download file names When downloading manually from Nexus, the file name encodes information needed to map the file to the mod object. --- lib/nexusmods/Cargo.toml | 1 + lib/nexusmods/src/lib.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/lib/nexusmods/Cargo.toml b/lib/nexusmods/Cargo.toml index 613aa7e..1c94289 100644 --- a/lib/nexusmods/Cargo.toml +++ b/lib/nexusmods/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] lazy_static = "1.4.0" +regex = "1.7.1" reqwest = { version = "0.11.14" } serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.94" diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index 3f3fb5e..e761a77 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -1,6 +1,7 @@ use std::convert::Infallible; use lazy_static::lazy_static; +use regex::Regex; use reqwest::header::{HeaderMap, HeaderValue, InvalidHeaderValue}; use reqwest::{Client, RequestBuilder, Url}; use serde::Deserialize; @@ -87,6 +88,26 @@ impl Api { let req = self.client.get(url); self.send(req).await } + + pub fn parse_file_name>(name: S) -> Option<(String, u64, String, u64)> { + lazy_static! { + static ref RE: Regex = Regex::new(r#"^(?P.+?)-(?P[1-9]\d*)-(?P.+?)-(?P[1-9]\d*)(?:\.\w+)?$"#).unwrap(); + } + + RE.captures(name.as_ref()).and_then(|cap| { + let name = cap.name("name")?; + let mod_id = cap.name("mod_id")?; + let version = cap.name("version")?; + let updated = cap.name("updated")?; + + Some(( + name.as_str().to_string(), + mod_id.as_str().parse().ok()?, + version.as_str().to_string(), + updated.as_str().parse().ok()?, + )) + }) + } } #[cfg(test)] @@ -125,4 +146,15 @@ mod test { .await .expect("failed to query 'mods_id'"); } + + #[test] + fn parse_file_name() { + let file = "Darktide Mod Framework-8-23-3-04-1677966575.zip"; + let (name, mod_id, version, updated) = Api::parse_file_name(file).unwrap(); + + assert_eq!(name, String::from("Darktide Mod Framework")); + assert_eq!(mod_id, 8); + assert_eq!(version, String::from("23-3-04")); + assert_eq!(updated, 1677966575); + } } From 2fb0d8fb721087020f97f15eda3b3a501310940c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 8 Mar 2023 09:18:19 +0100 Subject: [PATCH 066/335] feat(nexusmods): Implement NXM URI parsing --- lib/nexusmods/Cargo.toml | 1 + lib/nexusmods/src/lib.rs | 155 ++++++++++++++++++++++++++++++++++--- lib/nexusmods/src/types.rs | 10 ++- 3 files changed, 153 insertions(+), 13 deletions(-) diff --git a/lib/nexusmods/Cargo.toml b/lib/nexusmods/Cargo.toml index 1c94289..d7967ef 100644 --- a/lib/nexusmods/Cargo.toml +++ b/lib/nexusmods/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +futures = "0.3.26" lazy_static = "1.4.0" regex = "1.7.1" reqwest = { version = "0.11.14" } diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index e761a77..a0700ae 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::convert::Infallible; use lazy_static::lazy_static; @@ -8,10 +9,12 @@ use serde::Deserialize; use thiserror::Error; mod types; +use time::OffsetDateTime; pub use types::*; // TODO: Add OS information const USER_AGENT: &str = concat!("DTMM/", env!("CARGO_PKG_VERSION")); +const GAME_ID: &str = "warhammer40kdarktide"; lazy_static! { static ref BASE_URL: Url = Url::parse("https://api.nexusmods.com/v1/").unwrap(); @@ -34,10 +37,20 @@ pub enum Error { InvalidHeaderValue(#[from] InvalidHeaderValue), #[error("this error cannot happen")] Infallible(#[from] Infallible), + #[error("invalid NXM URL '{}': {0}", .1.as_str())] + InvalidNXM(&'static str, Url), } pub type Result = std::result::Result; +pub struct Nxm { + pub mod_id: u64, + pub file_id: u64, + pub user_id: u64, + pub key: String, + pub expires: OffsetDateTime, +} + pub struct Api { client: Client, } @@ -89,29 +102,138 @@ impl Api { self.send(req).await } - pub fn parse_file_name>(name: S) -> Option<(String, u64, String, u64)> { + pub fn parse_file_name>( + name: S, + ) -> Option<(String, u64, String, OffsetDateTime)> { lazy_static! { static ref RE: Regex = Regex::new(r#"^(?P.+?)-(?P[1-9]\d*)-(?P.+?)-(?P[1-9]\d*)(?:\.\w+)?$"#).unwrap(); } RE.captures(name.as_ref()).and_then(|cap| { - let name = cap.name("name")?; - let mod_id = cap.name("mod_id")?; - let version = cap.name("version")?; - let updated = cap.name("updated")?; + let name = cap.name("name").map(|s| s.as_str().to_string())?; + let mod_id = cap.name("mod_id").and_then(|s| s.as_str().parse().ok())?; + let version = cap.name("version").map(|s| s.as_str().to_string())?; + let updated = cap + .name("updated") + .and_then(|s| s.as_str().parse().ok()) + .and_then(|s| OffsetDateTime::from_unix_timestamp(s).ok())?; - Some(( - name.as_str().to_string(), - mod_id.as_str().parse().ok()?, - version.as_str().to_string(), - updated.as_str().parse().ok()?, - )) + Some((name, mod_id, version, updated)) + }) + } + + #[tracing::instrument(skip(self))] + pub async fn mods_download_link( + &self, + mod_id: u64, + file_id: u64, + key: String, + expires: OffsetDateTime, + ) -> Result> { + let url = + BASE_URL_GAME.join(&format!("mods/{mod_id}/files/{file_id}/download_link.json"))?; + let req = self + .client + .get(url) + .query(&[("key", key)]) + .query(&[("expires", expires.unix_timestamp())]); + self.send(req).await + } + + pub async fn handle_nxm(&self, url: Url) -> Result<(Mod, Vec)> { + let nxm = Self::parse_nxm(url.clone())?; + + let user = self.user_validate().await?; + + if nxm.user_id != user.user_id { + return Err(Error::InvalidNXM("user_id mismtach", url)); + } + + let (mod_data, download_info) = futures::try_join!( + self.mods_id(nxm.mod_id), + self.mods_download_link(nxm.mod_id, nxm.file_id, nxm.key, nxm.expires) + )?; + + let Some(download_url) = download_info.get(0).map(|i| i.uri.clone()) else { + return Err(Error::InvalidNXM("no download link", url)); + }; + + let req = self.client.get(download_url); + let data = req.send().await?.bytes().await?; + + Ok((mod_data, data.to_vec())) + } + + pub fn parse_nxm(nxm: Url) -> Result { + if nxm.scheme() != "nxm" { + return Err(Error::InvalidNXM("Invalid scheme", nxm)); + } + + // Now it makes sense, why Nexus calls this field `game_domain_name`, when it's just + // another path segmentin the regular API calls. + if nxm.host_str() != Some(GAME_ID) { + return Err(Error::InvalidNXM("Invalid game domain name", nxm)); + } + + let Some(mut segments) = nxm.path_segments() else { + return Err(Error::InvalidNXM("Cannot be a base", nxm)); + }; + + if segments.next() != Some("mods") { + return Err(Error::InvalidNXM("Unexpected path segment", nxm)); + } + + let Some(mod_id) = segments.next().and_then(|id| id.parse().ok()) else { + return Err(Error::InvalidNXM("Invalid mod ID", nxm)); + }; + + if segments.next() != Some("files") { + return Err(Error::InvalidNXM("Unexpected path segment", nxm)); + } + + let Some(file_id) = segments.next().and_then(|id| id.parse().ok()) else { + return Err(Error::InvalidNXM("Invalid file ID", nxm)); + }; + + let mut query = HashMap::new(); + let pairs = nxm.query_pairs(); + + for (key, val) in pairs { + query.insert(key, val); + } + + let Some(key) = query.get("key") else { + return Err(Error::InvalidNXM("Missing 'key'", nxm)); + }; + + let expires = query + .get("expires") + .and_then(|expires| expires.parse().ok()) + .and_then(|expires| OffsetDateTime::from_unix_timestamp(expires).ok()); + let Some(expires) = expires else { + return Err(Error::InvalidNXM("Missing 'expires'", nxm)); + }; + + let user_id = query.get("user_id").and_then(|id| id.parse().ok()); + let Some(user_id) = user_id else { + return Err(Error::InvalidNXM("Missing 'user_id'", nxm)); + }; + + Ok(Nxm { + mod_id, + file_id, + key: key.to_string(), + expires, + user_id, }) } } #[cfg(test)] mod test { + use reqwest::Url; + use time::OffsetDateTime; + use crate::Api; fn make_api() -> Api { @@ -155,6 +277,15 @@ mod test { assert_eq!(name, String::from("Darktide Mod Framework")); assert_eq!(mod_id, 8); assert_eq!(version, String::from("23-3-04")); - assert_eq!(updated, 1677966575); + assert_eq!( + updated, + OffsetDateTime::from_unix_timestamp(1677966575).unwrap() + ); + } + + #[test] + fn parse_nxm() { + let nxm = Url::parse("nxm://warhammer40kdarktide/mods/8/files/1000172397?key=VZ86Guj_LosPvtkD90-ZQg&expires=1678359882&user_id=1234567").expect("invalid NXM example"); + Api::parse_nxm(nxm).expect("failed to parse nxm link"); } } diff --git a/lib/nexusmods/src/types.rs b/lib/nexusmods/src/types.rs index fade6c1..e811d29 100644 --- a/lib/nexusmods/src/types.rs +++ b/lib/nexusmods/src/types.rs @@ -63,7 +63,15 @@ pub struct Mod { // pub contains_adult_content: bool, } -#[derive(Clone, Debug, Deserialize)] +#[derive(Debug, Deserialize)] +pub struct DownloadLink { + pub name: String, + pub short_name: String, + #[serde(alias = "URI")] + pub uri: Url, +} + +#[derive(Debug, Deserialize)] pub struct UpdateInfo { pub mod_id: u64, #[serde(with = "time::serde::timestamp")] From c7203127bbb22e8928c0426a669fb6f3228f1fb8 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Mar 2023 10:19:23 +0100 Subject: [PATCH 067/335] feat(dtmm): Implement importing Nexus downloads For now, this merely parses and retains the API information encoded in the archive's file name. --- Cargo.lock | 1 + crates/dtmm/Cargo.toml | 1 + crates/dtmm/src/controller/app.rs | 39 ++++++++++++++++++++++++++++--- crates/dtmm/src/state/data.rs | 13 +++++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a029489..25c5e4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -756,6 +756,7 @@ dependencies = [ "dtmt-shared", "futures", "lazy_static", + "nexusmods", "oodle-sys", "path-slash", "sdk", diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 2265229..11b71f7 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -15,6 +15,7 @@ dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } futures = "0.3.25" oodle-sys = { path = "../../lib/oodle-sys", version = "*" } sdk = { path = "../../lib/sdk", version = "*" } +nexusmods = { path = "../../lib/nexusmods", version = "*" } serde_sjson = { path = "../../lib/serde_sjson", version = "*" } serde = { version = "1.0.152", features = ["derive", "rc"] } tokio = { version = "1.23.0", features = ["rt", "fs", "tracing", "sync"] } diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index 9aa5a6d..64c4184 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -8,13 +8,14 @@ use color_eyre::{Help, Report, Result}; use druid::im::Vector; use druid::{FileInfo, ImageBuf}; use dtmt_shared::ModConfig; +use nexusmods::Api as NexusApi; use tokio::fs::{self, DirEntry}; use tokio::runtime::Runtime; use tokio_stream::wrappers::ReadDirStream; use tokio_stream::StreamExt; use zip::ZipArchive; -use crate::state::{ActionState, ModInfo, ModOrder, PackageInfo}; +use crate::state::{ActionState, ModInfo, ModOrder, NexusInfo, PackageInfo}; use crate::util::config::{ConfigSerialize, LoadOrderEntry}; use super::read_sjson_file; @@ -26,6 +27,17 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result Result<()> { async fn read_mod_dir_entry(res: Result) -> Result { let entry = res?; let config_path = entry.path().join("dtmt.cfg"); + let nexus_path = entry.path().join("nexus.sjson"); let index_path = entry.path().join("files.sjson"); let cfg: ModConfig = read_sjson_file(&config_path) .await .wrap_err_with(|| format!("Failed to read mod config '{}'", config_path.display()))?; + let nexus: Option = match read_sjson_file(&nexus_path) + .await + .wrap_err_with(|| format!("Failed to read Nexus info '{}'", nexus_path.display())) + { + Ok(nexus) => Some(nexus), + Err(err) if err.is::() => match err.downcast_ref::() { + Some(err) if err.kind() == std::io::ErrorKind::NotFound => None, + _ => return Err(err), + }, + Err(err) => return Err(err), + }; + let files: HashMap> = read_sjson_file(&index_path) .await .wrap_err_with(|| format!("Failed to read file index '{}'", index_path.display()))?; @@ -222,7 +255,7 @@ async fn read_mod_dir_entry(res: Result) -> Result { .into_iter() .map(|(name, files)| Arc::new(PackageInfo::new(name, files.into_iter().collect()))) .collect(); - let info = ModInfo::new(cfg, packages, image); + let info = ModInfo::new(cfg, packages, image, nexus); Ok(info) } diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 09e3a97..848c6bb 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -5,6 +5,7 @@ use druid::{ Data, ImageBuf, Lens, WindowHandle, WindowId, }; use dtmt_shared::ModConfig; +use time::OffsetDateTime; use super::SelectedModLens; @@ -69,6 +70,15 @@ impl From for ModDependency { } } +#[derive(Clone, Data, Debug, Lens, serde::Serialize, serde::Deserialize)] +pub(crate) struct NexusInfo { + pub id: u64, + pub version: Option, + #[data(ignore)] + #[serde(with = "time::serde::timestamp")] + pub updated: OffsetDateTime, +} + #[derive(Clone, Data, Debug, Lens)] pub(crate) struct ModInfo { pub id: String, @@ -87,6 +97,7 @@ pub(crate) struct ModInfo { #[data(ignore)] pub resources: ModResourceInfo, pub depends: Vector, + pub nexus: Option, } impl ModInfo { @@ -94,6 +105,7 @@ impl ModInfo { cfg: ModConfig, packages: Vector>, image: Option, + nexus: Option, ) -> Self { Self { id: cfg.id, @@ -112,6 +124,7 @@ impl ModInfo { localization: cfg.resources.localization, }, depends: cfg.depends.into_iter().map(ModDependency::from).collect(), + nexus, } } } From 8edb8b357eaeb66f6a59fc95c2c7803028f50788 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Mar 2023 13:59:19 +0100 Subject: [PATCH 068/335] feat(dtmm): Implement rudimentary update check The UI for it is rather ugly, still, but it works. --- Cargo.lock | 427 ++++++++++++++++++++++++++- crates/dtmm/src/controller/app.rs | 54 +++- crates/dtmm/src/controller/worker.rs | 24 ++ crates/dtmm/src/main.rs | 1 + crates/dtmm/src/state/data.rs | 5 +- crates/dtmm/src/state/delegate.rs | 50 ++++ crates/dtmm/src/ui/window/main.rs | 34 ++- 7 files changed, 589 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25c5e4e..0ab730a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,6 +130,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "base64ct" version = "1.6.0" @@ -832,6 +838,15 @@ dependencies = [ "wio", ] +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + [[package]] name = "endian-type" version = "0.1.2" @@ -1022,6 +1037,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -1374,6 +1398,25 @@ dependencies = [ "syn", ] +[[package]] +name = "h2" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1410,6 +1453,87 @@ dependencies = [ "digest", ] +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "im" version = "15.1.0" @@ -1517,6 +1641,12 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "ipnet" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + [[package]] name = "is-terminal" version = "0.4.4" @@ -1723,6 +1853,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1756,6 +1892,41 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nexusmods" +version = "0.1.0" +dependencies = [ + "futures", + "lazy_static", + "regex", + "reqwest", + "serde", + "serde_json", + "thiserror", + "time", + "tokio", + "tracing", + "url", +] + [[package]] name = "nibble_vec" version = "0.1.0" @@ -1987,6 +2158,51 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd2523381e46256e40930512c7fd25562b9eae4812cb52078f155e87217c9d1e" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176be2629957c157240f68f61f2d0053ad3a4ecfdd9ebf1e6521d18d9635cf67" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "os_str_bytes" version = "6.4.1" @@ -2093,6 +2309,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + [[package]] name = "pest" version = "2.5.6" @@ -2398,6 +2620,43 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +[[package]] +name = "reqwest" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +dependencies = [ + "base64 0.21.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "resvg" version = "0.25.0" @@ -2533,6 +2792,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -2566,6 +2834,29 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "self_cell" version = "0.10.2" @@ -2598,6 +2889,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_sjson" version = "1.0.0" @@ -2607,6 +2909,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha1" version = "0.10.5" @@ -2687,6 +3001,16 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "steamid-ng" version = "1.0.0" @@ -2926,6 +3250,21 @@ dependencies = [ "displaydoc", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.26.0" @@ -2940,6 +3279,7 @@ dependencies = [ "num_cpus", "pin-project-lite", "signal-hook-registry", + "socket2", "tokio-macros", "tracing", "windows-sys 0.45.0", @@ -2956,6 +3296,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.12" @@ -2967,6 +3317,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml" version = "0.5.11" @@ -2993,6 +3357,12 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.37" @@ -3076,6 +3446,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + [[package]] name = "ttf-parser" version = "0.17.1" @@ -3211,6 +3587,15 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-script" version = "0.5.5" @@ -3235,13 +3620,25 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + [[package]] name = "usvg" version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "585bb2d87c8fd6041a479dea01479dcf9094e61b5f9af221606927e61a2bd939" dependencies = [ - "base64", + "base64 0.13.1", "data-url", "flate2", "fontdb", @@ -3279,6 +3676,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version-compare" version = "0.1.1" @@ -3323,6 +3726,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3354,6 +3767,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.84" diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index 64c4184..9e46305 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -34,7 +34,7 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result<()> { Ok(()) } + +#[tracing::instrument(skip(info, api), fields(id = info.id, name = info.name, version = info.version))] +async fn check_mod_update(info: Arc, api: Arc) -> Result> { + let Some(nexus) = &info.nexus else { + return Ok(None); + }; + + let updated_info = api + .mods_id(nexus.id) + .await + .wrap_err_with(|| format!("Failed to query mod {} from Nexus", nexus.id))?; + + let updated_nexus = NexusInfo { + id: nexus.id, + version: updated_info.version, + updated: updated_info.updated_timestamp, + }; + + let mut info = Arc::unwrap_or_clone(info); + info.nexus = Some(updated_nexus); + + Ok(Some(info)) +} + +#[tracing::instrument(skip(state))] +pub(crate) async fn check_updates(state: ActionState) -> Result> { + if state.nexus_api_key.is_empty() { + eyre::bail!("Nexus API key not set. Cannot check for updates."); + } + + let api = NexusApi::new(state.nexus_api_key.to_string()) + .wrap_err("Failed to initialize Nexus API")?; + let api = Arc::new(api); + + let tasks = state + .mods + .iter() + .map(|info| check_mod_update(info.clone(), api.clone())); + + let results = futures::future::join_all(tasks).await; + let updates = results + .into_iter() + .filter_map(|res| match res { + Ok(info) => info, + Err(err) => { + tracing::error!("{:?}", err); + None + } + }) + .collect(); + Ok(updates) +} diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index 48e9c1e..fafeebe 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -12,6 +12,7 @@ use tokio::sync::RwLock; use crate::controller::app::*; use crate::controller::game::*; use crate::state::AsyncAction; +use crate::state::ACTION_FINISH_CHECK_UPDATE; use crate::state::ACTION_FINISH_SAVE_SETTINGS; use crate::state::ACTION_SHOW_ERROR_DIALOG; use crate::state::{ @@ -120,6 +121,29 @@ async fn handle_action( .submit_command(ACTION_FINISH_SAVE_SETTINGS, (), Target::Auto) .expect("failed to send command"); }), + AsyncAction::CheckUpdates(state) => tokio::spawn(async move { + let updates = match check_updates(state) + .await + .wrap_err("Failed to check for updates") + { + Ok(updates) => updates, + Err(err) => { + tracing::error!("{:?}", err); + send_error(event_sink.clone(), err).await; + vec![] + } + }; + + event_sink + .write() + .await + .submit_command( + ACTION_FINISH_CHECK_UPDATE, + SingleUse::new(updates), + Target::Auto, + ) + .expect("failed to send command"); + }), }; } } diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index de8ca8d..ea7bacb 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -1,5 +1,6 @@ #![recursion_limit = "256"] #![feature(let_chains)] +#![feature(arc_unwrap_or_clone)] #![windows_subsystem = "windows"] use std::path::PathBuf; diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 848c6bb..a250660 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -73,7 +73,7 @@ impl From for ModDependency { #[derive(Clone, Data, Debug, Lens, serde::Serialize, serde::Deserialize)] pub(crate) struct NexusInfo { pub id: u64, - pub version: Option, + pub version: String, #[data(ignore)] #[serde(with = "time::serde::timestamp")] pub updated: OffsetDateTime, @@ -97,6 +97,7 @@ pub(crate) struct ModInfo { #[data(ignore)] pub resources: ModResourceInfo, pub depends: Vector, + #[data(ignore)] pub nexus: Option, } @@ -139,6 +140,7 @@ pub(crate) struct State { pub is_reset_in_progress: bool, pub is_save_in_progress: bool, pub is_next_save_pending: bool, + pub is_update_in_progress: bool, pub game_dir: Arc, pub data_dir: Arc, pub nexus_api_key: Arc, @@ -177,6 +179,7 @@ impl State { is_reset_in_progress: false, is_save_in_progress: false, is_next_save_pending: false, + is_update_in_progress: false, config_path: Arc::new(config_path), game_dir: Arc::new(game_dir), data_dir: Arc::new(data_dir), diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index 1dbe2a2..4a0fc17 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -39,6 +39,11 @@ pub(crate) const ACTION_START_SAVE_SETTINGS: Selector = pub(crate) const ACTION_FINISH_SAVE_SETTINGS: Selector = Selector::new("dtmm.action.finish-save-settings"); +pub(crate) const ACTION_START_CHECK_UPDATE: Selector = + Selector::new("dtmm.action.start-check-update"); +pub(crate) const ACTION_FINISH_CHECK_UPDATE: Selector>> = + Selector::new("dtmm.action.finish-check-update"); + pub(crate) const ACTION_SET_DIRTY: Selector = Selector::new("dtmm.action.set-dirty"); pub(crate) const ACTION_SHOW_ERROR_DIALOG: Selector> = @@ -79,6 +84,7 @@ pub(crate) enum AsyncAction { AddMod(ActionState, FileInfo), DeleteMod(ActionState, Arc), SaveSettings(ActionState), + CheckUpdates(ActionState), } pub(crate) struct Delegate { @@ -304,6 +310,50 @@ impl AppDelegate for Delegate { state.windows.insert(id, handle); Handled::Yes } + cmd if cmd.is(ACTION_START_CHECK_UPDATE) => { + if self + .sender + .send(AsyncAction::CheckUpdates(state.clone().into())) + .is_ok() + { + state.is_update_in_progress = true; + } else { + tracing::error!("Failed to queue action to check updates"); + } + Handled::Yes + } + cmd if cmd.is(ACTION_FINISH_CHECK_UPDATE) => { + let mut updates = cmd + .get(ACTION_FINISH_CHECK_UPDATE) + .and_then(SingleUse::take) + .expect("command type matched but didn't contain the expected value"); + + if tracing::enabled!(tracing::Level::DEBUG) { + let mods: Vec<_> = updates + .iter() + .map(|info| { + format!( + "{}: {} -> {:?}", + info.name, + info.version, + info.nexus.as_ref().map(|n| &n.version) + ) + }) + .collect(); + + tracing::info!("Mod updates:\n{}", mods.join("\n")); + } + + for mod_info in state.mods.iter_mut() { + if let Some(index) = updates.iter().position(|i2| i2.id == mod_info.id) { + let update = updates.swap_remove(index); + *mod_info = Arc::new(update); + } + } + + state.is_update_in_progress = false; + Handled::Yes + } cmd => { if cfg!(debug_assertions) { tracing::warn!("Unknown command: {:?}", cmd); diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 67ab0e5..16be5f0 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -15,8 +15,8 @@ use lazy_static::lazy_static; use crate::state::{ ModInfo, State, View, ACTION_ADD_MOD, ACTION_SELECTED_MOD_DOWN, ACTION_SELECTED_MOD_UP, - ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE, ACTION_START_DELETE_SELECTED_MOD, - ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT, + ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE, ACTION_START_CHECK_UPDATE, + ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT, }; use crate::ui::theme::{self, ColorExt}; use crate::ui::widget::border::Border; @@ -49,6 +49,12 @@ fn build_top_bar() -> impl Widget { state.current_view = View::Settings; }); + let check_update_button = Button::with_label("Check for updates") + .on_click(|ctx, _: &mut State, _| { + ctx.submit_command(ACTION_START_CHECK_UPDATE); + }) + .disabled_if(|data, _| data.is_update_in_progress); + let deploy_button = { let icon = Svg::new(SvgData::from_str(theme::icons::ALERT_CIRCLE).expect("invalid SVG")) .fix_height(druid::theme::TEXT_SIZE_NORMAL); @@ -85,6 +91,8 @@ fn build_top_bar() -> impl Widget { ) .with_child( Flex::row() + .with_child(check_update_button) + .with_default_spacer() .with_child(deploy_button) .with_default_spacer() .with_child(reset_button), @@ -118,10 +126,30 @@ fn build_mod_list() -> impl Widget { let name = Label::raw().lens(lens!((usize, Arc, bool), 1).then(ModInfo::name.in_arc())); + let version = Label::dynamic(|info: &Arc, _| { + let has_update = info + .nexus + .as_ref() + .map(|n| info.version != n.version) + .unwrap_or(false); + if has_update { + format!("! {}", info.version) + } else { + info.version.to_string() + } + }) + .lens(lens!((usize, Arc, bool), 1)); + + let fields = Flex::row() + .must_fill_main_axis(true) + .main_axis_alignment(MainAxisAlignment::SpaceBetween) + .with_child(name) + .with_child(version); + Flex::row() .must_fill_main_axis(true) .with_child(checkbox) - .with_child(name) + .with_flex_child(fields, 1.) .padding((5.0, 4.0)) .background(theme::keys::KEY_MOD_LIST_ITEM_BG_COLOR) .on_click(|ctx, (i, _, _), _env| ctx.submit_command(ACTION_SELECT_MOD.with(*i))) From ac44883199441de9a66ed17b8a6368b3c8676c6b Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Mar 2023 20:54:06 +0100 Subject: [PATCH 069/335] refactor(dtmm): Remove unused field --- crates/dtmm/src/controller/app.rs | 7 +------ crates/dtmm/src/state/data.rs | 13 ++++--------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index 9e46305..88dee2e 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -32,11 +32,7 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result, api: Arc) -> Result for ModDependency { pub(crate) struct NexusInfo { pub id: u64, pub version: String, - #[data(ignore)] - #[serde(with = "time::serde::timestamp")] - pub updated: OffsetDateTime, } #[derive(Clone, Data, Debug, Lens)] From d43f9c46b5f6df1c145ba6c7f3b1a9da2d43a8b5 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Mar 2023 20:56:26 +0100 Subject: [PATCH 070/335] chore: Add changelog entry --- CHANGELOG.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index ecfed2b..2a19595 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -12,6 +12,7 @@ - dtmm: show dialog for critical errors - dtmm: check mod order before deployment - dtmt: add mod dependencies to config +- dtmm: match mods to Nexus and check for updates === Fixed From 1c470b51f84123d4ada2f7bee1f051f76506c22f Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Mar 2023 21:11:37 +0100 Subject: [PATCH 071/335] fix(dtmm): Fix fetching endorsed mods --- lib/nexusmods/src/types.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/nexusmods/src/types.rs b/lib/nexusmods/src/types.rs index e811d29..b0dffd5 100644 --- a/lib/nexusmods/src/types.rs +++ b/lib/nexusmods/src/types.rs @@ -21,6 +21,7 @@ pub enum ModStatus { #[derive(Copy, Clone, Debug, Deserialize)] pub enum EndorseStatus { + Endorsed, Undecided, } From e434535d9679db8a09ed31282a3b5137a252442c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Mar 2023 21:45:04 +0100 Subject: [PATCH 072/335] WIP more nexus mod details --- crates/dtmm/src/controller/app.rs | 24 ++++++++++----- crates/dtmm/src/state/data.rs | 16 ++++++++++ crates/dtmm/src/state/lens.rs | 50 ++++++++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 9 deletions(-) diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index 88dee2e..b4b69ca 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -27,12 +27,25 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result, api: Arc) -> Result for ModDependency { pub(crate) struct NexusInfo { pub id: u64, pub version: String, + pub author: String, + pub summary: String, + pub description: Arc, +} + +impl From for NexusInfo { + fn from(value: NexusMod) -> Self { + Self { + id: value.mod_id, + version: value.version, + author: value.author, + summary: value.summary, + description: Arc::new(value.description), + } + } } #[derive(Clone, Data, Debug, Lens)] diff --git a/crates/dtmm/src/state/lens.rs b/crates/dtmm/src/state/lens.rs index 1156f52..c26c298 100644 --- a/crates/dtmm/src/state/lens.rs +++ b/crates/dtmm/src/state/lens.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use druid::im::Vector; use druid::{Data, Lens}; -use super::{ModInfo, State}; +use super::{ModInfo, NexusInfo, State}; pub(crate) struct SelectedModLens; @@ -73,3 +73,51 @@ impl Lens, Vector<(usize, T)>> for IndexedVectorLens { ret } } + +/// A Lens that first checks a key in a mod's `NexusInfo`, then falls back to +/// the regular one. +pub(crate) struct NexusInfoLens +where + L: Lens, + R: Lens, +{ + value: L, + fallback: R, + _marker: std::marker::PhantomData, +} + +impl NexusInfoLens +where + L: Lens, + R: Lens, +{ + pub fn new(value: L, fallback: R) -> Self { + Self { + value, + fallback, + _marker: std::marker::PhantomData, + } + } +} + +impl Lens for NexusInfoLens +where + L: Lens, + R: Lens, +{ + fn with V>(&self, data: &ModInfo, f: F) -> V { + if let Some(nexus) = &data.nexus { + self.value.with(nexus, f) + } else { + self.fallback.with(data, f) + } + } + + fn with_mut V>(&self, data: &mut ModInfo, f: F) -> V { + if let Some(nexus) = &mut data.nexus { + self.value.with_mut(nexus, f) + } else { + self.fallback.with_mut(data, f) + } + } +} From 6a52f3efc264aa28fe59e75489bf7b6c7cad392c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 Mar 2023 19:37:47 +0100 Subject: [PATCH 073/335] feat(dtmm): Improve update icon --- Cargo.lock | 1 + crates/dtmm/Cargo.toml | 1 + crates/dtmm/src/ui/theme/icons.rs | 40 ++++++++++++++++++++++++++++ crates/dtmm/src/ui/window/main.rs | 43 ++++++++++++++++++++----------- 4 files changed, 70 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0ab730a..03f77fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -775,6 +775,7 @@ dependencies = [ "tracing", "tracing-error", "tracing-subscriber", + "usvg", "zip", ] diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 11b71f7..c98a1e9 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -29,3 +29,4 @@ time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-off strip-ansi-escapes = "0.1.1" lazy_static = "1.4.0" colors-transform = "0.2.11" +usvg = "0.25.0" diff --git a/crates/dtmm/src/ui/theme/icons.rs b/crates/dtmm/src/ui/theme/icons.rs index a947871..69156ad 100644 --- a/crates/dtmm/src/ui/theme/icons.rs +++ b/crates/dtmm/src/ui/theme/icons.rs @@ -1 +1,41 @@ +use druid::Color; +use usvg::{ + Error, Fill, LineCap, LineJoin, NodeKind, NonZeroPositiveF64, Options, Paint, Stroke, Tree, +}; + pub static ALERT_CIRCLE: &str = include_str!("../../../assets/icons/icons/alert-circle.svg"); +pub static ALERT_TRIANGLE: &str = include_str!("../../../assets/icons/icons/alert-triangle.svg"); + +pub fn parse_svg(svg: &str) -> Result { + let opt = Options::default(); + Tree::from_str(svg, &opt.to_ref()) +} + +pub fn recolor_icon(tree: Tree, stroke: bool, color: Color) -> Tree { + let (red, green, blue, _) = color.as_rgba8(); + + let mut children = tree.root.children(); + // The first element is always some kind of background placeholder + children.next(); + + for node in children { + if let NodeKind::Path(ref mut path) = *node.borrow_mut() { + if stroke { + path.stroke = Some(Stroke { + paint: Paint::Color(usvg::Color { red, green, blue }), + width: NonZeroPositiveF64::new(2.).expect("the value is not zero"), + linecap: LineCap::Round, + linejoin: LineJoin::Round, + ..Default::default() + }); + } else { + path.fill = Some(Fill { + paint: Paint::Color(usvg::Color { red, green, blue }), + ..Default::default() + }); + } + } + } + + tree +} diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 16be5f0..b251767 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -2,15 +2,16 @@ use std::str::FromStr; use std::sync::Arc; use druid::im::Vector; +use druid::lens; use druid::widget::{ Checkbox, CrossAxisAlignment, Either, Flex, Image, Label, LineBreaking, List, MainAxisAlignment, Maybe, Scroll, SizedBox, Split, Svg, SvgData, TextBox, ViewSwitcher, }; -use druid::{lens, Data, ImageBuf, LifeCycleCtx}; use druid::{ Color, FileDialogOptions, FileSpec, FontDescriptor, FontFamily, LensExt, SingleUse, Widget, WidgetExt, WindowDesc, WindowId, }; +use druid::{Data, ImageBuf, LifeCycleCtx}; use lazy_static::lazy_static; use crate::state::{ @@ -18,7 +19,7 @@ use crate::state::{ ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE, ACTION_START_CHECK_UPDATE, ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT, }; -use crate::ui::theme::{self, ColorExt}; +use crate::ui::theme::{self, ColorExt, COLOR_YELLOW_LIGHT}; use crate::ui::widget::border::Border; use crate::ui::widget::button::Button; use crate::ui::widget::controller::{ @@ -126,19 +127,31 @@ fn build_mod_list() -> impl Widget { let name = Label::raw().lens(lens!((usize, Arc, bool), 1).then(ModInfo::name.in_arc())); - let version = Label::dynamic(|info: &Arc, _| { - let has_update = info - .nexus - .as_ref() - .map(|n| info.version != n.version) - .unwrap_or(false); - if has_update { - format!("! {}", info.version) - } else { - info.version.to_string() - } - }) - .lens(lens!((usize, Arc, bool), 1)); + let version = { + let icon = { + let tree = + theme::icons::parse_svg(theme::icons::ALERT_TRIANGLE).expect("invalid SVG"); + + let tree = theme::icons::recolor_icon(tree, true, COLOR_YELLOW_LIGHT); + + Svg::new(Arc::new(tree)).fix_height(druid::theme::TEXT_SIZE_NORMAL) + }; + + Either::new( + |info, _| { + info.nexus + .as_ref() + .map(|n| info.version != n.version) + .unwrap_or(false) + }, + Flex::row() + .with_child(icon) + .with_spacer(3.) + .with_child(Label::raw().lens(ModInfo::version.in_arc())), + Label::raw().lens(ModInfo::version.in_arc()), + ) + .lens(lens!((usize, Arc, bool), 1)) + }; let fields = Flex::row() .must_fill_main_axis(true) From 146714d88267b638cf5bb50c16859885dd504447 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 Mar 2023 19:46:53 +0100 Subject: [PATCH 074/335] feat(dtmm): Use Nexus mod details when available --- crates/dtmm/src/state/data.rs | 4 ++-- crates/dtmm/src/ui/window/main.rs | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 83c979f..37d66e7 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -74,7 +74,7 @@ pub(crate) struct NexusInfo { pub id: u64, pub version: String, pub author: String, - pub summary: String, + pub summary: Arc, pub description: Arc, } @@ -84,7 +84,7 @@ impl From for NexusInfo { id: value.mod_id, version: value.version, author: value.author, - summary: value.summary, + summary: Arc::new(value.summary), description: Arc::new(value.description), } } diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index b251767..c8d5bf9 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -15,8 +15,8 @@ use druid::{Data, ImageBuf, LifeCycleCtx}; use lazy_static::lazy_static; use crate::state::{ - ModInfo, State, View, ACTION_ADD_MOD, ACTION_SELECTED_MOD_DOWN, ACTION_SELECTED_MOD_UP, - ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE, ACTION_START_CHECK_UPDATE, + ModInfo, NexusInfo, NexusInfoLens, State, View, ACTION_ADD_MOD, ACTION_SELECTED_MOD_DOWN, + ACTION_SELECTED_MOD_UP, ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE, ACTION_START_CHECK_UPDATE, ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT, }; use crate::ui::theme::{self, ColorExt, COLOR_YELLOW_LIGHT}; @@ -294,12 +294,18 @@ fn build_mod_details_info() -> impl Widget { .lens(ModInfo::name.in_arc()); let summary = Label::raw() .with_line_break_mode(LineBreaking::WordWrap) - .lens(ModInfo::summary.in_arc()); + .lens(NexusInfoLens::new(NexusInfo::summary, ModInfo::summary).in_arc()); // TODO: Image/icon? let version_line = Label::dynamic(|info: &Arc, _| { - if let Some(author) = &info.author { + let author = info + .nexus + .as_ref() + .map(|n| &n.author) + .or(info.author.as_ref()); + + if let Some(author) = &author { format!("Version: {}, by {author}", info.version) } else { format!("Version: {}", info.version) From b64aea684c75f726ece980d4fc8eb229b0893b1f Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 Mar 2023 21:31:38 +0100 Subject: [PATCH 075/335] chore: Update serde_sjson --- lib/serde_sjson | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/serde_sjson b/lib/serde_sjson index 6e413b7..73d2b23 160000 --- a/lib/serde_sjson +++ b/lib/serde_sjson @@ -1 +1 @@ -Subproject commit 6e413b7bf5fde09ca66bfcee1394443264c99ab1 +Subproject commit 73d2b23ce50e75b184f5092ad515e97a0adbe6da From 81a9e068a67f47a6f47f9874b62b8fa445172eab Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 Mar 2023 21:42:42 +0100 Subject: [PATCH 076/335] feat(dtmm): Select accent color As voted, though with rather poor participation. --- crates/dtmm/src/ui/theme/colors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/dtmm/src/ui/theme/colors.rs b/crates/dtmm/src/ui/theme/colors.rs index cc86781..078dab0 100644 --- a/crates/dtmm/src/ui/theme/colors.rs +++ b/crates/dtmm/src/ui/theme/colors.rs @@ -63,7 +63,7 @@ pub mod gruvbox_dark { make_color!(COLOR_ORANGE_DARK, 0xd6, 0x5d, 0x0e); make_color!(COLOR_ORANGE_LIGHT, 0xfe, 0x80, 0x19); - make_color!(COLOR_ACCENT, COLOR_RED_LIGHT); + make_color!(COLOR_ACCENT, COLOR_BLUE_LIGHT); make_color!(COLOR_ACCENT_FG, COLOR_BG0_H); } From 60780656cf0338477054991ef21aeafa0655e075 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 Mar 2023 09:19:40 +0100 Subject: [PATCH 077/335] fix(dtmm): Disable update check when there is no API key Ref: #71. --- crates/dtmm/src/ui/window/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index c8d5bf9..c654ac0 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -54,7 +54,7 @@ fn build_top_bar() -> impl Widget { .on_click(|ctx, _: &mut State, _| { ctx.submit_command(ACTION_START_CHECK_UPDATE); }) - .disabled_if(|data, _| data.is_update_in_progress); + .disabled_if(|data, _| data.nexus_api_key.is_empty() || data.is_update_in_progress); let deploy_button = { let icon = Svg::new(SvgData::from_str(theme::icons::ALERT_CIRCLE).expect("invalid SVG")) From 705ecd8b591b10f3a044c3cf92d260960da7385d Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 Mar 2023 09:56:27 +0100 Subject: [PATCH 078/335] feat(dtmm): Add tooltip to update button Fixes #71. --- Cargo.lock | 21 ++++++++++++++++++--- crates/dtmm/Cargo.toml | 3 ++- crates/dtmm/src/ui/window/main.rs | 23 +++++++++++++++++------ 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 03f77fd..a36e154 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -685,7 +685,8 @@ dependencies = [ [[package]] name = "druid" version = "0.8.3" -source = "git+https://github.com/linebender/druid.git#c02452ddeebc527992e8f112f434f23ce24c934d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ece41814b410c87e6379441caa7316539500b2e387b8d691f2ba5c0f4aff631" dependencies = [ "console_error_panic_hook", "druid-derive", @@ -710,7 +711,8 @@ dependencies = [ [[package]] name = "druid-derive" version = "0.5.0" -source = "git+https://github.com/linebender/druid.git#c02452ddeebc527992e8f112f434f23ce24c934d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1234fc14b0c07e3d4ff2ae8862eb2c24c87e1f3d6eeef0ec6b9d564fe13cef2" dependencies = [ "proc-macro2", "quote", @@ -720,7 +722,8 @@ dependencies = [ [[package]] name = "druid-shell" version = "0.8.3" -source = "git+https://github.com/linebender/druid.git#c02452ddeebc527992e8f112f434f23ce24c934d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7682d9c8fbf934504c30970775bfcfba7858a600f2f6e56bed331989958350fc" dependencies = [ "anyhow", "bitflags", @@ -749,6 +752,17 @@ dependencies = [ "wio", ] +[[package]] +name = "druid-widget-nursery" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fe4e3cbc027e5c9a4f1c135b339ac92aaf99681a7c1b51ff63111b5719194e5" +dependencies = [ + "druid", + "log", + "tracing", +] + [[package]] name = "dtmm" version = "0.1.0" @@ -759,6 +773,7 @@ dependencies = [ "colors-transform", "confy", "druid", + "druid-widget-nursery", "dtmt-shared", "futures", "lazy_static", diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index c98a1e9..2b368ed 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -10,7 +10,7 @@ bitflags = "1.3.2" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } color-eyre = "0.6.2" confy = "0.5.1" -druid = { git = "https://github.com/linebender/druid.git", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] } +druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] } dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } futures = "0.3.25" oodle-sys = { path = "../../lib/oodle-sys", version = "*" } @@ -30,3 +30,4 @@ strip-ansi-escapes = "0.1.1" lazy_static = "1.4.0" colors-transform = "0.2.11" usvg = "0.25.0" +druid-widget-nursery = "0.1" diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index c654ac0..988882e 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -2,16 +2,17 @@ use std::str::FromStr; use std::sync::Arc; use druid::im::Vector; -use druid::lens; use druid::widget::{ Checkbox, CrossAxisAlignment, Either, Flex, Image, Label, LineBreaking, List, MainAxisAlignment, Maybe, Scroll, SizedBox, Split, Svg, SvgData, TextBox, ViewSwitcher, }; +use druid::{lens, Env}; use druid::{ Color, FileDialogOptions, FileSpec, FontDescriptor, FontFamily, LensExt, SingleUse, Widget, WidgetExt, WindowDesc, WindowId, }; use druid::{Data, ImageBuf, LifeCycleCtx}; +use druid_widget_nursery::WidgetExt as _; use lazy_static::lazy_static; use crate::state::{ @@ -50,11 +51,21 @@ fn build_top_bar() -> impl Widget { state.current_view = View::Settings; }); - let check_update_button = Button::with_label("Check for updates") - .on_click(|ctx, _: &mut State, _| { - ctx.submit_command(ACTION_START_CHECK_UPDATE); - }) - .disabled_if(|data, _| data.nexus_api_key.is_empty() || data.is_update_in_progress); + let check_update_button = { + let make_button = || { + Button::with_label("Check for updates").on_click(|ctx, _: &mut State, _| { + ctx.submit_command(ACTION_START_CHECK_UPDATE); + }) + }; + + Either::new( + |data, _| data.nexus_api_key.is_empty(), + make_button() + .tooltip(|_: &State, _: &Env| "A Nexus API key is required") + .disabled_if(|_, _| true), + make_button().disabled_if(|data, _| data.is_update_in_progress), + ) + }; let deploy_button = { let icon = Svg::new(SvgData::from_str(theme::icons::ALERT_CIRCLE).expect("invalid SVG")) From 272f4ef0168315bf066e34f9a26ee6d4e701285a Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 Mar 2023 10:00:09 +0100 Subject: [PATCH 079/335] fix(dtmm): Prevent logging from causing state changes The log view itself still works correctly, as it is lensed onto the field. But ignoring log from general Data comparisons prevents it generating state changes. This also removes the log for unknown commands. In practice, missing to implement a command hasn't been an issue. --- crates/dtmm/src/state/data.rs | 2 ++ crates/dtmm/src/state/delegate.rs | 7 +------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 37d66e7..3f63a2a 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -155,6 +155,8 @@ pub(crate) struct State { pub game_dir: Arc, pub data_dir: Arc, pub nexus_api_key: Arc, + + #[data(ignore)] pub log: Arc, #[lens(ignore)] diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index 4a0fc17..33a2136 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -354,12 +354,7 @@ impl AppDelegate for Delegate { state.is_update_in_progress = false; Handled::Yes } - cmd => { - if cfg!(debug_assertions) { - tracing::warn!("Unknown command: {:?}", cmd); - } - Handled::No - } + _ => Handled::No, } } From 04f76e38e06707a9bb6b0af414c3361423d71372 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 Mar 2023 11:41:51 +0100 Subject: [PATCH 080/335] feat(dtmm): Improve error dialog window Druid doesn't yet implement options necessary to create a "standard" message dialog. So for now, we'll have to approximate that with what we've got. Fixes #70. --- crates/dtmm/src/ui/window/dialog.rs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/crates/dtmm/src/ui/window/dialog.rs b/crates/dtmm/src/ui/window/dialog.rs index 64ce88d..0c2bc18 100644 --- a/crates/dtmm/src/ui/window/dialog.rs +++ b/crates/dtmm/src/ui/window/dialog.rs @@ -1,18 +1,22 @@ use color_eyre::Report; -use druid::widget::{Button, CrossAxisAlignment, Flex, Label, LineBreaking, MainAxisAlignment}; -use druid::{Data, WidgetExt, WindowDesc, WindowHandle, WindowLevel, WindowSizePolicy}; +use druid::widget::{CrossAxisAlignment, Flex, Label, LineBreaking, MainAxisAlignment}; +use druid::{Data, WidgetExt, WindowDesc, WindowHandle, WindowSizePolicy}; -const ERROR_DIALOG_SIZE: (f64, f64) = (750., 400.); +use crate::ui::widget::button::Button; -pub fn error(err: Report, parent: WindowHandle) -> WindowDesc { - let msg = format!("A critical error ocurred: {:?}", err); - let stripped = - strip_ansi_escapes::strip(msg.as_bytes()).expect("failed to strip ANSI in error"); - let msg = String::from_utf8_lossy(&stripped); +pub fn error(err: Report, _parent: WindowHandle) -> WindowDesc { + let msg = { + let msg = format!("A critical error ocurred: {:?}", err); + if let Ok(stripped) = strip_ansi_escapes::strip(msg.as_bytes()) { + String::from_utf8_lossy(&stripped).to_string() + } else { + msg + } + }; - let text = Label::new(msg.to_string()).with_line_break_mode(LineBreaking::WordWrap); + let text = Label::new(msg).with_line_break_mode(LineBreaking::WordWrap); - let button = Button::new("Ok") + let button = Button::with_label("Ok") .on_click(|ctx, _, _| { ctx.window().close(); }) @@ -29,9 +33,8 @@ pub fn error(err: Report, parent: WindowHandle) -> WindowDesc { WindowDesc::new(widget) .title("Error") - .with_min_size(ERROR_DIALOG_SIZE) - .resizable(false) + .show_titlebar(true) .window_size_policy(WindowSizePolicy::Content) .set_always_on_top(true) - .set_level(WindowLevel::Modal(parent)) + .resizable(false) } From 9428b076f06e21603857cc48aa6f8cf1b57193c2 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 Mar 2023 14:14:27 +0100 Subject: [PATCH 081/335] feat(dtmm): Delay initial load Delays the loading of the configuration file and mod data, so that any error can be shown in the UI. Closes #72. --- crates/dtmm/src/controller/app.rs | 103 ++++++++++++++---------- crates/dtmm/src/controller/worker.rs | 24 ++++++ crates/dtmm/src/main.rs | 79 ++++++++++-------- crates/dtmm/src/state/data.rs | 22 ++--- crates/dtmm/src/state/delegate.rs | 24 +++++- crates/dtmm/src/ui/widget/controller.rs | 13 +-- crates/dtmm/src/util/config.rs | 31 ++++--- 7 files changed, 183 insertions(+), 113 deletions(-) diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index b4b69ca..3073b9b 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use std::io::{Cursor, ErrorKind, Read}; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::sync::Arc; use color_eyre::eyre::{self, Context}; @@ -10,12 +10,12 @@ use druid::{FileInfo, ImageBuf}; use dtmt_shared::ModConfig; use nexusmods::Api as NexusApi; use tokio::fs::{self, DirEntry}; -use tokio::runtime::Runtime; use tokio_stream::wrappers::ReadDirStream; use tokio_stream::StreamExt; use zip::ZipArchive; -use crate::state::{ActionState, ModInfo, ModOrder, NexusInfo, PackageInfo}; +use crate::state::{ActionState, InitialLoadResult, ModInfo, ModOrder, NexusInfo, PackageInfo}; +use crate::util; use crate::util::config::{ConfigSerialize, LoadOrderEntry}; use super::read_sjson_file; @@ -198,7 +198,7 @@ pub(crate) async fn save_settings(state: ActionState) -> Result<()> { .await .wrap_err_with(|| { format!( - "failed to write config to '{}'", + "Failed to write config to '{}'", state.config_path.display() ) }) @@ -269,51 +269,47 @@ async fn read_mod_dir_entry(res: Result) -> Result { } #[tracing::instrument(skip(mod_order))] -pub(crate) fn load_mods<'a, P, S>(mod_dir: P, mod_order: S) -> Result>> +pub(crate) async fn load_mods<'a, P, S>(mod_dir: P, mod_order: S) -> Result>> where S: Iterator, P: AsRef + std::fmt::Debug, { - let rt = Runtime::new()?; - - rt.block_on(async move { - let mod_dir = mod_dir.as_ref(); - let read_dir = match fs::read_dir(mod_dir).await { - Ok(read_dir) => read_dir, - Err(err) if err.kind() == ErrorKind::NotFound => { - return Ok(Vector::new()); - } - Err(err) => { - return Err(err) - .wrap_err_with(|| format!("Failed to open directory '{}'", mod_dir.display())); - } - }; - - let stream = ReadDirStream::new(read_dir) - .map(|res| res.wrap_err("Failed to read dir entry")) - .then(read_mod_dir_entry); - tokio::pin!(stream); - - let mut mods: HashMap = HashMap::new(); - - while let Some(res) = stream.next().await { - let info = res?; - mods.insert(info.id.clone(), info); + let mod_dir = mod_dir.as_ref(); + let read_dir = match fs::read_dir(mod_dir).await { + Ok(read_dir) => read_dir, + Err(err) if err.kind() == ErrorKind::NotFound => { + return Ok(Vector::new()); } + Err(err) => { + return Err(err) + .wrap_err_with(|| format!("Failed to open directory '{}'", mod_dir.display())); + } + }; - let mods = mod_order - .filter_map(|entry| { - if let Some(mut info) = mods.remove(&entry.id) { - info.enabled = entry.enabled; - Some(Arc::new(info)) - } else { - None - } - }) - .collect(); + let stream = ReadDirStream::new(read_dir) + .map(|res| res.wrap_err("Failed to read dir entry")) + .then(read_mod_dir_entry); + tokio::pin!(stream); - Ok::<_, color_eyre::Report>(mods) - }) + let mut mods: HashMap = HashMap::new(); + + while let Some(res) = stream.next().await { + let info = res?; + mods.insert(info.id.clone(), info); + } + + let mods = mod_order + .filter_map(|entry| { + if let Some(mut info) = mods.remove(&entry.id) { + info.enabled = entry.enabled; + Some(Arc::new(info)) + } else { + None + } + }) + .collect(); + + Ok(mods) } pub(crate) fn check_mod_order(state: &ActionState) -> Result<()> { @@ -422,3 +418,26 @@ pub(crate) async fn check_updates(state: ActionState) -> Result> { .collect(); Ok(updates) } + +pub(crate) async fn load_initial(path: PathBuf, is_default: bool) -> Result { + let config = util::config::read_config(path, is_default) + .await + .wrap_err("Failed to read config file")?; + + let game_info = tokio::task::spawn_blocking(dtmt_shared::collect_game_info) + .await + .wrap_err("Failed to collect Steam game info")?; + + { + if config.game_dir.is_none() && game_info.is_none() { + tracing::error!("No Game Directory set. Head to the 'Settings' tab to set it manually",); + } + } + + let mod_dir = config.data_dir.join("mods"); + let mods = load_mods(mod_dir, config.mod_order.iter()) + .await + .wrap_err("Failed to load mods")?; + + Ok((config, mods)) +} diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index fafeebe..b30ca9c 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -13,6 +13,7 @@ use crate::controller::app::*; use crate::controller::game::*; use crate::state::AsyncAction; use crate::state::ACTION_FINISH_CHECK_UPDATE; +use crate::state::ACTION_FINISH_LOAD_INITIAL; use crate::state::ACTION_FINISH_SAVE_SETTINGS; use crate::state::ACTION_SHOW_ERROR_DIALOG; use crate::state::{ @@ -144,6 +145,29 @@ async fn handle_action( ) .expect("failed to send command"); }), + AsyncAction::LoadInitial((path, is_default)) => tokio::spawn(async move { + let data = match load_initial(path, is_default) + .await + .wrap_err("Failed to load initial application data") + { + Ok(data) => Some(data), + Err(err) => { + tracing::error!("{:?}", err); + send_error(event_sink.clone(), err).await; + None + } + }; + + event_sink + .write() + .await + .submit_command( + ACTION_FINISH_LOAD_INITIAL, + SingleUse::new(data), + Target::Auto, + ) + .expect("failed to send command"); + }), }; } } diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index ea7bacb..0f43fdd 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -7,19 +7,16 @@ use std::path::PathBuf; use std::sync::Arc; use clap::command; +use clap::parser::ValueSource; use clap::value_parser; use clap::Arg; use color_eyre::eyre; -use color_eyre::eyre::Context; use color_eyre::{Report, Result}; use druid::AppLauncher; -use druid::SingleUse; -use druid::Target; use tokio::sync::RwLock; -use crate::controller::app::load_mods; use crate::controller::worker::work_thread; -use crate::state::ACTION_SHOW_ERROR_DIALOG; +use crate::state::AsyncAction; use crate::state::{Delegate, State}; use crate::ui::theme; @@ -64,41 +61,53 @@ fn main() -> Result<()> { } let (action_tx, action_rx) = tokio::sync::mpsc::unbounded_channel(); - let delegate = Delegate::new(action_tx); + + // let config = util::config::read_config(&default_config_path, &matches) + // .wrap_err("Failed to read config file")?; + // let game_info = dtmt_shared::collect_game_info(); + + // tracing::debug!(?config, ?game_info); + + // let game_dir = config.game_dir.or_else(|| game_info.map(|i| i.path)); + // if game_dir.is_none() { + // let err = + // eyre::eyre!("No Game Directory set. Head to the 'Settings' tab to set it manually",); + // event_sink + // .submit_command(ACTION_SHOW_ERROR_DIALOG, SingleUse::new(err), Target::Auto) + // .expect("failed to send command"); + // } + + // let initial_state = { + // let mut state = State::new( + // config.path, + // game_dir.unwrap_or_default(), + // config.data_dir.unwrap_or_default(), + // config.nexus_api_key.unwrap_or_default(), + // ); + // state.mods = load_mods(state.get_mod_dir(), config.mod_order.iter()) + // .wrap_err("Failed to load mods")?; + // state + // }; + + let config_path = matches + .get_one::("config") + .cloned() + .expect("argument has default value"); + let is_config_default = matches.value_source("config") != Some(ValueSource::DefaultValue); + if action_tx + .send(AsyncAction::LoadInitial((config_path, is_config_default))) + .is_err() + { + let err = eyre::eyre!("Failed to send action"); + return Err(err); + } let launcher = AppLauncher::with_window(ui::window::main::new()) - .delegate(delegate) + .delegate(Delegate::new(action_tx)) .configure_env(theme::set_theme_env); let event_sink = launcher.get_external_handle(); - let config = util::config::read_config(&default_config_path, &matches) - .wrap_err("Failed to read config file")?; - let game_info = dtmt_shared::collect_game_info(); - - tracing::debug!(?config, ?game_info); - - let game_dir = config.game_dir.or_else(|| game_info.map(|i| i.path)); - if game_dir.is_none() { - let err = - eyre::eyre!("No Game Directory set. Head to the 'Settings' tab to set it manually",); - event_sink - .submit_command(ACTION_SHOW_ERROR_DIALOG, SingleUse::new(err), Target::Auto) - .expect("failed to send command"); - } - - let initial_state = { - let mut state = State::new( - config.path, - game_dir.unwrap_or_default(), - config.data_dir.unwrap_or_default(), - config.nexus_api_key.unwrap_or_default(), - ); - state.mods = load_mods(state.get_mod_dir(), config.mod_order.iter()) - .wrap_err("Failed to load mods")?; - state - }; - std::thread::spawn(move || { let event_sink = Arc::new(RwLock::new(event_sink)); let action_rx = Arc::new(RwLock::new(action_rx)); @@ -110,5 +119,5 @@ fn main() -> Result<()> { } }); - launcher.launch(initial_state).map_err(Report::new) + launcher.launch(State::new()).map_err(Report::new) } diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 3f63a2a..8bdcd92 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -158,6 +158,8 @@ pub(crate) struct State { #[data(ignore)] pub log: Arc, + // True, when the initial loading of configuration and mods is still in progress + pub loading: bool, #[lens(ignore)] #[data(ignore)] @@ -174,12 +176,7 @@ impl State { #[allow(non_upper_case_globals)] pub const selected_mod: SelectedModLens = SelectedModLens; - pub fn new( - config_path: PathBuf, - game_dir: PathBuf, - data_dir: PathBuf, - nexus_api_key: String, - ) -> Self { + pub fn new() -> Self { let ctx = sdk::Context::new(); Self { @@ -193,12 +190,13 @@ impl State { is_save_in_progress: false, is_next_save_pending: false, is_update_in_progress: false, - config_path: Arc::new(config_path), - game_dir: Arc::new(game_dir), - data_dir: Arc::new(data_dir), - nexus_api_key: Arc::new(nexus_api_key), + config_path: Arc::new(PathBuf::new()), + game_dir: Arc::new(PathBuf::new()), + data_dir: Arc::new(PathBuf::new()), + nexus_api_key: Arc::new(String::new()), log: Arc::new(String::new()), windows: HashMap::new(), + loading: true, } } @@ -226,10 +224,6 @@ impl State { self.selected_mod_index.map(|i| i > 0).unwrap_or(false) } - pub(crate) fn get_mod_dir(&self) -> PathBuf { - self.data_dir.join("mods") - } - pub(crate) fn add_log_line(&mut self, line: String) { let log = Arc::make_mut(&mut self.log); log.push_str(&line); diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index 33a2136..02c93d4 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -7,7 +7,7 @@ use druid::{ }; use tokio::sync::mpsc::UnboundedSender; -use crate::ui::window; +use crate::{ui::window, util::config::Config}; use super::{ModInfo, State}; @@ -52,6 +52,10 @@ pub(crate) const ACTION_SHOW_ERROR_DIALOG: Selector> = pub(crate) const ACTION_SET_WINDOW_HANDLE: Selector> = Selector::new("dtmm.action.set-window-handle"); +pub(crate) type InitialLoadResult = (Config, Vector>); +pub(crate) const ACTION_FINISH_LOAD_INITIAL: Selector>> = + Selector::new("dtmm.action.finish-load-initial"); + // A sub-selection of `State`'s fields that are required in `AsyncAction`s and that are // `Send + Sync` pub(crate) struct ActionState { @@ -85,6 +89,7 @@ pub(crate) enum AsyncAction { DeleteMod(ActionState, Arc), SaveSettings(ActionState), CheckUpdates(ActionState), + LoadInitial((PathBuf, bool)), } pub(crate) struct Delegate { @@ -354,6 +359,23 @@ impl AppDelegate for Delegate { state.is_update_in_progress = false; Handled::Yes } + cmd if cmd.is(ACTION_FINISH_LOAD_INITIAL) => { + let data = cmd + .get(ACTION_FINISH_LOAD_INITIAL) + .and_then(SingleUse::take) + .expect("command type matched but didn't contain the expected value"); + + if let Some((config, mods)) = data { + state.mods = mods; + state.config_path = Arc::new(config.path); + state.data_dir = Arc::new(config.data_dir); + state.game_dir = Arc::new(config.game_dir.unwrap_or_default()); + } + + state.loading = false; + + Handled::Yes + } _ => Handled::No, } } diff --git a/crates/dtmm/src/ui/widget/controller.rs b/crates/dtmm/src/ui/widget/controller.rs index 45170d4..b6d3806 100644 --- a/crates/dtmm/src/ui/widget/controller.rs +++ b/crates/dtmm/src/ui/widget/controller.rs @@ -78,12 +78,15 @@ impl> Controller for DirtyStateController { data: &State, env: &Env, ) { - if compare_state_fields!(old_data, data, mods, game_dir, data_dir, nexus_api_key) { - ctx.submit_command(ACTION_START_SAVE_SETTINGS); - } + // Only start tracking changes after the initial load has finished + if old_data.loading == data.loading { + if compare_state_fields!(old_data, data, mods, game_dir, data_dir, nexus_api_key) { + ctx.submit_command(ACTION_START_SAVE_SETTINGS); + } - if compare_state_fields!(old_data, data, mods, game_dir) { - ctx.submit_command(ACTION_SET_DIRTY); + if compare_state_fields!(old_data, data, mods, game_dir) { + ctx.submit_command(ACTION_SET_DIRTY); + } } child.update(ctx, old_data, data, env) diff --git a/crates/dtmm/src/util/config.rs b/crates/dtmm/src/util/config.rs index c2f2045..e0fde55 100644 --- a/crates/dtmm/src/util/config.rs +++ b/crates/dtmm/src/util/config.rs @@ -1,11 +1,11 @@ use std::io::ErrorKind; +use std::path::Path; use std::path::PathBuf; use std::sync::Arc; -use std::{fs, path::Path}; -use clap::{parser::ValueSource, ArgMatches}; use color_eyre::{eyre::Context, Result}; use serde::{Deserialize, Serialize}; +use tokio::fs; use crate::state::{ActionState, ModInfo}; @@ -58,7 +58,8 @@ pub(crate) struct LoadOrderEntry { pub(crate) struct Config { #[serde(skip)] pub path: PathBuf, - pub data_dir: Option, + #[serde(default = "get_default_data_dir")] + pub data_dir: PathBuf, pub game_dir: Option, pub nexus_api_key: Option, #[serde(default)] @@ -99,21 +100,19 @@ pub fn get_default_data_dir() -> PathBuf { #[cfg(target_os = "windows")] pub fn get_default_data_dir() -> PathBuf { - let data_dir = std::env::var("APPDATA").expect("appdata env var not set"); + let data_dir = std::env::var("LOCALAPPDATA").expect("appdata env var not set"); PathBuf::from(data_dir).join("dtmm") } -#[tracing::instrument(skip(matches),fields(path = ?matches.get_one::("config")))] -pub(crate) fn read_config

(default: P, matches: &ArgMatches) -> Result +#[tracing::instrument] +pub(crate) async fn read_config

(path: P, is_default: bool) -> Result where P: Into + std::fmt::Debug, { - let path = matches - .get_one::("config") - .expect("argument missing despite default"); - let default_path = default.into(); + let path = path.into(); + let default_path = get_default_config_path(); - match fs::read(path) { + match fs::read(&path).await { Ok(data) => { let data = String::from_utf8(data).wrap_err_with(|| { format!("Config file '{}' contains invalid UTF-8", path.display()) @@ -121,11 +120,11 @@ where let mut cfg: Config = serde_sjson::from_str(&data) .wrap_err_with(|| format!("Invalid config file {}", path.display()))?; - cfg.path = path.clone(); + cfg.path = path; Ok(cfg) } Err(err) if err.kind() == ErrorKind::NotFound => { - if matches.value_source("config") != Some(ValueSource::DefaultValue) { + if !is_default { return Err(err) .wrap_err_with(|| format!("Failed to read config file {}", path.display()))?; } @@ -134,14 +133,14 @@ where let parent = default_path .parent() .expect("a file path always has a parent directory"); - fs::create_dir_all(parent).wrap_err_with(|| { + fs::create_dir_all(parent).await.wrap_err_with(|| { format!("Failed to create directories {}", parent.display()) })?; } let config = Config { path: default_path, - data_dir: Some(get_default_data_dir()), + data_dir: get_default_data_dir(), game_dir: None, nexus_api_key: None, mod_order: Vec::new(), @@ -150,7 +149,7 @@ where { let data = serde_sjson::to_string(&config) .wrap_err("Failed to serialize default config value")?; - fs::write(&config.path, data).wrap_err_with(|| { + fs::write(&config.path, data).await.wrap_err_with(|| { format!( "failed to write default config to {}", config.path.display() From 6513ee5976f8f23bc947c73a918a5699d8a9dae1 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 Mar 2023 17:53:57 +0100 Subject: [PATCH 082/335] fix(dtmt): Fix parameter being positional Fixes #77. --- crates/dtmt/src/cmd/watch.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/dtmt/src/cmd/watch.rs b/crates/dtmt/src/cmd/watch.rs index 54e2274..b9689f3 100644 --- a/crates/dtmt/src/cmd/watch.rs +++ b/crates/dtmt/src/cmd/watch.rs @@ -16,6 +16,8 @@ pub(crate) fn command_definition() -> Command { .about("Watch for file system changes and re-build the mod archive.") .arg( Arg::new("debounce") + .long("debounce") + .short('b') .default_value("150") .value_parser(value_parser!(u64)) .help( From ba753cf6bb52a74d3457d4dee0e54289796bc228 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 10 Mar 2023 23:10:15 +0100 Subject: [PATCH 083/335] feat: Implement static linking, second attempt This is mostly just the code from the previous attempt. All that was missing were the `.lib` files to link to on Windows. --- .gitignore | 5 +- Cargo.lock | 89 +- Cargo.toml | 2 +- crates/dtmm/Cargo.toml | 2 +- crates/dtmm/src/main.rs | 10 - crates/dtmt/Cargo.toml | 2 +- crates/dtmt/src/cmd/build.rs | 10 - crates/dtmt/src/cmd/bundle/mod.rs | 12 +- crates/dtmt/src/cmd/watch.rs | 10 - lib/oodle-sys/.gitignore | 2 - lib/oodle-sys/src/lib.rs | 77 -- lib/oodle-sys/src/library.rs | 154 --- lib/oodle-sys/src/types.rs | 197 ---- lib/{oodle-sys => oodle}/Cargo.toml | 8 +- lib/oodle/build.rs | 51 + lib/oodle/oodle2.h | 1643 +++++++++++++++++++++++++++ lib/oodle/oodle2base.h | 167 +++ lib/oodle/src/lib.rs | 145 +++ lib/sdk/Cargo.toml | 2 +- lib/sdk/src/bundle/mod.rs | 8 +- 20 files changed, 2106 insertions(+), 490 deletions(-) delete mode 100644 lib/oodle-sys/.gitignore delete mode 100644 lib/oodle-sys/src/lib.rs delete mode 100644 lib/oodle-sys/src/library.rs delete mode 100644 lib/oodle-sys/src/types.rs rename lib/{oodle-sys => oodle}/Cargo.toml (69%) create mode 100644 lib/oodle/build.rs create mode 100644 lib/oodle/oodle2.h create mode 100644 lib/oodle/oodle2base.h create mode 100644 lib/oodle/src/lib.rs diff --git a/.gitignore b/.gitignore index 8f1bf6a..4a1b934 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /target /data .envrc -liboo2corelinux64.so -oo2core_8_win64.dll +*.so +*.dll +*.lib dictionary.csv diff --git a/Cargo.lock b/Cargo.lock index a36e154..9cb3b5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -142,6 +142,28 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bindgen" +version = "0.64.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -269,6 +291,15 @@ dependencies = [ "jobserver", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.3", +] + [[package]] name = "cfg-expr" version = "0.11.0" @@ -293,6 +324,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "clang-sys" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.1.8" @@ -778,7 +820,7 @@ dependencies = [ "futures", "lazy_static", "nexusmods", - "oodle-sys", + "oodle", "path-slash", "sdk", "serde", @@ -811,7 +853,7 @@ dependencies = [ "libloading", "nanorand", "notify", - "oodle-sys", + "oodle", "path-clean", "path-slash", "pin-project-lite", @@ -854,6 +896,12 @@ dependencies = [ "wio", ] +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + [[package]] name = "encoding_rs" version = "0.8.32" @@ -1770,6 +1818,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.140" @@ -2160,11 +2214,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] -name = "oodle-sys" +name = "oodle" version = "0.1.0" dependencies = [ - "libloading", - "thiserror", + "bindgen", + "color-eyre", "tracing", ] @@ -2325,6 +2379,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "percent-encoding" version = "2.2.0" @@ -2839,7 +2899,7 @@ dependencies = [ "libloading", "luajit2-sys", "nanorand", - "oodle-sys", + "oodle", "path-slash", "pin-project-lite", "serde", @@ -2968,6 +3028,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -3840,6 +3906,17 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +[[package]] +name = "which" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 6478737..56d5fbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = [ "crates/dtmt", "crates/dtmm", "lib/dtmt-shared", - "lib/oodle-sys", + "lib/oodle", "lib/sdk", "lib/serde_sjson", "lib/steamlocate-rs", diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 2b368ed..4b651cf 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -13,7 +13,7 @@ confy = "0.5.1" druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] } dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } futures = "0.3.25" -oodle-sys = { path = "../../lib/oodle-sys", version = "*" } +oodle = { path = "../../lib/oodle", version = "*" } sdk = { path = "../../lib/sdk", version = "*" } nexusmods = { path = "../../lib/nexusmods", version = "*" } serde_sjson = { path = "../../lib/serde_sjson", version = "*" } diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 0f43fdd..288f1f1 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -37,12 +37,6 @@ fn main() -> Result<()> { tracing::trace!(default_config_path = %default_config_path.display()); let matches = command!() - .arg(Arg::new("oodle").long("oodle").help( - "The oodle library to load. This may either be:\n\ - - A library name that will be searched for in the system's default paths.\n\ - - A file path relative to the current working directory.\n\ - - An absolute file path.", - )) .arg( Arg::new("config") .long("config") @@ -56,10 +50,6 @@ fn main() -> Result<()> { let (log_tx, log_rx) = tokio::sync::mpsc::unbounded_channel(); util::log::create_tracing_subscriber(log_tx); - unsafe { - oodle_sys::init(matches.get_one::("oodle")); - } - let (action_tx, action_rx) = tokio::sync::mpsc::unbounded_channel(); // let config = util::config::read_config(&default_config_path, &matches) diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index f89c27c..22fba15 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -15,7 +15,7 @@ futures-util = "0.3.24" glob = "0.3.0" libloading = "0.7.4" nanorand = "0.7.0" -oodle-sys = { path = "../../lib/oodle-sys", version = "*" } +oodle = { path = "../../lib/oodle", version = "*" } pin-project-lite = "0.2.9" promptly = "0.3.1" sdk = { path = "../../lib/sdk", version = "*" } diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index 9bdddf7..77ab629 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -33,12 +33,6 @@ pub(crate) fn command_definition() -> Command { If omitted, dtmt will search from the current working directory upward.", ), ) - .arg(Arg::new("oodle").long("oodle").help( - "The oodle library to load. This may either be:\n\ - - A library name that will be searched for in the system's default paths.\n\ - - A file path relative to the current working directory.\n\ - - An absolute file path.", - )) .arg( Arg::new("out") .long("out") @@ -391,10 +385,6 @@ where #[tracing::instrument(skip_all)] pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { - unsafe { - oodle_sys::init(matches.get_one::("oodle")); - } - let cfg = read_project_config(matches.get_one::("directory").cloned()).await?; let game_dir = matches diff --git a/crates/dtmt/src/cmd/bundle/mod.rs b/crates/dtmt/src/cmd/bundle/mod.rs index 03ab3f5..0e7c9f7 100644 --- a/crates/dtmt/src/cmd/bundle/mod.rs +++ b/crates/dtmt/src/cmd/bundle/mod.rs @@ -1,4 +1,4 @@ -use clap::{Arg, ArgMatches, Command}; +use clap::{ArgMatches, Command}; use color_eyre::eyre::Result; mod decompress; @@ -10,12 +10,6 @@ pub(crate) fn command_definition() -> Command { Command::new("bundle") .subcommand_required(true) .about("Manipulate the game's bundle files") - .arg(Arg::new("oodle").long("oodle").help( - "The oodle library to load. This may either be:\n\ - - A library name that will be searched for in the system's default paths.\n\ - - A file path relative to the current working directory.\n\ - - An absolute file path.", - )) .subcommand(decompress::command_definition()) .subcommand(extract::command_definition()) .subcommand(inject::command_definition()) @@ -24,10 +18,6 @@ pub(crate) fn command_definition() -> Command { #[tracing::instrument(skip_all)] pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { - unsafe { - oodle_sys::init(matches.get_one::("oodle")); - } - match matches.subcommand() { Some(("decompress", sub_matches)) => decompress::run(ctx, sub_matches).await, Some(("extract", sub_matches)) => extract::run(ctx, sub_matches).await, diff --git a/crates/dtmt/src/cmd/watch.rs b/crates/dtmt/src/cmd/watch.rs index b9689f3..79fab67 100644 --- a/crates/dtmt/src/cmd/watch.rs +++ b/crates/dtmt/src/cmd/watch.rs @@ -34,12 +34,6 @@ pub(crate) fn command_definition() -> Command { If omitted, the current working directory is used.", ), ) - .arg(Arg::new("oodle").long("oodle").help( - "The oodle library to load. This may either be:\n\ - - A library name that will be searched for in the system's default paths.\n\ - - A file path relative to the current working directory.\n\ - - An absolute file path.", - )) .arg( Arg::new("out") .long("out") @@ -104,10 +98,6 @@ where #[tracing::instrument(skip_all)] pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { - unsafe { - oodle_sys::init(matches.get_one::("oodle")); - } - let cfg = read_project_config(matches.get_one::("directory").cloned()) .await .wrap_err("failed to load project config")?; diff --git a/lib/oodle-sys/.gitignore b/lib/oodle-sys/.gitignore deleted file mode 100644 index 4fffb2f..0000000 --- a/lib/oodle-sys/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -/Cargo.lock diff --git a/lib/oodle-sys/src/lib.rs b/lib/oodle-sys/src/lib.rs deleted file mode 100644 index 8346c5f..0000000 --- a/lib/oodle-sys/src/lib.rs +++ /dev/null @@ -1,77 +0,0 @@ -#![feature(c_size_t)] -#![feature(once_cell)] - -use std::ffi::OsStr; -use std::sync::OnceLock; - -mod library; -mod types; - -pub use library::Library; -pub use library::CHUNK_SIZE; -pub use types::*; - -#[derive(thiserror::Error, Debug)] -pub enum OodleError { - #[error("{0}")] - Oodle(String), - #[error(transparent)] - Library(#[from] libloading::Error), -} - -type Result = std::result::Result; - -static LIB: OnceLock = OnceLock::new(); - -/// Initialize the global library handle that this module's -/// functions operate on. -/// -/// # Safety -/// -/// The safety concerns as described by [`libloading::Library::new`] apply. -pub unsafe fn init>(name: Option

) { - let lib = match name { - Some(name) => Library::with_name(name), - None => Library::new(), - }; - - let lib = lib.expect("Failed to load library."); - if LIB.set(lib).is_err() { - panic!("Library was already initialized. Did you call `init` twice?"); - } -} - -fn get() -> Result<&'static Library> { - match LIB.get() { - Some(lib) => Ok(lib), - None => { - let err = OodleError::Oodle(String::from("Library has not been initialized, yet.")); - Err(err) - } - } -} - -pub fn decompress( - data: I, - fuzz_safe: OodleLZ_FuzzSafe, - check_crc: OodleLZ_CheckCRC, -) -> Result> -where - I: AsRef<[u8]>, -{ - let lib = get()?; - lib.decompress(data, fuzz_safe, check_crc) -} - -pub fn compress(data: I) -> Result> -where - I: AsRef<[u8]>, -{ - let lib = get()?; - lib.compress(data) -} - -pub fn get_decode_buffer_size(raw_size: usize, corruption_possible: bool) -> Result { - let lib = get()?; - lib.get_decode_buffer_size(raw_size, corruption_possible) -} diff --git a/lib/oodle-sys/src/library.rs b/lib/oodle-sys/src/library.rs deleted file mode 100644 index ef773e4..0000000 --- a/lib/oodle-sys/src/library.rs +++ /dev/null @@ -1,154 +0,0 @@ -use std::{ffi::OsStr, ptr}; - -use libloading::Symbol; - -use super::Result; -use crate::{types::*, OodleError}; - -// Hardcoded chunk size of Bitsquid's bundle compression -pub const CHUNK_SIZE: usize = 512 * 1024; -pub const COMPRESSOR: OodleLZ_Compressor = OodleLZ_Compressor::Kraken; -pub const LEVEL: OodleLZ_CompressionLevel = OodleLZ_CompressionLevel::Optimal2; - -#[cfg(target_os = "windows")] -const OODLE_LIB_NAME: &str = "oo2core_8_win64"; - -#[cfg(target_os = "linux")] -const OODLE_LIB_NAME: &str = "liboo2corelinux64.so"; - -pub struct Library { - inner: libloading::Library, -} - -impl Library { - /// Load the Oodle library by its default name. - /// - /// The default name is platform-specific: - /// - Windows: `oo2core_8_win64` - /// - Linux: `liboo2corelinux64.so` - /// - /// # Safety - /// - /// The safety concerns as described by [`libloading::Library::new`] apply. - pub unsafe fn new() -> Result { - Self::with_name(OODLE_LIB_NAME) - } - - /// Load the Oodle library by the given name or path. - /// - /// See [`libloading::Library::new`] for how the `name` parameter is handled. - /// - /// # Safety - /// - /// The safety concerns as described by [`libloading::Library::new`] apply. - pub unsafe fn with_name>(name: P) -> Result { - let inner = libloading::Library::new(name)?; - Ok(Self { inner }) - } - - #[tracing::instrument(skip(self, data))] - pub fn decompress( - &self, - data: I, - fuzz_safe: OodleLZ_FuzzSafe, - check_crc: OodleLZ_CheckCRC, - ) -> Result> - where - I: AsRef<[u8]>, - { - let data = data.as_ref(); - let mut out = vec![0; CHUNK_SIZE]; - - let verbosity = if tracing::enabled!(tracing::Level::INFO) { - OodleLZ_Verbosity::Minimal - } else if tracing::enabled!(tracing::Level::DEBUG) { - OodleLZ_Verbosity::Some - } else if tracing::enabled!(tracing::Level::TRACE) { - OodleLZ_Verbosity::Lots - } else { - OodleLZ_Verbosity::None - }; - - let ret = unsafe { - let decompress: Symbol = self.inner.get(b"OodleLZ_Decompress\0")?; - - decompress( - data.as_ptr() as *const _, - data.len(), - out.as_mut_ptr() as *mut _, - out.len(), - fuzz_safe, - check_crc, - verbosity, - ptr::null_mut(), - 0, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - 0, - OodleLZ_Decode_ThreadPhase::UNTHREADED, - ) - }; - - if ret == 0 { - let err = OodleError::Oodle(String::from("Decompression failed.")); - return Err(err); - } - - Ok(out) - } - - #[tracing::instrument(name = "Oodle::compress", skip(self, data))] - pub fn compress(&self, data: I) -> Result> - where - I: AsRef<[u8]>, - { - let mut raw = Vec::from(data.as_ref()); - raw.resize(CHUNK_SIZE, 0); - - // TODO: Query oodle for buffer size - let mut out = vec![0u8; CHUNK_SIZE]; - - let ret = unsafe { - let compress: Symbol = self.inner.get(b"OodleLZ_Compress\0")?; - - compress( - COMPRESSOR, - raw.as_ptr() as *const _, - raw.len(), - out.as_mut_ptr() as *mut _, - LEVEL, - ptr::null_mut(), - 0, - ptr::null_mut(), - ptr::null_mut(), - 0, - ) - }; - - tracing::debug!(compressed_size = ret, "Compressed chunk"); - - if ret == 0 { - let err = OodleError::Oodle(String::from("Compression failed.")); - return Err(err); - } - - out.resize(ret as usize, 0); - - Ok(out) - } - - pub fn get_decode_buffer_size( - &self, - raw_size: usize, - corruption_possible: bool, - ) -> Result { - unsafe { - let f: Symbol = - self.inner.get(b"OodleLZ_GetDecodeBufferSize\0")?; - - let size = f(COMPRESSOR, raw_size, corruption_possible); - Ok(size) - } - } -} diff --git a/lib/oodle-sys/src/types.rs b/lib/oodle-sys/src/types.rs deleted file mode 100644 index 5d306f8..0000000 --- a/lib/oodle-sys/src/types.rs +++ /dev/null @@ -1,197 +0,0 @@ -#![allow(dead_code)] -use core::ffi::{c_char, c_int, c_size_t, c_ulonglong, c_void}; - -// Type definitions taken from Unreal Engine's `oodle2.h` - -#[repr(C)] -#[allow(non_camel_case_types)] -#[derive(Clone, Copy, Debug)] -pub enum OodleLZ_FuzzSafe { - No = 0, - Yes = 1, -} - -impl From for OodleLZ_FuzzSafe { - fn from(value: bool) -> Self { - if value { - Self::Yes - } else { - Self::No - } - } -} - -#[repr(C)] -#[allow(non_camel_case_types)] -#[derive(Clone, Copy, Debug)] -pub enum OodleLZ_CheckCRC { - No = 0, - Yes = 1, - Force32 = 0x40000000, -} - -impl From for OodleLZ_CheckCRC { - fn from(value: bool) -> Self { - if value { - Self::Yes - } else { - Self::No - } - } -} - -#[repr(C)] -#[allow(non_camel_case_types)] -#[derive(Clone, Copy, Debug)] -pub enum OodleLZ_Verbosity { - None = 0, - Minimal = 1, - Some = 2, - Lots = 3, - Force32 = 0x40000000, -} - -#[repr(C)] -#[allow(non_camel_case_types)] -#[derive(Clone, Copy, Debug)] -pub enum OodleLZ_Decode_ThreadPhase { - Phase1 = 1, - Phase2 = 2, - PhaseAll = 3, -} - -impl OodleLZ_Decode_ThreadPhase { - pub const UNTHREADED: Self = OodleLZ_Decode_ThreadPhase::PhaseAll; -} - -#[repr(C)] -#[allow(non_camel_case_types)] -#[derive(Clone, Copy, Debug)] -pub enum OodleLZ_Compressor { - Invalid = -1, - // None = memcpy, pass through uncompressed bytes - None = 3, - - // NEW COMPRESSORS: - // Fast decompression and high compression ratios, amazing! - Kraken = 8, - // Leviathan = Kraken's big brother with higher compression, slightly slower decompression. - Leviathan = 13, - // Mermaid is between Kraken & Selkie - crazy fast, still decent compression. - Mermaid = 9, - // Selkie is a super-fast relative of Mermaid. For maximum decode speed. - Selkie = 11, - // Hydra, the many-headed beast = Leviathan, Kraken, Mermaid, or Selkie (see $OodleLZ_About_Hydra) - Hydra = 12, - BitKnit = 10, - // DEPRECATED but still supported - Lzb16 = 4, - Lzna = 7, - Lzh = 0, - Lzhlw = 1, - Lznib = 2, - Lzblw = 5, - Lza = 6, - Count = 14, - Force32 = 0x40000000, -} - -#[repr(C)] -#[allow(non_camel_case_types)] -#[derive(Clone, Copy, Debug)] -pub enum OodleLZ_CompressionLevel { - // don't compress, just copy raw bytes - None = 0, - // super fast mode, lower compression ratio - SuperFast = 1, - // fastest LZ mode with still decent compression ratio - VeryFast = 2, - // fast - good for daily use - Fast = 3, - // standard medium speed LZ mode - Normal = 4, - // optimal parse level 1 (faster optimal encoder) - Optimal1 = 5, - // optimal parse level 2 (recommended baseline optimal encoder) - Optimal2 = 6, - // optimal parse level 3 (slower optimal encoder) - Optimal3 = 7, - // optimal parse level 4 (very slow optimal encoder) - Optimal4 = 8, - // optimal parse level 5 (don't care about encode speed, maximum compression) - Optimal5 = 9, - // faster than SuperFast, less compression - HyperFast1 = -1, - // faster than HyperFast1, less compression - HyperFast2 = -2, - // faster than HyperFast2, less compression - HyperFast3 = -3, - // fastest, less compression - HyperFast4 = -4, - Force32 = 0x40000000, -} - -impl OodleLZ_CompressionLevel { - // alias hyperfast base level - pub const HYPERFAST: Self = OodleLZ_CompressionLevel::HyperFast1; - // alias optimal standard level - pub const OPTIMAL: Self = OodleLZ_CompressionLevel::Optimal2; - // maximum compression level - pub const MAX: Self = OodleLZ_CompressionLevel::Optimal5; - // fastest compression level - pub const MIN: Self = OodleLZ_CompressionLevel::HyperFast4; - pub const INVALID: Self = OodleLZ_CompressionLevel::Force32; -} - -#[allow(non_camel_case_types)] -pub type t_fp_OodleCore_Plugin_Printf = - extern "C" fn(level: c_int, file: *const c_char, line: c_int, fmt: *const c_char); - -#[allow(non_camel_case_types)] -pub type OodleLZ_Decompress = extern "C" fn( - compressed_buffer: *const c_void, - compressed_length: c_size_t, - raw_buffer: *mut c_void, - raw_length: c_size_t, - fuzz_safe: OodleLZ_FuzzSafe, - check_crc: OodleLZ_CheckCRC, - verbosity: OodleLZ_Verbosity, - decBufBase: *mut c_void, - decBufSize: c_size_t, - callback: *const c_void, - callback_user_data: *const c_void, - decoder_memory: *mut c_void, - decoder_memory_size: c_size_t, - thread_phase: OodleLZ_Decode_ThreadPhase, -) -> c_ulonglong; - -#[allow(non_camel_case_types)] -pub type OodleLZ_Compress = extern "C" fn( - compressor: OodleLZ_Compressor, - raw_buffer: *const c_void, - raw_len: c_size_t, - compressed_buffer: *mut c_void, - level: OodleLZ_CompressionLevel, - options: *const c_void, - dictionary_base: c_size_t, - lrm: *const c_void, - scratch_memory: *mut c_void, - scratch_size: c_size_t, -) -> c_ulonglong; - -#[allow(non_camel_case_types)] -pub type OodleLZ_GetDecodeBufferSize = extern "C" fn( - compressor: OodleLZ_Compressor, - raw_size: c_size_t, - corruption_possible: bool, -) -> c_size_t; - -#[allow(non_camel_case_types)] -pub type OodleCore_Plugins_SetPrintf = - extern "C" fn(f: t_fp_OodleCore_Plugin_Printf) -> t_fp_OodleCore_Plugin_Printf; - -#[allow(non_camel_case_types)] -pub type OodleCore_Plugin_Printf_Verbose = t_fp_OodleCore_Plugin_Printf; - -#[allow(non_camel_case_types)] -pub type OodleCore_Plugin_Printf_Default = t_fp_OodleCore_Plugin_Printf; diff --git a/lib/oodle-sys/Cargo.toml b/lib/oodle/Cargo.toml similarity index 69% rename from lib/oodle-sys/Cargo.toml rename to lib/oodle/Cargo.toml index 539427d..3283592 100644 --- a/lib/oodle-sys/Cargo.toml +++ b/lib/oodle/Cargo.toml @@ -1,11 +1,13 @@ [package] -name = "oodle-sys" +name = "oodle" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -libloading = "0.7.4" -thiserror = "1.0.38" +color-eyre = "0.6.2" tracing = "0.1.37" + +[build-dependencies] +bindgen = "0.64.0" diff --git a/lib/oodle/build.rs b/lib/oodle/build.rs new file mode 100644 index 0000000..cc416c5 --- /dev/null +++ b/lib/oodle/build.rs @@ -0,0 +1,51 @@ +extern crate bindgen; + +use std::env; +use std::path::PathBuf; + +fn main() { + // Tell cargo to look for shared libraries in the specified directory + if let Ok(manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") { + println!("cargo:rustc-link-search={}", manifest_dir); + dbg!(&manifest_dir); + } + + let lib_name = if std::env::var("CARGO_CFG_WINDOWS").is_ok() { + if cfg!(debug_assertions) { + "oo2core_win64_debug" + } else { + "oo2core_win64" + } + } else { + "oo2corelinux64" + }; + + println!("cargo:rustc-link-lib={}", lib_name); + dbg!(&lib_name); + + println!("cargo:rerun-if-changed=oodle2.h"); + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header("oodle2base.h") + .header("oodle2.h") + .blocklist_file("stdint.h") + .blocklist_file("stdlib.h") + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/lib/oodle/oodle2.h b/lib/oodle/oodle2.h new file mode 100644 index 0000000..ffe4152 --- /dev/null +++ b/lib/oodle/oodle2.h @@ -0,0 +1,1643 @@ + +//=================================================== +// Oodle2 Core header +// (C) Copyright 1994-2021 Epic Games Tools LLC +//=================================================== + +#ifndef __OODLE2_H_INCLUDED__ +#define __OODLE2_H_INCLUDED__ + +#ifndef OODLE2_PUBLIC_HEADER +#define OODLE2_PUBLIC_HEADER 1 +#endif + +#ifndef __OODLE2BASE_H_INCLUDED__ +#include "oodle2base.h" +#endif + +#ifdef _MSC_VER +#pragma pack(push, Oodle, 8) + +#pragma warning(push) +#pragma warning(disable : 4127) // conditional is constant +#endif + +// header version : +// the DLL is incompatible when MAJOR is bumped +// MINOR is for internal revs and bug fixes that don't affect API compatibility +#define OODLE2_VERSION_MAJOR 9 +#define OODLE2_VERSION_MINOR 5 + +// OodleVersion string is 1 . MAJOR . MINOR +// don't make it from macros cuz the doc tool has to parse the string literal + +#define OodleVersion "2.9.5" /* +*/ + +//----------------------------------------------------- +// OodleLZ + +#if 0 +#define OODLE_ALLOW_DEPRECATED_COMPRESSORS /* If you need to encode with the deprecated compressors, define this before including oodle2.h + + You may still decode with them without defining this. +*/ +#endif + +// Default verbosity selection of 0 will not even log when it sees corruption +typedef enum OodleLZ_Verbosity +{ + OodleLZ_Verbosity_None = 0, + OodleLZ_Verbosity_Minimal = 1, + OodleLZ_Verbosity_Some = 2, + OodleLZ_Verbosity_Lots = 3, + OodleLZ_Verbosity_Force32 = 0x40000000 +} OodleLZ_Verbosity; +/* Verbosity of LZ functions + LZ functions print information to the function set by $OodleCore_Plugins_SetPrintf + or $OodleXLog_Printf if using OodleX. +*/ + +OO_COMPILER_ASSERT( sizeof(OodleLZ_Verbosity) == 4 ); + +typedef enum OodleLZ_Compressor +{ + OodleLZ_Compressor_Invalid = -1, + OodleLZ_Compressor_None = 3, // None = memcpy, pass through uncompressed bytes + + // NEW COMPRESSORS : + OodleLZ_Compressor_Kraken = 8, // Fast decompression and high compression ratios, amazing! + OodleLZ_Compressor_Leviathan = 13,// Leviathan = Kraken's big brother with higher compression, slightly slower decompression. + OodleLZ_Compressor_Mermaid = 9, // Mermaid is between Kraken & Selkie - crazy fast, still decent compression. + OodleLZ_Compressor_Selkie = 11, // Selkie is a super-fast relative of Mermaid. For maximum decode speed. + OodleLZ_Compressor_Hydra = 12, // Hydra, the many-headed beast = Leviathan, Kraken, Mermaid, or Selkie (see $OodleLZ_About_Hydra) + +#ifdef OODLE_ALLOW_DEPRECATED_COMPRESSORS + OodleLZ_Compressor_BitKnit = 10, // no longer supported as of Oodle 2.9.0 + OodleLZ_Compressor_LZB16 = 4, // DEPRECATED but still supported + OodleLZ_Compressor_LZNA = 7, // no longer supported as of Oodle 2.9.0 + OodleLZ_Compressor_LZH = 0, // no longer supported as of Oodle 2.9.0 + OodleLZ_Compressor_LZHLW = 1, // no longer supported as of Oodle 2.9.0 + OodleLZ_Compressor_LZNIB = 2, // no longer supported as of Oodle 2.9.0 + OodleLZ_Compressor_LZBLW = 5, // no longer supported as of Oodle 2.9.0 + OodleLZ_Compressor_LZA = 6, // no longer supported as of Oodle 2.9.0 +#endif + + OodleLZ_Compressor_Count = 14, + OodleLZ_Compressor_Force32 = 0x40000000 +} OodleLZ_Compressor; +/* Selection of compression algorithm. + + Each compressor provides a different balance of speed vs compression ratio. + + New Oodle users should only use the new sea monster family of compressors. + + The OODLE_ALLOW_DEPRECATED_COMPRESSORS set of compressors is no longer supported + as of Oodle 2.9.0 ; see $Oodle_FAQ_deprecated_compressors + + The sea monsters are all fuzz safe and use whole-block quantum (not the 16k quantum) + ($OodleLZ_Compressor_UsesWholeBlockQuantum) + + If you need to encode the deprecated compressors, define $OODLE_ALLOW_DEPRECATED_COMPRESSORS before + including oodle2.h + + See $Oodle_FAQ_WhichLZ for a quick FAQ on which compressor to use + + See $OodleLZ_About for discussion of how to choose a compressor. +*/ + +OO_COMPILER_ASSERT( sizeof(OodleLZ_Compressor) == 4 ); + +typedef enum OodleLZ_PackedRawOverlap +{ + OodleLZ_PackedRawOverlap_No = 0, + OodleLZ_PackedRawOverlap_Yes = 1, + OodleLZ_PackedRawOverlap_Force32 = 0x40000000 +} OodleLZ_PackedRawOverlap; +/* Bool enum +*/ + +typedef enum OodleLZ_CheckCRC +{ + OodleLZ_CheckCRC_No = 0, + OodleLZ_CheckCRC_Yes = 1, + OodleLZ_CheckCRC_Force32 = 0x40000000 +} OodleLZ_CheckCRC; +/* Bool enum for the LZ decoder - should it check CRC before decoding or not? + + NOTE : the CRC's in the LZH decompress checks are the CRC's of the *compressed* bytes. This allows checking the CRc + prior to decompression, so corrupted data cannot be fed to the compressor. + + To use OodleLZ_CheckCRC_Yes, the compressed data must have been made with $(OodleLZ_CompressOptions:sendQuantumCRCs) set to true. + + If you want a CRC of the raw bytes, there is one optionally stored in the $OodleLZ_SeekTable and can be confirmed with + $OodleLZ_CheckSeekTableCRCs +*/ + + +typedef enum OodleLZ_Profile +{ + OodleLZ_Profile_Main=0, // Main profile (all current features allowed) + OodleLZ_Profile_Reduced=1, // Reduced profile (Kraken only, limited feature set) + OodleLZ_Profile_Force32 = 0x40000000 +} OodleLZ_Profile; +/* Decode profile to target */ + +// Not flagged for idoc and done using a #define since it's internal (testing) use only +#define OodleLZ_Profile_Internal_Custom ((OodleLZ_Profile)100) + +OO_COMPILER_ASSERT( sizeof(OodleLZ_Profile) == 4 ); + +typedef enum OodleDecompressCallbackRet +{ + OodleDecompressCallbackRet_Continue=0, + OodleDecompressCallbackRet_Cancel=1, + OodleDecompressCallbackRet_Invalid=2, + OodleDecompressCallbackRet_Force32 = 0x40000000 +} OodleDecompressCallbackRet; +/* Return value for $OodleDecompressCallback + return OodleDecompressCallbackRet_Cancel to abort the in-progress decompression +*/ + +OODEFFUNC typedef OodleDecompressCallbackRet (OODLE_CALLBACK OodleDecompressCallback)(void * userdata, const OO_U8 * rawBuf,OO_SINTa rawLen,const OO_U8 * compBuf,OO_SINTa compBufferSize , OO_SINTa rawDone, OO_SINTa compUsed); +/* User-provided callback for decompression + + $:userdata the data you passed for _pcbData_ + $:rawBuf the decompressed buffer + $:rawLen the total decompressed length + $:compBuf the compressed buffer + $:compBufferSize the total compressed length + $:rawDone number of bytes in rawBuf decompressed so far + $:compUsed number of bytes in compBuf consumed so far + + OodleDecompressCallback is called incrementally during decompression. +*/ + +typedef enum OodleLZ_CompressionLevel +{ + OodleLZ_CompressionLevel_None=0, // don't compress, just copy raw bytes + OodleLZ_CompressionLevel_SuperFast=1, // super fast mode, lower compression ratio + OodleLZ_CompressionLevel_VeryFast=2, // fastest LZ mode with still decent compression ratio + OodleLZ_CompressionLevel_Fast=3, // fast - good for daily use + OodleLZ_CompressionLevel_Normal=4, // standard medium speed LZ mode + + OodleLZ_CompressionLevel_Optimal1=5, // optimal parse level 1 (faster optimal encoder) + OodleLZ_CompressionLevel_Optimal2=6, // optimal parse level 2 (recommended baseline optimal encoder) + OodleLZ_CompressionLevel_Optimal3=7, // optimal parse level 3 (slower optimal encoder) + OodleLZ_CompressionLevel_Optimal4=8, // optimal parse level 4 (very slow optimal encoder) + OodleLZ_CompressionLevel_Optimal5=9, // optimal parse level 5 (don't care about encode speed, maximum compression) + + OodleLZ_CompressionLevel_HyperFast1=-1, // faster than SuperFast, less compression + OodleLZ_CompressionLevel_HyperFast2=-2, // faster than HyperFast1, less compression + OodleLZ_CompressionLevel_HyperFast3=-3, // faster than HyperFast2, less compression + OodleLZ_CompressionLevel_HyperFast4=-4, // fastest, less compression + + // aliases : + OodleLZ_CompressionLevel_HyperFast=OodleLZ_CompressionLevel_HyperFast1, // alias hyperfast base level + OodleLZ_CompressionLevel_Optimal = OodleLZ_CompressionLevel_Optimal2, // alias optimal standard level + OodleLZ_CompressionLevel_Max = OodleLZ_CompressionLevel_Optimal5, // maximum compression level + OodleLZ_CompressionLevel_Min = OodleLZ_CompressionLevel_HyperFast4, // fastest compression level + + OodleLZ_CompressionLevel_Force32 = 0x40000000, + OodleLZ_CompressionLevel_Invalid = OodleLZ_CompressionLevel_Force32 +} OodleLZ_CompressionLevel; +/* Selection of compression encoder complexity + + Higher numerical value of CompressionLevel = slower compression, but smaller compressed data. + + The compressed stream is always decodable with the same decompressors. + CompressionLevel controls the amount of work the encoder does to find the best compressed bit stream. + CompressionLevel does not primary affect decode speed, it trades off encode speed for compressed bit stream quality. + + I recommend starting with OodleLZ_CompressionLevel_Normal, then try up or down if you want + faster encoding or smaller output files. + + The Optimal levels are good for distribution when you compress rarely and decompress often; + they provide very high compression ratios but are slow to encode. Optimal2 is the recommended level + to start with of the optimal levels. + Optimal4 and 5 are not recommended for common use, they are very slow and provide the maximum compression ratio, + but the gain over Optimal3 is usually small. + + The HyperFast levels have negative numeric CompressionLevel values. + They are faster than SuperFast for when you're encoder CPU time constrained or want + something closer to symmetric compression vs. decompression time. + The HyperFast levels are currently only available in Kraken, Mermaid & Selkie. + Higher levels of HyperFast are faster to encode, eg. HyperFast4 is the fastest. + + The CompressionLevel does not affect decode speed much. Higher compression level does not mean + slower to decode. To trade off decode speed vs ratio, use _spaceSpeedTradeoffBytes_ in $OodleLZ_CompressOptions + +*/ + +OO_COMPILER_ASSERT( sizeof(OodleLZ_CompressionLevel) == 4 ); + +typedef enum OodleLZ_Jobify +{ + OodleLZ_Jobify_Default=0, // Use compressor default for level of internal job usage + OodleLZ_Jobify_Disable=1, // Don't use jobs at all + OodleLZ_Jobify_Normal=2, // Try to balance parallelism with increased memory usage + OodleLZ_Jobify_Aggressive=3, // Maximize parallelism even when doing so requires large amounts of memory + OodleLZ_Jobify_Count=4, + + OodleLZ_Jobify_Force32 = 0x40000000, +} OodleLZ_Jobify; +/* Controls the amount of internal threading in $OodleLZ_Compress calls + + Once you install a pluggable job system via $OodleCore_Plugins_SetJobSystem, Oodle can internally break + heavy-weight compression tasks into smaller jobs that can run in parallel. This can speed up + compression of large blocks of data at Optimal1 and higher levels substantially. + + The trade-off is that running more jobs concurrently rather than sequentially can greatly increase + memory requirements when there are multiple outstanding memory-intensive jobs. + + OodleLZ_Jobify_Default lets the compressor decide; typically compressors will default to "Normal" + when a pluggable job system has been installed, and "Disable" otherwise. + + OodleLZ_Jobify_Disable disables use of internal jobs entirely; all compression work is done on + the calling thread. This minimizes the amount of memory used, and is also appropriate when you're + getting parallelism in other ways, e.g. by running OodleLZ_Compress on many threads yourself. + + OodleLZ_Jobify_Normal uses jobs to increase compressor parallelism and speeds up compression of + large blocks of data, but avoids handing out many concurrent jobs for tasks that are memory-intensive. + + OodleLZ_Jobify_Aggressive will use concurrent jobs even for highly memory-intensive tasks. This + can speed up things further, but at a potentially significant increase in the amount of memory used + by Oodle. + +*/ + +#define OODLELZ_LOCALDICTIONARYSIZE_MAX (1<<30) /* Maximum value of maxLocalDictionarySize in OodleLZ_CompressOptions +*/ + +#define OODLELZ_SPACESPEEDTRADEOFFBYTES_DEFAULT (256) /* Default value of spaceSpeedTradeoffBytes in OodleLZ_CompressOptions + Changes how the encoder makes decisions in the bit stream + Higher spaceSpeedTradeoffBytes favors decode speed more (larger compressed files) + Lower spaceSpeedTradeoffBytes favors smaller compressed files (slower decoder) + Goes in a power of 2 scale; so try 64,128 and 512,1024 + (OODLELZ_SPACESPEEDTRADEOFFBYTES_DEFAULT/2) or (OODLELZ_SPACESPEEDTRADEOFFBYTES_DEFAULT*2) +*/ + + +typedef OOSTRUCT OodleLZ_CompressOptions +{ + OO_U32 unused_was_verbosity; // unused ; was verbosity (set to zero) + OO_S32 minMatchLen; // minimum match length ; cannot be used to reduce a compressor's default MML, but can be higher. On some types of data, a large MML (6 or 8) is a space-speed win. + OO_BOOL seekChunkReset; // whether chunks should be independent, for seeking and parallelism + OO_S32 seekChunkLen; // length of independent seek chunks (if seekChunkReset) ; must be a power of 2 and >= $OODLELZ_BLOCK_LEN ; you can use $OodleLZ_MakeSeekChunkLen + OodleLZ_Profile profile; // decoder profile to target (set to zero) + OO_S32 dictionarySize; // sets a maximum offset for matches, if lower than the maximum the format supports. <= 0 means infinite (use whole buffer). Often power of 2 but doesn't have to be. + OO_S32 spaceSpeedTradeoffBytes; // this is a number of bytes; I must gain at least this many bytes of compressed size to accept a speed-decreasing decision + OO_S32 unused_was_maxHuffmansPerChunk; // unused ; was maxHuffmansPerChunk + OO_BOOL sendQuantumCRCs; // should the encoder send a CRC of each compressed quantum, for integrity checks; this is necessary if you want to use OodleLZ_CheckCRC_Yes on decode + OO_S32 maxLocalDictionarySize; // (Optimals) size of local dictionary before needing a long range matcher. This does not set a window size for the decoder; it's useful to limit memory use and time taken in the encoder. maxLocalDictionarySize must be a power of 2. Must be <= OODLELZ_LOCALDICTIONARYSIZE_MAX + OO_BOOL makeLongRangeMatcher; // (Optimals) should the encoder find matches beyond maxLocalDictionarySize using an LRM + OO_S32 matchTableSizeLog2; //(non-Optimals) when variable, sets the size of the match finder structure (often a hash table) ; use 0 for the compressor's default + + OodleLZ_Jobify jobify; // controls internal job usage by compressors + void * jobifyUserPtr; // user pointer passed through to RunJob and WaitJob callbacks + + OO_S32 farMatchMinLen; // far matches must be at least this len + OO_S32 farMatchOffsetLog2; // if not zero, the log2 of an offset that must meet farMatchMinLen + + OO_U32 reserved[4]; // reserved space for adding more options; zero these! +} OodleLZ_CompressOptions; +/* Options for the compressor + + Typically filled by calling $OodleLZ_CompressOptions_GetDefault , then individual options may be modified, like : + + OodleLZ_CompressOptions my_options = *OodleLZ_CompressOptions_GetDefault() + + To ensure you have set up the options correctly, call $OodleLZ_CompressOptions_Validate. + + _unused_was_verbosity_ : place holder, set to zero + + _minMatchLen_ : rarely useful. Default value of 0 means let the compressor decide. On some types of data, + bumping this up to 4,6, or 8 can improve decode speed with little effect on compression ratio. Most of the + Oodle compressors use a default MML of 4 at levels below 7, and MML 3 at levels >= 7. If you want to keep MML 4 + at the higher levels, set _minMatchLen_ here to 4. _minMatchLen_ cannot be used to reduce the base MML of the compressor, only to increase it. + + _seekChunkReset_ must be true if you want the decode to be able to run "Wide", with pieces that can be + decoded independently (not keeping previous pieces in memory for match references). + + _seekChunkLen_ : length of independent seek chunks (if seekChunkReset) ; must be a power of 2 and >= $OODLELZ_BLOCK_LEN ; you can use $OodleLZ_MakeSeekChunkLen + + _profile_ : tells the encoder to target alternate bitstream profile. Default value of zero for normal use. + + _dictionarySize_ : limits the encoder to partial buffer access for matches. Can be useful for decoding incrementally + without keeping the entire output buffer in memory. + + _spaceSpeedTradeoffBytes_ is a way to trade off compression ratio for decode speed. If you make it smaller, + you get more compression ratio and slower decodes. It's the number of bytes that a decision must save to + be worth a slower decode. Default is 256 (OODLELZ_SPACESPEEDTRADEOFFBYTES_DEFAULT). So that means the encoder must be able to save >= 256 bytes to + accept something that will slow down decoding (like adding another Huffman table). The typical range is + 64-1024. + + Lower _spaceSpeedTradeoffBytes_ = more compression, slower decode + Higher _spaceSpeedTradeoffBytes_ = less compression, faster decode + + _spaceSpeedTradeoffBytes_ is the primary parameter for controlling Hydra. The default value of 256 will make + Hydra decodes that are just a little bit faster than Kraken. You get Kraken speeds around 200, and Mermaid + speeds around 1200. + + At the extreme, a _spaceSpeedTradeoffBytes_ of zero would mean all you care about is compression ratio, not decode + speed, you want the encoder to make the smallest possible output. (you cannot actually set zero, as zero values + always mean "use default" in this struct; you never really want zero anyway) + Generally _spaceSpeedTradeoffBytes_ below 16 provides diminishing gains in size with pointless decode speed loss. + + _spaceSpeedTradeoffBytes_ is on sort of powers of 2 scale, so you might want to experiment with 32,64,128,256,512 + + _spaceSpeedTradeoffBytes_ outside the range [16 - 2048] is not recommended. + + _unused_was_maxHuffmansPerChunk_ : place holder, set to zero + + _sendQuantumCRCs_ : send hashes of the compressed data to verify in the decoder; not recommended, if you need data + verification, use your own system outside of Oodle. DEPRECATED, not recommended. For backwards compatibility only. + + _maxLocalDictionarySize_ : only applies to optimal parsers at level >= Optimal2. This limits the encoder memory use. + Making it larger = more compression, higher memory use. Matches within maxLocalDictionarySize are found exactly, + outside the maxLocalDictionarySize window an approximate long range matcher is used. + + _makeLongRangeMatcher_ : whether an LRM should be used to find matches outside the _maxLocalDictionarySize_ window + (Optimal levels only) + + _matchTableSizeLog2_ : for non-optimal levels (level <= Normal), controls the hash table size. Making this very + small can sometimes boost encoder speed. For the very fastest encoding, use the SuperFast level and change + _matchTableSizeLog2_ to 12 or 13. + + _matchTableSizeLog2_ should usually be left zero to use the encoder's default + + _matchTableSizeLog2_ allows you to limit memory use of the non-Optimal encoder levels. Memory use is roughly + ( 1 MB + 4 << matchTableSizeLog2 ) + + _jobify_ tells compressors how to use internal jobs for compression tasks. Jobs can be run in parallel using the + job system plugins set with $OodleCore_Plugins_SetJobSystem. Not all compressors or compression level support + jobs, but the slower ones generally do. The default value of jobify is to use a thread system if one is installed. + + _farMatchMinLen_ and _farMatchOffsetLog2_ can be used to tune the encoded stream for a known cache size on the + decoding hardware. If set, then offsets with log2 greater or each to _farMatchOffsetLog2_ must have a minimum + length of _farMatchMinLen_. For example to target a machine with a 2 MB cache, set _farMatchOffsetLog2_ to 21, + and _farMatchMinLen_ to something large, like 16 or 20. + + Without _farMatchMinLen_ and _farMatchOffsetLog2_ set, the Oodle encoders tune for a blend of cache sizes that works + well on most machines. _dictionarySize_ can also be used to tune for cache size, but cuts off all matches + beyond a certain distance. That may be more appropriate when you don't want to go out of cache at all. + _farMatchMinLen_ can only be used to make the standard blend target more restrictive; it can reduce the target cache size + but can't make it larger (or it can raise min match len outside cache but can't make it shorter). + + For help on setting up OodleLZ_CompressOptions contact support at oodle@radgametools.com + + NOTE : fields you do not set should always be zero initialized. In particular the _reserved_ fields should be zeroed. + Zero always means "use default" and is a future-portable initialization value. + + If you set fields to zero to mean "use default" you can call $OodleLZ_CompressOptions_Validate to change them + to default values. This is done automatically internally if you don't do it explicitly. + +*/ + +typedef enum OodleLZ_Decode_ThreadPhase +{ + OodleLZ_Decode_ThreadPhase1 = 1, + OodleLZ_Decode_ThreadPhase2 = 2, + OodleLZ_Decode_ThreadPhaseAll = 3, + OodleLZ_Decode_Unthreaded = OodleLZ_Decode_ThreadPhaseAll +} OodleLZ_Decode_ThreadPhase; +/* ThreadPhase for threaded Oodle decode + + Check $OodleLZ_Compressor_CanDecodeThreadPhased + (currently only used by Kraken) + + See $OodleLZ_About_ThreadPhasedDecode + +*/ + +typedef enum OodleLZ_FuzzSafe +{ + OodleLZ_FuzzSafe_No = 0, + OodleLZ_FuzzSafe_Yes = 1 +} OodleLZ_FuzzSafe; +/* OodleLZ_FuzzSafe (deprecated) + + About fuzz safety: + + Fuzz Safe decodes will not crash on corrupt data. They may or may not return failure, and produce garbage output. + + Fuzz safe decodes will not read out of bounds. They won't put data on the stack or previously in memory + into the output buffer. + + As of Oodle 2.9.0 all compressors supported are fuzzsafe, so OodleLZ_FuzzSafe_Yes should always be used and this + enum is deprecated. + +*/ + +#define OODLELZ_BLOCK_LEN (1<<18) /* The number of raw bytes per "seek chunk" + Seek chunks can be decompressed independently if $(OodleLZ_CompressOptions:seekChunkReset) is set. +*/ + +#define OODLELZ_BLOCK_MAXIMUM_EXPANSION (2) +#define OODLELZ_BLOCK_MAX_COMPLEN (OODLELZ_BLOCK_LEN+OODLELZ_BLOCK_MAXIMUM_EXPANSION) /* Maximum expansion per $OODLELZ_BLOCK_LEN is 1 byte. + Note that the compressed buffer must be allocated bigger than this (use $OodleLZ_GetCompressedBufferSizeNeeded) +*/ + +#define OODLELZ_QUANTUM_LEN (1<<14) /* Minimum decompression quantum (for old legacy codecs only) + + Deprecated. + + The new sea monster family of compressors use a whole block quantum (OODLELZ_BLOCK_LEN). + Check $OodleLZ_Compressor_UsesWholeBlockQuantum +*/ + +// 5 byte expansion per-quantum with CRC's +#define OODLELZ_QUANTUM_MAXIMUM_EXPANSION (5) + +#define OODLELZ_QUANTUM_MAX_COMPLEN (OODLELZ_QUANTUM_LEN+OODLELZ_QUANTUM_MAXIMUM_EXPANSION) + +#define OODLELZ_SEEKCHUNKLEN_MIN OODLELZ_BLOCK_LEN +#define OODLELZ_SEEKCHUNKLEN_MAX (1<<29) // half GB + +typedef OOSTRUCT OodleLZ_DecodeSome_Out +{ + OO_S32 decodedCount; // number of uncompressed bytes decoded + OO_S32 compBufUsed; // number of compressed bytes consumed + + + OO_S32 curQuantumRawLen; // tells you the current quantum size. you must have at least this much room available in the output buffer to be able to decode anything. + OO_S32 curQuantumCompLen; // if you didn't pass in enough data, nothing will decode (decodedCount will be 0), and this will tell you how much is needed +} OodleLZ_DecodeSome_Out; +/* Output value of $OodleLZDecoder_DecodeSome +*/ + +//--------------------------------------------- + +//======================================================= + +typedef OOSTRUCT OodleLZ_SeekTable +{ + OodleLZ_Compressor compressor; // which compressor was used + OO_BOOL seekChunksIndependent; // are the seek chunks independent, or must they be decompressed in sequence + + OO_S64 totalRawLen; // total uncompressed data lenth + OO_S64 totalCompLen; // sum of seekChunkCompLens + + OO_S32 numSeekChunks; // derived from rawLen & seekChunkLen + OO_S32 seekChunkLen; // multiple of OODLELZ_BLOCK_LEN + + OO_U32 * seekChunkCompLens; // array of compressed lengths of seek chunks + OO_U32 * rawCRCs; // crc of the raw bytes of the chunk (optional; NULL unless $OodleLZSeekTable_Flags_MakeRawCRCs was specified) +} OodleLZ_SeekTable; + +typedef enum OodleLZSeekTable_Flags +{ + OodleLZSeekTable_Flags_None = 0, // default + OodleLZSeekTable_Flags_MakeRawCRCs = 1, // make the _rawCRCs_ member of $OodleLZ_SeekTable + OodleLZSeekTable_Flags_Force32 = 0x40000000 +} OodleLZSeekTable_Flags; + +//===================================================== + + +typedef OOSTRUCT OodleConfigValues +{ + OO_S32 m_OodleLZ_LW_LRM_step; // LZHLW LRM : bytes between LRM entries + OO_S32 m_OodleLZ_LW_LRM_hashLength; // LZHLW LRM : bytes hashed for each LRM entries + OO_S32 m_OodleLZ_LW_LRM_jumpbits; // LZHLW LRM : bits of hash used for jump table + + OO_S32 m_OodleLZ_Decoder_Max_Stack_Size; // if OodleLZ_Decompress needs to allocator a Decoder object, and it's smaller than this size, it's put on the stack instead of the heap + OO_S32 m_OodleLZ_Small_Buffer_LZ_Fallback_Size_Unused; // deprecated + OO_S32 m_OodleLZ_BackwardsCompatible_MajorVersion; // if you need to encode streams that can be read with an older version of Oodle, set this to the Oodle2 MAJOR version number that you need compatibility with. eg to be compatible with oodle 2.7.3 you would put 7 here + + OO_U32 m_oodle_header_version; // = OODLE_HEADER_VERSION + +} OodleConfigValues; +/* OodleConfigValues + + Struct of user-settable low level config values. See $Oodle_SetConfigValues. + + May have different defaults per platform. +*/ + +OOFUNC1 void OOFUNC2 Oodle_GetConfigValues(OodleConfigValues * ptr); +/* Get $OodleConfigValues + + $:ptr filled with OodleConfigValues + + Gets the current $OodleConfigValues. + + May be different per platform. +*/ + +OOFUNC1 void OOFUNC2 Oodle_SetConfigValues(const OodleConfigValues * ptr); +/* Set $OodleConfigValues + + $:ptr your desired OodleConfigValues + + Sets the global $OodleConfigValues from your struct. + + You should call $Oodle_GetConfigValues to fill the struct, then change the values you + want to change, then call $Oodle_SetConfigValues. + + This should generally be done before doing anything with Oodle (eg. even before OodleX_Init). + Changing OodleConfigValues after Oodle has started has undefined effects. +*/ + +typedef enum Oodle_UsageWarnings +{ + Oodle_UsageWarnings_Enabled = 0, + Oodle_UsageWarnings_Disabled = 1, + Oodle_UsageWarnings_Force32 = 0x40000000 +} Oodle_UsageWarnings; +/* Whether Oodle usage warnings are enable or disabled. */ + +OOFUNC1 void OOFUNC2 Oodle_SetUsageWarnings(Oodle_UsageWarnings state); +/* Enables or disables Oodle usage warnings. + + $:state whether usage warnings should be enabled or disabled. + + Usage warnings are enabled by default and try to be low-noise, but in case you want to + disable them, this is how. + + This should generally be done once at startup. Setting this state while there are Oodle + calls running on other threads has undefined results. +*/ + +// function pointers to mallocs needed : + +OODEFFUNC typedef void * (OODLE_CALLBACK t_fp_OodleCore_Plugin_MallocAligned)( OO_SINTa bytes, OO_S32 alignment); +/* Function pointer type for OodleMallocAligned + + $:bytes number of bytes to allocate + $:alignment required alignment of returned pointer + $:return pointer to memory allocated (must not be NULL) + + _alignment_ will always be a power of two + + _alignment_ will always be >= $OODLE_MALLOC_MINIMUM_ALIGNMENT + +*/ + +OODEFFUNC typedef void (OODLE_CALLBACK t_fp_OodleCore_Plugin_Free)( void * ptr ); +/* Function pointer type for OodleFree + + $:return pointer to memory to free + +*/ + +OOFUNC1 void OOFUNC2 OodleCore_Plugins_SetAllocators( + t_fp_OodleCore_Plugin_MallocAligned * fp_OodleMallocAligned, + t_fp_OodleCore_Plugin_Free * fp_OodleFree); +/* Set the function pointers for allocation needed by Oodle2 Core + + If these are not set, the default implementation on most platforms uses the C stdlib. + On Microsoft platforms the default implementation uses HeapAlloc. + + These must not be changed once they are set! Set them once then don't change them. + + NOTE: if you are using Oodle Ext, do NOT call this. OodleX_Init will install an allocator for Oodle Core. Do not mix your own allocator with the OodleX allocator. See $OodleXAPI_Malloc. + + If you want to ensure that Oodle is not doing any allocations, you can call OodleCore_Plugins_SetAllocators(NULL,NULL); + If you do that, then any time Oodle needs to allocate memory internally, it will stop the process. + It is STRONGLY not recommended that you ship that way. You can verify that Oodle is not allocating, but then leave some + fallback allocator installed when you actually ship just in case. + + Also note that on many consoles the standard allocation practices may not + leave much heap memory for the C stdlib malloc. In this case Oodle may fail to allocate. + +*/ + +OODEFFUNC typedef OO_U64 (OODLE_CALLBACK t_fp_OodleCore_Plugin_RunJob)( t_fp_Oodle_Job * fp_job, void * job_data , OO_U64 * dependencies, int num_dependencies, void * user_ptr ); +/* Function pointer type for OodleCore_Plugins_SetJobSystem + + $:dependencies array of handles of other pending jobs. All guaranteed to be nonzero. + $:num_dependencies number of dependencies. Guaranteed to be no more than OODLE_JOB_MAX_DEPENDENCIES. + $:user_ptr is passed through from the OodleLZ_CompressOptions. + $:return handle to the async job, or 0 if it was run synchronously + + RunJob will call fp_job(job_data) + + it may be done on a thread, or it may run the function synchronously and return 0, indicating the job is already done. + The returned OO_U64 is a handle passed to WaitJob, unless it is 0, in which case WaitJob won't get called. + + fp_job should not run until all the dependencies are done. This function should not delete the dependencies. + + RunJob must be callable from within an Oodle Job, i.e. jobs may spawn their own sub-jobs directly. + However, the matching WaitJob calls will only ever occur on the thread that called the + internally threaded Oodle API function. + + See $Oodle_About_Job_Threading_Plugins +*/ + +OODEFFUNC typedef void (OODLE_CALLBACK t_fp_OodleCore_Plugin_WaitJob)( OO_U64 job_handle, void * user_ptr ); +/* Function pointer type for OodleCore_Plugins_SetJobSystem + + $:job_handle a job handle returned from RunJob. Never 0. + $:user_ptr is passed through from the OodleLZ_CompressOptions. + + Waits until the job specified by job_handle is done and cleans up any associated resources. Oodle + will call WaitJob exactly once for every RunJob call that didn't return 0. + + If job_handle was already completed, this should clean it up without waiting. + + A handle value should not be reused by another RunJob until WaitJob has been done with that value. + + WaitJob will not be called from running jobs. It will be only be called from the original thread that + invoked Oodle. If you are running Oodle from a worker thread, ensure that that thread is allowed to wait + on other job threads. + + See $Oodle_About_Job_Threading_Plugins +*/ + +OOFUNC1 void OOFUNC2 OodleCore_Plugins_SetJobSystem( + t_fp_OodleCore_Plugin_RunJob * fp_RunJob, + t_fp_OodleCore_Plugin_WaitJob * fp_WaitJob); +/* DEPRECATED use OodleCore_Plugins_SetJobSystemAndCount instead + + See $OodleCore_Plugins_SetJobSystemAndCount +*/ + + +OOFUNC1 void OOFUNC2 OodleCore_Plugins_SetJobSystemAndCount( + t_fp_OodleCore_Plugin_RunJob * fp_RunJob, + t_fp_OodleCore_Plugin_WaitJob * fp_WaitJob, + int target_parallelism); +/* Set the function pointers for async job system needed by Oodle2 Core + + $:fp_RunJob pointer to RunJob function + $:fp_WaitJob pointer to WaitJob function + $:target_parallelism goal of number of jobs to run simultaneously + + If these are not set, the default implementation runs jobs synchronously on the calling thread. + + These must not be changed once they are set! Set them once then don't change them. + + _target_parallelism_ allows you to tell Oodle how many Jobs it should try to keep in flight at once. + Depending on the operation it may not be able to split work into this many jobs (so fewer will be used), + but it will not exceed this count. + + For Oodle Data LZ work, typically _target_parallelism_ is usually best at the number of hardware cores + not including hyper threads). + + For Oodle Texture BCN encoding work, _target_parallelism_ is usually best as the full number of hyper cores. + + In some cases you may wish to reduce _target_parallelism_ by 1 or 2 cores to leave some of the CPU free for + other work. + + For example on a CPU with 16 cores and 32 hardware threads, for LZ work you might set _target_parallelism_ to 15 + when calling OodleCorePlugins. For BC7 encoding you might set _target_parallelism_ to 30 when calling OodleTexPlugins. + + NOTE : if you are using Oodle Ext, do NOT call this. OodleX_Init will install a job system for Oodle Core. + Note OodleX only installs automatically to Oodle Core, not Net or Tex. See example_jobify.cpp for manual + plugin. + + Replaces deprecated $OodleCore_Plugins_SetJobSystem + + See $Oodle_About_Job_Threading_Plugins +*/ + +// the main func pointer for log : +OODEFFUNC typedef void (OODLE_CALLBACK t_fp_OodleCore_Plugin_Printf)(int verboseLevel,const char * file,int line,const char * fmt,...); +/* Function pointer to Oodle Core printf + + $:verboseLevel verbosity of the message; 0-2 ; lower = more important + $:file C file that sent the message + $:line C line that sent the message + $:fmt vararg printf format string + + The logging function installed here must parse varargs like printf. + + _verboseLevel_ may be used to omit verbose messages. +*/ + +OOFUNC1 t_fp_OodleCore_Plugin_Printf * OOFUNC2 OodleCore_Plugins_SetPrintf(t_fp_OodleCore_Plugin_Printf * fp_rrRawPrintf); +/* Install the callback used by Oodle Core for logging + + $:fp_rrRawPrintf function pointer to your log function; may be NULL to disable all logging + $:return returns the previous function pointer + + Use this function to install your own printf for Oodle Core. + + The default implementation in debug builds, if you install nothing, uses the C stdio printf for logging. + On Microsoft platforms, it uses OutputDebugString and not stdio. + + To disable all logging, call OodleCore_Plugins_SetPrintf(NULL) + + WARNING : this function is NOT thread safe! It should be done only once and done in a place where the caller can guarantee thread safety. + + In the debug build of Oodle, you can install OodleCore_Plugin_Printf_Verbose to get more verbose logging + +*/ + +OODEFFUNC typedef OO_BOOL (OODLE_CALLBACK t_fp_OodleCore_Plugin_DisplayAssertion)(const char * file,const int line,const char * function,const char * message); +/* Function pointer to Oodle Core assert callback + + $:file C file that triggered the assert + $:line C line that triggered the assert + $:function C function that triggered the assert (may be NULL) + $:message assert message + $:return true to break execution at the assertion site, false to continue + + This callback is called by Oodle Core when it detects an assertion condition. + + This will only happen in debug builds. + + +*/ + +OOFUNC1 t_fp_OodleCore_Plugin_DisplayAssertion * OOFUNC2 OodleCore_Plugins_SetAssertion(t_fp_OodleCore_Plugin_DisplayAssertion * fp_rrDisplayAssertion); +/* Install the callback used by Oodle Core for asserts + + $:fp_rrDisplayAssertion function pointer to your assert display function + $:return returns the previous function pointer + + Use this function to install your own display for Oodle Core assertions. + This will only happen in debug builds. + + The default implementation in debug builds, if you install nothing, uses the C stderr printf for logging, + except on Microsoft platforms where it uses OutputDebugString. + + WARNING : this function is NOT thread safe! It should be done only once and done in a place where the caller can guarantee thread safety. + +*/ + +//============================================================= + + +OOFUNC1 void * OOFUNC2 OodleCore_Plugin_MallocAligned_Default(OO_SINTa size,OO_S32 alignment); +OOFUNC1 void OOFUNC2 OodleCore_Plugin_Free_Default(void * ptr); +OOFUNC1 void OOFUNC2 OodleCore_Plugin_Printf_Default(int verboseLevel,const char * file,int line,const char * fmt,...); +OOFUNC1 void OOFUNC2 OodleCore_Plugin_Printf_Verbose(int verboseLevel,const char * file,int line,const char * fmt,...); +OOFUNC1 OO_BOOL OOFUNC2 OodleCore_Plugin_DisplayAssertion_Default(const char * file,const int line,const char * function,const char * message); +OOFUNC1 OO_U64 OOFUNC2 OodleCore_Plugin_RunJob_Default( t_fp_Oodle_Job * fp_job, void * job_data, OO_U64 * dependencies, int num_dependencies, void * user_ptr ); +OOFUNC1 void OOFUNC2 OodleCore_Plugin_WaitJob_Default( OO_U64 job_handle, void * user_ptr ); + +//============================================================= + +//---------------------------------------------- +// OodleLZ + +#define OODLELZ_FAILED (0) /* Return value of OodleLZ_Decompress on failure +*/ + +//======================================================= + +OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_Compress(OodleLZ_Compressor compressor, + const void * rawBuf,OO_SINTa rawLen,void * compBuf, + OodleLZ_CompressionLevel level, + const OodleLZ_CompressOptions * pOptions OODEFAULT(NULL), + const void * dictionaryBase OODEFAULT(NULL), + const void * lrm OODEFAULT(NULL), + void * scratchMem OODEFAULT(NULL), + OO_SINTa scratchSize OODEFAULT(0) ); +/* Compress some data from memory to memory, synchronously, with OodleLZ + + $:compressor which OodleLZ variant to use in compression + $:rawBuf raw data to compress + $:rawLen number of bytes in rawBuf to compress + $:compBuf pointer to write compressed data to ; should be at least $OodleLZ_GetCompressedBufferSizeNeeded + $:level OodleLZ_CompressionLevel controls how much CPU effort is put into maximizing compression + $:pOptions (optional) options; if NULL, $OodleLZ_CompressOptions_GetDefault is used + $:dictionaryBase (optional) if not NULL, provides preceding data to prime the dictionary; must be contiguous with rawBuf, the data between the pointers _dictionaryBase_ and _rawBuf_ is used as the preconditioning data. The exact same precondition must be passed to encoder and decoder. + $:lrm (optional) long range matcher + $:scratchMem (optional) pointer to scratch memory + $:scratchSize (optional) size of scratch memory (see $OodleLZ_GetCompressScratchMemBound) + $:return size of compressed data written, or $OODLELZ_FAILED for failure + + Performs synchronous memory to memory LZ compression. + + In tools, you should generally use $OodleXLZ_Compress_AsyncAndWait instead to get parallelism. (in the Oodle2 Ext lib) + + You can compress a large buffer in several calls by setting _dictionaryBase_ to the start + of the buffer, and then making _rawBuf_ and _rawLen_ select portions of that buffer. As long + as _rawLen_ is a multiple of $OODLELZ_BLOCK_LEN , the compressed chunks can simply be + concatenated together. + + If _scratchMem_ is provided, it will be used for the compressor's scratch memory needs before OodleMalloc is + called. If the scratch is big enough, no malloc will be done. If the scratch is not big enough, the compress + will not fail, instead OodleMalloc will be used. OodleMalloc should not return null. There is currently no way + to make compress fail cleanly due to using too much memory, it must either succeed or abort the process. + + If _scratchSize_ is at least $OodleLZ_GetCompressScratchMemBound , additional allocations will not be needed. + + See $OodleLZ_About for tips on setting the compression options. + + If _dictionaryBase_ is provided, the backup distance from _rawBuf_ must be a multiple of $OODLELZ_BLOCK_LEN + + If $(OodleLZ_CompressOptions:seekChunkReset) is enabled, and _dictionaryBase_ is not NULL or _rawBuf_ , then the + seek chunk boundaries are relative to _dictionaryBase_, not to _rawBuf_. + +*/ + +// Decompress returns raw (decompressed) len received +// Decompress returns 0 (OODLELZ_FAILED) if it detects corruption +OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_Decompress(const void * compBuf,OO_SINTa compBufSize,void * rawBuf,OO_SINTa rawLen, + OodleLZ_FuzzSafe fuzzSafe OODEFAULT(OodleLZ_FuzzSafe_Yes), + OodleLZ_CheckCRC checkCRC OODEFAULT(OodleLZ_CheckCRC_No), + OodleLZ_Verbosity verbosity OODEFAULT(OodleLZ_Verbosity_None), + void * decBufBase OODEFAULT(NULL), + OO_SINTa decBufSize OODEFAULT(0), + OodleDecompressCallback * fpCallback OODEFAULT(NULL), + void * callbackUserData OODEFAULT(NULL), + void * decoderMemory OODEFAULT(NULL), + OO_SINTa decoderMemorySize OODEFAULT(0), + OodleLZ_Decode_ThreadPhase threadPhase OODEFAULT(OodleLZ_Decode_Unthreaded) + ); +/* Decompress a some data from memory to memory, synchronously. + + $:compBuf pointer to compressed data + $:compBufSize number of compressed bytes available (must be greater or equal to the number consumed) + $:rawBuf pointer to output uncompressed data into + $:rawLen number of uncompressed bytes to output + $:fuzzSafe (optional) should the decode fail if it contains non-fuzz safe codecs? + $:checkCRC (optional) if data could be corrupted and you want to know about it, pass OodleLZ_CheckCRC_Yes + $:verbosity (optional) if not OodleLZ_Verbosity_None, logs some info + $:decBufBase (optional) if not NULL, provides preceding data to prime the dictionary; must be contiguous with rawBuf, the data between the pointers _dictionaryBase_ and _rawBuf_ is used as the preconditioning data. The exact same precondition must be passed to encoder and decoder. The decBufBase must be a reset point. + $:decBufSize (optional) size of decode buffer starting at decBufBase, if 0, _rawLen_ is assumed + $:fpCallback (optional) OodleDecompressCallback to call incrementally as decode proceeds + $:callbackUserData (optional) passed as userData to fpCallback + $:decoderMemory (optional) pre-allocated memory for the Decoder, of size _decoderMemorySize_ + $:decoderMemorySize (optional) size of the buffer at _decoderMemory_; must be at least $OodleLZDecoder_MemorySizeNeeded bytes to be used + $:threadPhase (optional) for threaded decode; see $OodleLZ_About_ThreadPhasedDecode (default OodleLZ_Decode_Unthreaded) + $:return the number of decompressed bytes output, $OODLELZ_FAILED (0) if none can be decompressed + + Decodes data encoded with any $OodleLZ_Compressor. + + Note : _rawLen_ must be the actual number of bytes to output, the same as the number that were encoded with the corresponding + OodleLZ_Compress size. You must store this somewhere in your own header and pass it in to this call. _compBufSize_ does NOT + need to be the exact number of compressed bytes, is the number of bytes available in the buffer, it must be greater or equal to + the actual compressed length. + + Note that the new compressors (Kraken,Mermaid,Selkie,BitKnit) are all fuzz safe and you can use OodleLZ_FuzzSafe_Yes + with them and no padding of the decode target buffer. + + If checkCRC is OodleLZ_CheckCRC_Yes, then corrupt data will be detected and the decode aborted. + If checkCRC is OodleLZ_CheckCRC_No, then corruption might result in invalid data, but no detection of any error (garbage in, garbage out). + + If corruption is possible, _fuzzSafe_ is No and _checkCRC_ is OodleLZ_CheckCRC_No, $OodleLZ_GetDecodeBufferSize must be used to allocate + _rawBuf_ large enough to prevent overrun. + + $OodleLZ_GetDecodeBufferSize should always be used to ensure _rawBuf_ is large enough, even when corruption is not + possible (when fuzzSafe is No). + + _compBuf_ and _rawBuf_ are allowed to overlap for "in place" decoding, but then _rawBuf_ must be allocated to + the size given by $OodleLZ_GetInPlaceDecodeBufferSize , and the compressed data must be at the end of that buffer. + + An easy way to take the next step to parallel decoding is with $OodleXLZ_Decompress_MakeSeekTable_Wide_Async (in the Oodle2 Ext lib) + + NOTE : the return value is the *total* number of decompressed bytes output so far. If rawBuf is > decBufBase, that means + the initial inset of (rawBuf - decBufBase) is included! (eg. you won't just get _rawLen_) + + If _decBufBase_ is provided, the backup distance from _rawBuf_ must be a multiple of $OODLELZ_BLOCK_LEN + + About fuzz safety: + + OodleLZ_Decompress is guaranteed not to crash even if the data is corrupted when _fuzzSafe_ is set to OodleLZ_FuzzSafe_Yes. + When _fuzzSafe_ is Yes, the target buffer (_rawBuf_ and _rawLen_) will never be overrun. Note that corrupted data might not + be detected (the return value might indicate success). + + Fuzz Safe decodes will not crash on corrupt data. They may or may not return failure, and produce garbage output. + + Fuzz safe decodes will not read out of bounds. They won't put data on the stack or previously in memory + into the output buffer. + + Fuzz safe decodes will not output more than the uncompressed size. (eg. the output buffer does not need to + be padded like OodleLZ_GetDecodeBufferSize) + + If you ask for a fuzz safe decode and the compressor doesn't satisfy OodleLZ_Compressor_CanDecodeFuzzSafe + then it will return failure. + + The _fuzzSafe_ argument should always be OodleLZ_FuzzSafe_Yes as of Oodle 2.9.0 ; older compressors did not + support fuzz safety but they now all do. + + Use of OodleLZ_FuzzSafe_No is deprecated. + +*/ + + +//------------------------------------------- +// Incremental Decoder functions : + +struct _OodleLZDecoder; +typedef struct _OodleLZDecoder OodleLZDecoder; +/* Opaque type for OodleLZDecoder + + See $OodleLZDecoder_Create +*/ + + +OOFUNC1 OodleLZDecoder * OOFUNC2 OodleLZDecoder_Create(OodleLZ_Compressor compressor,OO_S64 rawLen,void * memory, OO_SINTa memorySize); +/* Create a OodleLZDecoder + + $:compressor the type of data you will decode; use $OodleLZ_Compressor_Invalid if unknown + $:rawLen total raw bytes of the decode + $:memory (optional) provide memory for the OodleLZDecoder object (not the window) + $:memorySize (optional) if memory is provided, this is its size in bytes + $:return the OodleLZDecoder + + If memory is provided, it must be of size $OodleLZDecoder_MemorySizeNeeded. If it is NULL it will be + allocated with the malloc specified by $OodleAPI_OodleCore_Plugins. + + Free with $OodleLZDecoder_Destroy. You should Destroy even if you passed in the memory. + + Providing _compressor_ lets the OodleLZDecoder be the minimum size needed for that type of data. + If you pass $OodleLZ_Compressor_Invalid, then any type of data may be decoded, and the Decoder is allocated + large enought to handle any of them. + + If you are going to pass rawLen to OodleLZDecoder_Reset , then you can pass 0 to rawLen here. + + See $OodleLZDecoder_DecodeSome for more. +*/ + +OOFUNC1 OO_S32 OOFUNC2 OodleLZDecoder_MemorySizeNeeded(OodleLZ_Compressor compressor OODEFAULT(OodleLZ_Compressor_Invalid), OO_SINTa rawLen OODEFAULT(-1)); +/* If you want to provide the memory needed by $OodleLZDecoder_Create , this tells you how big it must be. + + $:compressor the type of data you will decode; use $OodleLZ_Compressor_Invalid if unknown + $:rawLen should almost always be -1, which supports any size of raw data decompression + $:return bytes to allocate or reserve, 0 for failure + + NOTE : using $OodleLZ_Compressor_Invalid lets you decode any time of compressed data. + It requests as much memory as the largest compressor. This may be a *lot* more than your data needs; + try to use the correct compressor type. + + If _rawLen_ is -1 (default) then the Decoder object created can be used on any length of raw data + decompression. If _rawLen_ is specified here, then you can only use it to decode data shorter than + the length you specified here. This use case is very rare, contact support for details. +*/ + +OOFUNC1 OO_S32 OOFUNC2 OodleLZ_ThreadPhased_BlockDecoderMemorySizeNeeded(void); +/* Returns the size of the decoder needed for ThreadPhased decode + + For use with $OodleLZ_Decode_ThreadPhase + See $OodleLZ_About_ThreadPhasedDecode +*/ + +OOFUNC1 void OOFUNC2 OodleLZDecoder_Destroy(OodleLZDecoder * decoder); +/* Pairs with $OodleLZDecoder_Create + + You should always call Destroy even if you provided the memory for $OodleLZDecoder_Create +*/ + +// Reset decoder - can reset to the start of any OODLELZ_BLOCK_LEN chunk +OOFUNC1 OO_BOOL OOFUNC2 OodleLZDecoder_Reset(OodleLZDecoder * decoder, OO_SINTa decPos, OO_SINTa decLen OODEFAULT(0)); +/* Reset an OodleLZDecoder to restart at given pos + + $:decoder the OodleLZDecoder, made by $OodleLZDecoder_Create + $:decPos position to reset to; must be a multiple of OODLELZ_BLOCK_LEN + $:decLen (optional) if not zero, change the length of the data we expect to decode + $:return true for success + + If you are seeking in a packed stream, you must seek to a seek chunk reset point, as was made at compress time. + + That is, $(OodleLZ_CompressOptions:seekChunkReset) must have been true, and + _decPos_ must be a multiple of $(OodleLZ_CompressOptions:seekChunkLen) that was used at compress time. + + You can use $OodleLZ_GetChunkCompressor to verify that you are at a valid + independent chunk start point. + +*/ + +// returns false if corruption detected +OOFUNC1 OO_BOOL OOFUNC2 OodleLZDecoder_DecodeSome( + OodleLZDecoder * decoder, + OodleLZ_DecodeSome_Out * out, + + // the decode sliding window : we output here & read from this for matches + void * decBuf, + OO_SINTa decBufPos, + OO_SINTa decBufferSize, // decBufferSize should be the result of OodleLZDecoder_MakeDecodeBufferSize() + OO_SINTa decBufAvail, // usually Size - Pos, but maybe less if you have pending IO flushes + + // compressed data : + const void * compPtr, + OO_SINTa compAvail, + + OodleLZ_FuzzSafe fuzzSafe OODEFAULT(OodleLZ_FuzzSafe_No), + OodleLZ_CheckCRC checkCRC OODEFAULT(OodleLZ_CheckCRC_No), + OodleLZ_Verbosity verbosity OODEFAULT(OodleLZ_Verbosity_None), + OodleLZ_Decode_ThreadPhase threadPhase OODEFAULT(OodleLZ_Decode_Unthreaded) + + ); +/* Incremental decode some LZ compressed data + + $:decoder the OodleLZDecoder, made by $OodleLZDecoder_Create + $:out filled with results + $:decBuf the decode buffer (window) + $:decBufPos the current position in the buffer + $:decBufferSize size of decBuf ; this must be either equal to the total decompressed size (_rawLen_ passed to $OodleLZDecoder_Create) or the result of $OodleLZDecoder_MakeValidCircularWindowSize + $:decBufAvail the number of bytes available after decBufPos in decBuf ; usually (decBufferSize - decBufPos), but can be less + $:compPtr pointer to compressed data to read + $:compAvail number of compressed bytes available at compPtr + $:fuzzSafe (optional) should the decode be fuzz safe + $:checkCRC (optional) if data could be corrupted and you want to know about it, pass OodleLZ_CheckCRC_Yes + $:verbosity (optional) if not OodleLZ_Verbosity_None, logs some info + $:threadPhase (optional) for threaded decode; see $OodleLZ_About_ThreadPhasedDecode (default OodleLZ_Decode_Unthreaded) + $:return true if success, false if invalid arguments or data is encountered + + Decodes data encoded with an OodleLZ compressor. + + Decodes an integer number of quanta; quanta are $OODLELZ_QUANTUM_LEN uncompressed bytes. + + _decBuf_ can either be a circular window or the whole _rawLen_ array. + In either case, _decBufPos_ should be in the range [0,_decBufferSize_). + If _decBuf_ is a circular window, then _decBufferSize_ should come from $OodleLZDecoder_MakeValidCircularWindowSize. + + (circular windows are deprecated as of 2.9.0) + + NOTE : all the new LZ codecs (Kraken, etc.) do not do circular windows. They can do sliding windows, see lz_test_11 in $example_lz. + They should always have decBufferSize = total raw size, even if the decode buffer is smaller than that. + + NOTE : insufficient data provided (with _compAvail_ > 0 but not enough to decode a quantum) is a *success* case + (return value of true), even though nothing is decoded. A return of false always indicates a non-recoverable error. + + If _decBufAvail_ or _compAvail_ is insufficient for any decompression, the "curQuantum" fields of $OodleLZ_DecodeSome_Out + will tell you how much you must provide to proceed. That is, if enough compressed bytes are provided to get a quantum header, but not enough to decode a quantum, this + function returns true and fills out the $OodleLZ_DecodeSome_Out structure with the size of the quantum. + + See $OodleLZ_Decompress about fuzz safety. + + NOTE : DecodeSome expect to decode either one full quantum (of len $OODLELZ_QUANTUM_LEN) or up to the length of the total buffer specified in the +call to $OodleLZDecoder_Create or $OodleLZDecoder_Reset. That total buffer length +must match what was use during compression (or be a seek-chunk portion thereof). +That is, you cannot decompress partial streams in intervals smaller than +$OODLELZ_QUANTUM_LEN except for the final partial quantum at the end of the stream. + +*/ + +// pass in how much you want to alloc and it will tell you a valid size as close that as possible +// the main use is just to call OodleLZDecoder_MakeDecodeBufferSize(0) to get the min size; the min size is a good size +OOFUNC1 OO_S32 OOFUNC2 OodleLZDecoder_MakeValidCircularWindowSize(OodleLZ_Compressor compressor,OO_S32 minWindowSize OODEFAULT(0)); +/* Get a valid "Window" size for an LZ + + $:compressor which compressor you will be decoding + $:minWindowSize (optional) minimum size of the window + + NOTE: circular windows are deprecated as of 2.9.0 + + Most common usage is OodleLZDecoder_MakeValidCircularWindowSize(0) to get the minimum window size. + + Only compressors which pass $OodleLZ_Compressor_CanDecodeInCircularWindow can be decoded in a circular window. + + WARNING : this is NOT the size to malloc the window! you need to call $OodleLZ_GetDecodeBufferSize() and + pass in the window size to get the malloc size. +*/ + +//======================================================= + +//======================================================= +// remember if you want to IO the SeekEntries you need to make them endian-independent +// see WriteOOZHeader for example + +#define OODLELZ_SEEKPOINTCOUNT_DEFAULT 16 + +OOFUNC1 OO_S32 OOFUNC2 OodleLZ_MakeSeekChunkLen(OO_S64 rawLen, OO_S32 desiredSeekPointCount); +/* Compute a valid seekChunkLen + + $:rawLen total length of uncompressed data + $:desiredSeekPointCount desired number of seek chunks + $:return a valid seekChunkLen for use in $OodleLZ_CreateSeekTable + + Returns a seekChunkLen which is close to (rawLen/desiredSeekPointCount) but is a power of two multiple of $OODLELZ_BLOCK_LEN + + _desiredSeekPointCount_ = 16 is good for parallel decompression. + (OODLELZ_SEEKPOINTCOUNT_DEFAULT) +*/ + +OOFUNC1 OO_S32 OOFUNC2 OodleLZ_GetNumSeekChunks(OO_S64 rawLen, OO_S32 seekChunkLen); +/* Compute the number of seek chunks + + $:rawLen total length of uncompressed data + $:seekChunkLen the length of a seek chunk (eg from $OodleLZ_MakeSeekChunkLen) + $:return the number of seek chunks + + returns (rawLen+seekChunkLen-1)/seekChunkLen +*/ + +OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_GetSeekTableMemorySizeNeeded(OO_S32 numSeekChunks,OodleLZSeekTable_Flags flags); +/* Tells you the size in bytes to allocate the seekTable before calling $OodleLZ_FillSeekTable + + $:numSeekChunks number of seek chunks (eg from $OodleLZ_GetNumSeekChunks) + $:flags options that will be passed to $OodleLZ_CreateSeekTable + $:return size in bytes of memory needed for seek table + + If you wish to provide the memory for the seek table yourself, you may call this to get the required size, + allocate the memory, and then simply point a $OodleLZ_SeekTable at your memory. + Then use $OodleLZ_FillSeekTable to fill it out. + + Do NOT use sizeof(OodleLZ_SeekTable) ! +*/ + +OOFUNC1 OO_BOOL OOFUNC2 OodleLZ_FillSeekTable(OodleLZ_SeekTable * pTable,OodleLZSeekTable_Flags flags,OO_S32 seekChunkLen,const void * rawBuf, OO_SINTa rawLen,const void * compBuf,OO_SINTa compLen); +/* scan compressed LZ stream to fill the seek table + + $:pTable pointer to table to be filled + $:flags options + $:seekChunkLen the length of a seek chunk (eg from $OodleLZ_MakeSeekChunkLen) + $:rawBuf (optional) uncompressed buffer; used to compute the _rawCRCs_ member of $OodleLZ_SeekTable + $:rawLen size of rawBuf + $:compBuf compressed buffer + $:compLen size of compBuf + $:return true for success + + _pTable_ must be able to hold at least $OodleLZ_GetSeekTableMemorySizeNeeded + + _seekChunkLen_ must be a multiple of $OODLELZ_BLOCK_LEN. + _seekChunkLen_ must match what was in CompressOptions when the buffer was made, or any integer multiple thereof. +*/ + + +OOFUNC1 OodleLZ_SeekTable * OOFUNC2 OodleLZ_CreateSeekTable(OodleLZSeekTable_Flags flags,OO_S32 seekChunkLen,const void * rawBuf, OO_SINTa rawLen,const void * compBuf,OO_SINTa compLen); +/* allocate a table, then scan compressed LZ stream to fill the seek table + + $:flags options + $:seekChunkLen the length of a seek chunk (eg from $OodleLZ_MakeSeekChunkLen) + $:rawBuf (optional) uncompressed buffer; used to compute the _rawCRCs_ member of $OodleLZ_SeekTable + $:rawLen size of rawBuf + $:compBuf compressed buffer + $:compLen size of compBuf + $:return pointer to table if succeeded, null if failed + + Same as $OodleLZ_FillSeekTable , but allocates the memory for you. Use $OodleLZ_FreeSeekTable to free. + + _seekChunkLen_ must be a multiple of $OODLELZ_BLOCK_LEN. + _seekChunkLen_ must match what was in CompressOptions when the buffer was made, or any integer multiple thereof. + +*/ + +OOFUNC1 void OOFUNC2 OodleLZ_FreeSeekTable(OodleLZ_SeekTable * pTable); +/* Frees a table allocated by $OodleLZ_CreateSeekTable +*/ + +OOFUNC1 OO_BOOL OOFUNC2 OodleLZ_CheckSeekTableCRCs(const void * rawBuf,OO_SINTa rawLen, const OodleLZ_SeekTable * seekTable); +/* Check the CRC's in seekTable vs rawBuf + + $:rawBuf uncompressed buffer + $:rawLen size of rawBuf + $:seekTable result of $OodleLZ_CreateSeekTable + $:return true if the CRC's check out + + Note that $OodleLZ_Decompress option of $OodleLZ_CheckCRC checks the CRC of *compressed* data, + this call checks the CRC of the *raw* (uncompressed) data. + + OodleLZ data contains a CRC of the compressed data if it was made with $(OodleLZ_CompressOptions:sendQuantumCRCs). + The SeekTable contains a CRC of the raw data if it was made with $OodleLZSeekTable_Flags_MakeRawCRCs. + + Checking the CRC of compressed data is faster, but does not verify that the decompress succeeded. +*/ + +OOFUNC1 OO_S32 OOFUNC2 OodleLZ_FindSeekEntry( OO_S64 rawPos, const OodleLZ_SeekTable * seekTable); +/* Find the seek entry that contains a raw position + + $:rawPos uncompressed position to look for + $:seekTable result of $OodleLZ_CreateSeekTable + $:return a seek entry index + + returns the index of the chunk that contains _rawPos_ +*/ + +OOFUNC1 OO_S64 OOFUNC2 OodleLZ_GetSeekEntryPackedPos( OO_S32 seekI , const OodleLZ_SeekTable * seekTable ); +/* Get the compressed position of a seek entry + + $:seekI seek entry index , in [0,numSeekEntries) + $:seekTable result of $OodleLZ_CreateSeekTable + $:return compressed buffer position of the start of this seek entry + + +*/ + +//============================================================= + +OOFUNC1 const char * OOFUNC2 OodleLZ_CompressionLevel_GetName(OodleLZ_CompressionLevel compressSelect); +/* Provides a string naming a $OodleLZ_CompressionLevel compressSelect +*/ + +OOFUNC1 const char * OOFUNC2 OodleLZ_Compressor_GetName(OodleLZ_Compressor compressor); +/* Provides a string naming a $OodleLZ_Compressor compressor +*/ + +OOFUNC1 const char * OOFUNC2 OodleLZ_Jobify_GetName(OodleLZ_Jobify jobify); +/* Provides a string naming a $OodleLZ_Jobify enum +*/ + +OOFUNC1 const OodleLZ_CompressOptions * OOFUNC2 OodleLZ_CompressOptions_GetDefault( + OodleLZ_Compressor compressor OODEFAULT(OodleLZ_Compressor_Invalid), + OodleLZ_CompressionLevel lzLevel OODEFAULT(OodleLZ_CompressionLevel_Normal)); +/* Provides a pointer to default compression options + + $:compressor deprecated, ignored + $:lzLevel deprecated, ignored + + Use to fill your own $OodleLZ_CompressOptions then change individual fields. + +*/ + +// after you fiddle with options, call this to ensure they are allowed +OOFUNC1 void OOFUNC2 OodleLZ_CompressOptions_Validate(OodleLZ_CompressOptions * pOptions); +/* Clamps the values in _pOptions_ to be in valid range + +*/ + +// inline functions for compressor property queries +OODEFSTART + +OO_BOOL OodleLZ_Compressor_UsesWholeBlockQuantum(OodleLZ_Compressor compressor); +/* OodleLZ_Compressor properties helper. + + Tells you if this compressor is "whole block quantum" ; must decode in steps of + $OODLELZ_BLOCK_LEN , not $OODLELZ_QUANTUM_LEN like others. +*/ +OO_BOOL OodleLZ_Compressor_UsesLargeWindow(OodleLZ_Compressor compressor); +/* OodleLZ_Compressor properties helper. + + Tells you if this compressor is "LargeWindow" or not, meaning it can benefit from + a Long-Range-Matcher and windows larger than $OODLELZ_BLOCK_LEN +*/ +OO_BOOL OodleLZ_Compressor_CanDecodeInCircularWindow(OodleLZ_Compressor compressor); +/* OodleLZ_Compressor properties helper. + + Tells you if this compressor can be decoded using a fixed size circular window. + deprecated as of 2.9.0 +*/ +OO_BOOL OodleLZ_Compressor_CanDecodeThreadPhased(OodleLZ_Compressor compressor); +/* OodleLZ_Compressor properties helper. + + Tells you if this compressor can be used with the $OodleLZ_Decode_ThreadPhase. + + See $OodleLZ_About_ThreadPhasedDecode +*/ +OO_BOOL OodleLZ_Compressor_CanDecodeInPlace(OodleLZ_Compressor compressor); +/* OodleLZ_Compressor properties helper. + + Tells you if this compressor can be used with "in-place" decoding. + + This is now always true (all compressors support in-place decoding). The function is left + for backward compatibility. + + All compressors in the future will support in-place, you don't need to check this property. + +*/ +OO_BOOL OodleLZ_Compressor_MustDecodeWithoutResets(OodleLZ_Compressor compressor); +/* OodleLZ_Compressor properties helper. + + Tells you if this compressor must decode contiguous ranges of buffer with the same Decoder. + + That is, most of the compressors can be Reset and restart on any block, not just seek blocks, + as long as the correct window data is provided. That is, if this returns false then the only + state required across a non-reset block is the dictionary of previously decoded data. + + But if OodleLZ_Compressor_MustDecodeWithoutResets returns true, then you cannot do that, + because the Decoder object must carry state across blocks (except reset blocks). + + This does not apply to seek points - you can always reset and restart decompression at a seek point. +*/ +OO_BOOL OodleLZ_Compressor_CanDecodeFuzzSafe(OodleLZ_Compressor compressor); +/* OodleLZ_Compressor properties helper. + + Tells you if this compressor is "fuzz safe" which means it can accept corrupted data + and won't crash or overrun any buffers. +*/ + +OO_BOOL OodleLZ_Compressor_RespectsDictionarySize(OodleLZ_Compressor compressor); +/* OodleLZ_Compressor properties helper. + + Tells you if this compressor obeys $(OodleLZ_CompressOptions:dictionarySize) which limits + match references to a finite bound. (eg. for sliding window decompression). + + All the new codecs do (Kraken,Mermaid,Selkie,Leviathan). Some old codecs don't. +*/ +//===================================================================== + +#define OODLELZ_COMPRESSOR_MASK(c) (((OO_U32)1)<<((OO_S32)(c))) +// OODLELZ_COMPRESSOR_BOOLBIT : extract a value of 1 or 0 so it maps to "bool" +#define OODLELZ_COMPRESSOR_BOOLBIT(s,c) (((s)>>(OO_S32)(c))&1) + +OOINLINEFUNC OO_BOOL OodleLZ_Compressor_IsNewLZFamily(OodleLZ_Compressor compressor) +{ + const OO_U32 set = + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Kraken) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Leviathan) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Mermaid) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Selkie) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Hydra); + return OODLELZ_COMPRESSOR_BOOLBIT(set,compressor); +} + +OOINLINEFUNC OO_BOOL OodleLZ_Compressor_CanDecodeFuzzSafe(OodleLZ_Compressor compressor) +{ + #ifdef OODLE_ALLOW_DEPRECATED_COMPRESSORS + const OO_U32 set = + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_None) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Kraken) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Leviathan) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Mermaid) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Selkie) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Hydra) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_BitKnit) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_LZB16); + return OODLELZ_COMPRESSOR_BOOLBIT(set,compressor); + #else + // all new compressors are fuzz safe + return compressor != OodleLZ_Compressor_Invalid; + #endif +} + +OOINLINEFUNC OO_BOOL OodleLZ_Compressor_RespectsDictionarySize(OodleLZ_Compressor compressor) +{ + #ifdef OODLE_ALLOW_DEPRECATED_COMPRESSORS + const OO_U32 set = + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_None) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Kraken) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Leviathan) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Mermaid) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Selkie) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_Hydra) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_LZNA) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_BitKnit); + return OODLELZ_COMPRESSOR_BOOLBIT(set,compressor); + #else + // all new compressors respect dictionarySize + return compressor != OodleLZ_Compressor_Invalid; + #endif +} + +OOINLINEFUNC OO_BOOL OodleLZ_Compressor_UsesWholeBlockQuantum(OodleLZ_Compressor compressor) +{ + return OodleLZ_Compressor_IsNewLZFamily(compressor); +} + +OOINLINEFUNC OO_BOOL OodleLZ_Compressor_CanDecodeThreadPhased(OodleLZ_Compressor compressor) +{ + return OodleLZ_Compressor_IsNewLZFamily(compressor); +} + +OOINLINEFUNC OO_BOOL OodleLZ_Compressor_CanDecodeInPlace(OodleLZ_Compressor compressor) +{ + // all compressors can now decode in place : + return compressor != OodleLZ_Compressor_Invalid; +} + +OOINLINEFUNC OO_BOOL OodleLZ_Compressor_CanDecodeInCircularWindow(OodleLZ_Compressor compressor) +{ + #ifdef OODLE_ALLOW_DEPRECATED_COMPRESSORS + const OO_U32 set = + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_LZH) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_LZB16); + #else + const OO_U32 set = 0; + #endif + + return OODLELZ_COMPRESSOR_BOOLBIT(set,compressor); +} + +OOINLINEFUNC OO_BOOL OodleLZ_Compressor_UsesLargeWindow(OodleLZ_Compressor compressor) +{ + // all but LZH and LZB16 now are large window + return ! OodleLZ_Compressor_CanDecodeInCircularWindow(compressor); +} + +OOINLINEFUNC OO_BOOL OodleLZ_Compressor_MustDecodeWithoutResets(OodleLZ_Compressor compressor) +{ + #ifdef OODLE_ALLOW_DEPRECATED_COMPRESSORS + const OO_U32 set = + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_BitKnit) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_LZA) | + OODLELZ_COMPRESSOR_MASK(OodleLZ_Compressor_LZNA); + #else + const OO_U32 set = 0; + #endif + + return OODLELZ_COMPRESSOR_BOOLBIT(set,compressor); +} + +OODEFEND + +//======================================================= + + +#define OODLELZ_SCRATCH_MEM_NO_BOUND (-1) /* Scratch mem size when bound is unknown. + Installed allocator may be used no matter how much scratch mem you provide. +*/ + +OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_GetCompressScratchMemBound( + OodleLZ_Compressor compressor, + OodleLZ_CompressionLevel level, + OO_SINTa rawLen, + const OodleLZ_CompressOptions * pOptions OODEFAULT(NULL) + ); +/* Return the maximum amount of scratch mem that will be needed by OodleLZ_Compress + + $:compressor which OodleLZ variant to use in compression + $:level OodleLZ_CompressionLevel controls how much CPU effort is put into maximizing compression + $:rawLen maximum number of bytes you will compress (plus dictionary backup) + $:pOptions (optional) options; if NULL, $OodleLZ_CompressOptions_GetDefault is used + + If you pass scratch mem to $OodleLZ_Compress of this size, it is gauranteed to do no allocations. + (normally if it runs out of scratch mem, it falls back to the installed allocator) + + For _rawLen_ pass at least the maximum size you will ever encode. If your data is divided into chunks, + pass the chunk size. If you will encode full buffers of unbounded size, pass -1. + + The options must be the same as when you call $OodleLZ_Compress + + Some options and levels may not have simple finite bounds. Then $OODLELZ_SCRATCH_MEM_NO_BOUND is returned + and the call to $OodleLZ_Compress may use the allocator even if infinite scratch memory is provided. + Currently this applies to all the Optimal levels. + + When OODLELZ_SCRATCH_MEM_NO_BOUND is returned, you can still pass in scratch mem which will be used before + going to the plugin allocator. + +*/ + +// get maximum expanded size for compBuf alloc : +// (note this is actually larger than the maximum compressed stream, it includes trash padding) +OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_GetCompressedBufferSizeNeeded(OodleLZ_Compressor compressor,OO_SINTa rawSize); +/* Return the size you must malloc the compressed buffer + + $:compressor compressor used; OodleLZ_Compressor_Invalid to make it enough for any compressor + $:rawSize uncompressed size you will compress into this buffer + + The _compBuf_ passed to $OodleLZ_Compress must be allocated at least this big. + + note this is actually larger than the maximum size of a compressed stream, it includes overrun padding. + +*/ + +// decBuf needs to be a little larger than rawLen, +// this will tell you exactly how much : +OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_GetDecodeBufferSize(OodleLZ_Compressor compressor,OO_SINTa rawSize,OO_BOOL corruptionPossible); +/* Get the size you must malloc the decode (raw) buffer + + $:compressor compressor used; OodleLZ_Compressor_Invalid to make it enough for any compressor + $:rawSize uncompressed (raw) size without padding + $:corruptionPossible true if it is possible for the decoder to get corrupted data + $:return size of buffer to malloc; slightly larger than rawSize if padding is needed + + As of Oodle 2.9.0 this function is deprecated. For all new codecs you can just use the size of the + uncompressed data for the decode buffer size (_rawSize_), no padding is needed. + + Note that LZB16 is still supported in 2.9.0 but does require padding when used in a circular + window (which is deprecated). + + This padding is necessary for the older compressors when FuzzSafe_No is used. The old compressors + and FuzzSafe_No are no longer supported. + + If _corruptionPossible_ is true, a slightly larger buffer size is returned. + + If _corruptionPossible_ is false, then you must ensure that the decoder does not get corrupted data, + either by passing $OodleLZ_CheckCRC_Yes , or by your own mechanism. + + Note about possible overrun in LZ decoding (applies to the old non-fuzz-safe compressors) : + as long as the compresseddata is not corrupted, + and you decode either the entire compressed buffer, or an integer number of "seek chunks" ($OODLELZ_BLOCK_LEN), + then there will be no overrun. So you can decode LZ data in place and it won't stomp any following bytes. + If those conditions are not true (eg. decoding only part of a larger compressed stream, decoding + around a circular window, decoding data that may be corrupted), then there may be some limited amount of + overrun on decode, as returned by $OodleLZ_GetDecodeBufferSize. + + +*/ + +// OodleLZ_GetInPlaceDecodeBufferSize : +// after compressing, ask how big the in-place buffer needs to be +OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_GetInPlaceDecodeBufferSize(OodleLZ_Compressor compressor,OO_SINTa compLen, OO_SINTa rawLen); +/* Get the size of buffer needed for "in place" decode + + $:compressor compressor used; OodleLZ_Compressor_Invalid to make it enough for any compressor + $:compLen compressed data length + $:rawLen decompressed data length + $:return size of buffer needed for "in place" decode ; slighly larger than rawLen + + To do an "in place" decode, allocate a buffer of this size (or larger). Read the compressed data into the end of + the buffer, and decompress to the front of the buffer. The size returned here guarantees that the writes to the + front of the buffer don't conflict with the reads from the end. + + If _compressor_ is one of the new codecs (Kraken,Mermaid,Selkie,Leviathan), the padding for in place decodes can be + very small indeed. It is assumed you will be passing FuzzSafe_Yes to the decompress call. + + If _compLen_ is unknown, you want an in place buffer size that can accomodate any compressed data, then + pass compLen = 0. + + See $OodleLZ_Decompress for more. +*/ + +// GetCompressedStepForRawStep is at OODLELZ_QUANTUM_LEN granularity +// returns how many packed bytes to step to get the desired raw count step +OOFUNC1 OO_SINTa OOFUNC2 OodleLZ_GetCompressedStepForRawStep( + const void * compPtr, OO_SINTa compAvail, + OO_SINTa startRawPos, OO_SINTa rawSeekBytes, + OO_SINTa * pEndRawPos OODEFAULT(NULL), + OO_BOOL * pIndependent OODEFAULT(NULL) ); +/* How many bytes to step a compressed pointer to advance a certain uncompressed amount + + $:compPtr current compressed pointer + $:compAvail compressed bytes available at compPtr + $:startRawPos initial raw pos (corresponding to compPtr) + $:rawSeekBytes the desired step in raw bytes, must be a multiple of $OODLELZ_QUANTUM_LEN or $OODLELZ_BLOCK_LEN + $:pEndRawPos (optional) filled with the end raw pos actually reached + $:pIndependent (optional) filled with a bool that is true if the current chunk is independent from previous + $:return the number of compressed bytes to step + + You should try to use GetCompressedStepForRawStep only at block granularity - both _startRawPos_ and + _rawSeekBytes_ should be multiples of OODLELZ_BLOCK_LEN (except at the end of the stream). As long as you + do that, then *pEndRawPos will = startRawPos + rawSeekBytes. + + You can use it at quantum granularity (OODLELZ_QUANTUM_LEN), but there are some caveats. You cannot step + quanta inside uncompressed blocks, only in normal LZ blocks. If you try to seek quanta inside an uncompressed + block, you will get *pEndRawPos = the end of the block. + + You can only resume seeking from *pEndRawPos . + + returns 0 for valid not-enough-data case + returns -1 for error + + If _compAvail_ is not the whole compressed buffer, then the returned step may be less than the amount you requested. + eg. if the compressed data in _compAvail_ does not contain enough data to make a step of _rawSeekBytes_ a smaller + step will be taken. + NOTE : *can* return comp step > comp avail ! + + +*/ + +OOFUNC1 OodleLZ_Compressor OOFUNC2 OodleLZ_GetAllChunksCompressor(const void * compBuf,OO_SINTa compBufSize, + OO_SINTa rawLen); +/* ask who compressed all chunks in this buf chunk + + $:compBuf pointer to compressed data; must be the start of compressed buffer, or a step of $OODLELZ_BLOCK_LEN raw bytes + $:compBufSize size of _compBuf_ + $:rawLen rawlen of data in _compBuf_ + $:return the $OodleLZ_Compressor used to encode this chunk + + returns a simple compressor (for example OodleLZ_Compressor_Kraken) if that was used on all chunks + + returns OodleLZ_Compressor_Hydra if different NewLZ encoders were used (for example Kraken+Mermaid) + + returns OodleLZ_Compressor_Count if a heterogenous mix of compressors was used (not just NewLZ) + + returns OodleLZ_Compressor_Invalid on error + + note this is only for this chunk - later chunks may have different compressors (eg. with Hydra) + if you compressed all chunks the same it's up to you to store that info in your header + + returns OodleLZ_Compressor_Invalid if _compBufSize_ is too small or any chunk is corrupt +*/ + +OOFUNC1 OodleLZ_Compressor OOFUNC2 OodleLZ_GetFirstChunkCompressor(const void * compChunkPtr, + OO_SINTa compBufAvail, + OO_BOOL * pIndependent); +/* ask who compressed this chunk + + $:compChunkPtr pointer to compressed data; must be the start of compressed buffer, or a step of $OODLELZ_BLOCK_LEN raw bytes + $:compBufAvail number of bytes at _compChunkPtr_ available to read + $:pIndependent (optional) filled with a bool for whether this chunk is independent of predecessors + $:return the $OodleLZ_Compressor used to encode this chunk + + note this is only for this chunk - later chunks may have different compressors (eg. with Hydra) + if you compressed all chunks the same it's up to you to store that info in your header + + Use $OodleLZ_GetAllChunksCompressor for data that might be mixed compressors. + + This replaces the deprecated function $OodleLZ_GetChunkCompressor + + returns OodleLZ_Compressor_Invalid if _compBufAvail_ is too small or the chunk is corrupt +*/ + +OOFUNC1 OodleLZ_Compressor OOFUNC2 OodleLZ_GetChunkCompressor(const void * compChunkPtr, + OO_SINTa compBufAvail, + OO_BOOL * pIndependent); +/* Deprecated entry point for backwards compatibility + + Use $OodleLZ_GetFirstChunkCompressor or $OodleLZ_GetAllChunksCompressor + +*/ + +//======================================================= + +#define OODLE_HEADER_VERSION ((46<<24)|(OODLE2_VERSION_MAJOR<<16)|(OODLE2_VERSION_MINOR<<8)|(OO_U32)sizeof(OodleLZ_SeekTable)) /* OODLE_HEADER_VERSION is used to ensure the Oodle header matches the lib. Don't copy the value of this macro, it will change when + the header is rev'ed. + + This is what you pass to $OodleX_Init or $Oodle_CheckVersion +*/ + +OOFUNC1 OO_BOOL OOFUNC2 Oodle_CheckVersion(OO_U32 oodle_header_version, OO_U32 * pOodleLibVersion OODEFAULT(NULL)); +/* Check the Oodle lib version against the header you are compiling with + + $:oodle_header_version pass $OODLE_HEADER_VERSION here + $:pOodleLibVersion (optional) filled with the Oodle lib version + $:return false if $OODLE_HEADER_VERSION is not compatible with this lib + + If you use the Oodle2 Ext lib,, $OodleX_Init does it for you. But if you want to check that you have a + compatible lib before trying to Init, then use this. +*/ + +OOFUNC1 void OOFUNC2 Oodle_LogHeader(void); +/* Log the Oodle version & copyright + + Uses the log set with $OodleCore_Plugins_SetPrintf +*/ + +// define old names so they still compile : +#define OODLECORE_PLUGIN_JOB_MAX_DEPENDENCIES OODLE_JOB_MAX_DEPENDENCIES +#define t_fp_OodleCore_Plugin_Job t_fp_Oodle_Job + +#ifdef _MSC_VER +#pragma warning(pop) +#pragma pack(pop, Oodle) +#endif + +#endif // __OODLE2_H_INCLUDED__ diff --git a/lib/oodle/oodle2base.h b/lib/oodle/oodle2base.h new file mode 100644 index 0000000..05f73f3 --- /dev/null +++ b/lib/oodle/oodle2base.h @@ -0,0 +1,167 @@ + +//=================================================== +// Oodle2 Base header +// (C) Copyright 1994-2021 Epic Games Tools LLC +//=================================================== + +#ifndef __OODLE2BASE_H_INCLUDED__ +#define __OODLE2BASE_H_INCLUDED__ + +#ifndef OODLE2BASE_PUBLIC_HEADER +#define OODLE2BASE_PUBLIC_HEADER 1 +#endif + +#ifdef _MSC_VER +#pragma pack(push, Oodle, 8) + +#pragma warning(push) +#pragma warning(disable : 4127) // conditional is constant +#endif + +#ifndef OODLE_BASE_TYPES_H +#define OODLE_BASE_TYPES_H + +#include + +#define OOCOPYRIGHT "Copyright (C) 1994-2021, Epic Games Tools LLC" + +// Typedefs +typedef int8_t OO_S8; +typedef uint8_t OO_U8; +typedef int16_t OO_S16; +typedef uint16_t OO_U16; +typedef int32_t OO_S32; +typedef uint32_t OO_U32; +typedef int64_t OO_S64; +typedef uint64_t OO_U64; +typedef float OO_F32; +typedef double OO_F64; +typedef intptr_t OO_SINTa; +typedef uintptr_t OO_UINTa; +typedef int32_t OO_BOOL; + +// Struct packing handling and inlining +#if defined(__GNUC__) || defined(__clang__) + #define OOSTRUCT struct __attribute__((__packed__)) + #define OOINLINEFUNC inline +#elif defined(_MSC_VER) + // on VC++, we use pragmas for the struct packing + #define OOSTRUCT struct + #define OOINLINEFUNC __inline +#endif + +// Linkage stuff +#if defined(_WIN32) + #define OOLINK __stdcall + #define OOEXPLINK __stdcall +#else + #define OOLINK + #define OOEXPLINK +#endif + +// C++ name demangaling +#ifdef __cplusplus + #define OODEFFUNC extern "C" + #define OODEFSTART extern "C" { + #define OODEFEND } + #define OODEFAULT( val ) =val +#else + #define OODEFFUNC + #define OODEFSTART + #define OODEFEND + #define OODEFAULT( val ) +#endif + +// ======================================================== +// Exported function declarations +#define OOEXPFUNC OODEFFUNC + +//=========================================================================== +// OO_STRING_JOIN joins strings in the preprocessor and works with LINESTRING +#define OO_STRING_JOIN(arg1, arg2) OO_STRING_JOIN_DELAY(arg1, arg2) +#define OO_STRING_JOIN_DELAY(arg1, arg2) OO_STRING_JOIN_IMMEDIATE(arg1, arg2) +#define OO_STRING_JOIN_IMMEDIATE(arg1, arg2) arg1 ## arg2 + +//=========================================================================== +// OO_NUMBERNAME is a macro to make a name unique, so that you can use it to declare +// variable names and they won't conflict with each other +// using __LINE__ is broken in MSVC with /ZI , but __COUNTER__ is an MSVC extension that works + +#ifdef _MSC_VER + #define OO_NUMBERNAME(name) OO_STRING_JOIN(name,__COUNTER__) +#else + #define OO_NUMBERNAME(name) OO_STRING_JOIN(name,__LINE__) +#endif + +//=================================================================== +// simple compiler assert +// this happens at declaration time, so if it's inside a function in a C file, drop {} around it +#ifndef OO_COMPILER_ASSERT + #if defined(__clang__) + #define OO_COMPILER_ASSERT_UNUSED __attribute__((unused)) // hides warnings when compiler_asserts are in a local scope + #else + #define OO_COMPILER_ASSERT_UNUSED + #endif + + #define OO_COMPILER_ASSERT(exp) typedef char OO_NUMBERNAME(_dummy_array) [ (exp) ? 1 : -1 ] OO_COMPILER_ASSERT_UNUSED +#endif + + +#endif + + + +// Oodle2 base header + +#ifndef OODLE2_PUBLIC_CORE_DEFINES +#define OODLE2_PUBLIC_CORE_DEFINES 1 + +#define OOFUNC1 OOEXPFUNC +#define OOFUNC2 OOEXPLINK +#define OOFUNCSTART +#define OODLE_CALLBACK OOLINK + +// Check build flags + #if defined(OODLE_BUILDING_LIB) || defined(OODLE_BUILDING_DLL) + #error Should not see OODLE_BUILDING set for users of oodle.h + #endif + +#ifndef NULL +#define NULL (0) +#endif + +// OODLE_MALLOC_MINIMUM_ALIGNMENT is 8 in 32-bit, 16 in 64-bit +#define OODLE_MALLOC_MINIMUM_ALIGNMENT ((OO_SINTa)(2*sizeof(void *))) + +typedef void (OODLE_CALLBACK t_OodleFPVoidVoid)(void); +/* void-void callback func pointer + takes void, returns void +*/ + +typedef void (OODLE_CALLBACK t_OodleFPVoidVoidStar)(void *); +/* void-void-star callback func pointer + takes void pointer, returns void +*/ + +#define OODLE_JOB_MAX_DEPENDENCIES (4) /* Maximum number of dependencies Oodle will ever pass to a RunJob callback +*/ + +#define OODLE_JOB_NULL_HANDLE (0) /* Value 0 of Jobify handles is reserved to mean none +* Wait(OODLE_JOB_NULL_HANDLE) is a nop +* if RunJob returns OODLE_JOB_NULL_HANDLE it means the job +* was run synchronously and no wait is required +*/ + +#define t_fp_Oodle_Job t_OodleFPVoidVoidStar /* Job function pointer for Plugin Jobify system + + takes void pointer returns void +*/ + +#endif // OODLE2_PUBLIC_CORE_DEFINES + +#ifdef _MSC_VER +#pragma warning(pop) +#pragma pack(pop, Oodle) +#endif + +#endif // __OODLE2BASE_H_INCLUDED__ diff --git a/lib/oodle/src/lib.rs b/lib/oodle/src/lib.rs new file mode 100644 index 0000000..76b1d16 --- /dev/null +++ b/lib/oodle/src/lib.rs @@ -0,0 +1,145 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +use std::ptr; + +use color_eyre::{eyre, Result}; + +#[allow(dead_code)] +mod bindings { + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +} + +// Hardcoded chunk size of Bitsquid's bundle compression +pub const CHUNK_SIZE: usize = 512 * 1024; +pub const COMPRESSOR: bindings::OodleLZ_Compressor = + bindings::OodleLZ_Compressor_OodleLZ_Compressor_Kraken; +pub const LEVEL: bindings::OodleLZ_CompressionLevel = + bindings::OodleLZ_CompressionLevel_OodleLZ_CompressionLevel_Optimal2; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum OodleLZ_FuzzSafe { + Yes, + No, +} + +impl From for bindings::OodleLZ_FuzzSafe { + fn from(value: OodleLZ_FuzzSafe) -> Self { + match value { + OodleLZ_FuzzSafe::Yes => bindings::OodleLZ_FuzzSafe_OodleLZ_FuzzSafe_Yes, + OodleLZ_FuzzSafe::No => bindings::OodleLZ_FuzzSafe_OodleLZ_FuzzSafe_No, + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum OodleLZ_CheckCRC { + Yes, + No, +} + +impl From for bindings::OodleLZ_CheckCRC { + fn from(value: OodleLZ_CheckCRC) -> Self { + match value { + OodleLZ_CheckCRC::Yes => bindings::OodleLZ_CheckCRC_OodleLZ_CheckCRC_Yes, + OodleLZ_CheckCRC::No => bindings::OodleLZ_CheckCRC_OodleLZ_CheckCRC_No, + } + } +} + +#[tracing::instrument(skip(data))] +pub fn decompress( + data: I, + fuzz_safe: OodleLZ_FuzzSafe, + check_crc: OodleLZ_CheckCRC, +) -> Result> +where + I: AsRef<[u8]>, +{ + let data = data.as_ref(); + let mut out = vec![0; CHUNK_SIZE]; + + let verbosity = if tracing::enabled!(tracing::Level::INFO) { + bindings::OodleLZ_Verbosity_OodleLZ_Verbosity_Minimal + } else if tracing::enabled!(tracing::Level::DEBUG) { + bindings::OodleLZ_Verbosity_OodleLZ_Verbosity_Some + } else if tracing::enabled!(tracing::Level::TRACE) { + bindings::OodleLZ_Verbosity_OodleLZ_Verbosity_Lots + } else { + bindings::OodleLZ_Verbosity_OodleLZ_Verbosity_None + }; + + let ret = unsafe { + bindings::OodleLZ_Decompress( + data.as_ptr() as *const _, + data.len() as isize, + out.as_mut_ptr() as *mut _, + out.len() as isize, + fuzz_safe.into(), + check_crc.into(), + verbosity, + ptr::null_mut(), + 0, + None, + ptr::null_mut(), + ptr::null_mut(), + 0, + bindings::OodleLZ_Decode_ThreadPhase_OodleLZ_Decode_Unthreaded, + ) + }; + + if ret == 0 { + eyre::bail!("Decompression failed"); + } + + Ok(out) +} + +#[tracing::instrument(skip(data))] +pub fn compress(data: I) -> Result> +where + I: AsRef<[u8]>, +{ + let mut raw = Vec::from(data.as_ref()); + raw.resize(CHUNK_SIZE, 0); + + // TODO: Query oodle for buffer size + let mut out = vec![0u8; CHUNK_SIZE]; + + let ret = unsafe { + bindings::OodleLZ_Compress( + COMPRESSOR, + raw.as_ptr() as *const _, + raw.len() as isize, + out.as_mut_ptr() as *mut _, + LEVEL, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + 0, + ) + }; + + tracing::debug!(compressed_size = ret, "Compressed chunk"); + + if ret == 0 { + eyre::bail!("Compression failed"); + } + + out.resize(ret as usize, 0); + + Ok(out) +} + +pub fn get_decode_buffer_size(raw_size: usize, corruption_possible: bool) -> Result { + let size = unsafe { + bindings::OodleLZ_GetDecodeBufferSize( + COMPRESSOR, + raw_size as isize, + if corruption_possible { 1 } else { 0 }, + ) + }; + Ok(size as usize) +} diff --git a/lib/sdk/Cargo.toml b/lib/sdk/Cargo.toml index 8df7524..e834bfc 100644 --- a/lib/sdk/Cargo.toml +++ b/lib/sdk/Cargo.toml @@ -17,7 +17,7 @@ nanorand = "0.7.0" pin-project-lite = "0.2.9" serde = { version = "1.0.147", features = ["derive"] } serde_sjson = { path = "../../lib/serde_sjson", version = "*" } -oodle-sys = { path = "../../lib/oodle-sys", version = "*" } +oodle = { path = "../../lib/oodle", version = "*" } tokio = { version = "1.21.2", features = ["rt-multi-thread", "fs", "process", "macros", "tracing", "io-util", "io-std"] } tokio-stream = { version = "0.1.11", features = ["fs", "io-util"] } tracing = { version = "0.1.37", features = ["async-await"] } diff --git a/lib/sdk/src/bundle/mod.rs b/lib/sdk/src/bundle/mod.rs index af87d29..96a2ec2 100644 --- a/lib/sdk/src/bundle/mod.rs +++ b/lib/sdk/src/bundle/mod.rs @@ -4,7 +4,7 @@ use std::path::Path; use color_eyre::eyre::{self, Context, Result}; use color_eyre::{Help, Report, SectionExt}; -use oodle_sys::{OodleLZ_CheckCRC, OodleLZ_FuzzSafe, CHUNK_SIZE}; +use oodle::{OodleLZ_CheckCRC, OodleLZ_FuzzSafe, CHUNK_SIZE}; use crate::binary::sync::*; use crate::bundle::file::Properties; @@ -159,7 +159,7 @@ impl Bundle { decompressed.append(&mut compressed_buffer); } else { // TODO: Optimize to not reallocate? - let mut raw_buffer = oodle_sys::decompress( + let mut raw_buffer = oodle::decompress( &compressed_buffer, OodleLZ_FuzzSafe::No, OodleLZ_CheckCRC::No, @@ -257,7 +257,7 @@ impl Bundle { let mut chunk_sizes = Vec::with_capacity(num_chunks); for chunk in chunks { - let compressed = oodle_sys::compress(chunk)?; + let compressed = oodle::compress(chunk)?; tracing::trace!( raw_chunk_size = chunk.len(), compressed_chunk_size = compressed.len() @@ -359,7 +359,7 @@ where r.read_exact(&mut compressed_buffer)?; // TODO: Optimize to not reallocate? - let mut raw_buffer = oodle_sys::decompress( + let mut raw_buffer = oodle::decompress( &compressed_buffer, OodleLZ_FuzzSafe::No, OodleLZ_CheckCRC::No, From 089acdbc28c778db64df40d6c4e2655a6f5ddbf9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 21 Mar 2023 11:04:21 +0100 Subject: [PATCH 084/335] feat: Implement MSVC cross compilation --- build.rs | 123 +++++++++++++++++-------------------------------------- 1 file changed, 37 insertions(+), 86 deletions(-) diff --git a/build.rs b/build.rs index 08e2bbe..28fe052 100644 --- a/build.rs +++ b/build.rs @@ -3,103 +3,54 @@ use fs_extra::dir::CopyOptions; use std::env; use std::process::{Command, Stdio}; +const LIB_NAME: &str = "luajit"; + fn main() { let luajit_dir = format!("{}/luajit", env!("CARGO_MANIFEST_DIR")); let out_dir = env::var("OUT_DIR").unwrap(); let src_dir = format!("{}/luajit/src", out_dir); + let lib_path = format!("{}/lib{}.a", &src_dir, LIB_NAME); dbg!(&luajit_dir); dbg!(&out_dir); dbg!(&src_dir); + dbg!(&lib_path); - // DEP_LUAJIT_INCLUDE - // DEB_LUAJIT_LIB_NAME + let mut copy_options = CopyOptions::new(); + copy_options.overwrite = true; - let lib_name = build_luajit(&luajit_dir, &out_dir, &src_dir); + dir::copy(&luajit_dir, &out_dir, ©_options).expect("Failed to copy LuaJIT source"); - println!("cargo:lib-name={}", lib_name); + let mut buildcmd = Command::new("make"); + buildcmd.current_dir(&src_dir); + buildcmd.stderr(Stdio::inherit()); + buildcmd.arg("BUILDMODE=static"); + + if env::var("CARGO_CFG_WINDOWS").is_ok() { + buildcmd.arg("TARGET_SYS=Windows"); + buildcmd.arg("CROSS=x86_64-w64-mingw32-"); + } + + if cfg!(target_pointer_width = "32") { + buildcmd.env("HOST_CC", "gcc -m32"); + buildcmd.arg("-e"); + } else { + buildcmd.env("HOST_CC", "gcc"); + } + + let mut child = buildcmd.spawn().expect("failed to run make"); + + if !child + .wait() + .map(|status| status.success()) + .map_err(|_| false) + .unwrap_or(false) + { + panic!("Failed to build luajit"); + } + + println!("cargo:lib-name={}", LIB_NAME); println!("cargo:include={}", src_dir); println!("cargo:rustc-link-search=native={}", src_dir); - println!("cargo:rustc-link-lib=static={}", lib_name); - - // if cfg!(target_os = "macos") && cfg!(target_pointer_width = "64") { - // // RUSTFLAGS='-C link-args=-pagezero_size 10000 -image_base 100000000' - // } -} - -#[cfg(target_env = "msvc")] -fn build_luajit(luajit_dir: &str, out_dir: &str, src_dir: &str) -> &'static str { - const LIB_NAME: &'static str = "lua51"; - let lib_path = format!("{}/{}.lib", &src_dir, LIB_NAME); - dbg!(&lib_path); - if !std::fs::metadata(&lib_path).is_ok() { - let target = env::var("TARGET").unwrap(); - let cl_exe: cc::Tool = - cc::windows_registry::find_tool(&target, "cl.exe").expect("cl.exe not found"); - let msvcbuild_bat = format!("{}/msvcbuild.bat", &src_dir); - dbg!(&msvcbuild_bat); - - let mut copy_options = CopyOptions::new(); - copy_options.overwrite = true; - dir::copy(&luajit_dir, &out_dir, ©_options) - .expect("failed to copy luajit source to out dir"); - - let mut buildcmd = Command::new(msvcbuild_bat); - for (name, value) in cl_exe.env() { - eprintln!("{:?} = {:?}", name, value); - buildcmd.env(name, value); - } - - buildcmd.env("Configuration", "Release"); - buildcmd.args(&["static"]); - buildcmd.current_dir(&src_dir); - buildcmd.stderr(Stdio::inherit()); - - let mut child = buildcmd.spawn().expect("failed to run msvcbuild.bat"); - - if !child - .wait() - .map(|status| status.success()) - .map_err(|_| false) - .unwrap_or(false) - { - panic!("Failed to build luajit"); - } - } - - LIB_NAME -} - -#[cfg(not(target_env = "msvc"))] -fn build_luajit(luajit_dir: &str, out_dir: &str, src_dir: &str) -> &'static str { - const LIB_NAME: &'static str = "luajit"; - let lib_path = format!("{}/lib{}.a", &src_dir, LIB_NAME); - dbg!(&lib_path); - if !std::fs::metadata(&lib_path).is_ok() { - let mut copy_options = CopyOptions::new(); - copy_options.overwrite = true; - dir::copy(&luajit_dir, &out_dir, ©_options).unwrap(); - std::fs::copy(format!("etc/Makefile"), format!("{}/Makefile", &src_dir)).unwrap(); - - let mut buildcmd = Command::new("make"); - buildcmd.current_dir(&src_dir); - buildcmd.stderr(Stdio::inherit()); - - if cfg!(target_pointer_width = "32") { - buildcmd.env("HOST_CC", "gcc -m32"); - buildcmd.arg("-e"); - } - - let mut child = buildcmd.spawn().expect("failed to run make"); - - if !child - .wait() - .map(|status| status.success()) - .map_err(|_| false) - .unwrap_or(false) - { - panic!("Failed to build luajit"); - } - } - LIB_NAME + println!("cargo:rustc-link-lib=static={}", LIB_NAME); } From 206f7884e32837532bf02b9712fd145a92032f35 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 21 Mar 2023 11:55:46 +0100 Subject: [PATCH 085/335] Move bindgen to build script --- Cargo.toml | 1 + bindgen.sh | 26 -- build.rs | 34 ++ ffi.h | 4 - src/ffi.rs | 1113 ---------------------------------------------------- src/lib.rs | 8 +- 6 files changed, 41 insertions(+), 1145 deletions(-) delete mode 100644 bindgen.sh delete mode 100644 ffi.h delete mode 100644 src/ffi.rs diff --git a/Cargo.toml b/Cargo.toml index 248c937..8758afc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,5 +15,6 @@ links = "luajit" libc = "0.2" [build-dependencies] +bindgen = "0.64.0" cc = "1.0.40" fs_extra = "1.1.0" diff --git a/bindgen.sh b/bindgen.sh deleted file mode 100644 index 27e7b9e..0000000 --- a/bindgen.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -BINDGEN_VERSION=$(bindgen --version | grep -v -e '^cargo') - -bindgen -o src/ffi.rs \ - --raw-line "/// Generated with: ${BINDGEN_VERSION}" \ - --whitelist-var "LUA.*" \ - --whitelist-var "LUAJIT.*" \ - --whitelist-type "lua_.*" \ - --whitelist-type "luaL_.*" \ - --whitelist-function "lua_.*" \ - --whitelist-function "luaL_.*" \ - --whitelist-function "luaJIT.*" \ - --ctypes-prefix "libc" \ - --use-core \ - --impl-debug \ - ffi.h -- -I luajit/src - -sed -i -e 's/pub fn \(luaJIT_[^\(]*\)/\/\/\/ \n pub fn \1/' src/ffi.rs -sed -i -e 's/pub fn \(lua_[^\(]*\)/\/\/\/ \n pub fn \1/' src/ffi.rs -sed -i -e 's/pub fn \(luaL_[^\(]*\)/\/\/\/ \n pub fn \1/' src/ffi.rs -sed -i -e 's/pub type \(lua_[^\=]*\)/\/\/\/ \n pub type \1/' src/ffi.rs -sed -i -e 's/pub struct \(lua_[^\{]*\)/\/\/\/ \n pub struct \1/' src/ffi.rs -sed -i -e 's/pub struct \(luaL_[^\{]*\)/\/\/\/ \n pub struct \1/' src/ffi.rs - -cargo +stable fmt \ No newline at end of file diff --git a/build.rs b/build.rs index 28fe052..bbafb9a 100644 --- a/build.rs +++ b/build.rs @@ -1,9 +1,11 @@ use fs_extra::dir; use fs_extra::dir::CopyOptions; use std::env; +use std::path::PathBuf; use std::process::{Command, Stdio}; const LIB_NAME: &str = "luajit"; +const LUAJIT_HEADERS: [&str; 4] = ["lua.h", "lualib.h", "lauxlib.h", "luajit.h"]; fn main() { let luajit_dir = format!("{}/luajit", env!("CARGO_MANIFEST_DIR")); @@ -53,4 +55,36 @@ fn main() { println!("cargo:include={}", src_dir); println!("cargo:rustc-link-search=native={}", src_dir); println!("cargo:rustc-link-lib=static={}", LIB_NAME); + + let mut bindings = bindgen::Builder::default(); + + for header in LUAJIT_HEADERS { + println!("cargo:rerun-if-changed={}/src/{}", luajit_dir, header); + bindings = bindings.header(format!("{}/src/{}", luajit_dir, header)); + } + + let bindings = bindings + .allowlist_var("LUA.*") + .allowlist_var("LUAJIT.*") + .allowlist_type("lua_.*") + .allowlist_type("luaL_.*") + .allowlist_function("lua_.*") + .allowlist_function("luaL_.*") + .allowlist_function("luaJIT.*") + .ctypes_prefix("libc") + .impl_debug(true) + .use_core() + .clang_arg("-Iluajit/src") + // Make it pretty + .rustfmt_bindings(true) + .sort_semantically(true) + .merge_extern_blocks(true) + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .generate() + .expect("Failed to generate bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Failed to write bindings"); } diff --git a/ffi.h b/ffi.h deleted file mode 100644 index a9fe40a..0000000 --- a/ffi.h +++ /dev/null @@ -1,4 +0,0 @@ -#include "lua.h" -#include "lualib.h" -#include "lauxlib.h" -#include "luajit.h" \ No newline at end of file diff --git a/src/ffi.rs b/src/ffi.rs deleted file mode 100644 index a33ac54..0000000 --- a/src/ffi.rs +++ /dev/null @@ -1,1113 +0,0 @@ -/* automatically generated by rust-bindgen */ - -// Generated with: bindgen 0.52.0 - -pub const LUA_LDIR: &'static [u8; 7usize] = b"!\\lua\\\0"; -pub const LUA_CDIR: &'static [u8; 3usize] = b"!\\\0"; -pub const LUA_PATH_DEFAULT: &'static [u8; 38usize] = - b".\\?.lua;!\\lua\\?.lua;!\\lua\\?\\init.lua;\0"; -pub const LUA_CPATH_DEFAULT: &'static [u8; 30usize] = b".\\?.dll;!\\?.dll;!\\loadall.dll\0"; -pub const LUA_PATH: &'static [u8; 9usize] = b"LUA_PATH\0"; -pub const LUA_CPATH: &'static [u8; 10usize] = b"LUA_CPATH\0"; -pub const LUA_INIT: &'static [u8; 9usize] = b"LUA_INIT\0"; -pub const LUA_DIRSEP: &'static [u8; 2usize] = b"\\\0"; -pub const LUA_PATHSEP: &'static [u8; 2usize] = b";\0"; -pub const LUA_PATH_MARK: &'static [u8; 2usize] = b"?\0"; -pub const LUA_EXECDIR: &'static [u8; 2usize] = b"!\0"; -pub const LUA_IGMARK: &'static [u8; 2usize] = b"-\0"; -pub const LUA_PATH_CONFIG: &'static [u8; 11usize] = b"\\\n;\n?\n!\n-\n\0"; -pub const LUAI_MAXSTACK: u32 = 65500; -pub const LUAI_MAXCSTACK: u32 = 8000; -pub const LUAI_GCPAUSE: u32 = 200; -pub const LUAI_GCMUL: u32 = 200; -pub const LUA_MAXCAPTURES: u32 = 32; -pub const LUA_IDSIZE: u32 = 60; -pub const LUA_NUMBER_SCAN: &'static [u8; 4usize] = b"%lf\0"; -pub const LUA_NUMBER_FMT: &'static [u8; 6usize] = b"%.14g\0"; -pub const LUAI_MAXNUMBER2STR: u32 = 32; -pub const LUA_INTFRMLEN: &'static [u8; 2usize] = b"l\0"; -pub const LUA_VERSION: &'static [u8; 8usize] = b"Lua 5.1\0"; -pub const LUA_RELEASE: &'static [u8; 10usize] = b"Lua 5.1.4\0"; -pub const LUA_VERSION_NUM: u32 = 501; -pub const LUA_COPYRIGHT: &'static [u8; 41usize] = b"Copyright (C) 1994-2008 Lua.org, PUC-Rio\0"; -pub const LUA_AUTHORS: &'static [u8; 49usize] = - b"R. Ierusalimschy, L. H. de Figueiredo & W. Celes\0"; -pub const LUA_SIGNATURE: &'static [u8; 5usize] = b"\x1BLua\0"; -pub const LUA_MULTRET: i32 = -1; -pub const LUA_REGISTRYINDEX: i32 = -10000; -pub const LUA_ENVIRONINDEX: i32 = -10001; -pub const LUA_GLOBALSINDEX: i32 = -10002; -pub const LUA_OK: u32 = 0; -pub const LUA_YIELD: u32 = 1; -pub const LUA_ERRRUN: u32 = 2; -pub const LUA_ERRSYNTAX: u32 = 3; -pub const LUA_ERRMEM: u32 = 4; -pub const LUA_ERRERR: u32 = 5; -pub const LUA_TNONE: i32 = -1; -pub const LUA_TNIL: u32 = 0; -pub const LUA_TBOOLEAN: u32 = 1; -pub const LUA_TLIGHTUSERDATA: u32 = 2; -pub const LUA_TNUMBER: u32 = 3; -pub const LUA_TSTRING: u32 = 4; -pub const LUA_TTABLE: u32 = 5; -pub const LUA_TFUNCTION: u32 = 6; -pub const LUA_TUSERDATA: u32 = 7; -pub const LUA_TTHREAD: u32 = 8; -pub const LUA_MINSTACK: u32 = 20; -pub const LUA_GCSTOP: u32 = 0; -pub const LUA_GCRESTART: u32 = 1; -pub const LUA_GCCOLLECT: u32 = 2; -pub const LUA_GCCOUNT: u32 = 3; -pub const LUA_GCCOUNTB: u32 = 4; -pub const LUA_GCSTEP: u32 = 5; -pub const LUA_GCSETPAUSE: u32 = 6; -pub const LUA_GCSETSTEPMUL: u32 = 7; -pub const LUA_GCISRUNNING: u32 = 9; -pub const LUA_HOOKCALL: u32 = 0; -pub const LUA_HOOKRET: u32 = 1; -pub const LUA_HOOKLINE: u32 = 2; -pub const LUA_HOOKCOUNT: u32 = 3; -pub const LUA_HOOKTAILRET: u32 = 4; -pub const LUA_MASKCALL: u32 = 1; -pub const LUA_MASKRET: u32 = 2; -pub const LUA_MASKLINE: u32 = 4; -pub const LUA_MASKCOUNT: u32 = 8; -pub const LUA_FILEHANDLE: &'static [u8; 6usize] = b"FILE*\0"; -pub const LUA_COLIBNAME: &'static [u8; 10usize] = b"coroutine\0"; -pub const LUA_MATHLIBNAME: &'static [u8; 5usize] = b"math\0"; -pub const LUA_STRLIBNAME: &'static [u8; 7usize] = b"string\0"; -pub const LUA_TABLIBNAME: &'static [u8; 6usize] = b"table\0"; -pub const LUA_IOLIBNAME: &'static [u8; 3usize] = b"io\0"; -pub const LUA_OSLIBNAME: &'static [u8; 3usize] = b"os\0"; -pub const LUA_LOADLIBNAME: &'static [u8; 8usize] = b"package\0"; -pub const LUA_DBLIBNAME: &'static [u8; 6usize] = b"debug\0"; -pub const LUA_BITLIBNAME: &'static [u8; 4usize] = b"bit\0"; -pub const LUA_JITLIBNAME: &'static [u8; 4usize] = b"jit\0"; -pub const LUA_FFILIBNAME: &'static [u8; 4usize] = b"ffi\0"; -pub const LUA_ERRFILE: u32 = 6; -pub const LUA_NOREF: i32 = -2; -pub const LUA_REFNIL: i32 = -1; -pub const LUAJIT_VERSION: &'static [u8; 19usize] = b"LuaJIT 2.1.0-beta3\0"; -pub const LUAJIT_VERSION_NUM: u32 = 20100; -pub const LUAJIT_COPYRIGHT: &'static [u8; 34usize] = b"Copyright (C) 2005-2017 Mike Pall\0"; -pub const LUAJIT_URL: &'static [u8; 19usize] = b"http://luajit.org/\0"; -pub const LUAJIT_MODE_MASK: u32 = 255; -pub const LUAJIT_MODE_OFF: u32 = 0; -pub const LUAJIT_MODE_ON: u32 = 256; -pub const LUAJIT_MODE_FLUSH: u32 = 512; -pub type va_list = __builtin_va_list; -#[repr(C)] -#[derive(Debug, Copy, Clone)] -/// -pub struct lua_State { - _unused: [u8; 0], -} -/// -pub type lua_CFunction = - ::core::option::Option libc::c_int>; -/// -pub type lua_Reader = ::core::option::Option< - unsafe extern "C" fn( - L: *mut lua_State, - ud: *mut libc::c_void, - sz: *mut usize, - ) -> *const libc::c_char, ->; -/// -pub type lua_Writer = ::core::option::Option< - unsafe extern "C" fn( - L: *mut lua_State, - p: *const libc::c_void, - sz: usize, - ud: *mut libc::c_void, - ) -> libc::c_int, ->; -/// -pub type lua_Alloc = ::core::option::Option< - unsafe extern "C" fn( - ud: *mut libc::c_void, - ptr: *mut libc::c_void, - osize: usize, - nsize: usize, - ) -> *mut libc::c_void, ->; -/// -pub type lua_Number = f64; -/// -pub type lua_Integer = isize; -extern "C" { - /// - pub fn lua_newstate(f: lua_Alloc, ud: *mut libc::c_void) -> *mut lua_State; -} -extern "C" { - /// - pub fn lua_close(L: *mut lua_State); -} -extern "C" { - /// - pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State; -} -extern "C" { - /// - pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction; -} -extern "C" { - /// - pub fn lua_gettop(L: *mut lua_State) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_settop(L: *mut lua_State, idx: libc::c_int); -} -extern "C" { - /// - pub fn lua_pushvalue(L: *mut lua_State, idx: libc::c_int); -} -extern "C" { - /// - pub fn lua_remove(L: *mut lua_State, idx: libc::c_int); -} -extern "C" { - /// - pub fn lua_insert(L: *mut lua_State, idx: libc::c_int); -} -extern "C" { - /// - pub fn lua_replace(L: *mut lua_State, idx: libc::c_int); -} -extern "C" { - /// - pub fn lua_checkstack(L: *mut lua_State, sz: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_xmove(from: *mut lua_State, to: *mut lua_State, n: libc::c_int); -} -extern "C" { - /// - pub fn lua_isnumber(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_isstring(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_iscfunction(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_isuserdata(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_type(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_typename(L: *mut lua_State, tp: libc::c_int) -> *const libc::c_char; -} -extern "C" { - /// - pub fn lua_equal(L: *mut lua_State, idx1: libc::c_int, idx2: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_rawequal(L: *mut lua_State, idx1: libc::c_int, idx2: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_lessthan(L: *mut lua_State, idx1: libc::c_int, idx2: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_tonumber(L: *mut lua_State, idx: libc::c_int) -> lua_Number; -} -extern "C" { - /// - pub fn lua_tointeger(L: *mut lua_State, idx: libc::c_int) -> lua_Integer; -} -extern "C" { - /// - pub fn lua_toboolean(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_tolstring( - L: *mut lua_State, - idx: libc::c_int, - len: *mut usize, - ) -> *const libc::c_char; -} -extern "C" { - /// - pub fn lua_objlen(L: *mut lua_State, idx: libc::c_int) -> usize; -} -extern "C" { - /// - pub fn lua_tocfunction(L: *mut lua_State, idx: libc::c_int) -> lua_CFunction; -} -extern "C" { - /// - pub fn lua_touserdata(L: *mut lua_State, idx: libc::c_int) -> *mut libc::c_void; -} -extern "C" { - /// - pub fn lua_tothread(L: *mut lua_State, idx: libc::c_int) -> *mut lua_State; -} -extern "C" { - /// - pub fn lua_topointer(L: *mut lua_State, idx: libc::c_int) -> *const libc::c_void; -} -extern "C" { - /// - pub fn lua_pushnil(L: *mut lua_State); -} -extern "C" { - /// - pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number); -} -extern "C" { - /// - pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer); -} -extern "C" { - /// - pub fn lua_pushlstring(L: *mut lua_State, s: *const libc::c_char, l: usize); -} -extern "C" { - /// - pub fn lua_pushstring(L: *mut lua_State, s: *const libc::c_char); -} -extern "C" { - /// - pub fn lua_pushvfstring( - L: *mut lua_State, - fmt: *const libc::c_char, - argp: va_list, - ) -> *const libc::c_char; -} -extern "C" { - /// - pub fn lua_pushfstring(L: *mut lua_State, fmt: *const libc::c_char, ...) - -> *const libc::c_char; -} -extern "C" { - /// - pub fn lua_pushcclosure(L: *mut lua_State, fn_: lua_CFunction, n: libc::c_int); -} -extern "C" { - /// - pub fn lua_pushboolean(L: *mut lua_State, b: libc::c_int); -} -extern "C" { - /// - pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut libc::c_void); -} -extern "C" { - /// - pub fn lua_pushthread(L: *mut lua_State) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_gettable(L: *mut lua_State, idx: libc::c_int); -} -extern "C" { - /// - pub fn lua_getfield(L: *mut lua_State, idx: libc::c_int, k: *const libc::c_char); -} -extern "C" { - /// - pub fn lua_rawget(L: *mut lua_State, idx: libc::c_int); -} -extern "C" { - /// - pub fn lua_rawgeti(L: *mut lua_State, idx: libc::c_int, n: libc::c_int); -} -extern "C" { - /// - pub fn lua_createtable(L: *mut lua_State, narr: libc::c_int, nrec: libc::c_int); -} -extern "C" { - /// - pub fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut libc::c_void; -} -extern "C" { - /// - pub fn lua_getmetatable(L: *mut lua_State, objindex: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_getfenv(L: *mut lua_State, idx: libc::c_int); -} -extern "C" { - /// - pub fn lua_settable(L: *mut lua_State, idx: libc::c_int); -} -extern "C" { - /// - pub fn lua_setfield(L: *mut lua_State, idx: libc::c_int, k: *const libc::c_char); -} -extern "C" { - /// - pub fn lua_rawset(L: *mut lua_State, idx: libc::c_int); -} -extern "C" { - /// - pub fn lua_rawseti(L: *mut lua_State, idx: libc::c_int, n: libc::c_int); -} -extern "C" { - /// - pub fn lua_setmetatable(L: *mut lua_State, objindex: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_setfenv(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_call(L: *mut lua_State, nargs: libc::c_int, nresults: libc::c_int); -} -extern "C" { - /// - pub fn lua_pcall( - L: *mut lua_State, - nargs: libc::c_int, - nresults: libc::c_int, - errfunc: libc::c_int, - ) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_cpcall(L: *mut lua_State, func: lua_CFunction, ud: *mut libc::c_void) - -> libc::c_int; -} -extern "C" { - /// - pub fn lua_load( - L: *mut lua_State, - reader: lua_Reader, - dt: *mut libc::c_void, - chunkname: *const libc::c_char, - ) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut libc::c_void) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_yield(L: *mut lua_State, nresults: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_resume(L: *mut lua_State, narg: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_status(L: *mut lua_State) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_gc(L: *mut lua_State, what: libc::c_int, data: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_error(L: *mut lua_State) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_next(L: *mut lua_State, idx: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_concat(L: *mut lua_State, n: libc::c_int); -} -extern "C" { - /// - pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut libc::c_void) -> lua_Alloc; -} -extern "C" { - /// - pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut libc::c_void); -} -extern "C" { - /// - pub fn lua_setlevel(from: *mut lua_State, to: *mut lua_State); -} -/// -pub type lua_Hook = - ::core::option::Option; -extern "C" { - /// - pub fn lua_getstack(L: *mut lua_State, level: libc::c_int, ar: *mut lua_Debug) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_getinfo( - L: *mut lua_State, - what: *const libc::c_char, - ar: *mut lua_Debug, - ) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_getlocal( - L: *mut lua_State, - ar: *const lua_Debug, - n: libc::c_int, - ) -> *const libc::c_char; -} -extern "C" { - /// - pub fn lua_setlocal( - L: *mut lua_State, - ar: *const lua_Debug, - n: libc::c_int, - ) -> *const libc::c_char; -} -extern "C" { - /// - pub fn lua_getupvalue( - L: *mut lua_State, - funcindex: libc::c_int, - n: libc::c_int, - ) -> *const libc::c_char; -} -extern "C" { - /// - pub fn lua_setupvalue( - L: *mut lua_State, - funcindex: libc::c_int, - n: libc::c_int, - ) -> *const libc::c_char; -} -extern "C" { - /// - pub fn lua_sethook( - L: *mut lua_State, - func: lua_Hook, - mask: libc::c_int, - count: libc::c_int, - ) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_gethook(L: *mut lua_State) -> lua_Hook; -} -extern "C" { - /// - pub fn lua_gethookmask(L: *mut lua_State) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_gethookcount(L: *mut lua_State) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_upvalueid(L: *mut lua_State, idx: libc::c_int, n: libc::c_int) -> *mut libc::c_void; -} -extern "C" { - /// - pub fn lua_upvaluejoin( - L: *mut lua_State, - idx1: libc::c_int, - n1: libc::c_int, - idx2: libc::c_int, - n2: libc::c_int, - ); -} -extern "C" { - /// - pub fn lua_loadx( - L: *mut lua_State, - reader: lua_Reader, - dt: *mut libc::c_void, - chunkname: *const libc::c_char, - mode: *const libc::c_char, - ) -> libc::c_int; -} -extern "C" { - /// - pub fn lua_version(L: *mut lua_State) -> *const lua_Number; -} -extern "C" { - /// - pub fn lua_copy(L: *mut lua_State, fromidx: libc::c_int, toidx: libc::c_int); -} -extern "C" { - /// - pub fn lua_tonumberx( - L: *mut lua_State, - idx: libc::c_int, - isnum: *mut libc::c_int, - ) -> lua_Number; -} -extern "C" { - /// - pub fn lua_tointegerx( - L: *mut lua_State, - idx: libc::c_int, - isnum: *mut libc::c_int, - ) -> lua_Integer; -} -extern "C" { - /// - pub fn lua_isyieldable(L: *mut lua_State) -> libc::c_int; -} -#[repr(C)] -#[derive(Copy, Clone)] -/// -pub struct lua_Debug { - pub event: libc::c_int, - pub name: *const libc::c_char, - pub namewhat: *const libc::c_char, - pub what: *const libc::c_char, - pub source: *const libc::c_char, - pub currentline: libc::c_int, - pub nups: libc::c_int, - pub linedefined: libc::c_int, - pub lastlinedefined: libc::c_int, - pub short_src: [libc::c_char; 60usize], - pub i_ci: libc::c_int, -} -#[test] -fn bindgen_test_layout_lua_Debug() { - assert_eq!( - ::core::mem::size_of::(), - 120usize, - concat!("Size of: ", stringify!(lua_Debug)) - ); - assert_eq!( - ::core::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(lua_Debug)) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).event as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(lua_Debug), - "::", - stringify!(event) - ) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).name as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(lua_Debug), - "::", - stringify!(name) - ) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).namewhat as *const _ as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(lua_Debug), - "::", - stringify!(namewhat) - ) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).what as *const _ as usize }, - 24usize, - concat!( - "Offset of field: ", - stringify!(lua_Debug), - "::", - stringify!(what) - ) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).source as *const _ as usize }, - 32usize, - concat!( - "Offset of field: ", - stringify!(lua_Debug), - "::", - stringify!(source) - ) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).currentline as *const _ as usize }, - 40usize, - concat!( - "Offset of field: ", - stringify!(lua_Debug), - "::", - stringify!(currentline) - ) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).nups as *const _ as usize }, - 44usize, - concat!( - "Offset of field: ", - stringify!(lua_Debug), - "::", - stringify!(nups) - ) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).linedefined as *const _ as usize }, - 48usize, - concat!( - "Offset of field: ", - stringify!(lua_Debug), - "::", - stringify!(linedefined) - ) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).lastlinedefined as *const _ as usize }, - 52usize, - concat!( - "Offset of field: ", - stringify!(lua_Debug), - "::", - stringify!(lastlinedefined) - ) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).short_src as *const _ as usize }, - 56usize, - concat!( - "Offset of field: ", - stringify!(lua_Debug), - "::", - stringify!(short_src) - ) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).i_ci as *const _ as usize }, - 116usize, - concat!( - "Offset of field: ", - stringify!(lua_Debug), - "::", - stringify!(i_ci) - ) - ); -} -impl ::core::fmt::Debug for lua_Debug { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - write ! ( f , "lua_Debug {{ event: {:?}, name: {:?}, namewhat: {:?}, what: {:?}, source: {:?}, currentline: {:?}, nups: {:?}, linedefined: {:?}, lastlinedefined: {:?}, short_src: [...], i_ci: {:?} }}" , self . event , self . name , self . namewhat , self . what , self . source , self . currentline , self . nups , self . linedefined , self . lastlinedefined , self . i_ci ) - } -} -extern "C" { - /// - pub fn luaL_openlibs(L: *mut lua_State); -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -/// -pub struct luaL_Reg { - pub name: *const libc::c_char, - pub func: lua_CFunction, -} -#[test] -fn bindgen_test_layout_luaL_Reg() { - assert_eq!( - ::core::mem::size_of::(), - 16usize, - concat!("Size of: ", stringify!(luaL_Reg)) - ); - assert_eq!( - ::core::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(luaL_Reg)) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).name as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(luaL_Reg), - "::", - stringify!(name) - ) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).func as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(luaL_Reg), - "::", - stringify!(func) - ) - ); -} -extern "C" { - /// - pub fn luaL_openlib( - L: *mut lua_State, - libname: *const libc::c_char, - l: *const luaL_Reg, - nup: libc::c_int, - ); -} -extern "C" { - /// - pub fn luaL_register(L: *mut lua_State, libname: *const libc::c_char, l: *const luaL_Reg); -} -extern "C" { - /// - pub fn luaL_getmetafield( - L: *mut lua_State, - obj: libc::c_int, - e: *const libc::c_char, - ) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_callmeta( - L: *mut lua_State, - obj: libc::c_int, - e: *const libc::c_char, - ) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_typerror( - L: *mut lua_State, - narg: libc::c_int, - tname: *const libc::c_char, - ) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_argerror( - L: *mut lua_State, - numarg: libc::c_int, - extramsg: *const libc::c_char, - ) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_checklstring( - L: *mut lua_State, - numArg: libc::c_int, - l: *mut usize, - ) -> *const libc::c_char; -} -extern "C" { - /// - pub fn luaL_optlstring( - L: *mut lua_State, - numArg: libc::c_int, - def: *const libc::c_char, - l: *mut usize, - ) -> *const libc::c_char; -} -extern "C" { - /// - pub fn luaL_checknumber(L: *mut lua_State, numArg: libc::c_int) -> lua_Number; -} -extern "C" { - /// - pub fn luaL_optnumber(L: *mut lua_State, nArg: libc::c_int, def: lua_Number) -> lua_Number; -} -extern "C" { - /// - pub fn luaL_checkinteger(L: *mut lua_State, numArg: libc::c_int) -> lua_Integer; -} -extern "C" { - /// - pub fn luaL_optinteger(L: *mut lua_State, nArg: libc::c_int, def: lua_Integer) -> lua_Integer; -} -extern "C" { - /// - pub fn luaL_checkstack(L: *mut lua_State, sz: libc::c_int, msg: *const libc::c_char); -} -extern "C" { - /// - pub fn luaL_checktype(L: *mut lua_State, narg: libc::c_int, t: libc::c_int); -} -extern "C" { - /// - pub fn luaL_checkany(L: *mut lua_State, narg: libc::c_int); -} -extern "C" { - /// - pub fn luaL_newmetatable(L: *mut lua_State, tname: *const libc::c_char) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_checkudata( - L: *mut lua_State, - ud: libc::c_int, - tname: *const libc::c_char, - ) -> *mut libc::c_void; -} -extern "C" { - /// - pub fn luaL_where(L: *mut lua_State, lvl: libc::c_int); -} -extern "C" { - /// - pub fn luaL_error(L: *mut lua_State, fmt: *const libc::c_char, ...) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_checkoption( - L: *mut lua_State, - narg: libc::c_int, - def: *const libc::c_char, - lst: *const *const libc::c_char, - ) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_ref(L: *mut lua_State, t: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_unref(L: *mut lua_State, t: libc::c_int, ref_: libc::c_int); -} -extern "C" { - /// - pub fn luaL_loadfile(L: *mut lua_State, filename: *const libc::c_char) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_loadbuffer( - L: *mut lua_State, - buff: *const libc::c_char, - sz: usize, - name: *const libc::c_char, - ) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_loadstring(L: *mut lua_State, s: *const libc::c_char) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_newstate() -> *mut lua_State; -} -extern "C" { - /// - pub fn luaL_gsub( - L: *mut lua_State, - s: *const libc::c_char, - p: *const libc::c_char, - r: *const libc::c_char, - ) -> *const libc::c_char; -} -extern "C" { - /// - pub fn luaL_findtable( - L: *mut lua_State, - idx: libc::c_int, - fname: *const libc::c_char, - szhint: libc::c_int, - ) -> *const libc::c_char; -} -extern "C" { - /// - pub fn luaL_fileresult( - L: *mut lua_State, - stat: libc::c_int, - fname: *const libc::c_char, - ) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_execresult(L: *mut lua_State, stat: libc::c_int) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_loadfilex( - L: *mut lua_State, - filename: *const libc::c_char, - mode: *const libc::c_char, - ) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_loadbufferx( - L: *mut lua_State, - buff: *const libc::c_char, - sz: usize, - name: *const libc::c_char, - mode: *const libc::c_char, - ) -> libc::c_int; -} -extern "C" { - /// - pub fn luaL_traceback( - L: *mut lua_State, - L1: *mut lua_State, - msg: *const libc::c_char, - level: libc::c_int, - ); -} -extern "C" { - /// - pub fn luaL_setfuncs(L: *mut lua_State, l: *const luaL_Reg, nup: libc::c_int); -} -extern "C" { - /// - pub fn luaL_pushmodule(L: *mut lua_State, modname: *const libc::c_char, sizehint: libc::c_int); -} -extern "C" { - /// - pub fn luaL_testudata( - L: *mut lua_State, - ud: libc::c_int, - tname: *const libc::c_char, - ) -> *mut libc::c_void; -} -extern "C" { - /// - pub fn luaL_setmetatable(L: *mut lua_State, tname: *const libc::c_char); -} -#[repr(C)] -#[derive(Copy, Clone)] -/// -pub struct luaL_Buffer { - pub p: *mut libc::c_char, - pub lvl: libc::c_int, - pub L: *mut lua_State, - pub buffer: [libc::c_char; 512usize], -} -#[test] -fn bindgen_test_layout_luaL_Buffer() { - assert_eq!( - ::core::mem::size_of::(), - 536usize, - concat!("Size of: ", stringify!(luaL_Buffer)) - ); - assert_eq!( - ::core::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(luaL_Buffer)) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).p as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(luaL_Buffer), - "::", - stringify!(p) - ) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).lvl as *const _ as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(luaL_Buffer), - "::", - stringify!(lvl) - ) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).L as *const _ as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(luaL_Buffer), - "::", - stringify!(L) - ) - ); - assert_eq!( - unsafe { &(*(::core::ptr::null::())).buffer as *const _ as usize }, - 24usize, - concat!( - "Offset of field: ", - stringify!(luaL_Buffer), - "::", - stringify!(buffer) - ) - ); -} -impl ::core::fmt::Debug for luaL_Buffer { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - write!( - f, - "luaL_Buffer {{ p: {:?}, lvl: {:?}, L: {:?}, buffer: [...] }}", - self.p, self.lvl, self.L - ) - } -} -extern "C" { - /// - pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Buffer); -} -extern "C" { - /// - pub fn luaL_prepbuffer(B: *mut luaL_Buffer) -> *mut libc::c_char; -} -extern "C" { - /// - pub fn luaL_addlstring(B: *mut luaL_Buffer, s: *const libc::c_char, l: usize); -} -extern "C" { - /// - pub fn luaL_addstring(B: *mut luaL_Buffer, s: *const libc::c_char); -} -extern "C" { - /// - pub fn luaL_addvalue(B: *mut luaL_Buffer); -} -extern "C" { - /// - pub fn luaL_pushresult(B: *mut luaL_Buffer); -} -pub const LUAJIT_MODE_ENGINE: _bindgen_ty_1 = 0; -pub const LUAJIT_MODE_DEBUG: _bindgen_ty_1 = 1; -pub const LUAJIT_MODE_FUNC: _bindgen_ty_1 = 2; -pub const LUAJIT_MODE_ALLFUNC: _bindgen_ty_1 = 3; -pub const LUAJIT_MODE_ALLSUBFUNC: _bindgen_ty_1 = 4; -pub const LUAJIT_MODE_TRACE: _bindgen_ty_1 = 5; -pub const LUAJIT_MODE_WRAPCFUNC: _bindgen_ty_1 = 16; -pub const LUAJIT_MODE_MAX: _bindgen_ty_1 = 17; -pub type _bindgen_ty_1 = i32; -extern "C" { - /// - pub fn luaJIT_setmode(L: *mut lua_State, idx: libc::c_int, mode: libc::c_int) -> libc::c_int; -} -pub type luaJIT_profile_callback = ::core::option::Option< - unsafe extern "C" fn( - data: *mut libc::c_void, - L: *mut lua_State, - samples: libc::c_int, - vmstate: libc::c_int, - ), ->; -extern "C" { - /// - pub fn luaJIT_profile_start( - L: *mut lua_State, - mode: *const libc::c_char, - cb: luaJIT_profile_callback, - data: *mut libc::c_void, - ); -} -extern "C" { - /// - pub fn luaJIT_profile_stop(L: *mut lua_State); -} -extern "C" { - /// - pub fn luaJIT_profile_dumpstack( - L: *mut lua_State, - fmt: *const libc::c_char, - depth: libc::c_int, - len: *mut usize, - ) -> *const libc::c_char; -} -extern "C" { - /// - pub fn luaJIT_version_2_1_0_beta3(); -} -pub type __builtin_va_list = *mut libc::c_char; diff --git a/src/lib.rs b/src/lib.rs index e0d799f..c48ca06 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,8 @@ #![no_std] #![allow(non_snake_case)] #![allow(non_camel_case_types)] +#![allow(clippy::deprecated_semver)] +#![allow(clippy::missing_safety_doc)] //! # LuaJIT 2.1 //! @@ -15,7 +17,9 @@ //! //! -mod ffi; +mod ffi { + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +} pub use ffi::*; use core::ptr; @@ -92,7 +96,7 @@ pub unsafe fn lua_isthread(L: *mut lua_State, idx: libc::c_int) -> libc::c_int { /// #[inline] pub unsafe fn lua_isnone(L: *mut lua_State, idx: libc::c_int) -> libc::c_int { - (lua_type(L, idx) == LUA_TNONE as i32) as i32 + (lua_type(L, idx) == LUA_TNONE) as i32 } /// From 9ab05ce18cc35b3235b0cc7665d6e860193c9b11 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Mar 2023 13:35:56 +0100 Subject: [PATCH 086/335] feat: Implement cross compilation for MSVC toolchain --- Cargo.toml | 4 +- build.rs | 179 +++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 156 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8758afc..23f0cd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "luajit2-sys" version = "0.0.2" description = "LuaJIT-2.1 FFI Bindings" authors = ["Aaron Loucks "] -edition = "2018" +edition = "2021" keywords = ["lua", "luajit", "script"] license = "MIT OR Apache-2.0" readme = "README.md" @@ -16,5 +16,5 @@ libc = "0.2" [build-dependencies] bindgen = "0.64.0" -cc = "1.0.40" +cc = "1" fs_extra = "1.1.0" diff --git a/build.rs b/build.rs index bbafb9a..cfcebdd 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,4 @@ +use cc::Build; use fs_extra::dir; use fs_extra::dir::CopyOptions; use std::env; @@ -6,38 +7,101 @@ use std::process::{Command, Stdio}; const LIB_NAME: &str = "luajit"; const LUAJIT_HEADERS: [&str; 4] = ["lua.h", "lualib.h", "lauxlib.h", "luajit.h"]; +const LUAJIT_SRC: [&str; 69] = [ + // LJCORE_O + // The MSVC toolchain cannot compile this assembler file, + // as it contains GNU-specific directives + // "lj_vm.S", + "lj_assert.c", + "lj_gc.c", + "lj_err.c", + "lj_char.c", + "lj_bc.c", + "lj_obj.c", + "lj_buf.c", + "lj_str.c", + "lj_tab.c", + "lj_func.c", + "lj_udata.c", + "lj_meta.c", + "lj_debug.c", + "lj_prng.c", + "lj_state.c", + "lj_dispatch.c", + "lj_vmevent.c", + "lj_vmmath.c", + "lj_strscan.c", + "lj_strfmt.c", + "lj_strfmt_num.c", + "lj_serialize.c", + "lj_api.c", + "lj_profile.c", + "lj_lex.c", + "lj_parse.c", + "lj_bcread.c", + "lj_bcwrite.c", + "lj_load.c", + "lj_ir.c", + "lj_opt_mem.c", + "lj_opt_fold.c", + "lj_opt_narrow.c", + "lj_opt_dce.c", + "lj_opt_loop.c", + "lj_opt_split.c", + "lj_opt_sink.c", + "lj_mcode.c", + "lj_snap.c", + "lj_record.c", + "lj_crecord.c", + "lj_ffrecord.c", + "lj_asm.c", + "lj_trace.c", + "lj_gdbjit.c", + "lj_ctype.c", + "lj_cdata.c", + "lj_cconv.c", + "lj_ccall.c", + "lj_ccallback.c", + "lj_carith.c", + "lj_clib.c", + "lj_cparse.c", + "lj_lib.c", + "lj_alloc.c", + // LJLIB_O + "lib_aux.c", + "lib_base.c", + "lib_math.c", + "lib_bit.c", + "lib_string.c", + "lib_table.c", + "lib_io.c", + "lib_os.c", + "lib_package.c", + "lib_debug.c", + "lib_jit.c", + "lib_ffi.c", + "lib_buffer.c", + "lib_init.c", +]; -fn main() { - let luajit_dir = format!("{}/luajit", env!("CARGO_MANIFEST_DIR")); - let out_dir = env::var("OUT_DIR").unwrap(); - let src_dir = format!("{}/luajit/src", out_dir); - let lib_path = format!("{}/lib{}.a", &src_dir, LIB_NAME); - - dbg!(&luajit_dir); - dbg!(&out_dir); - dbg!(&src_dir); - dbg!(&lib_path); - - let mut copy_options = CopyOptions::new(); - copy_options.overwrite = true; - - dir::copy(&luajit_dir, &out_dir, ©_options).expect("Failed to copy LuaJIT source"); - +fn build_gcc(src_dir: &str) { let mut buildcmd = Command::new("make"); buildcmd.current_dir(&src_dir); buildcmd.stderr(Stdio::inherit()); - buildcmd.arg("BUILDMODE=static"); + buildcmd.arg("--no-silent"); + // We do need to cross-compile even here, so that `lj_vm.o` is created + // for the correct architecture. if env::var("CARGO_CFG_WINDOWS").is_ok() { buildcmd.arg("TARGET_SYS=Windows"); buildcmd.arg("CROSS=x86_64-w64-mingw32-"); } if cfg!(target_pointer_width = "32") { - buildcmd.env("HOST_CC", "gcc -m32"); + buildcmd.arg("HOST_CC='gcc -m32'"); buildcmd.arg("-e"); } else { - buildcmd.env("HOST_CC", "gcc"); + buildcmd.arg("HOST_CC='gcc'"); } let mut child = buildcmd.spawn().expect("failed to run make"); @@ -50,11 +114,66 @@ fn main() { { panic!("Failed to build luajit"); } +} + +fn build_msvc(src_dir: &str, out_dir: &str) { + let mut cc = Build::new(); + // cc can't handle many of the `cland-dl`-specific flags, so + // we need to port them manually from a `make -n` run. + cc.out_dir(out_dir) + // `llvm-as` (which the clang-based toolchain for MSVC would use to compile `lj_vm.S` + // assembler) doesn't support some of the GNU-specific directives. + // However, the previous host-targeted compilation already created the + // object, so we simply link that. + .object(format!("{src_dir}/lj_vm.o")) + .define("_FILE_OFFSET_BITS", "64") + .define("_LARGEFILE_SOURCE", None) + .define("LUA_MULTILIB", "\"lib\"") + .define("LUAJIT_UNWIND_EXTERNAL", None) + .flag("-fcolor-diagnostics") + // Disable warnings + .flag("/W0") + .flag("/U _FORTIFY_SOURCE") + // Link statically + .flag("/MT") + // Omit frame pointers + .flag("/Oy"); + + for f in LUAJIT_SRC { + cc.file(format!("{src_dir}/{f}")); + } + + cc.compile(LIB_NAME); +} + +fn main() { + let luajit_dir = format!("{}/luajit", env!("CARGO_MANIFEST_DIR")); + let out_dir = env::var("OUT_DIR").unwrap(); + let src_dir = format!("{}/luajit/src", out_dir); + + dbg!(&luajit_dir); + dbg!(&out_dir); + dbg!(&src_dir); + + let mut copy_options = CopyOptions::new(); + copy_options.overwrite = true; + + dir::copy(&luajit_dir, &out_dir, ©_options).expect("Failed to copy LuaJIT source"); + + // The first run builds with and for the host architecture. + // This also creates all the tools and generated sources that a compilation needs. + build_gcc(&src_dir); + + // Then, for cross-compilation, we can utilize those generated + // sources to re-compile just the library. + if env::var("CARGO_CFG_WINDOWS").is_ok() { + build_msvc(&src_dir, &out_dir); + } println!("cargo:lib-name={}", LIB_NAME); println!("cargo:include={}", src_dir); - println!("cargo:rustc-link-search=native={}", src_dir); - println!("cargo:rustc-link-lib=static={}", LIB_NAME); + println!("cargo:rustc-link-search={}", out_dir); + println!("cargo:rustc-link-lib={}", LIB_NAME); let mut bindings = bindgen::Builder::default(); @@ -74,14 +193,24 @@ fn main() { .ctypes_prefix("libc") .impl_debug(true) .use_core() - .clang_arg("-Iluajit/src") + .detect_include_paths(true) // Make it pretty .rustfmt_bindings(true) .sort_semantically(true) .merge_extern_blocks(true) - .parse_callbacks(Box::new(bindgen::CargoCallbacks)) - .generate() - .expect("Failed to generate bindings"); + .parse_callbacks(Box::new(bindgen::CargoCallbacks)); + + let bindings = if env::var("CARGO_CFG_WINDOWS").is_ok() { + bindings + .clang_arg("-I/xwin/sdk/include/ucrt") + .clang_arg("-I/xwin/sdk/include/um") + .clang_arg("-I/xwin/sdk/include/shared") + .clang_arg("-I/xwin/crt/include") + .generate() + .expect("Failed to generate bindings") + } else { + bindings.generate().expect("Failed to generate bindings") + }; let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); bindings From e2a285a739b2f487b397a19b63b1b81662f08b46 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Mar 2023 13:42:03 +0100 Subject: [PATCH 087/335] feat: Add Docker images for CI compilation --- .ci/image/Dockerfile.linux | 22 ++++++++ .ci/image/Dockerfile.msvc | 103 +++++++++++++++++++++++++++++++++++++ .fdignore | 6 ++- .gitignore | 1 + .gitmodules | 3 ++ Cargo.lock | 3 +- Justfile | 11 ++++ lib/luajit2-sys | 1 + lib/sdk/Cargo.toml | 2 +- 9 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 .ci/image/Dockerfile.linux create mode 100644 .ci/image/Dockerfile.msvc create mode 100644 Justfile create mode 160000 lib/luajit2-sys diff --git a/.ci/image/Dockerfile.linux b/.ci/image/Dockerfile.linux new file mode 100644 index 0000000..523d0ea --- /dev/null +++ b/.ci/image/Dockerfile.linux @@ -0,0 +1,22 @@ +FROM rust:slim-bullseye + +RUN set -eux; \ + apt-get update; \ + apt-get install --no-install-recommends -y \ + build-essential \ + libatk1.0-dev \ + libclang-13-dev \ + libglib2.0-dev \ + libgtk-3-dev \ + libpango1.0-dev \ + libssl-dev \ + libzstd-dev \ + pkg-config; \ + apt-get remove -y --auto-remove; \ + rm -rf /var/lib/apt/lists/*; \ + rustup default nightly + +WORKDIR /src/dtmt + +COPY *.so /src + diff --git a/.ci/image/Dockerfile.msvc b/.ci/image/Dockerfile.msvc new file mode 100644 index 0000000..4c09735 --- /dev/null +++ b/.ci/image/Dockerfile.msvc @@ -0,0 +1,103 @@ +# https://jake-shadle.github.io/xwin/ +FROM rust:slim-bullseye + +ENV KEYRINGS /usr/local/share/keyrings +ARG XWIN_VERSION=0.2.11 +ARG XWIN_PREFIX="xwin-$XWIN_VERSION-x86_64-unknown-linux-musl" + +ADD https://apt.llvm.org/llvm-snapshot.gpg.key /root/llvm-snapshot.gpg.key +ADD https://dl.winehq.org/wine-builds/winehq.key /root/winehq.key +ADD https://github.com/Jake-Shadle/xwin/releases/download/$XWIN_VERSION/$XWIN_PREFIX.tar.gz /root/$XWIN_PREFIX.tar.gz + +RUN set -eux; \ + mkdir -p $KEYRINGS; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + gpg \ + curl; \ + # clang/lld/llvm + gpg --dearmor > $KEYRINGS/llvm.gpg < /root/llvm-snapshot.gpg.key; \ + # wine + gpg --dearmor > $KEYRINGS/winehq.gpg < /root/winehq.key; \ + echo "deb [signed-by=$KEYRINGS/llvm.gpg] http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-13 main" > /etc/apt/sources.list.d/llvm.list; \ + echo "deb [signed-by=$KEYRINGS/winehq.gpg] https://dl.winehq.org/wine-builds/debian/ bullseye main" > /etc/apt/sources.list.d/winehq.list; \ + dpkg --add-architecture i386; \ + apt-get update; \ + apt-get install --no-install-recommends -y \ + build-essential \ + libatk1.0-dev \ + libclang-13-dev \ + libglib2.0-dev \ + libgtk-3-dev \ + libpango1.0-dev \ + libssl-dev \ + libzstd-dev \ + pkg-config \ + gcc-mingw-w64-x86-64 \ + # g++-mingw-w64-x86-64 \ + clang-13 \ + llvm-13 \ + lld-13 \ + winehq-staging \ + tar; \ + # ensure that clang/clang++ are callable directly + ln -s clang-13 /usr/bin/clang && ln -s clang /usr/bin/clang++ && ln -s lld-13 /usr/bin/ld.lld; \ + # We also need to setup symlinks ourselves for the MSVC shims because they aren't in the debian packages + ln -s clang-13 /usr/bin/clang-cl && ln -s llvm-ar-13 /usr/bin/llvm-lib && ln -s lld-link-13 /usr/bin/lld-link; \ + # Verify the symlinks are correct + clang++ -v; \ + ld.lld -v; \ + # Doesn't have an actual -v/--version flag, but it still exits with 0 + llvm-lib -v; \ + clang-cl -v; \ + lld-link --version; \ + # Use clang instead of gcc when compiling and linking binaries targeting the host (eg proc macros, build files) + update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100; \ + update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100; \ + update-alternatives --install /usr/bin/ld ld /usr/bin/ld.lld 100; \ + rustup default nightly; \ + rustup target add x86_64-pc-windows-msvc; \ + rustup component add rust-src; \ + # Install xwin to cargo/bin via github release. Note you could also just use `cargo install xwin`. + tar -xzv -f /root/$XWIN_PREFIX.tar.gz -C /usr/local/cargo/bin --strip-components=1 $XWIN_PREFIX/xwin; \ + # Splat the CRT and SDK files to /xwin/crt and /xwin/sdk respectively + xwin --accept-license splat --output /xwin; \ + # Remove unneeded files to reduce image size + apt-get remove -y --auto-remove; \ + rm -rf \ + .xwin-cache \ + /usr/local/cargo/bin/xwin \ + /root/$XWIN_PREFIX.tar.gz \ + /var/lib/apt/lists/* \ + /root/*.key; + +# Note that we're using the full target triple for each variable instead of the +# simple CC/CXX/AR shorthands to avoid issues when compiling any C/C++ code for +# build dependencies that need to compile and execute in the host environment +ENV CC_x86_64_pc_windows_msvc="clang-cl" \ + CXX_x86_64_pc_windows_msvc="clang-cl" \ + AR_x86_64_pc_windows_msvc="llvm-lib" \ + # wine can be quite spammy with log messages and they're generally uninteresting + WINEDEBUG="-all" \ + # Use wine to run test executables + CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUNNER="wine" \ + # Note that we only disable unused-command-line-argument here since clang-cl + # doesn't implement all of the options supported by cl, but the ones it doesn't + # are _generally_ not interesting. + CL_FLAGS="-Wno-unused-command-line-argument -fuse-ld=lld-link /imsvc/xwin/crt/include /imsvc/xwin/sdk/include/ucrt /imsvc/xwin/sdk/include/um /imsvc/xwin/sdk/include/shared" \ + # Let cargo know what linker to invoke if you haven't already specified it + # in a .cargo/config.toml file + CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER="lld-link" \ + CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS="-Lnative=/xwin/crt/lib/x86_64 -Lnative=/xwin/sdk/lib/um/x86_64 -Lnative=/xwin/sdk/lib/ucrt/x86_64" + +# These are separate since docker/podman won't transform environment variables defined in the same ENV block +ENV CFLAGS_x86_64_pc_windows_msvc="$CL_FLAGS" \ + CXXFLAGS_x86_64_pc_windows_msvc="$CL_FLAGS" + +# Run wineboot just to setup the default WINEPREFIX so we don't do it every +# container run +RUN wine wineboot --init + +WORKDIR /src/dtmt + +COPY *.lib /src diff --git a/.fdignore b/.fdignore index cbb939d..05d32f8 100644 --- a/.fdignore +++ b/.fdignore @@ -1,3 +1,5 @@ /target -liboo2corelinux64.so -oo2core_8_win64.dll +*.a +*.so +*.dll +*.lib diff --git a/.gitignore b/.gitignore index 4a1b934..7f254dd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /target /data .envrc +*.a *.so *.dll *.lib diff --git a/.gitmodules b/.gitmodules index 1c9c3c2..7a31ce5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "crates/dtmm/assets/icons"] path = crates/dtmm/assets/icons url = https://github.com/tabler/tabler-icons +[submodule "lib/luajit2-sys"] + path = lib/luajit2-sys + url = git@github.com:sclu1034/luajit2-sys.git diff --git a/Cargo.lock b/Cargo.lock index 9cb3b5e..98ee3be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1858,9 +1858,8 @@ dependencies = [ [[package]] name = "luajit2-sys" version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33bb7acccd5a0224645ba06eba391af5f7194ff1762c2545860b43afcfd41af2" dependencies = [ + "bindgen", "cc", "fs_extra", "libc", diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..008dce2 --- /dev/null +++ b/Justfile @@ -0,0 +1,11 @@ +ci-image: ci-image-msvc ci-image-linux + +ci-image-msvc: + docker build -t dtmt-ci-base-msvc -f .ci/image/Dockerfile.msvc .ci/image + docker tag dtmt-ci-base-msvc registry.sclu1034.dev/dtmt-ci-base-msvc + docker push registry.sclu1034.dev/dtmt-ci-base-msvc + +ci-image-linux: + docker build -t dtmt-ci-base-linux -f .ci/image/Dockerfile.linux .ci/image + docker tag dtmt-ci-base-linux registry.sclu1034.dev/dtmt-ci-base-linux + docker push registry.sclu1034.dev/dtmt-ci-base-linux diff --git a/lib/luajit2-sys b/lib/luajit2-sys new file mode 160000 index 0000000..9ab05ce --- /dev/null +++ b/lib/luajit2-sys @@ -0,0 +1 @@ +Subproject commit 9ab05ce18cc35b3235b0cc7665d6e860193c9b11 diff --git a/lib/sdk/Cargo.toml b/lib/sdk/Cargo.toml index e834bfc..63f789b 100644 --- a/lib/sdk/Cargo.toml +++ b/lib/sdk/Cargo.toml @@ -22,6 +22,6 @@ tokio = { version = "1.21.2", features = ["rt-multi-thread", "fs", "process", "m tokio-stream = { version = "0.1.11", features = ["fs", "io-util"] } tracing = { version = "0.1.37", features = ["async-await"] } tracing-error = "0.2.0" -luajit2-sys = "0.0.2" +luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } async-recursion = "1.0.2" path-slash = "0.2.1" From 18797c4d2a53834210fd096dd39195ce7f2bce21 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Mar 2023 15:18:31 +0100 Subject: [PATCH 088/335] fix: Fix linking on Linux --- build.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build.rs b/build.rs index cfcebdd..d53448e 100644 --- a/build.rs +++ b/build.rs @@ -168,12 +168,14 @@ fn main() { // sources to re-compile just the library. if env::var("CARGO_CFG_WINDOWS").is_ok() { build_msvc(&src_dir, &out_dir); + println!("cargo:rustc-link-search={}", out_dir); + } else { + println!("cargo:rustc-link-search=native={}", src_dir); } println!("cargo:lib-name={}", LIB_NAME); println!("cargo:include={}", src_dir); - println!("cargo:rustc-link-search={}", out_dir); - println!("cargo:rustc-link-lib={}", LIB_NAME); + println!("cargo:rustc-link-lib=static={}", LIB_NAME); let mut bindings = bindgen::Builder::default(); From a0166f07ff9ba1a986761682784d0dd84b818f75 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Mar 2023 15:32:33 +0100 Subject: [PATCH 089/335] fix(ci): Fix linking on Linux --- Cargo.toml | 6 ++++++ lib/luajit2-sys | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 56d5fbe..2f47e65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,4 +12,10 @@ members = [ [profile.release] strip = "debuginfo" + +# The MSVC toolchain cannot handle LTO properly. Some symbol related to +# panic unwind would always be missing. +# So we use a separate profile for when we can compile with LTO. +[profile.release-lto] +inherits = "release" lto = true diff --git a/lib/luajit2-sys b/lib/luajit2-sys index 9ab05ce..18797c4 160000 --- a/lib/luajit2-sys +++ b/lib/luajit2-sys @@ -1 +1 @@ -Subproject commit 9ab05ce18cc35b3235b0cc7665d6e860193c9b11 +Subproject commit 18797c4d2a53834210fd096dd39195ce7f2bce21 From 25a11bb79e026c68e8235acd2ed744d574b93001 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Mar 2023 16:49:56 +0100 Subject: [PATCH 090/335] feat(ci): Use HTTPS URLs for submodules This saves setting up SSH access for the CI pipeline. --- .gitmodules | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 7a31ce5..fc31e70 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,12 @@ [submodule "lib/serde_sjson"] path = lib/serde_sjson - url = git@git.sclu1034.dev:lucas/serde_sjson.git + url = https://git.sclu1034.dev/lucas/serde_sjson.git [submodule "lib/steamlocate-rs"] path = lib/steamlocate-rs - url = git@github.com:sclu1034/steamlocate-rs.git + url = https://github.com/sclu1034/steamlocate-rs.git [submodule "crates/dtmm/assets/icons"] path = crates/dtmm/assets/icons url = https://github.com/tabler/tabler-icons [submodule "lib/luajit2-sys"] path = lib/luajit2-sys - url = git@github.com:sclu1034/luajit2-sys.git + url = https://github.com/sclu1034/luajit2-sys.git From 8b70eba79e3dd950c1f7d90cd791baffcf4266d2 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Mar 2023 18:01:31 +0100 Subject: [PATCH 091/335] feat(ci): Add tasks to build in CI --- .ci/tasks/build.sh | 32 ++++++++++++++++++++++++++++++++ .ci/tasks/build.yml | 24 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100755 .ci/tasks/build.sh create mode 100644 .ci/tasks/build.yml diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh new file mode 100755 index 0000000..384c0a8 --- /dev/null +++ b/.ci/tasks/build.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +set -eux + +OUTPUT="$1" + +case "$TARGET" in + msvc) + cp /src/*.lib ./lib/oodle/ + cargo build --color always --locked --release --target x86_64-pc-windows-msvc -Zbuild-std + + if [ -d "$OUTPUT" ]; then + install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmt.exe + install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmm.exe + fi + + ;; + linux) + cp /src/*.so ./lib/oodle/ + cargo build --color always --locked --profile release-lto + + if [ -d "$OUTPUT" ]; then + install -t "$OUTPUT/" target/release/dtmt + install -t "$OUTPUT/" target/release/dtmm + fi + + ;; + *) + set +x + echo "Env var 'TARGET' must either be 'msvc' or 'linux'. Got '$TARGET'." >&2 + exit 1 +esac diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml new file mode 100644 index 0000000..935f81b --- /dev/null +++ b/.ci/tasks/build.yml @@ -0,0 +1,24 @@ +--- +platform: linux + +image_resource: + type: registry-image + source: + repository: registry.local:5000/dtmt-ci-base-((target)) + tag: latest + +inputs: +- name: repo + +outputs: +- name: artifact + +params: + CI: true + TARGET: ((target)) + +run: + path: .ci/tasks/build.sh + dir: repo + args: + - /artifact From 4929b3f201ba3aa78e2fe3a9b596bf7ddee0d059 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Mar 2023 19:17:07 +0100 Subject: [PATCH 092/335] feat(ci): Add basic pipeline --- .ci/pipelines/check.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .ci/pipelines/check.yml diff --git a/.ci/pipelines/check.yml b/.ci/pipelines/check.yml new file mode 100644 index 0000000..4d350ac --- /dev/null +++ b/.ci/pipelines/check.yml @@ -0,0 +1,28 @@ +--- + +# The actual CI pipeline that is run per branch + +resources: +- name: repo + type: git + source: + uri: https://git.sclu1034.dev/bitsquid_dt/dtmt + branch: ((branch)) + +jobs: +- name: build-msvc + plan: + - get: repo + trigger: true + - task: build + file: repo/.ci/tasks/build.yml + vars: + target: msvc +- name: build-linux + plan: + - get: repo + trigger: true + - task: build + file: repo/.ci/tasks/build.yml + vars: + target: linux From 46faebe79a35704bd2c076cd37bf6a0484980faf Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Mar 2023 19:25:20 +0100 Subject: [PATCH 093/335] feat(ci): Add base pipeline This is the pipeline that monitors for branches and starts the actual pipeline(s) for each of them. --- .ci/pipelines/base-pipeline.yml | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .ci/pipelines/base-pipeline.yml diff --git a/.ci/pipelines/base-pipeline.yml b/.ci/pipelines/base-pipeline.yml new file mode 100644 index 0000000..f6195fe --- /dev/null +++ b/.ci/pipelines/base-pipeline.yml @@ -0,0 +1,37 @@ +--- + +# The base pipeline that runs continuously, checks for branches and +# creates a new pipeline instance for each of them. + +resource_types: +- name: git-branches + type: registry-image + source: + repository: aoldershaw/git-branches-resource + +resources: +- name: repo-branches + type: git-branches + source: + uri: https://git.sclu1034.dev/bitsquid_dt/dtmt +- name: repo + type: git + source: + uri: https://git.sclu1034.dev/bitsquid_dt/dtmt + +jobs: + - name: set-pipelines + plan: + - in_parallel: + - get: repo-branches + trigger: true + - get: repo + - load_var: branches + file: repo-branches/branches.json + - across: + - var: branch + values: ((.:branches)) + set_pipeline: dtmt-check + file: repo/.ci/pipelines/check.yml + instance_vars: + branch: ((.:branch.name)) From 4f99fdb5b038ed43f460ed2af10e9dcecae75276 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Mar 2023 14:45:21 +0200 Subject: [PATCH 094/335] feat(dtmm): Improve error dialog visuals Closes #62. --- .gitmodules | 3 ++ Cargo.lock | 2 - Cargo.toml | 6 +++ crates/dtmm/src/ui/window/dialog.rs | 77 ++++++++++++++++++++++------- lib/color-eyre | 1 + 5 files changed, 69 insertions(+), 20 deletions(-) create mode 160000 lib/color-eyre diff --git a/.gitmodules b/.gitmodules index fc31e70..c0fe5fd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "lib/luajit2-sys"] path = lib/luajit2-sys url = https://github.com/sclu1034/luajit2-sys.git +[submodule "lib/color-eyre"] + path = lib/color-eyre + url = https://github.com/sclu1034/color-eyre.git diff --git a/Cargo.lock b/Cargo.lock index 98ee3be..16bea81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -441,8 +441,6 @@ dependencies = [ [[package]] name = "color-eyre" version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" dependencies = [ "backtrace", "color-spantrace", diff --git a/Cargo.toml b/Cargo.toml index 2f47e65..ab29a65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,12 @@ members = [ "lib/steamlocate-rs", ] +[patch.crates-io] +color-eyre = { path = "lib/color-eyre" } + +[profile.dev.package.backtrace] +opt-level = 3 + [profile.release] strip = "debuginfo" diff --git a/crates/dtmm/src/ui/window/dialog.rs b/crates/dtmm/src/ui/window/dialog.rs index 0c2bc18..06a6c02 100644 --- a/crates/dtmm/src/ui/window/dialog.rs +++ b/crates/dtmm/src/ui/window/dialog.rs @@ -1,19 +1,39 @@ -use color_eyre::Report; -use druid::widget::{CrossAxisAlignment, Flex, Label, LineBreaking, MainAxisAlignment}; -use druid::{Data, WidgetExt, WindowDesc, WindowHandle, WindowSizePolicy}; +use color_eyre::{Handler, HelpInfo, Report}; +use druid::widget::{CrossAxisAlignment, Flex, Label, LineBreaking}; +use druid::{Data, WidgetExt, WindowDesc, WindowHandle}; +use crate::ui::theme; use crate::ui::widget::button::Button; +const WINDOW_SIZE: (f64, f64) = (600., 250.); + pub fn error(err: Report, _parent: WindowHandle) -> WindowDesc { - let msg = { - let msg = format!("A critical error ocurred: {:?}", err); - if let Ok(stripped) = strip_ansi_escapes::strip(msg.as_bytes()) { - String::from_utf8_lossy(&stripped).to_string() + let (title, msg) = { + let count = err.chain().count(); + + if count == 1 { + ( + String::from("An error occurred!"), + err.root_cause().to_string(), + ) } else { - msg + let first = err.chain().next().unwrap(); + let root = err.root_cause(); + + if count > 2 { + // The second to last one, the context to the root cause + let context = err.chain().nth(count - 2).unwrap(); + + (format!("{first}!"), format!("{}: {}", context, root)) + } else { + (format!("{first}!"), root.to_string()) + } } }; + let title = Label::new(title) + .with_text_size(24.) + .with_text_color(theme::COLOR_RED_LIGHT); let text = Label::new(msg).with_line_break_mode(LineBreaking::WordWrap); let button = Button::with_label("Ok") @@ -22,19 +42,40 @@ pub fn error(err: Report, _parent: WindowHandle) -> WindowDesc { }) .align_right(); - let widget = Flex::column() - .main_axis_alignment(MainAxisAlignment::SpaceBetween) - .must_fill_main_axis(true) - .cross_axis_alignment(CrossAxisAlignment::End) - .with_child(text) - .with_spacer(20.) - .with_child(button) - .padding(10.); + let mut widget = Flex::column() + .cross_axis_alignment(CrossAxisAlignment::Start) + .with_child(title) + .with_default_spacer() + .with_child(text); + + if let Some(handler) = err.handler().downcast_ref::() { + let mut first = true; + for section in handler.sections() { + if let HelpInfo::Suggestion(data, _) = section { + if first { + widget.add_default_spacer(); + first = false; + } + + let w = Flex::row() + .cross_axis_alignment(CrossAxisAlignment::Start) + .with_child(Label::new("Suggestion:").with_text_color(theme::COLOR_GREEN_LIGHT)) + .with_spacer(2.) + .with_child( + Label::new(data.to_string()).with_line_break_mode(LineBreaking::WordWrap), + ); + + widget.add_child(w); + } + } + } + + let widget = widget.with_flex_spacer(1.).with_child(button).padding(10.); WindowDesc::new(widget) - .title("Error") + .title("Critical Error") .show_titlebar(true) - .window_size_policy(WindowSizePolicy::Content) + .with_min_size(WINDOW_SIZE) .set_always_on_top(true) .resizable(false) } diff --git a/lib/color-eyre b/lib/color-eyre new file mode 160000 index 0000000..55f8c6b --- /dev/null +++ b/lib/color-eyre @@ -0,0 +1 @@ +Subproject commit 55f8c6b7481d462e50ee4a03a43253d80d648ae2 From f50a97ff163491081eb0bc731134e1c6f128251e Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Mar 2023 15:26:15 +0200 Subject: [PATCH 095/335] feat(dtmm): Improve deployment after game update Usually the backup created by the first deployment is used for a clean file to make further deployments from, but when the game is updated, that is reversed, as now the original file is newer than the backup. In such a case, we first copy the new file to our backup before continuing with the deployment. The current implementation is not fool-proof (e.g. it'll do weird stuff if users mess with the files manually) and doesn't cover everything. But it should be enough for the regular use case. Closes #90. --- crates/dtmm/src/controller/game.rs | 89 +++++++++++++++++++----------- 1 file changed, 58 insertions(+), 31 deletions(-) diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 68f9269..45ed27e 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -525,6 +525,7 @@ where ))] pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { let state = Arc::new(state); + let bundle_dir = state.game_dir.join("bundle"); let (_, game_info, deployment_info) = tokio::try_join!( async { @@ -565,16 +566,46 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { .map(|i| game_info.last_updated > i.timestamp) .unwrap_or(false) { - eyre::bail!("Game was updated since last mod deployment. Please reset first."); + tracing::warn!( + "Game was updated since last mod deployment. \ + Attempting to reconcile game files." + ); + + tokio::try_join!( + async { + let path = bundle_dir.join(BUNDLE_DATABASE_NAME); + let backup_path = path.with_extension("data.bak"); + + fs::copy(&path, &backup_path) + .await + .wrap_err("Failed to re-create backup for bundle database.") + }, + async { + let path = bundle_dir.join(BOOT_BUNDLE_NAME); + let backup_path = path.with_extension("bak"); + + fs::copy(&path, &backup_path) + .await + .wrap_err("Failed to re-create backup for boot bundle") + } + ) + .with_suggestion(|| { + "Reset the game using 'Reset Game', then verify game files.".to_string() + })?; + + tracing::info!( + "Successfully re-created game file backups. \ + Continuing mod deployment." + ); } } check_mod_order(&state)?; tracing::info!( - "Deploying {} mods to {}", + "Deploying {} mods to '{}'.", state.mods.iter().filter(|i| i.enabled).count(), - state.game_dir.join("bundle").display() + bundle_dir.display() ); tracing::info!("Build mod bundles"); @@ -589,36 +620,32 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { bundles.append(&mut more_bundles); if let Some(info) = &deployment_info { - let bundle_dir = Arc::new(state.game_dir.join("bundle")); - let tasks = info - .bundles - .iter() - .cloned() - .map(|v| (v, bundle_dir.clone())) - .filter_map(|(file_name, bundle_dir)| { - let contains = bundles.iter().any(|b2| { - let name = b2.name().to_murmur64().to_string(); - file_name == name - }); - - if !contains { - let task = async move { - let path = bundle_dir.join(&file_name); - - tracing::debug!("Removing unused bundle '{}'", file_name); - - if let Err(err) = fs::remove_file(&path).await.wrap_err_with(|| { - format!("Failed to remove unused bundle '{}'", path.display()) - }) { - tracing::error!("{:?}", err); - } - }; - Some(task) - } else { - None - } + let bundle_dir = Arc::new(bundle_dir); + let tasks = info.bundles.iter().cloned().filter_map(|file_name| { + let contains = bundles.iter().any(|b2| { + let name = b2.name().to_murmur64().to_string(); + file_name == name }); + if !contains { + let bundle_dir = bundle_dir.clone(); + let task = async move { + let path = bundle_dir.join(&file_name); + + tracing::debug!("Removing unused bundle '{}'", file_name); + + if let Err(err) = fs::remove_file(&path).await.wrap_err_with(|| { + format!("Failed to remove unused bundle '{}'", path.display()) + }) { + tracing::error!("{:?}", err); + } + }; + Some(task) + } else { + None + } + }); + futures::future::join_all(tasks).await; } From 11c4eddaa4667ea7fffad40b034cf3fcb19fbdd3 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Mar 2023 20:49:15 +0200 Subject: [PATCH 096/335] chore: Fix lcippy warning --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index d53448e..11b3984 100644 --- a/build.rs +++ b/build.rs @@ -86,7 +86,7 @@ const LUAJIT_SRC: [&str; 69] = [ fn build_gcc(src_dir: &str) { let mut buildcmd = Command::new("make"); - buildcmd.current_dir(&src_dir); + buildcmd.current_dir(src_dir); buildcmd.stderr(Stdio::inherit()); buildcmd.arg("--no-silent"); From 597bc7746478bcb73c166a3886de80b0bf1c8aa3 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Mar 2023 20:48:09 +0200 Subject: [PATCH 097/335] chore: Fix clippy warnings --- crates/dtmt/src/cmd/util.rs | 18 ++++++++++++------ lib/luajit2-sys | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/crates/dtmt/src/cmd/util.rs b/crates/dtmt/src/cmd/util.rs index c783ed1..c233425 100644 --- a/crates/dtmt/src/cmd/util.rs +++ b/crates/dtmt/src/cmd/util.rs @@ -8,7 +8,7 @@ use tokio::fs; use tokio_stream::wrappers::ReadDirStream; #[tracing::instrument] -pub async fn foo

(path: P) -> Vec +pub async fn process_path

(path: P) -> Vec where P: AsRef + std::fmt::Debug, { @@ -98,7 +98,10 @@ where I: Iterator + std::fmt::Debug, { let tasks = paths.map(|p| async move { - match tokio::spawn(async move { foo(&p).await }).await { + // Clippy doesn't understand that the block here is required to `move` in the reference. + // The task is spawned to make sure tokio can distribute these over threads. + #[allow(clippy::redundant_async_block)] + match tokio::spawn(async move { process_path(&p).await }).await { Ok(paths) => paths, Err(err) => { tracing::error!(%err, "failed to spawn task to resolve bundle paths"); @@ -111,6 +114,9 @@ where results.into_iter().flatten().collect() } +// `tracing::instrument` generates code that triggers this warning. +// Not much we can do to prevent that. +#[allow(clippy::let_with_type_underscore)] #[tracing::instrument(skip_all)] pub fn resolve_bundle_paths(paths: I) -> impl Stream where @@ -129,12 +135,12 @@ mod tests { use tempfile::tempdir; use tokio::process::Command; - use super::foo; + use super::process_path; #[tokio::test] async fn resolve_single_file() { let path = PathBuf::from("foo"); - let paths = foo(&path).await; + let paths = process_path(&path).await; assert_eq!(paths.len(), 1); assert_eq!(paths[0], path); } @@ -142,7 +148,7 @@ mod tests { #[tokio::test] async fn resolve_empty_directory() { let dir = tempdir().expect("failed to create temporary directory"); - let paths = foo(dir).await; + let paths = process_path(dir).await; assert!(paths.is_empty()); } @@ -170,7 +176,7 @@ mod tests { .await .expect("failed to create temporary files"); - let paths = foo(dir).await; + let paths = process_path(dir).await; assert_eq!(bundle_names.len(), paths.len()); diff --git a/lib/luajit2-sys b/lib/luajit2-sys index 18797c4..11c4edd 160000 --- a/lib/luajit2-sys +++ b/lib/luajit2-sys @@ -1 +1 @@ -Subproject commit 18797c4d2a53834210fd096dd39195ce7f2bce21 +Subproject commit 11c4eddaa4667ea7fffad40b034cf3fcb19fbdd3 From e48ef539b11d0c8c6f08aaffd6b934ab56fd81a4 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 27 Mar 2023 10:52:50 +0200 Subject: [PATCH 098/335] feat(dtmt): Implement mod migration utility Closes #87. --- CHANGELOG.adoc | 1 + Cargo.lock | 1 + crates/dtmt/Cargo.toml | 1 + crates/dtmt/src/cmd/migrate.rs | 408 +++++++++++++++++++++++++++++++++ crates/dtmt/src/main.rs | 4 + lib/dtmt-shared/src/lib.rs | 22 +- 6 files changed, 428 insertions(+), 9 deletions(-) create mode 100644 crates/dtmt/src/cmd/migrate.rs diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 2a19595..c9e4e70 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -13,6 +13,7 @@ - dtmm: check mod order before deployment - dtmt: add mod dependencies to config - dtmm: match mods to Nexus and check for updates +- dtmt: add utility to migrate mod projects === Fixed diff --git a/Cargo.lock b/Cargo.lock index 16bea81..1e94874 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -849,6 +849,7 @@ dependencies = [ "futures-util", "glob", "libloading", + "luajit2-sys", "nanorand", "notify", "oodle", diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index 22fba15..524ffaa 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -32,6 +32,7 @@ path-clean = "1.0.1" path-slash = "0.2.1" async-recursion = "1.0.2" notify = "5.1.0" +luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } [dev-dependencies] tempfile = "3.3.0" diff --git a/crates/dtmt/src/cmd/migrate.rs b/crates/dtmt/src/cmd/migrate.rs new file mode 100644 index 0000000..2c5e7f0 --- /dev/null +++ b/crates/dtmt/src/cmd/migrate.rs @@ -0,0 +1,408 @@ +use std::collections::HashMap; +use std::ffi::{CStr, CString}; +use std::path::{Path, PathBuf}; + +use clap::{value_parser, Arg, ArgMatches, Command}; +use color_eyre::eyre::{self, Context}; +use color_eyre::{Help, Report, Result}; +use dtmt_shared::{ModConfig, ModConfigResources, ModDependency}; +use futures::FutureExt; +use luajit2_sys as lua; +use tokio::fs; +use tokio_stream::wrappers::ReadDirStream; +use tokio_stream::StreamExt; + +pub(crate) fn command_definition() -> Command { + Command::new("migrate") + .about("Migrate a mod project from the loose file structure to DTMT.") + .arg( + Arg::new("mod-file") + .required(true) + .value_parser(value_parser!(PathBuf)) + .help("The path to the mod's '.mod' file."), + ) + .arg( + Arg::new("directory") + .required(true) + .value_parser(value_parser!(PathBuf)) + .help( + "The directory to create the mod in. Within this directory, \ + DTMT will create a new folder named after the mod ID and migrate files \ + into that folder.", + ), + ) +} + +#[derive(Clone, Debug)] +struct ModFile { + id: String, + init: PathBuf, + data: Option, + localization: Option, +} + +// This piece of Lua code stubs DMF functions and runs a mod's `.mod` file to extract +// the contained information. +static MOD_FILE_RUNNER: &str = r#" +_DATA = {} + +function fassert() end + +function new_mod(id, options) + _DATA.id = id + _DATA.init = options.mod_script + _DATA.data = options.mod_data + _DATA.localization = options.mod_localization +end + +dmf = { + dofile = function(self, file) + _DATA.init = file + end +} + +_MOD().run() +"#; + +#[tracing::instrument] +async fn evaluate_mod_file(path: impl AsRef + std::fmt::Debug) -> Result { + let path = path.as_ref(); + let code = fs::read(path) + .await + .wrap_err_with(|| format!("Failed to read file '{}'", path.display()))?; + + tokio::task::spawn_blocking(move || unsafe { + let state = lua::luaL_newstate(); + lua::luaL_openlibs(state); + + let code = CString::new(code).expect("Cannot build CString"); + let name = CString::new("_MOD").expect("Cannot build CString"); + + match lua::luaL_loadstring(state, code.as_ptr()) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRSYNTAX => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Invalid syntax: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory") + } + _ => unreachable!(), + } + + tracing::trace!("Loaded '.mod' code"); + + lua::lua_setglobal(state, name.as_ptr()); + + let code = CString::new(MOD_FILE_RUNNER).expect("Cannot build CString"); + match lua::luaL_loadstring(state, code.as_ptr()) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRSYNTAX => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Invalid syntax: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory") + } + _ => unreachable!(), + } + + match lua::lua_pcall(state, 0, 1, 0) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRRUN => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Failed to evaluate '.mod' file: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory") + } + // We don't use an error handler function, so this should be unreachable + lua::LUA_ERRERR => unreachable!(), + _ => unreachable!(), + } + + tracing::trace!("Loaded file runner code"); + + let name = CString::new("_DATA").expect("Cannot build CString"); + lua::lua_getglobal(state, name.as_ptr()); + + let id = { + let name = CString::new("id").expect("Cannot build CString"); + lua::lua_getfield(state, -1, name.as_ptr()); + let val = { + let ptr = lua::lua_tostring(state, -1); + let str = CStr::from_ptr(ptr); + str.to_str() + .expect("ID value is not a valid string") + .to_string() + }; + lua::lua_pop(state, 1); + val + }; + + let path_prefix = format!("{id}/"); + + let init = { + let name = CString::new("init").expect("Cannot build CString"); + lua::lua_getfield(state, -1, name.as_ptr()); + let val = { + let ptr = lua::lua_tostring(state, -1); + let str = CStr::from_ptr(ptr); + str.to_str().expect("ID value is not a valid string") + }; + lua::lua_pop(state, 1); + PathBuf::from(val.strip_prefix(&path_prefix).unwrap_or(val)) + }; + + let data = { + let name = CString::new("data").expect("Cannot build CString"); + lua::lua_getfield(state, -1, name.as_ptr()); + + if lua::lua_isnil(state, -1) > 0 { + None + } else { + let val = { + let ptr = lua::lua_tostring(state, -1); + let str = CStr::from_ptr(ptr); + str.to_str().expect("ID value is not a valid string") + }; + lua::lua_pop(state, 1); + Some(PathBuf::from(val.strip_prefix(&path_prefix).unwrap_or(val))) + } + }; + + let localization = { + let name = CString::new("localization").expect("Cannot build CString"); + lua::lua_getfield(state, -1, name.as_ptr()); + + if lua::lua_isnil(state, -1) > 0 { + None + } else { + let val = { + let ptr = lua::lua_tostring(state, -1); + let str = CStr::from_ptr(ptr); + str.to_str().expect("ID value is not a valid string") + }; + lua::lua_pop(state, 1); + Some(PathBuf::from(val.strip_prefix(&path_prefix).unwrap_or(val))) + } + }; + + lua::lua_close(state); + + let mod_file = ModFile { + id, + init, + data, + localization, + }; + + tracing::trace!(?mod_file); + + Ok(mod_file) + }) + .await + .map_err(Report::new) + .flatten() + .wrap_err("Failed to run mod file handler") +} + +#[async_recursion::async_recursion] +#[tracing::instrument] +async fn process_directory(path: P1, prefix: P2) -> Result<()> +where + P1: AsRef + std::fmt::Debug + std::marker::Send, + P2: AsRef + std::fmt::Debug + std::marker::Send, +{ + let path = path.as_ref(); + let prefix = prefix.as_ref(); + + let read_dir = fs::read_dir(&path) + .await + .wrap_err_with(|| format!("Failed to read directory '{}'", path.display()))?; + + let stream = ReadDirStream::new(read_dir).map(|res| res.wrap_err("Failed to read dir entry")); + tokio::pin!(stream); + + while let Some(res) = stream.next().await { + let entry = res?; + let in_path = entry.path(); + let out_path = prefix.join(entry.file_name()); + + let t = entry.file_type().await?; + + if t.is_dir() { + process_directory(in_path, out_path).await?; + } else { + tracing::trace!( + "Copying file '{}' -> '{}'", + in_path.display(), + out_path.display() + ); + let res = fs::create_dir_all(prefix) + .then(|_| fs::copy(&in_path, &out_path)) + .await + .wrap_err_with(|| { + format!( + "Failed to copy '{}' -> '{}'", + in_path.display(), + out_path.display() + ) + }); + if let Err(err) = res { + tracing::error!("{:?}", err); + } + } + } + + Ok(()) +} + +#[tracing::instrument(skip_all)] +pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { + let (mod_file, in_dir) = { + let path = matches + .get_one::("mod-file") + .expect("Parameter is required"); + + let mod_file = evaluate_mod_file(&path) + .await + .wrap_err("Failed to evaluate '.mod' file")?; + + ( + mod_file, + path.parent().expect("A file path always has a parent"), + ) + }; + + let out_dir = matches + .get_one::("directory") + .expect("Parameter is required"); + + { + let is_dir = fs::metadata(out_dir) + .await + .map(|meta| meta.is_dir()) + .unwrap_or(false); + + if !is_dir { + let err = eyre::eyre!("Invalid output directory '{}'", out_dir.display()); + return Err(err) + .with_suggestion(|| "Make sure the directory exists and is writable.".to_string()); + } + } + + let out_dir = out_dir.join(&mod_file.id); + + println!( + "Enter additional information about your mod '{}'!", + &mod_file.id + ); + + let name = promptly::prompt_default("Display name", mod_file.id.clone()) + .map(|s: String| s.trim().to_string())?; + let summary = promptly::prompt("Short summary").map(|s: String| s.trim().to_string())?; + let author = + promptly::prompt_opt("Author").map(|opt| opt.map(|s: String| s.trim().to_string()))?; + let version = promptly::prompt_default("Version", String::from("0.1.0")) + .map(|s: String| s.trim().to_string())?; + let categories = promptly::prompt("Categories (comma separated list)") + .map(|s: String| s.trim().to_string()) + .map(|s: String| s.split(',').map(|s| s.trim().to_string()).collect())?; + + let packages = vec![PathBuf::from("packages/mods").join(&mod_file.id)]; + + let dtmt_cfg = ModConfig { + dir: out_dir, + id: mod_file.id, + name, + summary, + author, + version, + description: None, + image: None, + categories, + packages, + resources: ModConfigResources { + init: mod_file.init, + data: mod_file.data, + localization: mod_file.localization, + }, + depends: vec![ModDependency::ID(String::from("DMF"))], + }; + + tracing::debug!(?dtmt_cfg); + + fs::create_dir(&dtmt_cfg.dir).await.wrap_err_with(|| { + format!( + "Failed to create mod directory '{}'", + dtmt_cfg.dir.display() + ) + })?; + + tracing::info!("Created mod directory '{}'", dtmt_cfg.dir.display()); + + { + let path = dtmt_cfg.dir.join("dtmt.cfg"); + let data = serde_sjson::to_string(&dtmt_cfg).wrap_err("Failed to serialize dtmt.cfg")?; + fs::write(&path, &data) + .await + .wrap_err_with(|| format!("Failed to write '{}'", path.display()))?; + + tracing::info!("Created mod configuration at '{}'", path.display()); + } + + { + let path = dtmt_cfg + .dir + .join(&dtmt_cfg.packages[0]) + .with_extension("package"); + + let data = { + let mut map = HashMap::new(); + map.insert("lua", vec![format!("scripts/mods/{}/*", dtmt_cfg.id)]); + map + }; + let data = serde_sjson::to_string(&data).wrap_err("Failed to serialize package file")?; + + fs::create_dir_all(path.parent().unwrap()) + .then(|_| fs::write(&path, &data)) + .await + .wrap_err_with(|| format!("Failed to write '{}'", path.display()))?; + + tracing::info!("Created package file at '{}'", path.display()); + } + + { + let path = in_dir.join("scripts"); + let scripts_dir = dtmt_cfg.dir.join("scripts"); + process_directory(&path, &scripts_dir) + .await + .wrap_err_with(|| { + format!( + "Failed to copy files from '{}' to '{}'", + path.display(), + scripts_dir.display() + ) + })?; + + tracing::info!("Copied script files to '{}'", scripts_dir.display()); + } + + Ok(()) +} diff --git a/crates/dtmt/src/main.rs b/crates/dtmt/src/main.rs index 3e1fba2..bd419e7 100644 --- a/crates/dtmt/src/main.rs +++ b/crates/dtmt/src/main.rs @@ -1,5 +1,6 @@ #![feature(io_error_more)] #![feature(let_chains)] +#![feature(result_flattening)] #![windows_subsystem = "console"] use std::path::PathBuf; @@ -19,6 +20,7 @@ mod cmd { pub mod build; pub mod bundle; pub mod dictionary; + pub mod migrate; pub mod murmur; pub mod new; pub mod package; @@ -52,6 +54,7 @@ async fn main() -> Result<()> { .subcommand(cmd::build::command_definition()) .subcommand(cmd::bundle::command_definition()) .subcommand(cmd::dictionary::command_definition()) + .subcommand(cmd::migrate::command_definition()) .subcommand(cmd::murmur::command_definition()) .subcommand(cmd::new::command_definition()) .subcommand(cmd::package::command_definition()) @@ -128,6 +131,7 @@ async fn main() -> Result<()> { Some(("build", sub_matches)) => cmd::build::run(ctx, sub_matches).await?, Some(("bundle", sub_matches)) => cmd::bundle::run(ctx, sub_matches).await?, Some(("dictionary", sub_matches)) => cmd::dictionary::run(ctx, sub_matches).await?, + Some(("migrate", sub_matches)) => cmd::migrate::run(ctx, sub_matches).await?, Some(("murmur", sub_matches)) => cmd::murmur::run(ctx, sub_matches).await?, Some(("new", sub_matches)) => cmd::new::run(ctx, sub_matches).await?, Some(("package", sub_matches)) => cmd::package::run(ctx, sub_matches).await?, diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index d556fdf..bc7f425 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -3,46 +3,50 @@ use std::path::PathBuf; mod log; pub use log::*; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use steamlocate::SteamDir; use time::OffsetDateTime; -#[derive(Clone, Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct ModConfigResources { pub init: PathBuf, - #[serde(default)] + #[serde(default, skip_serializing_if = "Option::is_none")] pub data: Option, - #[serde(default)] + #[serde(default, skip_serializing_if = "Option::is_none")] pub localization: Option, } -#[derive(Clone, Debug, PartialEq, Deserialize)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "snake_case")] pub enum ModOrder { Before, After, } -#[derive(Clone, Debug, PartialEq, Deserialize)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[serde(untagged)] pub enum ModDependency { ID(String), Config { id: String, order: ModOrder }, } -#[derive(Clone, Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct ModConfig { #[serde(skip)] pub dir: PathBuf, pub id: String, pub name: String, pub summary: String, - pub description: Option, - pub author: Option, pub version: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub author: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub image: Option, #[serde(default)] pub categories: Vec, + #[serde(default)] pub packages: Vec, pub resources: ModConfigResources, #[serde(default)] From 288adf356ba39a15a150ab885980e610dd2ad598 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 27 Mar 2023 15:31:29 +0200 Subject: [PATCH 099/335] feat(dtmm): Implement resetting dtkit-patch installations Closes #88. --- CHANGELOG.adoc | 1 + crates/dtmm/src/controller/game.rs | 67 +++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c9e4e70..2409280 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -14,6 +14,7 @@ - dtmt: add mod dependencies to config - dtmm: match mods to Nexus and check for updates - dtmt: add utility to migrate mod projects +- dtmm: reset dtkit-patch installations === Fixed diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 45ed27e..6f5da47 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -526,11 +526,27 @@ where pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { let state = Arc::new(state); let bundle_dir = state.game_dir.join("bundle"); + let boot_bundle_path = format!("{:016x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes())); + + if fs::metadata(bundle_dir.join(format!("{boot_bundle_path}.patch_999"))) + .await + .is_ok() + { + let err = eyre::eyre!("Found dtkit-patch-based mod installation."); + return Err(err) + .with_suggestion(|| { + "If you're a mod author and saved projects directly in 'mods/', \ + use DTMT to migrate them to the new project structure." + .to_string() + }) + .with_suggestion(|| { + "Click 'Reset Game' to remove the previous mod installation.".to_string() + }); + } let (_, game_info, deployment_info) = tokio::try_join!( async { - let path = state.game_dir.join("bundle"); - fs::metadata(&path) + fs::metadata(&bundle_dir) .await .wrap_err("Failed to open game bundle directory") .with_suggestion(|| "Double-check 'Game Directory' in the Settings tab.") @@ -668,6 +684,45 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { Ok(()) } +#[tracing::instrument(skip_all)] +async fn reset_dtkit_patch(state: ActionState) -> Result<()> { + let bundle_dir = state.game_dir.join("bundle"); + + { + let path = bundle_dir.join(BUNDLE_DATABASE_NAME); + let backup_path = path.with_extension("data.bak"); + fs::rename(&backup_path, &path).await.wrap_err_with(|| { + format!( + "Failed to move bundle datbase backup '{}' -> '{}'", + backup_path.display(), + path.display() + ) + })?; + tracing::trace!("Reverted bundle database from backup"); + } + + for path in [ + bundle_dir.join(format!( + "{:016x}.patch_999", + Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes()) + )), + state.game_dir.join("binaries/mod_loader"), + state.game_dir.join("toggle_darktide_mods.bat"), + state.game_dir.join("README.md"), + ] { + let _ = fs::remove_file(&path).await; + tracing::trace!("Removed file '{}'", path.display()); + } + + for path in [state.game_dir.join("mods"), state.game_dir.join("tools")] { + let _ = fs::remove_dir_all(&path).await; + tracing::trace!("Removed directory '{}'", path.display()); + } + + tracing::info!("Removed dtkit-patch-based mod installation."); + Ok(()) +} + #[tracing::instrument(skip(state))] pub(crate) async fn reset_mod_deployment(state: ActionState) -> Result<()> { let boot_bundle_path = format!("{:016x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes())); @@ -676,6 +731,14 @@ pub(crate) async fn reset_mod_deployment(state: ActionState) -> Result<()> { tracing::info!("Resetting mod deployment in {}", bundle_dir.display()); + if fs::metadata(bundle_dir.join(format!("{boot_bundle_path}.patch_999"))) + .await + .is_ok() + { + tracing::info!("Found dtkit-patch-based mod installation. Removing."); + return reset_dtkit_patch(state).await; + } + tracing::debug!("Reading mod deployment"); let info: DeploymentData = { From 21d95e492cc104f8e53839fc1c2006de88da7897 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Mar 2023 10:00:14 +0200 Subject: [PATCH 100/335] feat(dtmm): Improve reset logging --- crates/dtmm/src/controller/game.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 6f5da47..b5b7f3c 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -1,4 +1,4 @@ -use std::io::{Cursor, ErrorKind}; +use std::io::{self, Cursor, ErrorKind}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Arc; @@ -693,7 +693,7 @@ async fn reset_dtkit_patch(state: ActionState) -> Result<()> { let backup_path = path.with_extension("data.bak"); fs::rename(&backup_path, &path).await.wrap_err_with(|| { format!( - "Failed to move bundle datbase backup '{}' -> '{}'", + "Failed to move bundle database backup '{}' -> '{}'", backup_path.display(), path.display() ) @@ -710,13 +710,23 @@ async fn reset_dtkit_patch(state: ActionState) -> Result<()> { state.game_dir.join("toggle_darktide_mods.bat"), state.game_dir.join("README.md"), ] { - let _ = fs::remove_file(&path).await; - tracing::trace!("Removed file '{}'", path.display()); + match fs::remove_file(&path).await { + Ok(_) => tracing::trace!("Removed file '{}'", path.display()), + Err(err) if err.kind() != io::ErrorKind::NotFound => { + tracing::error!("Failed to remove file '{}': {}", path.display(), err) + } + Err(_) => {} + } } for path in [state.game_dir.join("mods"), state.game_dir.join("tools")] { - let _ = fs::remove_dir_all(&path).await; - tracing::trace!("Removed directory '{}'", path.display()); + match fs::remove_dir_all(&path).await { + Ok(_) => tracing::trace!("Removed directory '{}'", path.display()), + Err(err) if err.kind() != io::ErrorKind::NotFound => { + tracing::error!("Failed to remove directory '{}': {}", path.display(), err) + } + Err(_) => {} + } } tracing::info!("Removed dtkit-patch-based mod installation."); From 2014c3b18787a70585eb923f1717f30e93bcd979 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Mar 2023 20:17:44 +0200 Subject: [PATCH 101/335] feat(dtmt): Fail early in mod migration --- crates/dtmt/src/cmd/migrate.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/crates/dtmt/src/cmd/migrate.rs b/crates/dtmt/src/cmd/migrate.rs index 2c5e7f0..1a4d605 100644 --- a/crates/dtmt/src/cmd/migrate.rs +++ b/crates/dtmt/src/cmd/migrate.rs @@ -309,6 +309,12 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> let out_dir = out_dir.join(&mod_file.id); + fs::create_dir(&out_dir) + .await + .wrap_err_with(|| format!("Failed to create mod directory '{}'", out_dir.display()))?; + + tracing::info!("Created mod directory '{}'", out_dir.display()); + println!( "Enter additional information about your mod '{}'!", &mod_file.id @@ -348,15 +354,6 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> tracing::debug!(?dtmt_cfg); - fs::create_dir(&dtmt_cfg.dir).await.wrap_err_with(|| { - format!( - "Failed to create mod directory '{}'", - dtmt_cfg.dir.display() - ) - })?; - - tracing::info!("Created mod directory '{}'", dtmt_cfg.dir.display()); - { let path = dtmt_cfg.dir.join("dtmt.cfg"); let data = serde_sjson::to_string(&dtmt_cfg).wrap_err("Failed to serialize dtmt.cfg")?; From dbbfa6c9ae096ce886f3ee345f74d0fd51ff8b89 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Mar 2023 21:18:24 +0200 Subject: [PATCH 102/335] fix(dtmm): Fix re-creating boot bundle backup --- crates/dtmm/src/controller/game.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index b5b7f3c..7e2e6bb 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -597,7 +597,7 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { .wrap_err("Failed to re-create backup for bundle database.") }, async { - let path = bundle_dir.join(BOOT_BUNDLE_NAME); + let path = bundle_dir.join(boot_bundle_path); let backup_path = path.with_extension("bak"); fs::copy(&path, &backup_path) From e460de043a254962d209138124b5f54ce21237c5 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Mar 2023 21:32:34 +0200 Subject: [PATCH 103/335] chore(dtmm): Vendor icons Closes #86. --- .gitmodules | 3 --- crates/dtmm/assets/icons | 1 - crates/dtmm/assets/tabler-icons/LICENSE | 21 +++++++++++++++++++ .../dtmm/assets/tabler-icons/alert-circle.svg | 8 +++++++ .../assets/tabler-icons/alert-triangle.svg | 8 +++++++ crates/dtmm/src/ui/theme/icons.rs | 4 ++-- 6 files changed, 39 insertions(+), 6 deletions(-) delete mode 160000 crates/dtmm/assets/icons create mode 100644 crates/dtmm/assets/tabler-icons/LICENSE create mode 100644 crates/dtmm/assets/tabler-icons/alert-circle.svg create mode 100644 crates/dtmm/assets/tabler-icons/alert-triangle.svg diff --git a/.gitmodules b/.gitmodules index c0fe5fd..145bfcb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,6 @@ [submodule "lib/steamlocate-rs"] path = lib/steamlocate-rs url = https://github.com/sclu1034/steamlocate-rs.git -[submodule "crates/dtmm/assets/icons"] - path = crates/dtmm/assets/icons - url = https://github.com/tabler/tabler-icons [submodule "lib/luajit2-sys"] path = lib/luajit2-sys url = https://github.com/sclu1034/luajit2-sys.git diff --git a/crates/dtmm/assets/icons b/crates/dtmm/assets/icons deleted file mode 160000 index 74838de..0000000 --- a/crates/dtmm/assets/icons +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 74838ded9980b6f134bb6f7edcf916cca4a2d97f diff --git a/crates/dtmm/assets/tabler-icons/LICENSE b/crates/dtmm/assets/tabler-icons/LICENSE new file mode 100644 index 0000000..fe62055 --- /dev/null +++ b/crates/dtmm/assets/tabler-icons/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2023 Paweł Kuna + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/dtmm/assets/tabler-icons/alert-circle.svg b/crates/dtmm/assets/tabler-icons/alert-circle.svg new file mode 100644 index 0000000..35e7aad --- /dev/null +++ b/crates/dtmm/assets/tabler-icons/alert-circle.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/crates/dtmm/assets/tabler-icons/alert-triangle.svg b/crates/dtmm/assets/tabler-icons/alert-triangle.svg new file mode 100644 index 0000000..523111c --- /dev/null +++ b/crates/dtmm/assets/tabler-icons/alert-triangle.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/crates/dtmm/src/ui/theme/icons.rs b/crates/dtmm/src/ui/theme/icons.rs index 69156ad..8d82fc1 100644 --- a/crates/dtmm/src/ui/theme/icons.rs +++ b/crates/dtmm/src/ui/theme/icons.rs @@ -3,8 +3,8 @@ use usvg::{ Error, Fill, LineCap, LineJoin, NodeKind, NonZeroPositiveF64, Options, Paint, Stroke, Tree, }; -pub static ALERT_CIRCLE: &str = include_str!("../../../assets/icons/icons/alert-circle.svg"); -pub static ALERT_TRIANGLE: &str = include_str!("../../../assets/icons/icons/alert-triangle.svg"); +pub static ALERT_CIRCLE: &str = include_str!("../../../assets/tabler-icons/alert-circle.svg"); +pub static ALERT_TRIANGLE: &str = include_str!("../../../assets/tabler-icons/alert-triangle.svg"); pub fn parse_svg(svg: &str) -> Result { let opt = Options::default(); From feff4b83bee0a1e6594650ce9d1d3d5f340a5748 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 29 Mar 2023 18:39:31 +0200 Subject: [PATCH 104/335] feat(ci): Implement pipeline for PRs This includes using Gitea's Commit Status API to add checks to the PR UI. --- .ci/image/Dockerfile.linux | 6 ++-- .ci/image/Dockerfile.msvc | 18 ++---------- .ci/pipelines/base-pipeline.yml | 37 +++++++++++++++--------- .ci/pipelines/pr.yml | 49 ++++++++++++++++++++++++++++++++ .ci/tasks/build.sh | 5 ---- .ci/tasks/build.yml | 9 ++++-- .ci/tasks/clippy.sh | 7 +++++ .ci/tasks/clippy.yml | 25 +++++++++++++++++ .ci/util/run.sh | 50 +++++++++++++++++++++++++++++++++ Justfile | 4 +-- 10 files changed, 170 insertions(+), 40 deletions(-) create mode 100644 .ci/pipelines/pr.yml create mode 100755 .ci/tasks/clippy.sh create mode 100644 .ci/tasks/clippy.yml create mode 100755 .ci/util/run.sh diff --git a/.ci/image/Dockerfile.linux b/.ci/image/Dockerfile.linux index 523d0ea..c4f955f 100644 --- a/.ci/image/Dockerfile.linux +++ b/.ci/image/Dockerfile.linux @@ -4,6 +4,9 @@ RUN set -eux; \ apt-get update; \ apt-get install --no-install-recommends -y \ build-essential \ + curl \ + gpg \ + jq \ libatk1.0-dev \ libclang-13-dev \ libglib2.0-dev \ @@ -18,5 +21,4 @@ RUN set -eux; \ WORKDIR /src/dtmt -COPY *.so /src - +COPY liboo2corelinux64.so /src diff --git a/.ci/image/Dockerfile.msvc b/.ci/image/Dockerfile.msvc index 4c09735..7a501eb 100644 --- a/.ci/image/Dockerfile.msvc +++ b/.ci/image/Dockerfile.msvc @@ -1,5 +1,5 @@ # https://jake-shadle.github.io/xwin/ -FROM rust:slim-bullseye +FROM dtmt-ci-base-linux ENV KEYRINGS /usr/local/share/keyrings ARG XWIN_VERSION=0.2.11 @@ -11,10 +11,6 @@ ADD https://github.com/Jake-Shadle/xwin/releases/download/$XWIN_VERSION/$XWIN_PR RUN set -eux; \ mkdir -p $KEYRINGS; \ - apt-get update; \ - apt-get install -y --no-install-recommends \ - gpg \ - curl; \ # clang/lld/llvm gpg --dearmor > $KEYRINGS/llvm.gpg < /root/llvm-snapshot.gpg.key; \ # wine @@ -24,17 +20,8 @@ RUN set -eux; \ dpkg --add-architecture i386; \ apt-get update; \ apt-get install --no-install-recommends -y \ - build-essential \ - libatk1.0-dev \ libclang-13-dev \ - libglib2.0-dev \ - libgtk-3-dev \ - libpango1.0-dev \ - libssl-dev \ - libzstd-dev \ - pkg-config \ gcc-mingw-w64-x86-64 \ - # g++-mingw-w64-x86-64 \ clang-13 \ llvm-13 \ lld-13 \ @@ -55,7 +42,6 @@ RUN set -eux; \ update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100; \ update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100; \ update-alternatives --install /usr/bin/ld ld /usr/bin/ld.lld 100; \ - rustup default nightly; \ rustup target add x86_64-pc-windows-msvc; \ rustup component add rust-src; \ # Install xwin to cargo/bin via github release. Note you could also just use `cargo install xwin`. @@ -100,4 +86,4 @@ RUN wine wineboot --init WORKDIR /src/dtmt -COPY *.lib /src +COPY oo2core_win32.lib oo2core_win64.lib /src diff --git a/.ci/pipelines/base-pipeline.yml b/.ci/pipelines/base-pipeline.yml index f6195fe..9a73737 100644 --- a/.ci/pipelines/base-pipeline.yml +++ b/.ci/pipelines/base-pipeline.yml @@ -4,34 +4,45 @@ # creates a new pipeline instance for each of them. resource_types: -- name: git-branches +- name: gitea-pr type: registry-image source: - repository: aoldershaw/git-branches-resource + repository: registry.local:5000/gitea-pr resources: -- name: repo-branches - type: git-branches +- name: repo-pr + type: gitea-pr source: - uri: https://git.sclu1034.dev/bitsquid_dt/dtmt + access_token: ((gitea_api_key)) + owner: ((owner)) + repo: ((repo)) + url: https://git.sclu1034.dev - name: repo type: git source: uri: https://git.sclu1034.dev/bitsquid_dt/dtmt + # DEBUG: + branch: feat/ci + jobs: - name: set-pipelines plan: - in_parallel: - - get: repo-branches + - get: repo-pr trigger: true - get: repo - - load_var: branches - file: repo-branches/branches.json + - load_var: prs + file: repo-pr/prs.json - across: - - var: branch - values: ((.:branches)) - set_pipeline: dtmt-check - file: repo/.ci/pipelines/check.yml + - var: pr + values: ((.:prs)) + set_pipeline: dtmt-pr + file: repo/.ci/pipelines/pr.yml + vars: + pr: ((.:pr)) + gitea_api_key: ((gitea_api_key)) instance_vars: - branch: ((.:branch.name)) + pr: ((.:pr.number)) + + diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml new file mode 100644 index 0000000..7b79abe --- /dev/null +++ b/.ci/pipelines/pr.yml @@ -0,0 +1,49 @@ +--- + +# The actual CI pipeline that is run per branch + +resources: +- name: repo + type: git + source: + uri: https://git.sclu1034.dev/bitsquid_dt/dtmt + branch: ((pr.head.ref)) + +jobs: +- name: clippy + plan: + - get: repo + trigger: true + - load_var: ref + file: repo/.git/ref + - task: check + file: repo/.ci/tasks/clippy.yml + vars: + ref: ((.:ref)) + gitea_api_key: ((gitea_api_key)) + +- name: build-msvc + plan: + - get: repo + trigger: true + - load_var: ref + file: repo/.git/ref + - task: build + file: repo/.ci/tasks/build.yml + vars: + target: msvc + ref: ((.:ref)) + gitea_api_key: ((gitea_api_key)) + +- name: build-linux + plan: + - get: repo + trigger: true + - load_var: ref + file: repo/.git/ref + - task: build + file: repo/.ci/tasks/build.yml + vars: + target: linux + ref: ((.:ref)) + gitea_api_key: ((gitea_api_key)) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 384c0a8..fd74937 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -2,8 +2,6 @@ set -eux -OUTPUT="$1" - case "$TARGET" in msvc) cp /src/*.lib ./lib/oodle/ @@ -13,7 +11,6 @@ case "$TARGET" in install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmt.exe install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmm.exe fi - ;; linux) cp /src/*.so ./lib/oodle/ @@ -23,10 +20,8 @@ case "$TARGET" in install -t "$OUTPUT/" target/release/dtmt install -t "$OUTPUT/" target/release/dtmm fi - ;; *) - set +x echo "Env var 'TARGET' must either be 'msvc' or 'linux'. Got '$TARGET'." >&2 exit 1 esac diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml index 935f81b..3b411bc 100644 --- a/.ci/tasks/build.yml +++ b/.ci/tasks/build.yml @@ -16,9 +16,14 @@ outputs: params: CI: true TARGET: ((target)) + GITEA_API_KEY: ((gitea_api_key)) + REF: ((ref)) + OUTPUT: artifact run: - path: .ci/tasks/build.sh + path: .ci/util/run.sh dir: repo args: - - /artifact + - .ci/tasks/build.sh + - build/((target)) + - "Build for the target platform: ((target))" diff --git a/.ci/tasks/clippy.sh b/.ci/tasks/clippy.sh new file mode 100755 index 0000000..a7b6e11 --- /dev/null +++ b/.ci/tasks/clippy.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +set -eux + +rustup component add clippy + +cargo clippy --color always --no-deps diff --git a/.ci/tasks/clippy.yml b/.ci/tasks/clippy.yml new file mode 100644 index 0000000..35735c3 --- /dev/null +++ b/.ci/tasks/clippy.yml @@ -0,0 +1,25 @@ +--- +platform: linux + +image_resource: + type: registry-image + source: + repository: registry.local:5000/dtmt-ci-base-linux + tag: latest + +inputs: +- name: repo + +params: + CI: true + GITEA_API_KEY: ((gitea_api_key)) + REF: ((ref)) + +run: + path: .ci/util/run.sh + dir: repo + args: + - .ci/tasks/clippy.sh + - lint/clippy + - "Checking for common mistakes and opportunities for code improvement" + diff --git a/.ci/util/run.sh b/.ci/util/run.sh new file mode 100755 index 0000000..1de3b1c --- /dev/null +++ b/.ci/util/run.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +set -ux + +script="$1" +context="$2" +desc="$3" + +if [ -z "$script" ]; then + echo "No script to run" >&2 + exit 1 +fi + +if [ -z "$context" ]; then + echo "Missing 'context' for CI status report" >&2 + exit 1 +fi + +if [ -z "$REF" ]; then + echo "Environment variable 'REF' must be set to a valid Git ref." >&2 + exit 1 +fi + +if [ -z "$GITEA_API_KEY" ]; then + echo "Environment variable 'GITEA_API_KEY' must be set." >&2 + exit 1 +fi + +notify() { + curl -X 'POST' \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -H "Authorization: token $GITEA_API_KEY" \ + "https://git.sclu1034.dev/api/v1/repos/bitsquid_dt/dtmt/statuses/$REF" \ + --data @- < Date: Thu, 30 Mar 2023 11:22:07 +0200 Subject: [PATCH 105/335] fix(ci): Remove debug value --- .ci/pipelines/base-pipeline.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.ci/pipelines/base-pipeline.yml b/.ci/pipelines/base-pipeline.yml index 9a73737..88052a5 100644 --- a/.ci/pipelines/base-pipeline.yml +++ b/.ci/pipelines/base-pipeline.yml @@ -21,8 +21,6 @@ resources: type: git source: uri: https://git.sclu1034.dev/bitsquid_dt/dtmt - # DEBUG: - branch: feat/ci jobs: From 50a6a1c927271eff916f8306457262392b0b4a33 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 5 Apr 2023 09:38:32 +0200 Subject: [PATCH 106/335] feat(dtmm): Colorize log output Parses ANSI codes generated by tracing/color-eyre into druid's RichText attributes. --- Cargo.lock | 100 ++++++++++++++++++++++++--- crates/dtmm/Cargo.toml | 1 + crates/dtmm/src/controller/worker.rs | 4 +- crates/dtmm/src/main.rs | 1 + crates/dtmm/src/state/data.rs | 10 +-- crates/dtmm/src/state/delegate.rs | 11 +-- crates/dtmm/src/ui/window/main.rs | 21 +++--- crates/dtmm/src/util/ansi.rs | 93 +++++++++++++++++++++++++ crates/dtmm/src/util/log.rs | 11 ++- 9 files changed, 214 insertions(+), 38 deletions(-) create mode 100644 crates/dtmm/src/util/ansi.rs diff --git a/Cargo.lock b/Cargo.lock index 1e94874..547e72e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,6 +38,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "ansi-parser" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcb2392079bf27198570d6af79ecbd9ec7d8f16d3ec6b60933922fdb66287127" +dependencies = [ + "heapless", + "nom 4.2.3", +] + [[package]] name = "anyhow" version = "1.0.69" @@ -62,6 +72,18 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +[[package]] +name = "as-slice" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" +dependencies = [ + "generic-array 0.12.4", + "generic-array 0.13.3", + "generic-array 0.14.6", + "stable_deref_trait", +] + [[package]] name = "associative-cache" version = "1.0.1" @@ -191,7 +213,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array", + "generic-array 0.14.6", ] [[package]] @@ -321,7 +343,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array", + "generic-array 0.14.6", ] [[package]] @@ -614,7 +636,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array", + "generic-array 0.14.6", "typenum", ] @@ -807,6 +829,7 @@ dependencies = [ name = "dtmm" version = "0.1.0" dependencies = [ + "ansi-parser", "bitflags", "clap", "color-eyre", @@ -1272,6 +1295,24 @@ dependencies = [ "system-deps", ] +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.6" @@ -1279,7 +1320,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", - "version_check", + "version_check 0.9.4", ] [[package]] @@ -1480,12 +1521,33 @@ dependencies = [ "tracing", ] +[[package]] +name = "hash32" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "heapless" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74911a68a1658cfcfb61bc0ccfbd536e3b6e906f8c2f7883ee50157e3e2184f1" +dependencies = [ + "as-slice", + "generic-array 0.13.3", + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.4.1" @@ -1609,7 +1671,7 @@ dependencies = [ "serde", "sized-chunks", "typenum", - "version_check", + "version_check 0.9.4", ] [[package]] @@ -2023,6 +2085,16 @@ version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce" +[[package]] +name = "nom" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +dependencies = [ + "memchr", + "version_check 0.1.5", +] + [[package]] name = "nom" version = "7.1.3" @@ -2576,7 +2648,7 @@ dependencies = [ "proc-macro2", "quote", "syn", - "version_check", + "version_check 0.9.4", ] [[package]] @@ -2587,7 +2659,7 @@ checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", - "version_check", + "version_check 0.9.4", ] [[package]] @@ -3091,6 +3163,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "steamid-ng" version = "1.0.0" @@ -3634,7 +3712,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" dependencies = [ - "version_check", + "version_check 0.9.4", ] [[package]] @@ -3768,6 +3846,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" + [[package]] name = "version_check" version = "0.9.4" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 4b651cf..18ba7b1 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -31,3 +31,4 @@ lazy_static = "1.4.0" colors-transform = "0.2.11" usvg = "0.25.0" druid-widget-nursery = "0.1" +ansi-parser = "0.8.0" diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index b30ca9c..4b229b5 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -174,7 +174,7 @@ async fn handle_action( async fn handle_log( event_sink: Arc>, - log_queue: Arc>>, + log_queue: Arc>>>, ) { while let Some(line) = log_queue.write().await.recv().await { let event_sink = event_sink.clone(); @@ -189,7 +189,7 @@ async fn handle_log( pub(crate) fn work_thread( event_sink: Arc>, action_queue: Arc>>, - log_queue: Arc>>, + log_queue: Arc>>>, ) -> Result<()> { let rt = Runtime::new()?; diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 288f1f1..4967b6a 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -23,6 +23,7 @@ use crate::ui::theme; mod controller; mod state; mod util { + pub mod ansi; pub mod config; pub mod log; } diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 8bdcd92..d1eb2e2 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -2,6 +2,7 @@ use std::path::PathBuf; use std::sync::Arc; use druid::im::{HashMap, Vector}; +use druid::text::RichText; use druid::{Data, ImageBuf, Lens, WindowHandle, WindowId}; use dtmt_shared::ModConfig; use nexusmods::Mod as NexusMod; @@ -157,7 +158,7 @@ pub(crate) struct State { pub nexus_api_key: Arc, #[data(ignore)] - pub log: Arc, + pub log: Vector, // True, when the initial loading of configuration and mods is still in progress pub loading: bool, @@ -194,7 +195,7 @@ impl State { game_dir: Arc::new(PathBuf::new()), data_dir: Arc::new(PathBuf::new()), nexus_api_key: Arc::new(String::new()), - log: Arc::new(String::new()), + log: Vector::new(), windows: HashMap::new(), loading: true, } @@ -223,9 +224,4 @@ impl State { pub fn can_move_mod_up(&self) -> bool { self.selected_mod_index.map(|i| i > 0).unwrap_or(false) } - - pub(crate) fn add_log_line(&mut self, line: String) { - let log = Arc::make_mut(&mut self.log); - log.push_str(&line); - } } diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index 02c93d4..eb6af40 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -1,12 +1,14 @@ use std::{path::PathBuf, sync::Arc}; use color_eyre::Report; +use druid::im::Vector; use druid::{ - im::Vector, AppDelegate, Command, DelegateCtx, Env, FileInfo, Handled, Selector, SingleUse, - Target, WindowHandle, WindowId, + AppDelegate, Command, DelegateCtx, Env, FileInfo, Handled, Selector, SingleUse, Target, + WindowHandle, WindowId, }; use tokio::sync::mpsc::UnboundedSender; +use crate::util::ansi::ansi_to_rich_text; use crate::{ui::window, util::config::Config}; use super::{ModInfo, State}; @@ -32,7 +34,7 @@ pub(crate) const ACTION_ADD_MOD: Selector = Selector::new("dtmm.action pub(crate) const ACTION_FINISH_ADD_MOD: Selector>> = Selector::new("dtmm.action.finish-add-mod"); -pub(crate) const ACTION_LOG: Selector> = Selector::new("dtmm.action.log"); +pub(crate) const ACTION_LOG: Selector>> = Selector::new("dtmm.action.log"); pub(crate) const ACTION_START_SAVE_SETTINGS: Selector = Selector::new("dtmm.action.start-save-settings"); @@ -252,7 +254,8 @@ impl AppDelegate for Delegate { .get(ACTION_LOG) .expect("command type matched but didn't contain the expected value"); if let Some(line) = line.take() { - state.add_log_line(line); + let line = String::from_utf8_lossy(&line); + state.log.push_back(ansi_to_rich_text(&line)); } Handled::Yes } diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 988882e..fbedb8f 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -451,17 +451,18 @@ fn build_main() -> impl Widget { } fn build_log_view() -> impl Widget { - let font = FontDescriptor::new(FontFamily::MONOSPACE); - let label = Label::raw() - .with_font(font) - .with_line_break_mode(LineBreaking::WordWrap) - .lens(State::log) - .padding(4.) - .scroll() - .vertical() - .controller(AutoScrollController); + let list = List::new(|| { + Label::raw() + .with_font(FontDescriptor::new(FontFamily::MONOSPACE)) + .with_line_break_mode(LineBreaking::WordWrap) + }) + .lens(State::log) + .padding(4.) + .scroll() + .vertical() + .controller(AutoScrollController); - let inner = Border::new(label) + let inner = Border::new(list) .with_color(theme::COLOR_FG2) .with_top_border(1.); diff --git a/crates/dtmm/src/util/ansi.rs b/crates/dtmm/src/util/ansi.rs new file mode 100644 index 0000000..6bb8698 --- /dev/null +++ b/crates/dtmm/src/util/ansi.rs @@ -0,0 +1,93 @@ +use ansi_parser::{AnsiParser, AnsiSequence, Output}; +use druid::text::{RichText, RichTextBuilder}; +use druid::{Color, FontStyle, FontWeight}; + +use crate::ui::theme; + +#[derive(Default, Debug)] +struct TextState { + color: Option, + dim: bool, + bold: bool, + underline: bool, + strikethrough: bool, + italic: bool, +} + +pub fn ansi_to_rich_text(input: &str) -> RichText { + let mut builder = RichTextBuilder::new(); + + let mut state = TextState::default(); + + for token in input.ansi_parse() { + match token { + Output::TextBlock(text) => { + dbg!(&state); + dbg!(&text); + + let mut attr = builder.push(text); + attr.underline(state.underline); + attr.strikethrough(state.strikethrough); + + if state.bold { + attr.weight(FontWeight::BOLD); + } + + if state.italic { + attr.style(FontStyle::Italic); + } + + if let Some(color) = state.color { + attr.text_color(color); + } + } + Output::Escape(AnsiSequence::SetGraphicsMode(values)) => { + for v in values { + match v { + 0 => { + state = Default::default(); + break; + } + 1 => state.bold = true, + 2 => state.dim = true, + 3 => state.italic = true, + 4 => state.underline = true, + 9 => state.strikethrough = true, + 22 => { + state.bold = false; + state.dim = false; + } + 23 => state.italic = false, + 24 => state.underline = false, + 29 => state.underline = false, + 30..=40 | 90..=100 => { + let mut col = v - 30; + if col > 9 { + state.bold = true; + col -= 60; + } + + state.color = match col { + // This escape code is usually called 'black', but is actually used + // as "foreground color", in regards to light themes. + 1 => Some(theme::COLOR_FG), + 2 => Some(theme::COLOR_RED_LIGHT), + 3 => Some(theme::COLOR_GREEN_LIGHT), + 4 => Some(theme::COLOR_YELLOW_LIGHT), + 5 => Some(theme::COLOR_BLUE_LIGHT), + 6 => Some(theme::COLOR_PURPLE_LIGHT), + 7 => Some(theme::COLOR_AQUA_LIGHT), + 9 => None, + _ => unreachable!(), + }; + } + _ => {} + } + } + } + Output::Escape(_) => {} + } + } + + builder.build() +} diff --git a/crates/dtmm/src/util/log.rs b/crates/dtmm/src/util/log.rs index 1b379dc..97bb9a5 100644 --- a/crates/dtmm/src/util/log.rs +++ b/crates/dtmm/src/util/log.rs @@ -8,11 +8,11 @@ use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; pub struct ChannelWriter { - tx: UnboundedSender, + tx: UnboundedSender>, } impl ChannelWriter { - pub fn new(tx: UnboundedSender) -> Self { + pub fn new(tx: UnboundedSender>) -> Self { Self { tx } } } @@ -20,12 +20,9 @@ impl ChannelWriter { impl std::io::Write for ChannelWriter { fn write(&mut self, buf: &[u8]) -> std::io::Result { let tx = self.tx.clone(); - let stripped = strip_ansi_escapes::strip(buf)?; - let string = String::from_utf8_lossy(&stripped).to_string(); - // The `send` errors when the receiving end has closed. // But there's not much we can do at that point, so we just ignore it. - let _ = tx.send(string); + let _ = tx.send(buf.to_vec()); Ok(buf.len()) } @@ -35,7 +32,7 @@ impl std::io::Write for ChannelWriter { } } -pub fn create_tracing_subscriber(tx: UnboundedSender) { +pub fn create_tracing_subscriber(tx: UnboundedSender>) { let env_layer = if cfg!(debug_assertions) { EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")) } else { From 82e9361e1aa823409c466c069bc76709a07b67d2 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 5 Apr 2023 11:13:41 +0200 Subject: [PATCH 107/335] fix(dtmm): Fix creating default config Fixes #98. --- crates/dtmm/src/main.rs | 29 +---------------------------- crates/dtmm/src/util/config.rs | 2 +- 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 288f1f1..d02dc5b 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -52,38 +52,11 @@ fn main() -> Result<()> { let (action_tx, action_rx) = tokio::sync::mpsc::unbounded_channel(); - // let config = util::config::read_config(&default_config_path, &matches) - // .wrap_err("Failed to read config file")?; - // let game_info = dtmt_shared::collect_game_info(); - - // tracing::debug!(?config, ?game_info); - - // let game_dir = config.game_dir.or_else(|| game_info.map(|i| i.path)); - // if game_dir.is_none() { - // let err = - // eyre::eyre!("No Game Directory set. Head to the 'Settings' tab to set it manually",); - // event_sink - // .submit_command(ACTION_SHOW_ERROR_DIALOG, SingleUse::new(err), Target::Auto) - // .expect("failed to send command"); - // } - - // let initial_state = { - // let mut state = State::new( - // config.path, - // game_dir.unwrap_or_default(), - // config.data_dir.unwrap_or_default(), - // config.nexus_api_key.unwrap_or_default(), - // ); - // state.mods = load_mods(state.get_mod_dir(), config.mod_order.iter()) - // .wrap_err("Failed to load mods")?; - // state - // }; - let config_path = matches .get_one::("config") .cloned() .expect("argument has default value"); - let is_config_default = matches.value_source("config") != Some(ValueSource::DefaultValue); + let is_config_default = matches.value_source("config") == Some(ValueSource::DefaultValue); if action_tx .send(AsyncAction::LoadInitial((config_path, is_config_default))) .is_err() diff --git a/crates/dtmm/src/util/config.rs b/crates/dtmm/src/util/config.rs index e0fde55..1cafeae 100644 --- a/crates/dtmm/src/util/config.rs +++ b/crates/dtmm/src/util/config.rs @@ -151,7 +151,7 @@ where .wrap_err("Failed to serialize default config value")?; fs::write(&config.path, data).await.wrap_err_with(|| { format!( - "failed to write default config to {}", + "Failed to write default config to {}", config.path.display() ) })?; From c7ec318e83fb73c66ec30405ac4c655fede0e766 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 5 Apr 2023 13:42:16 +0200 Subject: [PATCH 108/335] chore(dtmm): Remove debug logs --- crates/dtmm/src/util/ansi.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/dtmm/src/util/ansi.rs b/crates/dtmm/src/util/ansi.rs index 6bb8698..f827c7d 100644 --- a/crates/dtmm/src/util/ansi.rs +++ b/crates/dtmm/src/util/ansi.rs @@ -22,9 +22,6 @@ pub fn ansi_to_rich_text(input: &str) -> RichText { for token in input.ansi_parse() { match token { Output::TextBlock(text) => { - dbg!(&state); - dbg!(&text); - let mut attr = builder.push(text); attr.underline(state.underline); attr.strikethrough(state.strikethrough); From c4425f5b6bfd0369e3e784b5753e01a899a538eb Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 5 Apr 2023 13:44:30 +0200 Subject: [PATCH 109/335] fix(dtmm): Trim whitespace in log lines --- crates/dtmm/src/state/delegate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index eb6af40..1cf3cda 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -255,7 +255,7 @@ impl AppDelegate for Delegate { .expect("command type matched but didn't contain the expected value"); if let Some(line) = line.take() { let line = String::from_utf8_lossy(&line); - state.log.push_back(ansi_to_rich_text(&line)); + state.log.push_back(ansi_to_rich_text(line.trim())); } Handled::Yes } From f30608e6f153a481dafc2c2704337489c5b3bb3c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 5 Apr 2023 13:45:26 +0200 Subject: [PATCH 110/335] feat(dtmm): Enable colors for regular log lines --- Cargo.lock | 10 ++++++++++ crates/dtmm/src/util/log.rs | 2 -- lib/dtmt-shared/Cargo.toml | 1 + lib/dtmt-shared/src/log.rs | 26 ++++++++++++++++++++++++-- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 547e72e..ba688e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,6 +48,15 @@ dependencies = [ "nom 4.2.3", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anyhow" version = "1.0.69" @@ -897,6 +906,7 @@ dependencies = [ name = "dtmt-shared" version = "0.1.0" dependencies = [ + "ansi_term", "color-eyre", "serde", "steamlocate", diff --git a/crates/dtmm/src/util/log.rs b/crates/dtmm/src/util/log.rs index 97bb9a5..75db550 100644 --- a/crates/dtmm/src/util/log.rs +++ b/crates/dtmm/src/util/log.rs @@ -47,8 +47,6 @@ pub fn create_tracing_subscriber(tx: UnboundedSender>) { }; let channel_layer = fmt::layer() - // TODO: Re-enable and implement a formatter for the Druid widget - .with_ansi(false) .event_format(dtmt_shared::Formatter) .fmt_fields(debug_fn(dtmt_shared::format_fields)) .with_writer(move || ChannelWriter::new(tx.clone())) diff --git a/lib/dtmt-shared/Cargo.toml b/lib/dtmt-shared/Cargo.toml index eb9591d..4412266 100644 --- a/lib/dtmt-shared/Cargo.toml +++ b/lib/dtmt-shared/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +ansi_term = "0.12.1" color-eyre = "0.6.2" serde = "1.0.152" steamlocate = { path = "../../lib/steamlocate-rs", version = "*" } diff --git a/lib/dtmt-shared/src/log.rs b/lib/dtmt-shared/src/log.rs index 3c46a4b..ab0b7b5 100644 --- a/lib/dtmt-shared/src/log.rs +++ b/lib/dtmt-shared/src/log.rs @@ -1,10 +1,11 @@ use std::fmt::Result; +use ansi_term::Color; use time::format_description::FormatItem; use time::macros::format_description; use time::OffsetDateTime; use tracing::field::Field; -use tracing::{Event, Metadata, Subscriber}; +use tracing::{Event, Level, Metadata, Subscriber}; use tracing_error::ErrorLayer; use tracing_subscriber::filter::FilterFn; use tracing_subscriber::fmt::format::{debug_fn, Writer}; @@ -49,7 +50,28 @@ where let time = OffsetDateTime::now_local().unwrap_or_else(|_| OffsetDateTime::now_utc()); let time = time.format(TIME_FORMAT).map_err(|_| std::fmt::Error)?; - write!(writer, "[{}] [{:>5}] ", time, meta.level())?; + let level = meta.level(); + // Sadly, tracing's `Level` is a struct, not an enum, so we can't properly `match` it. + let color = if *level == Level::TRACE { + Color::Purple + } else if *level == Level::DEBUG { + Color::Blue + } else if *level == Level::INFO { + Color::Green + } else if *level == Level::WARN { + Color::Yellow + } else if *level == Level::ERROR { + Color::Red + } else { + unreachable!() + }; + + write!( + writer, + "[{}] [{:>5}] ", + time, + color.bold().paint(format!("{}", level)) + )?; ctx.field_format().format_fields(writer.by_ref(), event)?; From 01b1428b3812db1d9ddcc08f7863009007cfb338 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 5 Apr 2023 14:48:38 +0200 Subject: [PATCH 111/335] fix(dtmm): Fix updating log view --- crates/dtmm/src/state/data.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index d1eb2e2..b1adcb6 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -156,8 +156,6 @@ pub(crate) struct State { pub game_dir: Arc, pub data_dir: Arc, pub nexus_api_key: Arc, - - #[data(ignore)] pub log: Vector, // True, when the initial loading of configuration and mods is still in progress pub loading: bool, From c25ea89055f0697154fe2150da8b11e0445adc8d Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 5 Apr 2023 15:36:47 +0200 Subject: [PATCH 112/335] fix(dtmm): Fix deployment deleting too many bundles Fixes #101. --- crates/dtmm/src/controller/game.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 7e2e6bb..f6d92f0 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -242,9 +242,7 @@ async fn build_bundles(state: Arc) -> Result> { mod_bundle.add_file(file); - let bundle_name = Murmur64::hash(&pkg_info.name) - .to_string() - .to_ascii_lowercase(); + let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); let src = mod_dir.join(&bundle_name); let dest = bundle_dir.join(&bundle_name); let pkg_name = pkg_info.name.clone(); @@ -377,9 +375,7 @@ async fn patch_boot_bundle(state: Arc) -> Result> { .get(0) .ok_or_else(|| eyre::eyre!("invalid mod package for DML")) .with_suggestion(|| "Re-download and import the newest version.".to_string())?; - let bundle_name = Murmur64::hash(&pkg_info.name) - .to_string() - .to_ascii_lowercase(); + let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); let src = state.mod_dir.join(&mod_info.id).join(&bundle_name); { @@ -639,7 +635,7 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { let bundle_dir = Arc::new(bundle_dir); let tasks = info.bundles.iter().cloned().filter_map(|file_name| { let contains = bundles.iter().any(|b2| { - let name = b2.name().to_murmur64().to_string(); + let name = format!("{:016x}", b2.name()); file_name == name }); From c63aee4aefdf3e85e32c8a3e2cb9718c57ad9bc9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 4 Apr 2023 15:44:15 +0200 Subject: [PATCH 113/335] feat(ci): Parameterize build task output Potentially useful when using the task for release artifact building later. --- .ci/pipelines/pr.yml | 2 ++ .ci/tasks/build.yml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index 7b79abe..6452c93 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -32,6 +32,7 @@ jobs: file: repo/.ci/tasks/build.yml vars: target: msvc + output: artifact ref: ((.:ref)) gitea_api_key: ((gitea_api_key)) @@ -45,5 +46,6 @@ jobs: file: repo/.ci/tasks/build.yml vars: target: linux + output: artifact ref: ((.:ref)) gitea_api_key: ((gitea_api_key)) diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml index 3b411bc..ab3a433 100644 --- a/.ci/tasks/build.yml +++ b/.ci/tasks/build.yml @@ -11,14 +11,14 @@ inputs: - name: repo outputs: -- name: artifact +- name: ((output)) params: CI: true TARGET: ((target)) GITEA_API_KEY: ((gitea_api_key)) REF: ((ref)) - OUTPUT: artifact + OUTPUT: ((output)) run: path: .ci/util/run.sh From 64c35a62a9ca4411db82e96de9a9cbf55faefa03 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 5 Apr 2023 20:22:29 +0200 Subject: [PATCH 114/335] feat(ci): Add Dockerfiles to build locally --- .ci/Dockerfile.linux | 35 +++++++++++++++++++++++++++++++++++ .ci/Dockerfile.msvc | 35 +++++++++++++++++++++++++++++++++++ .dockerignore | 9 +++++++++ Justfile | 18 +++++++++++++++++- 4 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 .ci/Dockerfile.linux create mode 100644 .ci/Dockerfile.msvc create mode 100644 .dockerignore diff --git a/.ci/Dockerfile.linux b/.ci/Dockerfile.linux new file mode 100644 index 0000000..2cb6339 --- /dev/null +++ b/.ci/Dockerfile.linux @@ -0,0 +1,35 @@ +FROM dtmt-ci-base-linux + +# Create dummy crates and copy their Cargo.toml, so that dependencies can be cached +RUN set -e; \ + cargo new --bin crates/dtmt; \ + cargo new --bin crates/dtmm; \ + cargo new --lib lib/dtmt-shared; \ + cargo new --lib lib/nexusmods; \ + cargo new --lib lib/sdk; \ + cargo new --lib lib/serde_sjson; \ + cargo new --lib lib/steamlocate-rs + +COPY Cargo.toml Cargo.lock /src/dtmt/ +COPY crates/dtmt/Cargo.toml /src/dtmt/crates/dtmt/ +COPY crates/dtmm/Cargo.toml /src/dtmt/crates/dtmm/ +COPY lib/dtmt-shared/Cargo.toml /src/dtmt/lib/dtmt-shared/ +COPY lib/nexusmods/Cargo.toml /src/dtmt/lib/nexusmods/ +COPY lib/sdk/Cargo.toml /src/dtmt/lib/sdk/ +COPY lib/serde_sjson/Cargo.toml /src/dtmt/lib/serde_sjson/ +COPY lib/steamlocate-rs/Cargo.toml /src/dtmt/lib/steamlocate-rs/ + +# Crates with build scripts cannot be split that way, but they shouldn't change too often +COPY lib/luajit2-sys /src/dtmt/lib/luajit2-sys +COPY lib/oodle /src/dtmt/lib/oodle +# color-eyre needs to be copied, too, then, as it's used by `oodle` +COPY lib/color-eyre /src/dtmt/lib/color-eyre +COPY --from=dtmt-ci-base-linux /src/*.lib /src/dtmt/lib/oodle/ + +RUN cargo build --release --locked +RUN rm -r crates lib + +COPY . /src/dtmt +COPY --from=dtmt-ci-base-linux /src/*.lib /src/dtmt/lib/oodle/ + +RUN cargo build --release --locked diff --git a/.ci/Dockerfile.msvc b/.ci/Dockerfile.msvc new file mode 100644 index 0000000..0d0a546 --- /dev/null +++ b/.ci/Dockerfile.msvc @@ -0,0 +1,35 @@ +FROM dtmt-ci-base-msvc + +# Create dummy crates and copy their Cargo.toml, so that dependencies can be cached +RUN set -e; \ + cargo new --bin crates/dtmt; \ + cargo new --bin crates/dtmm; \ + cargo new --lib lib/dtmt-shared; \ + cargo new --lib lib/nexusmods; \ + cargo new --lib lib/sdk; \ + cargo new --lib lib/serde_sjson; \ + cargo new --lib lib/steamlocate-rs + +COPY Cargo.toml Cargo.lock /src/dtmt/ +COPY crates/dtmt/Cargo.toml /src/dtmt/crates/dtmt/ +COPY crates/dtmm/Cargo.toml /src/dtmt/crates/dtmm/ +COPY lib/dtmt-shared/Cargo.toml /src/dtmt/lib/dtmt-shared/ +COPY lib/nexusmods/Cargo.toml /src/dtmt/lib/nexusmods/ +COPY lib/sdk/Cargo.toml /src/dtmt/lib/sdk/ +COPY lib/serde_sjson/Cargo.toml /src/dtmt/lib/serde_sjson/ +COPY lib/steamlocate-rs/Cargo.toml /src/dtmt/lib/steamlocate-rs/ + +# Crates with build scripts cannot be split that way, but they shouldn't change too often +COPY lib/luajit2-sys /src/dtmt/lib/luajit2-sys +COPY lib/oodle /src/dtmt/lib/oodle +# color-eyre needs to be copied, too, then, as it's used by `oodle` +COPY lib/color-eyre /src/dtmt/lib/color-eyre +COPY --from=dtmt-ci-base-msvc /src/*.lib /src/dtmt/lib/oodle/ + +RUN cargo build --release --target x86_64-pc-windows-msvc --locked -Zbuild-std +RUN rm -r crates lib + +COPY . /src/dtmt +COPY --from=dtmt-ci-base-msvc /src/*.lib /src/dtmt/lib/oodle/ + +RUN cargo build --release --target x86_64-pc-windows-msvc --frozen -Zbuild-std diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..30c19ad --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +target/ +docs/ +data/ +.git/ +README.adoc +CHANGELOG.adoc +LICENSE +dictionary.csv +Justfile diff --git a/Justfile b/Justfile index 4b6315f..3104fb1 100644 --- a/Justfile +++ b/Justfile @@ -1,4 +1,20 @@ -ci-image: ci-image-linux ci-image-msvc +ci-build: ci-build-msvc ci-build-linux + +ci-build-msvc: + docker run --rm -ti -v ./:/src/dtmt dtmt-ci-base-msvc cargo --color always build --release --target x86_64-pc-windows-msvc --locked -Zbuild-std + +ci-build-linux: + docker run --rm -ti -v ./:/src/dtmt dtmt-ci-base-linux cargo --color always build --profile release-lto --locked + +build-image: build-image-msvc build-image-linux + +build-image-msvc: + docker build -f .ci/Dockerfile.msvc . + +build-image-linux: + docker build -f .ci/Dockerfile.linux . + +ci-image: ci-image-msvc ci-image-linux ci-image-msvc: ci-image-linux docker build -t dtmt-ci-base-msvc -f .ci/image/Dockerfile.msvc .ci/image From 7395cf0d5360e78e511825b4b5a82b3cc50b4905 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 6 Apr 2023 11:16:14 +0200 Subject: [PATCH 115/335] fix: Disable 64bit GC --- build.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.rs b/build.rs index 11b3984..2f5fc48 100644 --- a/build.rs +++ b/build.rs @@ -90,6 +90,12 @@ fn build_gcc(src_dir: &str) { buildcmd.stderr(Stdio::inherit()); buildcmd.arg("--no-silent"); + // This became enabled by default in https://github.com/LuaJIT/LuaJIT/commit/bd00094c3b50e193fb32aad79b7ea8ea6b78ed25 + // but changes the generated bytecode. Bitsquid does not have it currently. + // The documentation changes in the commit mention that the bytecode change might be "rectified" + // in the future, though. + buildcmd.arg("XCFLAGS=-DLUAJIT_DISABLE_GC64"); + // We do need to cross-compile even here, so that `lj_vm.o` is created // for the correct architecture. if env::var("CARGO_CFG_WINDOWS").is_ok() { From 1887cdf2efc4f125d0d970ec7594eeeaf551ec4f Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 6 Apr 2023 12:14:20 +0200 Subject: [PATCH 116/335] fix(sdk): Fix creating invalid bytecode An update to LuaJIT made an option enabled by default that changed the bytecode output. Fixes #99. --- lib/luajit2-sys | 2 +- lib/sdk/src/filetype/lua.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/luajit2-sys b/lib/luajit2-sys index 11c4edd..7395cf0 160000 --- a/lib/luajit2-sys +++ b/lib/luajit2-sys @@ -1 +1 @@ -Subproject commit 11c4eddaa4667ea7fffad40b034cf3fcb19fbdd3 +Subproject commit 7395cf0d5360e78e511825b4b5a82b3cc50b4905 diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index 9c53a1c..78c60e4 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -34,7 +34,7 @@ where let state = lua::luaL_newstate(); lua::luaL_openlibs(state); - let name = CString::new(name.as_bytes()) + let name = CString::new(format!("@{name}").into_bytes()) .wrap_err_with(|| format!("Cannot convert name into CString: {}", name))?; match lua::luaL_loadbuffer( state, From 7567c41143a58b6fc48173b3d019d6ec4a9419cc Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 6 Apr 2023 13:00:30 +0200 Subject: [PATCH 117/335] fix(dtmm): Fix ANSI color parsing --- crates/dtmm/src/util/ansi.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/dtmm/src/util/ansi.rs b/crates/dtmm/src/util/ansi.rs index f827c7d..24855fc 100644 --- a/crates/dtmm/src/util/ansi.rs +++ b/crates/dtmm/src/util/ansi.rs @@ -67,13 +67,15 @@ pub fn ansi_to_rich_text(input: &str) -> RichText { state.color = match col { // This escape code is usually called 'black', but is actually used // as "foreground color", in regards to light themes. - 1 => Some(theme::COLOR_FG), - 2 => Some(theme::COLOR_RED_LIGHT), - 3 => Some(theme::COLOR_GREEN_LIGHT), - 4 => Some(theme::COLOR_YELLOW_LIGHT), - 5 => Some(theme::COLOR_BLUE_LIGHT), - 6 => Some(theme::COLOR_PURPLE_LIGHT), - 7 => Some(theme::COLOR_AQUA_LIGHT), + 0 => Some(theme::COLOR_FG), + 1 => Some(theme::COLOR_RED_LIGHT), + 2 => Some(theme::COLOR_GREEN_LIGHT), + 3 => Some(theme::COLOR_YELLOW_LIGHT), + 4 => Some(theme::COLOR_BLUE_LIGHT), + 5 => Some(theme::COLOR_PURPLE_LIGHT), + 6 => Some(theme::COLOR_AQUA_LIGHT), + // Similarly, 'white' is the background color + 7 => Some(theme::COLOR_BG), 9 => None, _ => unreachable!(), }; From a246e5acb64981b70d670f980baa18dc92f1b621 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 6 Apr 2023 14:49:43 +0200 Subject: [PATCH 118/335] feat(dtmm): Log to file --- crates/dtmm/src/controller/app.rs | 9 ++++++++- crates/dtmm/src/controller/worker.rs | 12 ++++++++++++ crates/dtmm/src/state/delegate.rs | 17 +++++++++++++++-- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index 3073b9b..d732801 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -9,7 +9,7 @@ use druid::im::Vector; use druid::{FileInfo, ImageBuf}; use dtmt_shared::ModConfig; use nexusmods::Api as NexusApi; -use tokio::fs::{self, DirEntry}; +use tokio::fs::{self, DirEntry, File}; use tokio_stream::wrappers::ReadDirStream; use tokio_stream::StreamExt; use zip::ZipArchive; @@ -424,6 +424,13 @@ pub(crate) async fn load_initial(path: PathBuf, is_default: bool) -> Result tokio::spawn(async move { + if let Ok(mut f) = OpenOptions::new() + .append(true) + .open(state.data_dir.join("dtmm.log")) + .await + { + let _ = f.write_all(&line).await; + } + }), }; } } diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index 1cf3cda..d5ae042 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -92,6 +92,7 @@ pub(crate) enum AsyncAction { SaveSettings(ActionState), CheckUpdates(ActionState), LoadInitial((PathBuf, bool)), + Log((ActionState, Vec)), } pub(crate) struct Delegate { @@ -253,10 +254,22 @@ impl AppDelegate for Delegate { let line = cmd .get(ACTION_LOG) .expect("command type matched but didn't contain the expected value"); + if let Some(line) = line.take() { - let line = String::from_utf8_lossy(&line); - state.log.push_back(ansi_to_rich_text(line.trim())); + { + let line = String::from_utf8_lossy(&line); + state.log.push_back(ansi_to_rich_text(line.trim())); + } + + if self + .sender + .send(AsyncAction::Log((state.clone().into(), line))) + .is_err() + { + tracing::error!("Failed to queue action to add mod"); + } } + Handled::Yes } cmd if cmd.is(ACTION_START_SAVE_SETTINGS) => { From ebe8966c262e14fc782689fb77616944c8a8b5ed Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 6 Apr 2023 14:54:46 +0200 Subject: [PATCH 119/335] feat(dtmm): Add log level option Closes #12. --- crates/dtmm/src/main.rs | 19 +++++++++++++++---- crates/dtmm/src/util/log.rs | 29 +++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 5a6844c..f60bfd8 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -6,10 +6,8 @@ use std::path::PathBuf; use std::sync::Arc; -use clap::command; use clap::parser::ValueSource; -use clap::value_parser; -use clap::Arg; +use clap::{command, value_parser, Arg}; use color_eyre::eyre; use color_eyre::{Report, Result}; use druid::AppLauncher; @@ -19,6 +17,7 @@ use crate::controller::worker::work_thread; use crate::state::AsyncAction; use crate::state::{Delegate, State}; use crate::ui::theme; +use crate::util::log::LogLevel; mod controller; mod state; @@ -46,10 +45,22 @@ fn main() -> Result<()> { .value_parser(value_parser!(PathBuf)) .default_value(default_config_path.to_string_lossy().to_string()), ) + .arg( + Arg::new("log-level") + .long("log-level") + .help("The maximum level of log events to print") + .value_parser(value_parser!(LogLevel)) + .default_value("info"), + ) .get_matches(); let (log_tx, log_rx) = tokio::sync::mpsc::unbounded_channel(); - util::log::create_tracing_subscriber(log_tx); + let level = if matches.value_source("log-level") == Some(ValueSource::DefaultValue) { + None + } else { + matches.get_one::("log-level").cloned() + }; + util::log::create_tracing_subscriber(log_tx, level); let (action_tx, action_rx) = tokio::sync::mpsc::unbounded_channel(); diff --git a/crates/dtmm/src/util/log.rs b/crates/dtmm/src/util/log.rs index 75db550..b01ef5f 100644 --- a/crates/dtmm/src/util/log.rs +++ b/crates/dtmm/src/util/log.rs @@ -1,3 +1,4 @@ +use clap::ValueEnum; use tokio::sync::mpsc::UnboundedSender; use tracing_error::ErrorLayer; use tracing_subscriber::filter::FilterFn; @@ -7,6 +8,28 @@ use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; +#[derive(Clone, Copy, ValueEnum)] +pub enum LogLevel { + Trace, + Debug, + Info, + Warn, + Error, +} + +impl From for EnvFilter { + fn from(level: LogLevel) -> Self { + let filter = match level { + LogLevel::Trace => "error,dtmm=trace,sdk=trace", + LogLevel::Debug => "error,dtmm=debug,sdk=debug", + LogLevel::Info => "error,dtmm=info", + LogLevel::Warn => "error,dtmm=warn", + LogLevel::Error => "error", + }; + EnvFilter::new(filter) + } +} + pub struct ChannelWriter { tx: UnboundedSender>, } @@ -32,8 +55,10 @@ impl std::io::Write for ChannelWriter { } } -pub fn create_tracing_subscriber(tx: UnboundedSender>) { - let env_layer = if cfg!(debug_assertions) { +pub fn create_tracing_subscriber(tx: UnboundedSender>, level: Option) { + let env_layer = if let Some(level) = level { + EnvFilter::from(level) + } else if cfg!(debug_assertions) { EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")) } else { EnvFilter::new("error,dtmm=info") From 7f5da1e09c9a09427453457c67197fd00cfb8329 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 9 Apr 2023 09:59:06 +0200 Subject: [PATCH 120/335] fix(dtmm): Skip mods folder in dtkit reset Fixes #107. --- crates/dtmm/src/controller/game.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index f6d92f0..d83d361 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -715,7 +715,11 @@ async fn reset_dtkit_patch(state: ActionState) -> Result<()> { } } - for path in [state.game_dir.join("mods"), state.game_dir.join("tools")] { + // We deliberately skip the `mods/` directory here. + // Many modders did their development right in there, and as people are prone to not read + // error messages and guides in full, there is bound to be someone who would have + // deleted all their source code if this removed the `mods/` folder. + for path in [state.game_dir.join("tools")] { match fs::remove_dir_all(&path).await { Ok(_) => tracing::trace!("Removed directory '{}'", path.display()), Err(err) if err.kind() != io::ErrorKind::NotFound => { From 24da35e631099e914d6fc1bcc863228c48e540ec Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 9 Apr 2023 12:21:29 +0200 Subject: [PATCH 121/335] fix: Fix LuaJIT on Windows It turned out that with disabling GC64, LuaJIT would immediately overflow the stack when opening a Lua state. For now, this reverts to a version from 2019 that works, but I highly doubt that LuaJIT woould have been broken on Windows for years. I'll have to investigate, or rather trial-and-error further. --- build.rs | 12 +----------- luajit | 2 +- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/build.rs b/build.rs index 2f5fc48..6e7e5a4 100644 --- a/build.rs +++ b/build.rs @@ -7,12 +7,11 @@ use std::process::{Command, Stdio}; const LIB_NAME: &str = "luajit"; const LUAJIT_HEADERS: [&str; 4] = ["lua.h", "lualib.h", "lauxlib.h", "luajit.h"]; -const LUAJIT_SRC: [&str; 69] = [ +const LUAJIT_SRC: [&str; 65] = [ // LJCORE_O // The MSVC toolchain cannot compile this assembler file, // as it contains GNU-specific directives // "lj_vm.S", - "lj_assert.c", "lj_gc.c", "lj_err.c", "lj_char.c", @@ -25,7 +24,6 @@ const LUAJIT_SRC: [&str; 69] = [ "lj_udata.c", "lj_meta.c", "lj_debug.c", - "lj_prng.c", "lj_state.c", "lj_dispatch.c", "lj_vmevent.c", @@ -33,7 +31,6 @@ const LUAJIT_SRC: [&str; 69] = [ "lj_strscan.c", "lj_strfmt.c", "lj_strfmt_num.c", - "lj_serialize.c", "lj_api.c", "lj_profile.c", "lj_lex.c", @@ -80,7 +77,6 @@ const LUAJIT_SRC: [&str; 69] = [ "lib_debug.c", "lib_jit.c", "lib_ffi.c", - "lib_buffer.c", "lib_init.c", ]; @@ -90,12 +86,6 @@ fn build_gcc(src_dir: &str) { buildcmd.stderr(Stdio::inherit()); buildcmd.arg("--no-silent"); - // This became enabled by default in https://github.com/LuaJIT/LuaJIT/commit/bd00094c3b50e193fb32aad79b7ea8ea6b78ed25 - // but changes the generated bytecode. Bitsquid does not have it currently. - // The documentation changes in the commit mention that the bytecode change might be "rectified" - // in the future, though. - buildcmd.arg("XCFLAGS=-DLUAJIT_DISABLE_GC64"); - // We do need to cross-compile even here, so that `lj_vm.o` is created // for the correct architecture. if env::var("CARGO_CFG_WINDOWS").is_ok() { diff --git a/luajit b/luajit index 505e2c0..70f4b15 160000 --- a/luajit +++ b/luajit @@ -1 +1 @@ -Subproject commit 505e2c03de35e2718eef0d2d3660712e06dadf1f +Subproject commit 70f4b15ee45a6137fe6b48b941faea79d72f7159 From 15c61fa67f0afbc29e07e8c3147d079d9fd23ccc Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 9 Apr 2023 14:32:21 +0200 Subject: [PATCH 122/335] feat: Run build commands as user This stops docker from creating build files as root, which in turn require root permissions to clean. --- Justfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Justfile b/Justfile index 3104fb1..09ce0a3 100644 --- a/Justfile +++ b/Justfile @@ -1,10 +1,10 @@ ci-build: ci-build-msvc ci-build-linux ci-build-msvc: - docker run --rm -ti -v ./:/src/dtmt dtmt-ci-base-msvc cargo --color always build --release --target x86_64-pc-windows-msvc --locked -Zbuild-std + docker run --rm -ti --user $(id -u) -v ./:/src/dtmt dtmt-ci-base-msvc cargo --color always build --release --target x86_64-pc-windows-msvc --locked -Zbuild-std ci-build-linux: - docker run --rm -ti -v ./:/src/dtmt dtmt-ci-base-linux cargo --color always build --profile release-lto --locked + docker run --rm -ti --user $(id -u) -v ./:/src/dtmt dtmt-ci-base-linux cargo --color always build --profile release-lto --locked build-image: build-image-msvc build-image-linux From 690098d7c7cad55b09d4ed279a3a255eec04c919 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 9 Apr 2023 14:33:55 +0200 Subject: [PATCH 123/335] feat(dtmm): Improve debug logging This re-enables stdout/stderr logging for release binaries for DTMM. As a GUI application, it usually won't be started from a CLI, and there should be no negative impact from that. But since stdout logging is synchronous and much faster than the async action that writes to the log file, it might get to log more when the application panics. --- crates/dtmm/src/controller/app.rs | 17 +++++++++++------ crates/dtmm/src/controller/game.rs | 21 +++++++++++++++++++++ crates/dtmm/src/controller/worker.rs | 2 ++ crates/dtmm/src/main.rs | 24 ++++++++++++++---------- crates/dtmm/src/state/delegate.rs | 21 +++++++++++++++++++++ crates/dtmm/src/util/log.rs | 7 +------ lib/sdk/src/filetype/lua.rs | 6 ++++++ 7 files changed, 76 insertions(+), 22 deletions(-) diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index d732801..5c5d980 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -321,12 +321,17 @@ pub(crate) fn check_mod_order(state: &ActionState) -> Result<()> { } } - state - .mods - .iter() - .filter(|i| i.enabled) - .enumerate() - .for_each(|(i, info)| tracing::debug!(i, ?info)); + if tracing::enabled!(tracing::Level::DEBUG) { + let order = state.mods.iter().filter(|i| i.enabled).enumerate().fold( + String::new(), + |mut s, (i, info)| { + s.push_str(&format!("{}: {} - {}\n", i, info.id, info.name)); + s + }, + ); + + tracing::debug!("Mod order:\n{}", order); + } for (i, mod_info) in state.mods.iter().filter(|i| i.enabled).enumerate() { for dep in &mod_info.depends { diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index d83d361..4f5633e 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -212,16 +212,25 @@ async fn build_bundles(state: Arc) -> Result> { let mut bundles = Vec::new(); { + tracing::trace!("Building mod data script"); + let span = tracing::debug_span!("Building mod data script"); let _enter = span.enter(); let lua = build_mod_data_lua(state.clone()); + + tracing::trace!("Compiling mod data script"); + let file = lua::compile(MOD_DATA_SCRIPT, &lua).wrap_err("Failed to compile mod data Lua file")?; + tracing::trace!("Compile mod data script"); + mod_bundle.add_file(file); } + tracing::trace!("Preparing tasks to deploy bundle files"); + for mod_info in state.mods.iter().filter(|m| m.id != "dml" && m.enabled) { let span = tracing::trace_span!("building mod packages", name = mod_info.name); let _enter = span.enter(); @@ -231,6 +240,12 @@ async fn build_bundles(state: Arc) -> Result> { let span = tracing::trace_span!("building package", name = pkg_info.name); let _enter = span.enter(); + tracing::trace!( + "Building package {} for mod {}", + pkg_info.name, + mod_info.name + ); + let pkg = make_package(pkg_info).wrap_err("Failed to make package")?; let mut variant = BundleFileVariant::new(); let bin = pkg @@ -240,6 +255,12 @@ async fn build_bundles(state: Arc) -> Result> { let mut file = BundleFile::new(pkg_info.name.clone(), BundleFileType::Package); file.add_variant(variant); + tracing::trace!( + "Compiled package {} for mod {}", + pkg_info.name, + mod_info.name + ); + mod_bundle.add_file(file); let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index db3c0f8..238b3f3 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -36,6 +36,8 @@ async fn handle_action( action_queue: Arc>>, ) { while let Some(action) = action_queue.write().await.recv().await { + tracing::debug!(?action); + let event_sink = event_sink.clone(); match action { AsyncAction::DeployMods(state) => tokio::spawn(async move { diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index f60bfd8..9ba838c 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use clap::parser::ValueSource; use clap::{command, value_parser, Arg}; -use color_eyre::eyre; +use color_eyre::eyre::{self, Context}; use color_eyre::{Report, Result}; use druid::AppLauncher; use tokio::sync::RwLock; @@ -83,16 +83,20 @@ fn main() -> Result<()> { let event_sink = launcher.get_external_handle(); - std::thread::spawn(move || { - let event_sink = Arc::new(RwLock::new(event_sink)); - let action_rx = Arc::new(RwLock::new(action_rx)); - let log_rx = Arc::new(RwLock::new(log_rx)); - loop { - if let Err(err) = work_thread(event_sink.clone(), action_rx.clone(), log_rx.clone()) { - tracing::error!("Work thread failed, restarting: {:?}", err); + std::thread::Builder::new() + .name("work-thread".into()) + .spawn(move || { + let event_sink = Arc::new(RwLock::new(event_sink)); + let action_rx = Arc::new(RwLock::new(action_rx)); + let log_rx = Arc::new(RwLock::new(log_rx)); + loop { + if let Err(err) = work_thread(event_sink.clone(), action_rx.clone(), log_rx.clone()) + { + tracing::error!("Work thread failed, restarting: {:?}", err); + } } - } - }); + }) + .wrap_err("Work thread panicked")?; launcher.launch(State::new()).map_err(Report::new) } diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index d5ae042..2e12bd0 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -95,6 +95,27 @@ pub(crate) enum AsyncAction { Log((ActionState, Vec)), } +impl std::fmt::Debug for AsyncAction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AsyncAction::DeployMods(_) => write!(f, "AsyncAction::DeployMods(_state)"), + AsyncAction::ResetDeployment(_) => write!(f, "AsyncAction::ResetDeployment(_state)"), + AsyncAction::AddMod(_, info) => write!(f, "AsyncAction::AddMod(_state, {:?})", info), + AsyncAction::DeleteMod(_, info) => { + write!(f, "AsyncAction::DeleteMod(_state, {:?})", info) + } + AsyncAction::SaveSettings(_) => write!(f, "AsyncAction::SaveSettings(_state)"), + AsyncAction::CheckUpdates(_) => write!(f, "AsyncAction::CheckUpdates(_state)"), + AsyncAction::LoadInitial((path, is_default)) => write!( + f, + "AsyncAction::LoadInitial(({:?}, {:?}))", + path, is_default + ), + AsyncAction::Log(_) => write!(f, "AsyncAction::Log(_)"), + } + } +} + pub(crate) struct Delegate { sender: UnboundedSender, } diff --git a/crates/dtmm/src/util/log.rs b/crates/dtmm/src/util/log.rs index b01ef5f..09fc9a6 100644 --- a/crates/dtmm/src/util/log.rs +++ b/crates/dtmm/src/util/log.rs @@ -64,12 +64,7 @@ pub fn create_tracing_subscriber(tx: UnboundedSender>, level: Option Date: Sun, 9 Apr 2023 14:37:07 +0200 Subject: [PATCH 124/335] fix(sdk): Fix compiling Lua on Windows The previous fix when the compiled bytecode wasn't accepted by the game did work fine for Linux. But apparently on Windows, it procudes a stack overflow when attempting to open a Lua state. This reverts LuaJIT to a commit from 2019, which is quite old, but does work. Further investigation is needed to determine if or how never versions of LuaJIT could be used. Fixes #110. --- lib/luajit2-sys | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/luajit2-sys b/lib/luajit2-sys index 7395cf0..24da35e 160000 --- a/lib/luajit2-sys +++ b/lib/luajit2-sys @@ -1 +1 @@ -Subproject commit 7395cf0d5360e78e511825b4b5a82b3cc50b4905 +Subproject commit 24da35e631099e914d6fc1bcc863228c48e540ec From 707a3ead8b0bedaad952a2d8d2fb934aa6bc8789 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 24 Apr 2023 16:45:49 +0200 Subject: [PATCH 125/335] feat(dtmm): Guard certain Lua libraries behind a setting Libraries like `io`, `os` and `ffi` allow practically unrestricted access to the system's files and running arbitrary operations. The base game removes them for this reason, and while we don't want to disable them permanently, very few mods should ever have a need for them. So we hide them behind a setting, worded so that people only enable it when absolutely needed. Closes #112. --- Cargo.lock | 1 + crates/dtmm/Cargo.toml | 1 + crates/dtmm/assets/mod_main.lua | 34 ++++++++++++++++++------- crates/dtmm/src/controller/game.rs | 10 +++++++- crates/dtmm/src/state/data.rs | 2 ++ crates/dtmm/src/state/delegate.rs | 9 +++++-- crates/dtmm/src/ui/widget/controller.rs | 12 +++++++-- crates/dtmm/src/ui/window/main.rs | 25 ++++++++++++++++++ crates/dtmm/src/util/config.rs | 5 ++++ 9 files changed, 85 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba688e1..bd49702 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -855,6 +855,7 @@ dependencies = [ "sdk", "serde", "serde_sjson", + "string_template", "strip-ansi-escapes", "time", "tokio", diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 18ba7b1..86444be 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -32,3 +32,4 @@ colors-transform = "0.2.11" usvg = "0.25.0" druid-widget-nursery = "0.1" ansi-parser = "0.8.0" +string_template = "0.2.1" diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua index 715397f..f811c5f 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua @@ -107,19 +107,35 @@ log("mod_main", "Initializing mods...") local require_store = {} +-- This token is treated as a string template and filled by DTMM during deployment. +-- This allows hiding unsafe I/O functions behind a setting. +-- It's also a valid table definition, thereby degrading gracefully when not replaced. +local is_io_enabled = {{is_io_enabled}} -- luacheck: ignore 113 +local lua_libs = { + debug = debug, + os = { + date = os.date, + time = os.time, + clock = os.clock, + getenv = os.getenv, + difftime = os.difftime, + }, + load = load, + loadfile = loadfile, + loadstring = loadstring, +} + +if is_io_enabled then + lua_libs.io = io + lua_libs.os = os + lua_libs.ffi = ffi +end + Mods = { -- Keep a backup of certain system libraries before -- Fatshark's code scrubs them. -- The loader can then decide to pass them on to mods, or ignore them - lua = setmetatable({}, { - io = io, - debug = debug, - ffi = ffi, - os = os, - load = load, - loadfile = loadfile, - loadstring = loadstring, - }), + lua = setmetatable({}, { __index = lua_libs }), require_store = require_store } diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 4f5633e..a5f6fa8 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::io::{self, Cursor, ErrorKind}; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -15,6 +16,7 @@ use sdk::{ Bundle, BundleDatabase, BundleFile, BundleFileType, BundleFileVariant, FromBinary, ToBinary, }; use serde::{Deserialize, Serialize}; +use string_template::Template; use time::OffsetDateTime; use tokio::fs; use tokio::io::AsyncWriteExt; @@ -452,7 +454,13 @@ async fn patch_boot_bundle(state: Arc) -> Result> { let span = tracing::debug_span!("Importing mod main script"); let _enter = span.enter(); - let lua = include_str!("../../assets/mod_main.lua"); + let is_io_enabled = format!("{}", state.is_io_enabled); + let mut data = HashMap::new(); + data.insert("is_io_enabled", is_io_enabled.as_str()); + + let tmpl = include_str!("../../assets/mod_main.lua"); + let lua = Template::new(tmpl).render(&data); + tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); let file = lua::compile(MOD_BOOT_SCRIPT, lua).wrap_err("Failed to compile mod main Lua file")?; diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index b1adcb6..55774aa 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -153,6 +153,7 @@ pub(crate) struct State { pub is_save_in_progress: bool, pub is_next_save_pending: bool, pub is_update_in_progress: bool, + pub is_io_enabled: bool, pub game_dir: Arc, pub data_dir: Arc, pub nexus_api_key: Arc, @@ -189,6 +190,7 @@ impl State { is_save_in_progress: false, is_next_save_pending: false, is_update_in_progress: false, + is_io_enabled: false, config_path: Arc::new(PathBuf::new()), game_dir: Arc::new(PathBuf::new()), data_dir: Arc::new(PathBuf::new()), diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index 2e12bd0..97c6e6b 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -1,4 +1,5 @@ -use std::{path::PathBuf, sync::Arc}; +use std::path::PathBuf; +use std::sync::Arc; use color_eyre::Report; use druid::im::Vector; @@ -8,8 +9,9 @@ use druid::{ }; use tokio::sync::mpsc::UnboundedSender; +use crate::ui::window; use crate::util::ansi::ansi_to_rich_text; -use crate::{ui::window, util::config::Config}; +use crate::util::config::Config; use super::{ModInfo, State}; @@ -68,6 +70,7 @@ pub(crate) struct ActionState { pub config_path: Arc, pub ctx: Arc, pub nexus_api_key: Arc, + pub is_io_enabled: bool, } impl From for ActionState { @@ -80,6 +83,7 @@ impl From for ActionState { config_path: state.config_path, ctx: state.ctx, nexus_api_key: state.nexus_api_key, + is_io_enabled: state.is_io_enabled, } } } @@ -407,6 +411,7 @@ impl AppDelegate for Delegate { state.config_path = Arc::new(config.path); state.data_dir = Arc::new(config.data_dir); state.game_dir = Arc::new(config.game_dir.unwrap_or_default()); + state.is_io_enabled = config.unsafe_io; } state.loading = false; diff --git a/crates/dtmm/src/ui/widget/controller.rs b/crates/dtmm/src/ui/widget/controller.rs index b6d3806..f789b5a 100644 --- a/crates/dtmm/src/ui/widget/controller.rs +++ b/crates/dtmm/src/ui/widget/controller.rs @@ -80,11 +80,19 @@ impl> Controller for DirtyStateController { ) { // Only start tracking changes after the initial load has finished if old_data.loading == data.loading { - if compare_state_fields!(old_data, data, mods, game_dir, data_dir, nexus_api_key) { + if compare_state_fields!( + old_data, + data, + mods, + game_dir, + data_dir, + nexus_api_key, + is_io_enabled + ) { ctx.submit_command(ACTION_START_SAVE_SETTINGS); } - if compare_state_fields!(old_data, data, mods, game_dir) { + if compare_state_fields!(old_data, data, mods, game_dir, is_io_enabled) { ctx.submit_command(ACTION_SET_DIRTY); } } diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index fbedb8f..7aa2819 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -425,6 +425,29 @@ fn build_view_settings() -> impl Widget { .with_flex_child(TextBox::new().expand_width().lens(State::nexus_api_key), 1.) .expand_width(); + let io_setting = Flex::row() + .must_fill_main_axis(true) + .main_axis_alignment(MainAxisAlignment::Start) + .with_child(Label::new("Enable unsafe I/O:")) + .with_default_spacer() + .with_child(Checkbox::from_label(Label::dynamic( + |enabled: &bool, _: &Env| { + if *enabled { + "Enabled".into() + } else { + "Disabled".into() + } + }, + ))) + .lens(State::is_io_enabled) + .tooltip(|_: &State, _: &Env| { + "Enabling this gives ANY mod full access to your files \ + and the ability to load arbitrary software libraries.\n\ + Only enable this if it is crucial for a mod's functionality, \ + and you are sure none of the ones you have installed are malicious." + }) + .expand_width(); + let content = Flex::column() .must_fill_main_axis(true) .cross_axis_alignment(CrossAxisAlignment::Start) @@ -432,6 +455,8 @@ fn build_view_settings() -> impl Widget { .with_default_spacer() .with_child(game_dir_setting) .with_default_spacer() + .with_child(io_setting) + .with_default_spacer() .with_child(nexus_apy_key_setting); SizedBox::new(content) diff --git a/crates/dtmm/src/util/config.rs b/crates/dtmm/src/util/config.rs index 1cafeae..3a0d0b2 100644 --- a/crates/dtmm/src/util/config.rs +++ b/crates/dtmm/src/util/config.rs @@ -30,6 +30,7 @@ pub(crate) struct ConfigSerialize<'a> { data_dir: &'a Path, nexus_api_key: &'a String, mod_order: Vec>, + unsafe_io: bool, } impl<'a> From<&'a ActionState> for ConfigSerialize<'a> { @@ -38,6 +39,7 @@ impl<'a> From<&'a ActionState> for ConfigSerialize<'a> { game_dir: &state.game_dir, data_dir: &state.data_dir, nexus_api_key: &state.nexus_api_key, + unsafe_io: state.is_io_enabled, mod_order: state .mods .iter() @@ -61,6 +63,8 @@ pub(crate) struct Config { #[serde(default = "get_default_data_dir")] pub data_dir: PathBuf, pub game_dir: Option, + #[serde(default)] + pub unsafe_io: bool, pub nexus_api_key: Option, #[serde(default)] pub mod_order: Vec, @@ -144,6 +148,7 @@ where game_dir: None, nexus_api_key: None, mod_order: Vec::new(), + unsafe_io: false, }; { From 9b8f07d125d312ed9ea8c89501dc8d8b01db11a5 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sat, 22 Jul 2023 14:39:35 +0200 Subject: [PATCH 126/335] fix: Fix FFI import --- crates/dtmm/assets/mod_main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua index f811c5f..0c61eac 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua @@ -128,7 +128,7 @@ local lua_libs = { if is_io_enabled then lua_libs.io = io lua_libs.os = os - lua_libs.ffi = ffi + lua_libs.ffi = require("ffi") end Mods = { From 6ef4c2e094476a3d6701e5b9c970a2fe3568eac6 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sat, 22 Jul 2023 14:39:51 +0200 Subject: [PATCH 127/335] refactor: Format Lua code --- crates/dtmm/assets/mod_main.lua | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua index 0c61eac..ee55fa3 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua @@ -18,7 +18,7 @@ local function patch_mod_loading_state() -- A necessary override. -- The original does not proxy `dt` to `_state_update`, but we need that. - StateBootSubStateBase.update = function (self, dt) + StateBootSubStateBase.update = function(self, dt) local done, error = self:_state_update(dt) local params = self._params @@ -39,7 +39,7 @@ local function patch_mod_loading_state() local StateBootLoadMods = class("StateBootLoadMods", "StateBootSubStateBase") - StateBootLoadMods.on_enter = function (self, parent, params) + StateBootLoadMods.on_enter = function(self, parent, params) log("StateBootLoadMods", "Entered") StateBootLoadMods.super.on_enter(self, parent, params) @@ -54,18 +54,17 @@ local function patch_mod_loading_state() } end - StateBootLoadMods._state_update = function (self, dt) + StateBootLoadMods._state_update = function(self, dt) local state = self._state local package_manager = self._package_manager if state == "load_package" and package_manager:update() then log("StateBootLoadMods", "Packages loaded, loading mods") self._state = "load_mods" - local mod_loader = require("scripts/mods/dml/init") - self._mod_loader = mod_loader + local ModLoader = require("scripts/mods/dml/init") local mod_data = require("scripts/mods/mod_data") - mod_loader:init(mod_data, self._parent:gui()) + self._mod_loader = ModLoader:new(mod_data, self._parent:gui()) elseif state == "load_mods" and self._mod_loader:update(dt) then log("StateBootLoadMods", "Mods loaded, exiting") return true, false @@ -110,7 +109,7 @@ local require_store = {} -- This token is treated as a string template and filled by DTMM during deployment. -- This allows hiding unsafe I/O functions behind a setting. -- It's also a valid table definition, thereby degrading gracefully when not replaced. -local is_io_enabled = {{is_io_enabled}} -- luacheck: ignore 113 +local is_io_enabled = { { is_io_enabled } } -- luacheck: ignore 113 local lua_libs = { debug = debug, os = { From 70e22e1dfb8730068767ef758ca6bac8f08ae556 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 30 Aug 2023 01:15:28 +0200 Subject: [PATCH 128/335] Assign mod loader as global manager --- crates/dtmm/assets/mod_main.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua index ee55fa3..6dbf3e2 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua @@ -64,7 +64,10 @@ local function patch_mod_loading_state() local ModLoader = require("scripts/mods/dml/init") local mod_data = require("scripts/mods/mod_data") - self._mod_loader = ModLoader:new(mod_data, self._parent:gui()) + local mod_loader = ModLoader:new(mod_data, self._parent:gui()) + + self._mod_loader = mod_loader + Managers.mod = mod_loader elseif state == "load_mods" and self._mod_loader:update(dt) then log("StateBootLoadMods", "Mods loaded, exiting") return true, false From 2f5939d44dfcd8848a9eb4b7c3c627fbf3f0460f Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 26 Sep 2023 09:56:31 +0200 Subject: [PATCH 129/335] Update dependencies Fixes #118. Through changes in nightly feature flags, `proc-macro2` needed to be updated. --- Cargo.lock | 1261 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 744 insertions(+), 517 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd49702..2655f9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.19.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -19,21 +19,20 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.7.5" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ "cfg-if", "cipher", "cpufeatures", - "opaque-debug", ] [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" dependencies = [ "memchr", ] @@ -58,16 +57,64 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.69" +name = "anstream" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arrayref" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" @@ -77,9 +124,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "as-slice" @@ -89,7 +136,7 @@ checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" dependencies = [ "generic-array 0.12.4", "generic-array 0.13.3", - "generic-array 0.14.6", + "generic-array 0.14.7", "stable_deref_trait", ] @@ -101,13 +148,13 @@ checksum = "46016233fc1bb55c23b856fe556b7db6ccd05119a0a392e04f0b3b7c79058f16" [[package]] name = "async-recursion" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796" +checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] @@ -117,7 +164,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39991bc421ddf72f70159011b323ff49b0f783cc676a7287c59453da2e2531cf" dependencies = [ "atk-sys", - "bitflags", + "bitflags 1.3.2", "glib", "libc", ] @@ -142,9 +189,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.67" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", @@ -163,9 +210,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "base64ct" @@ -179,7 +226,7 @@ version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", @@ -191,7 +238,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", + "syn 1.0.109", "which", ] @@ -201,6 +248,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "bitmaps" version = "2.1.0" @@ -222,26 +275,25 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", ] [[package]] name = "bstr" -version = "1.3.0" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1" +checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" dependencies = [ "memchr", - "once_cell", - "regex-automata", + "regex-automata 0.3.8", "serde", ] [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytecount" @@ -251,9 +303,9 @@ checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" [[package]] name = "bytemuck" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" [[package]] name = "byteorder" @@ -263,9 +315,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "bzip2" @@ -294,7 +346,7 @@ version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3125b15ec28b84c238f6f476c6034016a5f6cc0221cb514ca46c532139fc97d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-sys-rs", "glib", "libc", @@ -315,11 +367,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -333,11 +386,12 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.11.0" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" +checksum = "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3" dependencies = [ "smallvec", + "target-lexicon", ] [[package]] @@ -348,18 +402,19 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cipher" -version = "0.3.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "generic-array 0.14.6", + "crypto-common", + "inout", ] [[package]] name = "clang-sys" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ed9a53e5d4d9c573ae844bfac6872b159cb1d1585a83b29e7a64b7eef7332a" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" dependencies = [ "glob", "libc", @@ -368,42 +423,45 @@ dependencies = [ [[package]] name = "clap" -version = "4.1.8" +version = "4.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +checksum = "824956d0dca8334758a5b7f7e50518d66ea319330cbceedcf76905c2f6ab30e3" dependencies = [ - "bitflags", + "clap_builder", "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122ec64120a49b4563ccaedcbea7818d069ed8e9aa6d829b82d8a4128936b2ab" +dependencies = [ + "anstream", + "anstyle", "clap_lex", - "is-terminal", - "once_cell", "strsim", - "termcolor", "unicase", "unicode-width", ] [[package]] name = "clap_derive" -version = "4.1.8" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck", - "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] name = "clap_lex" -version = "0.3.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" -dependencies = [ - "os_str_bytes", -] +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "cli-table" @@ -424,7 +482,7 @@ checksum = "2af3bfb9da627b0a6c467624fb7963921433774ed435493b5c08a3053e829ad4" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -444,7 +502,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "cocoa-foundation", "core-foundation", @@ -456,15 +514,14 @@ dependencies = [ [[package]] name = "cocoa-foundation" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "core-foundation", "core-graphics-types", - "foreign-types", "libc", "objc", ] @@ -500,6 +557,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "colors-transform" version = "0.2.11" @@ -515,7 +578,7 @@ dependencies = [ "directories", "serde", "thiserror", - "toml", + "toml 0.5.11", ] [[package]] @@ -546,9 +609,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "core-graphics" @@ -556,7 +619,7 @@ version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-graphics-types", "foreign-types", @@ -565,13 +628,12 @@ dependencies = [ [[package]] name = "core-graphics-types" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", - "foreign-types", "libc", ] @@ -589,9 +651,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -622,9 +684,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -632,9 +694,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -645,7 +707,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.6", + "generic-array 0.14.7", "typenum", ] @@ -682,10 +744,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" [[package]] -name = "digest" -version = "0.10.6" +name = "deranged" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +dependencies = [ + "serde", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -744,13 +815,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] @@ -781,13 +852,13 @@ dependencies = [ [[package]] name = "druid-derive" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1234fc14b0c07e3d4ff2ae8862eb2c24c87e1f3d6eeef0ec6b9d564fe13cef2" +checksum = "808d664482b1888a2ccb7f4dc9fa24165174d65ba96726315964064bdbc7d6cb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -797,7 +868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7682d9c8fbf934504c30970775bfcfba7858a600f2f6e56bed331989958350fc" dependencies = [ "anyhow", - "bitflags", + "bitflags 1.3.2", "block", "cairo-rs", "cfg-if", @@ -839,7 +910,7 @@ name = "dtmm" version = "0.1.0" dependencies = [ "ansi-parser", - "bitflags", + "bitflags 1.3.2", "clap", "color-eyre", "colors-transform", @@ -931,15 +1002,15 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encoding_rs" -version = "0.8.32" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] @@ -960,14 +1031,20 @@ dependencies = [ ] [[package]] -name = "errno" -version = "0.2.8" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -1010,43 +1087,58 @@ dependencies = [ ] [[package]] -name = "fd-lock" -version = "3.0.10" +name = "fastrand" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef1a30ae415c3a691a4f41afddc2dbcd6d70baf338368d85ebc1e8ed92cedb9" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fd-lock" +version = "3.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" dependencies = [ "cfg-if", "rustix", - "windows-sys 0.45.0", + "windows-sys 0.48.0", +] + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", ] [[package]] name = "field-offset" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cf3a800ff6e860c863ca6d4b16fd999db8b752819c1606884047b73e468535" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset 0.8.0", + "memoffset 0.9.0", "rustc_version", ] [[package]] name = "filetime" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", - "redox_syscall", - "windows-sys 0.45.0", + "redox_syscall 0.3.5", + "windows-sys 0.48.0", ] [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" dependencies = [ "crc32fast", "miniz_oxide", @@ -1100,9 +1192,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fontconfig-parser" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab2e12762761366dcb876ab8b6e0cfa4797ddcd890575919f008b5ba655672a" +checksum = "674e258f4b5d2dcd63888c01c68413c51f565e8af99d2f7701c7b81d79ef41c4" dependencies = [ "roxmltree 0.18.0", ] @@ -1136,9 +1228,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -1160,9 +1252,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -1175,9 +1267,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -1185,15 +1277,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -1202,38 +1294,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -1253,7 +1345,7 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9cb33da481c6c040404a11f8212d193889e9b435db2c14fd86987f630d3ce1" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk-sys", @@ -1269,7 +1361,7 @@ version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3578c60dee9d029ad86593ed88cb40f35c1b83360e12498d055022385dd9a05" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", @@ -1326,9 +1418,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check 0.9.4", @@ -1336,9 +1428,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -1357,9 +1449,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "gio" @@ -1367,7 +1459,7 @@ version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a1c84b4534a290a29160ef5c6eff2a9c95833111472e824fc5cb78b513dd092" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", @@ -1396,11 +1488,11 @@ dependencies = [ [[package]] name = "glib" -version = "0.16.7" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd4df61a866ed7259d6189b8bcb1464989a77f1d85d25d002279bbe9dd38b2f" +checksum = "16aa2475c9debed5a32832cb5ff2af5a3f9e1ab9e69df58eaadc1ab2004d6eba" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-executor", @@ -1418,9 +1510,9 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.16.3" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e084807350b01348b6d9dbabb724d1a0bb987f47a2c85de200e98e12e30733bf" +checksum = "fb1a9325847aa46f1e96ffea37611b9d51fc4827e67f79e7de502a297560a67b" dependencies = [ "anyhow", "heck", @@ -1428,7 +1520,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1465,7 +1557,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4d3507d43908c866c805f74c9dd593c0ce7ba5c38e576e41846639cdcd4bee6" dependencies = [ "atk", - "bitflags", + "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", @@ -1501,23 +1593,23 @@ dependencies = [ [[package]] name = "gtk3-macros" -version = "0.16.0" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfd6557b1018b773e43c8de9d0d13581d6b36190d0501916cbec4731db5ccff" +checksum = "096eb63c6fedf03bafe65e5924595785eaf1bcb7200dac0f2cbe9c9738f05ad8" dependencies = [ "anyhow", "proc-macro-crate", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "h2" -version = "0.3.16" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -1525,7 +1617,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -1547,6 +1639,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heapless" version = "0.5.6" @@ -1567,18 +1665,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "hmac" @@ -1589,6 +1678,15 @@ dependencies = [ "digest", ] +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + [[package]] name = "http" version = "0.2.9" @@ -1619,15 +1717,15 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.25" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -1640,7 +1738,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -1662,9 +1760,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1687,16 +1785,16 @@ dependencies = [ [[package]] name = "image" -version = "0.24.5" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" dependencies = [ "bytemuck", "byteorder", "color_quant", "jpeg-decoder", "num-rational 0.4.1", - "num-traits 0.2.15", + "num-traits 0.2.16", "png", ] @@ -1708,12 +1806,22 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", ] [[package]] @@ -1722,7 +1830,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" dependencies = [ - "bitflags", + "bitflags 1.3.2", "inotify-sys", "libc", ] @@ -1736,6 +1844,15 @@ dependencies = [ "libc", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", +] + [[package]] name = "instant" version = "0.1.12" @@ -1767,39 +1884,17 @@ dependencies = [ "unic-langid", ] -[[package]] -name = "io-lifetimes" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" -dependencies = [ - "libc", - "windows-sys 0.45.0", -] - [[package]] name = "ipnet" -version = "2.7.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" - -[[package]] -name = "is-terminal" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.45.0", -] +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" @@ -1818,9 +1913,9 @@ checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -1831,7 +1926,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7668b7cff6a51fe61cdde64cd27c8a220786f399501b57ebe36f7d8112fd68" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1847,9 +1942,9 @@ dependencies = [ [[package]] name = "kqueue" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" dependencies = [ "kqueue-sys", "libc", @@ -1857,11 +1952,11 @@ dependencies = [ [[package]] name = "kqueue-sys" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", ] @@ -1871,16 +1966,16 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", ] [[package]] name = "kurbo" -version = "0.9.1" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8c31eaef73f18e0d938785e01ab471ec73e3f90c3389e84335ade689ba953b" +checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" dependencies = [ - "arrayvec 0.7.2", + "arrayvec 0.7.4", "serde", ] @@ -1898,9 +1993,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "libloading" @@ -1914,18 +2009,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "luajit2-sys" @@ -1952,7 +2044,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -1963,9 +2055,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "memmap2" @@ -1987,18 +2079,18 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minimal-lexical" @@ -2008,23 +2100,24 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", + "simd-adler32", ] [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "log", "wasi", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2083,7 +2176,7 @@ version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cc", "cfg-if", "libc", @@ -2118,9 +2211,9 @@ dependencies = [ [[package]] name = "nom_locate" -version = "4.1.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e299bf5ea7b212e811e71174c5d1a5d065c4c0ad0c8691ecb1f97e3e66025e" +checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" dependencies = [ "bytecount", "memchr", @@ -2129,11 +2222,11 @@ dependencies = [ [[package]] name = "notify" -version = "5.1.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9" +checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crossbeam-channel", "filetime", "fsevent-sys", @@ -2142,7 +2235,7 @@ dependencies = [ "libc", "mio", "walkdir", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -2166,7 +2259,7 @@ dependencies = [ "num-integer", "num-iter", "num-rational 0.3.2", - "num-traits 0.2.15", + "num-traits 0.2.16", ] [[package]] @@ -2177,7 +2270,7 @@ checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" dependencies = [ "autocfg", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.16", ] [[package]] @@ -2186,7 +2279,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" dependencies = [ - "num-traits 0.2.15", + "num-traits 0.2.16", ] [[package]] @@ -2196,7 +2289,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", - "num-traits 0.2.15", + "num-traits 0.2.16", ] [[package]] @@ -2207,7 +2300,7 @@ checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" dependencies = [ "autocfg", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.16", ] [[package]] @@ -2219,7 +2312,7 @@ dependencies = [ "autocfg", "num-bigint", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.16", ] [[package]] @@ -2230,7 +2323,7 @@ checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", "num-integer", - "num-traits 0.2.15", + "num-traits 0.2.16", ] [[package]] @@ -2239,25 +2332,25 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" dependencies = [ - "num-traits 0.2.15", + "num-traits 0.2.16", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi", "libc", ] @@ -2281,18 +2374,18 @@ dependencies = [ [[package]] name = "object" -version = "0.30.3" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "oodle" @@ -2303,19 +2396,13 @@ dependencies = [ "tracing", ] -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - [[package]] name = "openssl" -version = "0.10.46" +version = "0.10.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd2523381e46256e40930512c7fd25562b9eae4812cb52078f155e87217c9d1e" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" dependencies = [ - "bitflags", + "bitflags 2.4.0", "cfg-if", "foreign-types", "libc", @@ -2326,13 +2413,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] @@ -2343,23 +2430,16 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.81" +version = "0.9.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176be2629957c157240f68f61f2d0053ad3a4ecfdd9ebf1e6521d18d9635cf67" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", "vcpkg", ] -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - [[package]] name = "overload" version = "0.1.1" @@ -2378,7 +2458,7 @@ version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdff66b271861037b89d028656184059e03b0b6ccb36003820be19f7200b1e94" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gio", "glib", "libc", @@ -2404,7 +2484,7 @@ version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16ad2ec87789371b551fd2367c10aa37060412ffd3e60abd99491b21b93a3f9b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "glib", "libc", @@ -2468,25 +2548,26 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.5.6" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cbd939b234e95d72bc393d51788aec68aeeb5d51e748ca08ff3aad58cb722f7" +checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" dependencies = [ + "memchr", "thiserror", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.5.6" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a81186863f3d0a27340815be8f2078dd8050b14cd71913db9fbda795e5f707d7" +checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8" dependencies = [ "pest", "pest_generator", @@ -2494,22 +2575,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.6" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a1ef20bf3193c15ac345acb32e26b3dc3223aff4d77ae4fc5359567683796b" +checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] name = "pest_meta" -version = "2.5.6" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e3b284b1f13a20dc5ebc90aff59a51b8d7137c221131b52a7260c08cbc1cc80" +checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d" dependencies = [ "once_cell", "pest", @@ -2529,7 +2610,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e381186490a3e2017a506d62b759ea8eaf4be14666b13ed53973e8ae193451b1" dependencies = [ "image", - "kurbo 0.9.1", + "kurbo 0.9.5", "unic-bidi", ] @@ -2611,9 +2692,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -2623,18 +2704,19 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "png" -version = "0.17.7" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" +checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", + "fdeflate", "flate2", "miniz_oxide", ] @@ -2658,7 +2740,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check 0.9.4", ] @@ -2675,9 +2757,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] @@ -2693,9 +2775,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -2737,7 +2819,16 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", ] [[package]] @@ -2747,19 +2838,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-automata 0.3.8", + "regex-syntax 0.7.5", ] [[package]] @@ -2768,22 +2860,39 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.5", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ - "base64 0.21.0", + "base64 0.21.4", "bytes", "encoding_rs", "futures-core", @@ -2811,7 +2920,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "winreg 0.50.0", ] [[package]] @@ -2861,9 +2970,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -2882,16 +2991,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.9" +version = "0.38.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" +checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" dependencies = [ - "bitflags", + "bitflags 2.4.0", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2900,7 +3008,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab9e34ecf6900625412355a61bda0bd68099fe674de707c67e5e4aed2c05e489" dependencies = [ - "bitflags", + "bitflags 1.3.2", "bytemuck", "smallvec", "ttf-parser", @@ -2916,7 +3024,7 @@ version = "9.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db7826789c0e25614b03e5a54a0717a86f9ff6e6e5247f92b369472869320039" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "clipboard-win", "dirs-next", @@ -2936,9 +3044,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "same-file" @@ -2951,29 +3059,29 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sdk" version = "0.3.0" dependencies = [ "async-recursion", - "bitflags", + "bitflags 1.3.2", "byteorder", "color-eyre", "csv-async", - "fastrand", + "fastrand 1.9.0", "futures", "futures-util", "glob", @@ -2993,11 +3101,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -3006,9 +3114,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -3022,35 +3130,35 @@ checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" [[package]] name = "semver" -version = "1.0.16" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" [[package]] name = "serde" -version = "1.0.154" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cdd151213925e7f1ab45a9bbfb129316bd00799784b174b7cc7bcd16961c49e" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.154" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc80d722935453bcafdc2c9a73cd6fac4dc1938f0346035d84bf99fa9e33217" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -3066,6 +3174,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3080,9 +3197,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -3091,9 +3208,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -3111,9 +3228,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" [[package]] name = "signal-hook-registry" @@ -3124,6 +3241,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "simplecss" version = "0.2.1" @@ -3135,9 +3258,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "sized-chunks" @@ -3151,18 +3274,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "socket2" @@ -3174,6 +3297,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -3204,7 +3337,7 @@ dependencies = [ "keyvalues-parser", "steamid-ng", "steamy-vdf", - "winreg", + "winreg 0.10.1", ] [[package]] @@ -3224,9 +3357,9 @@ checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" [[package]] name = "strict-num" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9df65f20698aeed245efdde3628a6b559ea1239bbb871af1b6e3b58c413b2bd1" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" dependencies = [ "float-cmp", ] @@ -3257,9 +3390,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "svgfilters" @@ -3292,58 +3425,75 @@ dependencies = [ ] [[package]] -name = "system-deps" -version = "6.0.3" +name = "syn" +version = "2.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" dependencies = [ "cfg-expr", "heck", "pkg-config", - "toml", + "toml 0.7.8", "version-compare", ] [[package]] -name = "tempfile" -version = "3.4.0" +name = "target-lexicon" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" + +[[package]] +name = "tempfile" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", - "fastrand", - "redox_syscall", + "fastrand 2.0.1", + "redox_syscall 0.3.5", "rustix", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] name = "termcolor" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.39" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.39" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] @@ -3358,10 +3508,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.20" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" dependencies = [ + "deranged", "itoa", "libc", "num_threads", @@ -3372,27 +3523,27 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.8" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] [[package]] name = "tiny-skia" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfef3412c6975196fdfac41ef232f910be2bb37b9dd3313a49a1a6bc815a5bdb" +checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" dependencies = [ "arrayref", - "arrayvec 0.7.2", + "arrayvec 0.7.4", "bytemuck", "cfg-if", "png", @@ -3401,9 +3552,9 @@ dependencies = [ [[package]] name = "tiny-skia-path" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b5edac058fc98f51c935daea4d805b695b38e2f151241cad125ade2a2ac20d" +checksum = "adbfb5d3f3dd57a0e11d12f4f13d4ebbbc1b5c15b7ab0a156d030b21da5f677c" dependencies = [ "arrayref", "bytemuck", @@ -3412,9 +3563,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ac3f5b6856e931e15e07b478e98c8045239829a65f9156d4fa7e7788197a5ef" +checksum = "b07bb54ef1f8ff27564b08b861144d3b8d40263efe07684f64987f4c0d044e3e" dependencies = [ "displaydoc", ] @@ -3436,33 +3587,32 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.26.0" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ - "autocfg", + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.4", "tokio-macros", "tracing", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] @@ -3477,9 +3627,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -3488,9 +3638,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ "bytes", "futures-core", @@ -3510,18 +3660,35 @@ dependencies = [ ] [[package]] -name = "toml_datetime" -version = "0.6.1" +name = "toml" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" -version = "0.19.4" +version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap", + "indexmap 2.0.0", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -3546,20 +3713,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.37", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -3588,9 +3755,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", "nu-ansi-term", @@ -3638,15 +3805,15 @@ dependencies = [ [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unic-bidi" @@ -3719,18 +3886,18 @@ dependencies = [ [[package]] name = "unicase" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ "version_check 0.9.4", ] [[package]] name = "unicode-bidi" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524b68aca1d05e03fdf03fcdce2c6c94b6daf6d16861ddaa7e4f2b6638a9052c" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-bidi-mirroring" @@ -3752,9 +3919,9 @@ checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -3785,15 +3952,15 @@ checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "url" -version = "2.3.1" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", @@ -3892,22 +4059,20 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.3.2" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", - "winapi", "winapi-util", ] [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -3919,9 +4084,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3929,24 +4094,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.37", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -3956,9 +4121,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3966,28 +4131,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.37", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -4001,13 +4166,14 @@ checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix", ] [[package]] @@ -4028,9 +4194,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -4041,92 +4207,143 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - [[package]] name = "windows-sys" version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.3.5" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" dependencies = [ "memchr", ] @@ -4140,6 +4357,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wio" version = "0.2.2" @@ -4169,9 +4396,9 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" [[package]] name = "zip" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ "aes", "byteorder", @@ -4208,9 +4435,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.7+zstd.1.5.4" +version = "2.0.8+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" dependencies = [ "cc", "libc", From 93db78d58ff2972e3cf1bd92ad6fca748ba9c622 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 30 Oct 2023 09:18:56 +0100 Subject: [PATCH 130/335] sdk: Implement decompiling Lua Closes #48. --- CHANGELOG.adoc | 1 + Cargo.lock | 1 + crates/dtmt/Cargo.toml | 1 + crates/dtmt/src/cmd/bundle/extract.rs | 114 +++++++++++++++++++------- lib/sdk/src/binary.rs | 17 ++++ lib/sdk/src/context.rs | 53 +++++++++++- lib/sdk/src/filetype/lua.rs | 91 +++++++++++++++++++- lib/sdk/src/lib.rs | 2 +- 8 files changed, 243 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 2409280..2e2b7a4 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -15,6 +15,7 @@ - dtmm: match mods to Nexus and check for updates - dtmt: add utility to migrate mod projects - dtmm: reset dtkit-patch installations +- sdk: implement decompiling Lua files === Fixed diff --git a/Cargo.lock b/Cargo.lock index 2655f9c..78409bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -964,6 +964,7 @@ dependencies = [ "sdk", "serde", "serde_sjson", + "shlex", "string_template", "tempfile", "tokio", diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index 524ffaa..69bbc31 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -33,6 +33,7 @@ path-slash = "0.2.1" async-recursion = "1.0.2" notify = "5.1.0" luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } +shlex = "1.2.0" [dev-dependencies] tempfile = "3.3.0" diff --git a/crates/dtmt/src/cmd/bundle/extract.rs b/crates/dtmt/src/cmd/bundle/extract.rs index 35dee15..57b7fe9 100644 --- a/crates/dtmt/src/cmd/bundle/extract.rs +++ b/crates/dtmt/src/cmd/bundle/extract.rs @@ -1,3 +1,4 @@ +use std::ffi::OsStr; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -7,7 +8,7 @@ use color_eyre::{Help, Report}; use futures::future::try_join_all; use futures::StreamExt; use glob::Pattern; -use sdk::{Bundle, BundleFile}; +use sdk::{Bundle, BundleFile, CmdLine}; use tokio::fs; use crate::cmd::util::resolve_bundle_paths; @@ -89,30 +90,78 @@ pub(crate) fn command_definition() -> Command { Arg::new("ljd") .long("ljd") .help( - "Path to a custom ljd executable. If not set, \ - `ljd` will be called from PATH.", + "A custom command line to execute ljd with. It is treated as follows:\n\ + * if the argument is a valid path to an existing file:\n\ + ** if the file is called 'main.py', it is assumed that 'python.exe' \ + exists in PATH to execute this with.\n\ + ** otherwise it is treated as an executable\n\ + * if it's a single word, it's treated as an executable in PATH\n\ + * otherwise it is treated as a command line template.\n\ + In any case, the application being run must accept ljd's flags '-c' and '-f'.", ) .default_value("ljd"), ) - .arg( - Arg::new("revorb") - .long("revorb") - .help( - "Path to a custom revorb executable. If not set, \ - `revorb` will be called from PATH.", - ) - .default_value("revorb"), - ) - .arg( - Arg::new("ww2ogg") - .long("ww2ogg") - .help( - "Path to a custom ww2ogg executable. If not set, \ - `ww2ogg` will be called from PATH.\nSee the documentation for how \ - to set up the script for this.", - ) - .default_value("ww2ogg"), - ) + // .arg( + // Arg::new("revorb") + // .long("revorb") + // .help( + // "Path to a custom revorb executable. If not set, \ + // `revorb` will be called from PATH.", + // ) + // .default_value("revorb"), + // ) + // .arg( + // Arg::new("ww2ogg") + // .long("ww2ogg") + // .help( + // "Path to a custom ww2ogg executable. If not set, \ + // `ww2ogg` will be called from PATH.\nSee the documentation for how \ + // to set up the script for this.", + // ) + // .default_value("ww2ogg"), + // ) +} + +#[tracing::instrument] +async fn parse_command_line_template(tmpl: &String) -> Result { + if tmpl.trim().is_empty() { + eyre::bail!("Command line template must not be empty"); + } + + let mut cmd = if matches!(fs::try_exists(tmpl).await, Ok(true)) { + let path = PathBuf::from(tmpl); + if path.file_name() == Some(OsStr::new("main.py")) { + let arg = path.display().to_string(); + let mut cmd = CmdLine::new("python"); + cmd.arg("-c").arg(shlex::quote(&arg).to_string()); + cmd + } else { + CmdLine::new(path) + } + } else { + let Some(args) = shlex::split(tmpl) else { + eyre::bail!("Invalid shell syntax"); + }; + + // We already checked that the template is not empty + let mut cmd = CmdLine::new(args[0].clone()); + let mut it = args.iter(); + // Skip the first one, that's the command name + it.next(); + + for arg in it { + cmd.arg(arg); + } + + cmd + }; + + // Add ljd flags + cmd.arg("-c"); + + tracing::debug!("Parsed command line template: {:?}", cmd); + + Ok(cmd) } #[tracing::instrument(skip_all)] @@ -121,16 +170,19 @@ pub(crate) async fn run(mut ctx: sdk::Context, matches: &ArgMatches) -> Result<( let ljd_bin = matches .get_one::("ljd") .expect("no default value for 'ljd' parameter"); - let revorb_bin = matches - .get_one::("revorb") - .expect("no default value for 'revorb' parameter"); - let ww2ogg_bin = matches - .get_one::("ww2ogg") - .expect("no default value for 'ww2ogg' parameter"); + // let revorb_bin = matches + // .get_one::("revorb") + // .expect("no default value for 'revorb' parameter"); + // let ww2ogg_bin = matches + // .get_one::("ww2ogg") + // .expect("no default value for 'ww2ogg' parameter"); - ctx.ljd = Some(ljd_bin.clone()); - ctx.revorb = Some(revorb_bin.clone()); - ctx.ww2ogg = Some(ww2ogg_bin.clone()); + ctx.ljd = parse_command_line_template(ljd_bin) + .await + .map(Option::Some) + .wrap_err("Failed to parse command line template for flag 'ljd'")?; + // ctx.revorb = Some(revorb_bin.clone()); + // ctx.ww2ogg = Some(ww2ogg_bin.clone()); } let includes = match matches.get_many::("include") { diff --git a/lib/sdk/src/binary.rs b/lib/sdk/src/binary.rs index 9ce3f11..1fcc90e 100644 --- a/lib/sdk/src/binary.rs +++ b/lib/sdk/src/binary.rs @@ -133,6 +133,23 @@ pub mod sync { make_skip!(skip_u8, read_u8, u8); make_skip!(skip_u32, read_u32, u32); + // Implementation based on https://en.wikipedia.com/wiki/LEB128 + fn read_uleb128(&mut self) -> io::Result { + let mut result: u64 = 0; + let mut shift: u64 = 0; + + loop { + let byte = ReadExt::read_u8(self)? as u64; + result |= (byte & 0x7f) << shift; + + if byte < 0x80 { + return Ok(result); + } + + shift += 7; + } + } + fn skip_padding(&mut self) -> io::Result<()> { let pos = self.stream_position()?; let padding_size = 16 - (pos % 16); diff --git a/lib/sdk/src/context.rs b/lib/sdk/src/context.rs index b0de6dc..1500290 100644 --- a/lib/sdk/src/context.rs +++ b/lib/sdk/src/context.rs @@ -1,10 +1,59 @@ -use std::path::PathBuf; +use std::process::Command; +use std::{ffi::OsString, path::PathBuf}; use crate::murmur::{Dictionary, HashGroup, IdString64, Murmur32, Murmur64}; +pub struct CmdLine { + cmd: OsString, + args: Vec, +} + +impl CmdLine { + pub fn new(cmd: impl Into) -> Self { + Self { + cmd: cmd.into(), + args: vec![], + } + } + + pub fn arg(&mut self, arg: impl Into) -> &mut Self { + self.args.push(arg.into()); + self + } +} + +impl std::fmt::Debug for CmdLine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CmdLine") + .field("cmd", &self.cmd) + .field("args", &self.args) + .finish() + } +} + +impl std::fmt::Display for CmdLine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "\"{}\"", self.cmd.to_string_lossy())?; + + for arg in &self.args { + write!(f, " \"{}\"", arg.to_string_lossy())?; + } + + Ok(()) + } +} + +impl From<&CmdLine> for Command { + fn from(value: &CmdLine) -> Self { + let mut cmd = Command::new(&value.cmd); + cmd.args(&value.args); + cmd + } +} + pub struct Context { pub lookup: Dictionary, - pub ljd: Option, + pub ljd: Option, pub revorb: Option, pub ww2ogg: Option, pub game_dir: Option, diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index bb1908c..67d4eb3 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -1,24 +1,109 @@ +use std::env; use std::ffi::CStr; use std::ffi::CString; use std::io::Cursor; +use std::io::Read; use std::io::Write; +use std::process::Command; use color_eyre::eyre; use color_eyre::eyre::Context; use color_eyre::Result; use luajit2_sys as lua; +use tokio::fs; +use crate::binary::sync::ReadExt; use crate::binary::sync::WriteExt; use crate::bundle::file::{BundleFileVariant, UserFile}; use crate::{BundleFile, BundleFileType}; +const BITSQUID_LUAJIT_HEADER: u32 = 0x8253461B; + #[tracing::instrument(skip_all, fields(buf_len = data.as_ref().len()))] -pub(crate) async fn decompile(_ctx: &crate::Context, data: T) -> Result> +pub(crate) async fn decompile(ctx: &crate::Context, data: T) -> Result> where T: AsRef<[u8]>, { - let mut _r = Cursor::new(data.as_ref()); - todo!(); + let data = data.as_ref(); + let length = { + let mut r = Cursor::new(data); + r.read_u32()? as usize + }; + + // This skips the unknown bytes 5..12 + let content = &data[12..]; + eyre::ensure!( + content.len() == length, + "Content length doesn't match. Expected {}, got {}", + length, + content.len() + ); + + let name = { + let mut r = Cursor::new(content); + + eyre::ensure!( + r.read_u32()? == BITSQUID_LUAJIT_HEADER, + "Invalid magic bytes" + ); + + // Skip additional header bytes + let _ = r.read_uleb128()?; + let length = r.read_uleb128()? as usize; + + let mut buf = vec![0u8; length]; + r.read_exact(&mut buf)?; + let mut s = String::from_utf8(buf) + .wrap_err_with(|| format!("Invalid byte sequence for LuaJIT bytecode name"))?; + // Remove the leading `@` + s.remove(0); + s + }; + + let mut temp = env::temp_dir(); + // Using the actual file name and keeping it in case of an error makes debugging easier. + // But to avoid creating a bunch of folders, we flatten the name. + temp.push(name.replace('/', "_")); + temp.set_extension("luao"); + + tracing::debug!( + "Writing temporary LuaJIT bytecode file to '{}'", + temp.display() + ); + + fs::write(&temp, content) + .await + .wrap_err_with(|| format!("Failed to write LuaJIT bytecode to '{}'", temp.display()))?; + + let mut cmd = ctx + .ljd + .as_ref() + .map(|c| c.into()) + .unwrap_or_else(|| Command::new("ljd")); + + cmd.arg("-f").arg(&temp); + + tracing::debug!("Executing command: '{:?}'", cmd); + + let output = cmd.output().wrap_err("Failed to run ljd")?; + + if !output.stderr.is_empty() { + eyre::bail!( + "Decompilation failed: {}", + String::from_utf8_lossy(&output.stderr) + ); + } + + let content = output.stdout; + + if let Err(err) = fs::remove_file(&temp) + .await + .wrap_err("Failed to remove temporary file") + { + tracing::warn!("{:?}", err); + } + + Ok(vec![UserFile::with_name(content, name)]) } #[tracing::instrument(skip_all)] diff --git a/lib/sdk/src/lib.rs b/lib/sdk/src/lib.rs index e229e28..37a4d67 100644 --- a/lib/sdk/src/lib.rs +++ b/lib/sdk/src/lib.rs @@ -8,4 +8,4 @@ pub use binary::{FromBinary, ToBinary}; pub use bundle::database::BundleDatabase; pub use bundle::decompress; pub use bundle::{Bundle, BundleFile, BundleFileType, BundleFileVariant}; -pub use context::Context; +pub use context::{CmdLine, Context}; From 57224622d9c497cf32af709e4a4a23d746cb6c50 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 30 Oct 2023 09:21:47 +0100 Subject: [PATCH 131/335] sdk: Don't wait for cleanup --- lib/sdk/src/filetype/lua.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index 67d4eb3..cfc253a 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -96,12 +96,15 @@ where let content = output.stdout; - if let Err(err) = fs::remove_file(&temp) - .await - .wrap_err("Failed to remove temporary file") - { - tracing::warn!("{:?}", err); - } + // No need to wait for this, so we move it to a separate task. + tokio::spawn(async move { + if let Err(err) = fs::remove_file(&temp) + .await + .wrap_err_with(|| format!("Failed to remove temporary file '{}'", temp.display())) + { + tracing::warn!("{:?}", err); + } + }); Ok(vec![UserFile::with_name(content, name)]) } From 7080dcb8dec9f63e1275d849cf846af0222276bb Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 30 Oct 2023 14:07:00 +0100 Subject: [PATCH 132/335] sdk: Use common ljd flags --- crates/dtmt/src/cmd/bundle/extract.rs | 2 +- lib/sdk/src/filetype/lua.rs | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/dtmt/src/cmd/bundle/extract.rs b/crates/dtmt/src/cmd/bundle/extract.rs index 57b7fe9..5e1c03b 100644 --- a/crates/dtmt/src/cmd/bundle/extract.rs +++ b/crates/dtmt/src/cmd/bundle/extract.rs @@ -133,7 +133,7 @@ async fn parse_command_line_template(tmpl: &String) -> Result { if path.file_name() == Some(OsStr::new("main.py")) { let arg = path.display().to_string(); let mut cmd = CmdLine::new("python"); - cmd.arg("-c").arg(shlex::quote(&arg).to_string()); + cmd.arg(shlex::quote(&arg).to_string()); cmd } else { CmdLine::new(path) diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index cfc253a..3979b7a 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -81,7 +81,12 @@ where .map(|c| c.into()) .unwrap_or_else(|| Command::new("ljd")); - cmd.arg("-f").arg(&temp); + cmd.arg("--catch_asserts") + .args(["--function_def_sugar", "false"]) + .args(["--function_def_self_arg", "true"]) + .args(["--unsafe", "false"]) + .arg("-f") + .arg(&temp); tracing::debug!("Executing command: '{:?}'", cmd); From 48f98a43fe8d8888e20a300645b08d9d694fd25a Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 13 Sep 2023 16:46:26 +0200 Subject: [PATCH 133/335] oodle: Fix linking statically --- CHANGELOG.adoc | 1 + lib/oodle/build.rs | 23 ++++++++++------------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 2e2b7a4..4d1861c 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -21,6 +21,7 @@ - all: force unix path separators for engine values - dtmt: fix extracing files with non-flattened file names +- oodle: fix static linking == 2023-03-01 diff --git a/lib/oodle/build.rs b/lib/oodle/build.rs index cc416c5..7b200aa 100644 --- a/lib/oodle/build.rs +++ b/lib/oodle/build.rs @@ -4,24 +4,21 @@ use std::env; use std::path::PathBuf; fn main() { - // Tell cargo to look for shared libraries in the specified directory - if let Ok(manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") { - println!("cargo:rustc-link-search={}", manifest_dir); - dbg!(&manifest_dir); - } + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("No CARGO_MANIFEST_DIR"); + println!("cargo:rustc-link-search=native={}", &manifest_dir); + dbg!(&manifest_dir); - let lib_name = if std::env::var("CARGO_CFG_WINDOWS").is_ok() { - if cfg!(debug_assertions) { + if std::env::var("CARGO_CFG_WINDOWS").is_ok() { + let lib_name = if cfg!(debug_assertions) { "oo2core_win64_debug" } else { "oo2core_win64" - } + }; + println!("cargo:rustc-link-lib=static={}", lib_name); } else { - "oo2corelinux64" - }; - - println!("cargo:rustc-link-lib={}", lib_name); - dbg!(&lib_name); + println!("cargo:rustc-link-lib=static=oo2corelinux64"); + println!("cargo:rustc-link-lib=stdc++") + } println!("cargo:rerun-if-changed=oodle2.h"); From db8194efec2c20d82ec734a97de17358f7eff7dd Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 7 Nov 2023 09:25:01 +0100 Subject: [PATCH 134/335] Update crates --- Cargo.lock | 527 ++++++++++++++++++++++++++++------------------------- 1 file changed, 283 insertions(+), 244 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78409bb..30a717d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,9 +30,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -58,9 +58,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.5.0" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", @@ -72,15 +72,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] @@ -96,9 +96,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "2.1.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -154,7 +154,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -210,9 +210,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.4" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "base64ct" @@ -250,9 +250,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "bitmaps" @@ -280,12 +280,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.6.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" +checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" dependencies = [ "memchr", - "regex-automata 0.3.8", + "regex-automata 0.4.3", "serde", ] @@ -297,9 +297,9 @@ checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytecount" -version = "0.6.3" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" +checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" [[package]] name = "bytemuck" @@ -309,9 +309,9 @@ checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -423,9 +423,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.5" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824956d0dca8334758a5b7f7e50518d66ea319330cbceedcf76905c2f6ab30e3" +checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" dependencies = [ "clap_builder", "clap_derive", @@ -433,9 +433,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.5" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122ec64120a49b4563ccaedcbea7818d069ed8e9aa6d829b82d8a4128936b2ab" +checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" dependencies = [ "anstream", "anstyle", @@ -447,21 +447,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "cli-table" @@ -651,9 +651,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -669,9 +669,9 @@ dependencies = [ [[package]] name = "crc-catalog" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" @@ -730,9 +730,9 @@ dependencies = [ [[package]] name = "csv-core" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" dependencies = [ "memchr", ] @@ -745,10 +745,11 @@ checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" [[package]] name = "deranged" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" dependencies = [ + "powerfmt", "serde", ] @@ -821,7 +822,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -1039,25 +1040,14 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys 0.48.0", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "error-code" version = "2.3.1" @@ -1106,9 +1096,9 @@ dependencies = [ [[package]] name = "fdeflate" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868" dependencies = [ "simd-adler32", ] @@ -1137,9 +1127,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", @@ -1197,7 +1187,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "674e258f4b5d2dcd63888c01c68413c51f565e8af99d2f7701c7b81d79ef41c4" dependencies = [ - "roxmltree 0.18.0", + "roxmltree 0.18.1", ] [[package]] @@ -1253,9 +1243,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -1268,9 +1258,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -1278,15 +1268,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -1295,38 +1285,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-macro" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "futures-sink" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-channel", "futures-core", @@ -1642,9 +1632,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heapless" @@ -1739,7 +1729,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", + "socket2 0.4.10", "tokio", "tower-service", "tracing", @@ -1795,7 +1785,7 @@ dependencies = [ "color_quant", "jpeg-decoder", "num-rational 0.4.1", - "num-traits 0.2.16", + "num-traits 0.2.17", "png", ] @@ -1817,12 +1807,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.2", ] [[package]] @@ -1887,9 +1877,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "itoa" @@ -1899,9 +1889,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] @@ -1914,9 +1904,9 @@ checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" dependencies = [ "wasm-bindgen", ] @@ -1994,9 +1984,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libloading" @@ -2009,10 +1999,21 @@ dependencies = [ ] [[package]] -name = "linux-raw-sys" -version = "0.4.7" +name = "libredox" +version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "log" @@ -2056,9 +2057,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.6.3" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memmap2" @@ -2111,9 +2112,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "log", @@ -2260,7 +2261,7 @@ dependencies = [ "num-integer", "num-iter", "num-rational 0.3.2", - "num-traits 0.2.16", + "num-traits 0.2.17", ] [[package]] @@ -2271,7 +2272,7 @@ checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" dependencies = [ "autocfg", "num-integer", - "num-traits 0.2.16", + "num-traits 0.2.17", ] [[package]] @@ -2280,7 +2281,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" dependencies = [ - "num-traits 0.2.16", + "num-traits 0.2.17", ] [[package]] @@ -2290,7 +2291,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", - "num-traits 0.2.16", + "num-traits 0.2.17", ] [[package]] @@ -2301,7 +2302,7 @@ checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" dependencies = [ "autocfg", "num-integer", - "num-traits 0.2.16", + "num-traits 0.2.17", ] [[package]] @@ -2313,7 +2314,7 @@ dependencies = [ "autocfg", "num-bigint", "num-integer", - "num-traits 0.2.16", + "num-traits 0.2.17", ] [[package]] @@ -2324,7 +2325,7 @@ checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", "num-integer", - "num-traits 0.2.16", + "num-traits 0.2.17", ] [[package]] @@ -2333,14 +2334,14 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" dependencies = [ - "num-traits 0.2.16", + "num-traits 0.2.17", ] [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] @@ -2399,11 +2400,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.57" +version = "0.10.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", @@ -2420,7 +2421,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -2431,9 +2432,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.93" +version = "0.9.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" dependencies = [ "cc", "libc", @@ -2555,9 +2556,9 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4" +checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" dependencies = [ "memchr", "thiserror", @@ -2566,9 +2567,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8" +checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" dependencies = [ "pest", "pest_generator", @@ -2576,22 +2577,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a" +checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "pest_meta" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d" +checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" dependencies = [ "once_cell", "pest", @@ -2722,6 +2723,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -2729,7 +2736,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", ] [[package]] @@ -2758,9 +2765,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -2814,15 +2821,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.3.5" @@ -2833,26 +2831,35 @@ dependencies = [ ] [[package]] -name = "redox_users" -version = "0.4.3" +name = "redox_syscall" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", - "redox_syscall 0.2.16", + "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.9.5" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.8", - "regex-syntax 0.7.5", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", ] [[package]] @@ -2866,13 +2873,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.8" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.5", + "regex-syntax 0.8.2", ] [[package]] @@ -2883,17 +2890,17 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.20" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ - "base64 0.21.4", + "base64 0.21.5", "bytes", "encoding_rs", "futures-core", @@ -2914,6 +2921,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-native-tls", "tower-service", @@ -2944,9 +2952,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.36" +version = "0.8.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59" +checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" dependencies = [ "bytemuck", ] @@ -2962,9 +2970,9 @@ dependencies = [ [[package]] name = "roxmltree" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8f595a457b6b8c6cda66a48503e92ee8d19342f905948f29c383200ec9eb1d8" +checksum = "862340e351ce1b271a378ec53f304a5558f7db87f3769dc655a8f6ecbb68b302" dependencies = [ "xmlparser", ] @@ -2992,11 +3000,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.14" +version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", @@ -3131,35 +3139,35 @@ checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" [[package]] name = "semver" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", @@ -3177,9 +3185,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" dependencies = [ "serde", ] @@ -3209,9 +3217,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.7" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ "cfg-if", "cpufeatures", @@ -3220,9 +3228,9 @@ dependencies = [ [[package]] name = "sharded-slab" -version = "0.1.4" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ "lazy_static", ] @@ -3290,9 +3298,9 @@ checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "socket2" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", @@ -3300,9 +3308,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys 0.48.0", @@ -3427,9 +3435,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.37" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -3437,33 +3445,54 @@ dependencies = [ ] [[package]] -name = "system-deps" -version = "6.1.1" +name = "system-configuration" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c2de8a4d8f4b823d634affc9cd2a74ec98c53a756f317e529a48046cbf71f3" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331" dependencies = [ "cfg-expr", "heck", "pkg-config", - "toml 0.7.8", + "toml 0.8.6", "version-compare", ] [[package]] name = "target-lexicon" -version = "0.12.11" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand 2.0.1", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "rustix", "windows-sys 0.48.0", ] @@ -3479,22 +3508,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.48" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.48" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -3509,14 +3538,15 @@ dependencies = [ [[package]] name = "time" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ "deranged", "itoa", "libc", "num_threads", + "powerfmt", "serde", "time-core", "time-macros", @@ -3564,9 +3594,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b07bb54ef1f8ff27564b08b861144d3b8d40263efe07684f64987f4c0d044e3e" +checksum = "d5d0e245e80bdc9b4e5356fc45a72184abbc3861992603f515270e9340f5a219" dependencies = [ "displaydoc", ] @@ -3588,9 +3618,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ "backtrace", "bytes", @@ -3599,7 +3629,7 @@ dependencies = [ "num_cpus", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.4", + "socket2 0.5.5", "tokio-macros", "tracing", "windows-sys 0.48.0", @@ -3613,7 +3643,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] @@ -3639,9 +3669,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -3662,21 +3692,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.7.8" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" +checksum = "8ff9e3abce27ee2c9a37f9ad37238c1bdd4e789c84ba37df76aa4d528f5072cc" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.20.7", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] @@ -3687,7 +3717,18 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.1.0", "serde", "serde_spanned", "toml_datetime", @@ -3702,11 +3743,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3714,20 +3754,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", ] [[package]] name = "tracing-core" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", "valuable", @@ -3745,12 +3785,12 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" dependencies = [ - "lazy_static", "log", + "once_cell", "tracing-core", ] @@ -4085,9 +4125,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4095,24 +4135,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" dependencies = [ "cfg-if", "js-sys", @@ -4122,9 +4162,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4132,28 +4172,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", - "syn 2.0.37", + "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "web-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" dependencies = [ "js-sys", "wasm-bindgen", @@ -4342,9 +4382,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -version = "0.5.15" +version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" dependencies = [ "memchr", ] @@ -4385,9 +4425,9 @@ checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" [[package]] name = "xmlparser" -version = "0.13.5" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" [[package]] name = "xmlwriter" @@ -4436,11 +4476,10 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] From f197b1944b8cc4b66aaa75a01faf28519239f523 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 7 Nov 2023 10:54:13 +0100 Subject: [PATCH 135/335] ci: Fix incorrect exit code The runner wouldn't properly exit with a failure code, so it always looked like success in the UI. --- .ci/util/run.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/util/run.sh b/.ci/util/run.sh index 1de3b1c..60c8112 100755 --- a/.ci/util/run.sh +++ b/.ci/util/run.sh @@ -47,4 +47,5 @@ if sh "$script"; then notify 'success' "$context" "$desc" else notify 'failure' "$context" "$desc" + exit 1 fi From 08d417fd5d296e8229a8e70ef40752291817f9cf Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 7 Nov 2023 15:23:11 +0100 Subject: [PATCH 136/335] Update steamlocate-rs While this is an unstable dev release, its only significant change is exactly the one I need: dropping `steam-vdf` and its out-of-date `nom` dependency. --- .ci/Dockerfile.linux | 2 - .ci/Dockerfile.msvc | 2 - .gitmodules | 6 +- Cargo.lock | 161 ++++++++----------------------------- Cargo.toml | 1 - lib/ansi-parser | 1 + lib/dtmt-shared/Cargo.toml | 2 +- lib/dtmt-shared/src/lib.rs | 15 ++-- lib/steamlocate-rs | 1 - 9 files changed, 43 insertions(+), 148 deletions(-) create mode 160000 lib/ansi-parser delete mode 160000 lib/steamlocate-rs diff --git a/.ci/Dockerfile.linux b/.ci/Dockerfile.linux index 2cb6339..f8b8567 100644 --- a/.ci/Dockerfile.linux +++ b/.ci/Dockerfile.linux @@ -8,7 +8,6 @@ RUN set -e; \ cargo new --lib lib/nexusmods; \ cargo new --lib lib/sdk; \ cargo new --lib lib/serde_sjson; \ - cargo new --lib lib/steamlocate-rs COPY Cargo.toml Cargo.lock /src/dtmt/ COPY crates/dtmt/Cargo.toml /src/dtmt/crates/dtmt/ @@ -17,7 +16,6 @@ COPY lib/dtmt-shared/Cargo.toml /src/dtmt/lib/dtmt-shared/ COPY lib/nexusmods/Cargo.toml /src/dtmt/lib/nexusmods/ COPY lib/sdk/Cargo.toml /src/dtmt/lib/sdk/ COPY lib/serde_sjson/Cargo.toml /src/dtmt/lib/serde_sjson/ -COPY lib/steamlocate-rs/Cargo.toml /src/dtmt/lib/steamlocate-rs/ # Crates with build scripts cannot be split that way, but they shouldn't change too often COPY lib/luajit2-sys /src/dtmt/lib/luajit2-sys diff --git a/.ci/Dockerfile.msvc b/.ci/Dockerfile.msvc index 0d0a546..f3c722b 100644 --- a/.ci/Dockerfile.msvc +++ b/.ci/Dockerfile.msvc @@ -8,7 +8,6 @@ RUN set -e; \ cargo new --lib lib/nexusmods; \ cargo new --lib lib/sdk; \ cargo new --lib lib/serde_sjson; \ - cargo new --lib lib/steamlocate-rs COPY Cargo.toml Cargo.lock /src/dtmt/ COPY crates/dtmt/Cargo.toml /src/dtmt/crates/dtmt/ @@ -17,7 +16,6 @@ COPY lib/dtmt-shared/Cargo.toml /src/dtmt/lib/dtmt-shared/ COPY lib/nexusmods/Cargo.toml /src/dtmt/lib/nexusmods/ COPY lib/sdk/Cargo.toml /src/dtmt/lib/sdk/ COPY lib/serde_sjson/Cargo.toml /src/dtmt/lib/serde_sjson/ -COPY lib/steamlocate-rs/Cargo.toml /src/dtmt/lib/steamlocate-rs/ # Crates with build scripts cannot be split that way, but they shouldn't change too often COPY lib/luajit2-sys /src/dtmt/lib/luajit2-sys diff --git a/.gitmodules b/.gitmodules index 145bfcb..c31f9da 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,12 @@ [submodule "lib/serde_sjson"] path = lib/serde_sjson url = https://git.sclu1034.dev/lucas/serde_sjson.git -[submodule "lib/steamlocate-rs"] - path = lib/steamlocate-rs - url = https://github.com/sclu1034/steamlocate-rs.git [submodule "lib/luajit2-sys"] path = lib/luajit2-sys url = https://github.com/sclu1034/luajit2-sys.git [submodule "lib/color-eyre"] path = lib/color-eyre url = https://github.com/sclu1034/color-eyre.git +[submodule "lib/ansi-parser"] + path = lib/ansi-parser + url = git@gitlab.com:lschwiderski/ansi-parser.git diff --git a/Cargo.lock b/Cargo.lock index 30a717d..a7885f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -658,21 +658,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - [[package]] name = "crc32fast" version = "1.3.2" @@ -1023,15 +1008,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" -[[package]] -name = "enum_primitive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" -dependencies = [ - "num-traits 0.1.43", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -1784,8 +1760,8 @@ dependencies = [ "byteorder", "color_quant", "jpeg-decoder", - "num-rational 0.4.1", - "num-traits 0.2.17", + "num-rational", + "num-traits", "png", ] @@ -1931,6 +1907,20 @@ dependencies = [ "thiserror", ] +[[package]] +name = "keyvalues-serde" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da419ac133bb3ddf0dbf9c12fcc0ce01d994fcb65f6f1713faf15cc689320b5f" +dependencies = [ + "keyvalues-parser", + "once_cell", + "paste", + "regex", + "serde", + "thiserror", +] + [[package]] name = "kqueue" version = "1.0.8" @@ -2185,12 +2175,6 @@ dependencies = [ "memoffset 0.6.5", ] -[[package]] -name = "nom" -version = "1.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce" - [[package]] name = "nom" version = "4.2.3" @@ -2250,40 +2234,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational 0.3.2", - "num-traits 0.2.17", -] - -[[package]] -name = "num-bigint" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" -dependencies = [ - "autocfg", - "num-integer", - "num-traits 0.2.17", -] - -[[package]] -name = "num-complex" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" -dependencies = [ - "num-traits 0.2.17", -] - [[package]] name = "num-integer" version = "0.1.45" @@ -2291,30 +2241,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", - "num-traits 0.2.17", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits 0.2.17", -] - -[[package]] -name = "num-rational" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits 0.2.17", + "num-traits", ] [[package]] @@ -2325,16 +2252,7 @@ checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", "num-integer", - "num-traits 0.2.17", -] - -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -dependencies = [ - "num-traits 0.2.17", + "num-traits", ] [[package]] @@ -2518,6 +2436,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "path-clean" version = "1.0.1" @@ -3322,42 +3246,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "steamid-ng" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb049f8faa2cba570c5366dbaf88ee5849725b16edb771848639fac92e33673" -dependencies = [ - "enum_primitive", - "lazy_static", - "num", - "regex", - "serde", - "serde_derive", - "thiserror", -] - [[package]] name = "steamlocate" -version = "1.1.1" +version = "2.0.0-alpha.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b1568c4a70a26c4373fe1131ffa4eff055459631b6e40c6bc118615f2d870c3" dependencies = [ - "crc", "dirs", "keyvalues-parser", - "steamid-ng", - "steamy-vdf", + "keyvalues-serde", + "serde", "winreg 0.10.1", ] -[[package]] -name = "steamy-vdf" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533127ad49314bfe71c3d3fd36b3ebac3d24f40618092e70e1cfe8362c7fac79" -dependencies = [ - "nom 1.2.4", -] - [[package]] name = "str-buf" version = "1.0.6" @@ -4483,3 +4384,7 @@ dependencies = [ "cc", "pkg-config", ] + +[[patch.unused]] +name = "ansi-parser" +version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index ab29a65..4912871 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,6 @@ members = [ "lib/oodle", "lib/sdk", "lib/serde_sjson", - "lib/steamlocate-rs", ] [patch.crates-io] diff --git a/lib/ansi-parser b/lib/ansi-parser new file mode 160000 index 0000000..28001c3 --- /dev/null +++ b/lib/ansi-parser @@ -0,0 +1 @@ +Subproject commit 28001c31975ecaefd0133e952dd6bb6386f6c701 diff --git a/lib/dtmt-shared/Cargo.toml b/lib/dtmt-shared/Cargo.toml index 4412266..57b4a8c 100644 --- a/lib/dtmt-shared/Cargo.toml +++ b/lib/dtmt-shared/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" ansi_term = "0.12.1" color-eyre = "0.6.2" serde = "1.0.152" -steamlocate = { path = "../../lib/steamlocate-rs", version = "*" } +steamlocate = "2.0.0-alpha.0" time = { version = "0.3.19", features = ["formatting", "local-offset", "macros"] } tracing = "0.1.37" tracing-error = "0.2.0" diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index bc7f425..3907928 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -71,20 +71,15 @@ pub fn collect_game_info() -> Option { let found = dir .app(&STEAMAPP_ID) - .and_then(|app| app.vdf.get("LastUpdated").map(|v| (app.path.clone(), v))); + .and_then(|app| app.last_updated.map(|v| (app.path.clone(), v))); let Some((path, last_updated)) = found else { tracing::debug!("Found Steam, but failed to find game installation"); return None; }; - let Some(last_updated) = last_updated - .as_value() - .and_then(|v| v.to::()) - .and_then(|v| OffsetDateTime::from_unix_timestamp(v).ok()) else { - tracing::error!("Found Steam game, but couldn't read 'LastUpdate'."); - return None; - }; - - Some(GameInfo { path, last_updated }) + Some(GameInfo { + path, + last_updated: last_updated.into(), + }) } diff --git a/lib/steamlocate-rs b/lib/steamlocate-rs deleted file mode 160000 index 4d6898f..0000000 --- a/lib/steamlocate-rs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4d6898f632e20ea15d47b0b071daa4f3fa6c9574 From 9aae4c1e29224a98c1d7ccedf77ed9aa24f7c6cb Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 7 Nov 2023 15:47:38 +0100 Subject: [PATCH 137/335] Fix CI build task --- .ci/Dockerfile.linux | 2 +- .ci/tasks/build.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/Dockerfile.linux b/.ci/Dockerfile.linux index f8b8567..a8124b1 100644 --- a/.ci/Dockerfile.linux +++ b/.ci/Dockerfile.linux @@ -28,6 +28,6 @@ RUN cargo build --release --locked RUN rm -r crates lib COPY . /src/dtmt -COPY --from=dtmt-ci-base-linux /src/*.lib /src/dtmt/lib/oodle/ +COPY --from=dtmt-ci-base-linux /src/*.lib /src/*.so /src/dtmt/lib/oodle/ RUN cargo build --release --locked diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index fd74937..8db0e69 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -4,7 +4,7 @@ set -eux case "$TARGET" in msvc) - cp /src/*.lib ./lib/oodle/ + cp /src/*.lib /src/*.so ./lib/oodle/ cargo build --color always --locked --release --target x86_64-pc-windows-msvc -Zbuild-std if [ -d "$OUTPUT" ]; then From 5f43fd17bae70ffc4c407be168fe492328fc7d07 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 7 Nov 2023 17:12:21 +0100 Subject: [PATCH 138/335] Vendor ansi-parser The fork updates `nom` to version 7, removing rustc's deprecation warning. --- .ci/Dockerfile.linux | 2 ++ .ci/Dockerfile.msvc | 2 ++ .gitmodules | 3 ++- Cargo.toml | 5 +++++ lib/ansi-parser | 2 +- 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.ci/Dockerfile.linux b/.ci/Dockerfile.linux index a8124b1..9e93e44 100644 --- a/.ci/Dockerfile.linux +++ b/.ci/Dockerfile.linux @@ -8,6 +8,7 @@ RUN set -e; \ cargo new --lib lib/nexusmods; \ cargo new --lib lib/sdk; \ cargo new --lib lib/serde_sjson; \ + cargo new --lib lib/ansi-parser COPY Cargo.toml Cargo.lock /src/dtmt/ COPY crates/dtmt/Cargo.toml /src/dtmt/crates/dtmt/ @@ -16,6 +17,7 @@ COPY lib/dtmt-shared/Cargo.toml /src/dtmt/lib/dtmt-shared/ COPY lib/nexusmods/Cargo.toml /src/dtmt/lib/nexusmods/ COPY lib/sdk/Cargo.toml /src/dtmt/lib/sdk/ COPY lib/serde_sjson/Cargo.toml /src/dtmt/lib/serde_sjson/ +COPY lib/ansi-parser/Cargo.toml /src/dtmt/lib/ansi-parser/ # Crates with build scripts cannot be split that way, but they shouldn't change too often COPY lib/luajit2-sys /src/dtmt/lib/luajit2-sys diff --git a/.ci/Dockerfile.msvc b/.ci/Dockerfile.msvc index f3c722b..e8c9c32 100644 --- a/.ci/Dockerfile.msvc +++ b/.ci/Dockerfile.msvc @@ -8,6 +8,7 @@ RUN set -e; \ cargo new --lib lib/nexusmods; \ cargo new --lib lib/sdk; \ cargo new --lib lib/serde_sjson; \ + cargo new --lib lib/ansi-parser COPY Cargo.toml Cargo.lock /src/dtmt/ COPY crates/dtmt/Cargo.toml /src/dtmt/crates/dtmt/ @@ -16,6 +17,7 @@ COPY lib/dtmt-shared/Cargo.toml /src/dtmt/lib/dtmt-shared/ COPY lib/nexusmods/Cargo.toml /src/dtmt/lib/nexusmods/ COPY lib/sdk/Cargo.toml /src/dtmt/lib/sdk/ COPY lib/serde_sjson/Cargo.toml /src/dtmt/lib/serde_sjson/ +COPY lib/ansi-parser/Cargo.toml /src/dtmt/lib/ansi-parser/ # Crates with build scripts cannot be split that way, but they shouldn't change too often COPY lib/luajit2-sys /src/dtmt/lib/luajit2-sys diff --git a/.gitmodules b/.gitmodules index c31f9da..9398bb8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,4 +9,5 @@ url = https://github.com/sclu1034/color-eyre.git [submodule "lib/ansi-parser"] path = lib/ansi-parser - url = git@gitlab.com:lschwiderski/ansi-parser.git + url = https://gitlab.com/lschwiderski/ansi-parser.git + branch = "issue/outdated-nom" diff --git a/Cargo.toml b/Cargo.toml index 4912871..123b2ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,14 @@ members = [ "lib/sdk", "lib/serde_sjson", ] +exclude = [ + "lib/color-eyre", + "lib/ansi-parser", +] [patch.crates-io] color-eyre = { path = "lib/color-eyre" } +ansi-parser = { path = "lib/ansi-parser" } [profile.dev.package.backtrace] opt-level = 3 diff --git a/lib/ansi-parser b/lib/ansi-parser index 28001c3..27beb4b 160000 --- a/lib/ansi-parser +++ b/lib/ansi-parser @@ -1 +1 @@ -Subproject commit 28001c31975ecaefd0133e952dd6bb6386f6c701 +Subproject commit 27beb4bc1ffd2865a432e13f0588b5351ff419bf From fc759c005d38259585b92a5bd7d627e85a26105d Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 8 Nov 2023 09:43:50 +0100 Subject: [PATCH 139/335] oodle: Fix building for target Windows --- lib/oodle/build.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/oodle/build.rs b/lib/oodle/build.rs index 7b200aa..e33c7d5 100644 --- a/lib/oodle/build.rs +++ b/lib/oodle/build.rs @@ -6,9 +6,8 @@ use std::path::PathBuf; fn main() { let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("No CARGO_MANIFEST_DIR"); println!("cargo:rustc-link-search=native={}", &manifest_dir); - dbg!(&manifest_dir); - if std::env::var("CARGO_CFG_WINDOWS").is_ok() { + if std::env::var("CARGO_CFG_TARGET_FAMILY") == Ok(String::from("windows")) { let lib_name = if cfg!(debug_assertions) { "oo2core_win64_debug" } else { @@ -17,7 +16,7 @@ fn main() { println!("cargo:rustc-link-lib=static={}", lib_name); } else { println!("cargo:rustc-link-lib=static=oo2corelinux64"); - println!("cargo:rustc-link-lib=stdc++") + println!("cargo:rustc-link-lib=stdc++"); } println!("cargo:rerun-if-changed=oodle2.h"); From afe4e82c4720a5fe736ca3723f183da6fa60078a Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 8 Nov 2023 10:50:42 +0100 Subject: [PATCH 140/335] ci: Copy the correct library files --- .ci/image/Dockerfile.linux | 2 +- .ci/image/Dockerfile.msvc | 2 +- .ci/tasks/build.sh | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.ci/image/Dockerfile.linux b/.ci/image/Dockerfile.linux index c4f955f..4927810 100644 --- a/.ci/image/Dockerfile.linux +++ b/.ci/image/Dockerfile.linux @@ -21,4 +21,4 @@ RUN set -eux; \ WORKDIR /src/dtmt -COPY liboo2corelinux64.so /src +COPY *.so *.a /src/ diff --git a/.ci/image/Dockerfile.msvc b/.ci/image/Dockerfile.msvc index 7a501eb..a9eab62 100644 --- a/.ci/image/Dockerfile.msvc +++ b/.ci/image/Dockerfile.msvc @@ -86,4 +86,4 @@ RUN wine wineboot --init WORKDIR /src/dtmt -COPY oo2core_win32.lib oo2core_win64.lib /src +COPY *.lib /src diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 8db0e69..7029f44 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -4,7 +4,7 @@ set -eux case "$TARGET" in msvc) - cp /src/*.lib /src/*.so ./lib/oodle/ + cp /src/*.lib ./lib/oodle/ cargo build --color always --locked --release --target x86_64-pc-windows-msvc -Zbuild-std if [ -d "$OUTPUT" ]; then @@ -13,7 +13,7 @@ case "$TARGET" in fi ;; linux) - cp /src/*.so ./lib/oodle/ + cp /src/*.a ./lib/oodle/ cargo build --color always --locked --profile release-lto if [ -d "$OUTPUT" ]; then From a90614f2e9fafa22fcb31e8c05069a58c53d0b92 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 13 Nov 2023 14:31:12 +0100 Subject: [PATCH 141/335] ci: Implement uploading build artifacts Closes #127. --- .ci/pipelines/base-pipeline.yml | 3 --- .ci/pipelines/pr.yml | 13 ++++++++++++- .ci/tasks/build.yml | 8 +++++--- .ci/tasks/upload.sh | 32 ++++++++++++++++++++++++++++++++ .ci/tasks/upload.yml | 24 ++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 7 deletions(-) create mode 100755 .ci/tasks/upload.sh create mode 100644 .ci/tasks/upload.yml diff --git a/.ci/pipelines/base-pipeline.yml b/.ci/pipelines/base-pipeline.yml index 88052a5..48d5bb4 100644 --- a/.ci/pipelines/base-pipeline.yml +++ b/.ci/pipelines/base-pipeline.yml @@ -22,7 +22,6 @@ resources: source: uri: https://git.sclu1034.dev/bitsquid_dt/dtmt - jobs: - name: set-pipelines plan: @@ -42,5 +41,3 @@ jobs: gitea_api_key: ((gitea_api_key)) instance_vars: pr: ((.:pr.number)) - - diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index 6452c93..be1857d 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -1,3 +1,4 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/cappyzawa/concourse-pipeline-jsonschema/master/concourse_jsonschema.json#/definitions/Config --- # The actual CI pipeline that is run per branch @@ -6,7 +7,7 @@ resources: - name: repo type: git source: - uri: https://git.sclu1034.dev/bitsquid_dt/dtmt + uri: http://forgejo:3000/bitsquid_dt/dtmt branch: ((pr.head.ref)) jobs: @@ -34,6 +35,7 @@ jobs: target: msvc output: artifact ref: ((.:ref)) + gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) - name: build-linux @@ -48,4 +50,13 @@ jobs: target: linux output: artifact ref: ((.:ref)) + gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) + - task: upload + file: repo/.ci/tasks/upload.yml + vars: + input: artifact + pr: ((.:pr)) + gitea_api_key: ((gitea_api_key)) + gitea_user: bitsquid_dt + gitea_url: http://forgejo:3000 diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml index ab3a433..c58ed38 100644 --- a/.ci/tasks/build.yml +++ b/.ci/tasks/build.yml @@ -1,7 +1,9 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/cappyzawa/concourse-pipeline-jsonschema/master/concourse_jsonschema.json#/definitions/TaskConfig --- platform: linux image_resource: + name: ctmt-bi-base-((target)) type: registry-image source: repository: registry.local:5000/dtmt-ci-base-((target)) @@ -11,14 +13,14 @@ inputs: - name: repo outputs: -- name: ((output)) +- name: artifacts params: - CI: true + CI: "true" TARGET: ((target)) GITEA_API_KEY: ((gitea_api_key)) REF: ((ref)) - OUTPUT: ((output)) + OUTPUT: artifacts run: path: .ci/util/run.sh diff --git a/.ci/tasks/upload.sh b/.ci/tasks/upload.sh new file mode 100755 index 0000000..5c0dd59 --- /dev/null +++ b/.ci/tasks/upload.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -eu + +artifacts="$PWD/artifacts" +repo="$PWD/repo" + +base_url="${GITEA_URL}/api/packages/${GITEA_USER}/generic" + +cd "$repo" + +if [ -n "$PR" ]; then + echo "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" + ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short HEAD 2>/dev/null || echo 'manual')" +else + ref=$(git describe --tags) +fi + +echo "ref: $ref" + +# TODO: If this is a tag, check the tag name to determine which +# binary was affected and only upload that. +for f in dtmt dtmt.exe dtmm dtmm.exe; do + if [ -f "$artifacts/$f" ]; then + url="$base_url/$(basename -s .exe $f)/$ref/$f" + echo "$url" + curl -i -X 'PUT' \ + --user "concourse:$GITEA_API_KEY" \ + --upload-file "$artifacts/$f" \ + "$url" + fi +done diff --git a/.ci/tasks/upload.yml b/.ci/tasks/upload.yml new file mode 100644 index 0000000..de22cd2 --- /dev/null +++ b/.ci/tasks/upload.yml @@ -0,0 +1,24 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/cappyzawa/concourse-pipeline-jsonschema/master/concourse_jsonschema.json#/definitions/TaskConfig +--- +platform: linux + +image_resource: + name: python-script + type: registry-image + source: + repository: registry.local:5000/python-script + tag: latest + +inputs: +- name: repo +- name: ((input)) + +params: + CI: "true" + GITEA_API_KEY: ((gitea_api_key)) + GITEA_URL: ((gitea_url)) + GITEA_USER: ((user)) + PR: ((pr)) + +run: + path: repo/.ci/tasks/upload.sh From c2cdeedb2cae21ad85f999db2b84b268e70cd7d3 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 13 Nov 2023 15:00:32 +0100 Subject: [PATCH 142/335] ci: Add caches Closes #126. --- .ci/tasks/build.yml | 4 ++++ .ci/tasks/clippy.yml | 8 +++++++- .ci/tasks/upload.sh | 1 - 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml index c58ed38..3dd4215 100644 --- a/.ci/tasks/build.yml +++ b/.ci/tasks/build.yml @@ -15,6 +15,10 @@ inputs: outputs: - name: artifacts +caches: + - path: repo/target + - path: /usr/local/cargo/registry + params: CI: "true" TARGET: ((target)) diff --git a/.ci/tasks/clippy.yml b/.ci/tasks/clippy.yml index 35735c3..cea0e76 100644 --- a/.ci/tasks/clippy.yml +++ b/.ci/tasks/clippy.yml @@ -1,7 +1,9 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/cappyzawa/concourse-pipeline-jsonschema/master/concourse_jsonschema.json#/definitions/TaskConfig --- platform: linux image_resource: + name: dtmt-ci-base-linux type: registry-image source: repository: registry.local:5000/dtmt-ci-base-linux @@ -10,8 +12,12 @@ image_resource: inputs: - name: repo +caches: + - path: repo/target + - path: /usr/local/cargo/registry + params: - CI: true + CI: "true" GITEA_API_KEY: ((gitea_api_key)) REF: ((ref)) diff --git a/.ci/tasks/upload.sh b/.ci/tasks/upload.sh index 5c0dd59..5e1103e 100755 --- a/.ci/tasks/upload.sh +++ b/.ci/tasks/upload.sh @@ -23,7 +23,6 @@ echo "ref: $ref" for f in dtmt dtmt.exe dtmm dtmm.exe; do if [ -f "$artifacts/$f" ]; then url="$base_url/$(basename -s .exe $f)/$ref/$f" - echo "$url" curl -i -X 'PUT' \ --user "concourse:$GITEA_API_KEY" \ --upload-file "$artifacts/$f" \ From 46a61a7473c84017aa941ba8a610e48368b2ba97 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 17 Nov 2023 09:58:53 +0100 Subject: [PATCH 143/335] Use exit status to determine LJD failure --- lib/sdk/src/filetype/lua.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index 3979b7a..5cd371d 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -92,9 +92,10 @@ where let output = cmd.output().wrap_err("Failed to run ljd")?; - if !output.stderr.is_empty() { + if !output.status.success() { eyre::bail!( - "Decompilation failed: {}", + "LJD exited with code {:?}:\n{}", + output.status.code(), String::from_utf8_lossy(&output.stderr) ); } From 4ec44720b0a4c376225f95613950b4d51961a3ef Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 17 Nov 2023 15:10:20 +0100 Subject: [PATCH 144/335] sdk: Don't fail decompilation on LJD error --- lib/sdk/src/filetype/lua.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index 5cd371d..2458dec 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -93,11 +93,12 @@ where let output = cmd.output().wrap_err("Failed to run ljd")?; if !output.status.success() { - eyre::bail!( + let err = eyre::eyre!( "LJD exited with code {:?}:\n{}", output.status.code(), String::from_utf8_lossy(&output.stderr) ); + tracing::error!("Failed to decompile '{}':\n{:?}", name, err); } let content = output.stdout; From f30dc95385edb07f4311777e9417c3147a4c51c5 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 22 Nov 2023 09:55:09 +0100 Subject: [PATCH 145/335] ci: Use resource for Gitea package upload --- .ci/pipelines/base-pipeline.yml | 2 +- .ci/pipelines/pr.yml | 60 +++++++++++++++++++++++++++------ .ci/tasks/build.sh | 41 +++++++++++++++++----- .ci/tasks/build.yml | 10 +++--- .ci/tasks/upload.sh | 31 ----------------- .ci/tasks/upload.yml | 24 ------------- Justfile | 25 ++++++++++++++ 7 files changed, 113 insertions(+), 80 deletions(-) delete mode 100755 .ci/tasks/upload.sh delete mode 100644 .ci/tasks/upload.yml diff --git a/.ci/pipelines/base-pipeline.yml b/.ci/pipelines/base-pipeline.yml index 48d5bb4..ae9e323 100644 --- a/.ci/pipelines/base-pipeline.yml +++ b/.ci/pipelines/base-pipeline.yml @@ -40,4 +40,4 @@ jobs: pr: ((.:pr)) gitea_api_key: ((gitea_api_key)) instance_vars: - pr: ((.:pr.number)) + n: ((.:pr.number)) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index be1857d..d818490 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -2,6 +2,11 @@ --- # The actual CI pipeline that is run per branch +resource_types: +- name: gitea-package + type: registry-image + source: + repository: registry.local:5000/gitea-package resources: - name: repo @@ -9,6 +14,14 @@ resources: source: uri: http://forgejo:3000/bitsquid_dt/dtmt branch: ((pr.head.ref)) +- name: gitea-package + type: gitea-package + source: + access_token: ((gitea_api_key)) + url: http://forgejo:3000 + owner: concourse + type: generic + name: dtmt jobs: - name: clippy @@ -16,6 +29,7 @@ jobs: - get: repo trigger: true - load_var: ref + format: trim file: repo/.git/ref - task: check file: repo/.ci/tasks/clippy.yml @@ -28,35 +42,61 @@ jobs: - get: repo trigger: true - load_var: ref + format: trim file: repo/.git/ref - task: build file: repo/.ci/tasks/build.yml vars: target: msvc - output: artifact ref: ((.:ref)) + pr: ((pr)) gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) + - load_var: version_number + reveal: true + file: artifact/version + - put: package + resource: gitea-package + no_get: true + inputs: + - artifact + params: + version: ((.:version_number)) + fail_fast: true + override: true + globs: + - artifact/dtmt + - artifact/dtmm + - artifact/*.exe - name: build-linux plan: - get: repo trigger: true - load_var: ref + reveal: true file: repo/.git/ref - task: build file: repo/.ci/tasks/build.yml vars: target: linux - output: artifact ref: ((.:ref)) + pr: ((pr)) gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) - - task: upload - file: repo/.ci/tasks/upload.yml - vars: - input: artifact - pr: ((.:pr)) - gitea_api_key: ((gitea_api_key)) - gitea_user: bitsquid_dt - gitea_url: http://forgejo:3000 + - load_var: version_number + reveal: true + file: artifact/version + - put: package + resource: gitea-package + no_get: true + inputs: + - artifact + params: + version: ((.:version_number)) + fail_fast: true + override: true + globs: + - artifact/dtmt + - artifact/dtmm + - artifact/*.exe diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 7029f44..226f137 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -1,25 +1,48 @@ #!/bin/sh -set -eux +set -eu +if [ -n "$OUTPUT" ]; then + OUTPUT="$PWD/$OUTPUT" +else + OUTPUT=$(mktemp -d) +fi + +title() { + printf "\033[1m%s\033[0m\n" "$1" +} + +if [ -n "${PR:-}" ]; then + title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" + ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short HEAD 2>/dev/null || echo 'manual')" +else + ref=$(git describe --tags) +fi + +title "Version is '$ref'" +echo "$ref" > "$OUTPUT/version" + +cd "repo" case "$TARGET" in msvc) cp /src/*.lib ./lib/oodle/ + + title "Build project for target $TARGET" cargo build --color always --locked --release --target x86_64-pc-windows-msvc -Zbuild-std - if [ -d "$OUTPUT" ]; then - install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmt.exe - install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmm.exe - fi + title "Install artifacts" + install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmt.exe + install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmm.exe ;; linux) cp /src/*.a ./lib/oodle/ + + title "Build project for target $TARGET" cargo build --color always --locked --profile release-lto - if [ -d "$OUTPUT" ]; then - install -t "$OUTPUT/" target/release/dtmt - install -t "$OUTPUT/" target/release/dtmm - fi + title "Install artifacts" + install -t "$OUTPUT/" target/release-lto/dtmt + install -t "$OUTPUT/" target/release-lto/dtmm ;; *) echo "Env var 'TARGET' must either be 'msvc' or 'linux'. Got '$TARGET'." >&2 diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml index 3dd4215..1a561f4 100644 --- a/.ci/tasks/build.yml +++ b/.ci/tasks/build.yml @@ -13,7 +13,7 @@ inputs: - name: repo outputs: -- name: artifacts +- name: artifact caches: - path: repo/target @@ -24,12 +24,12 @@ params: TARGET: ((target)) GITEA_API_KEY: ((gitea_api_key)) REF: ((ref)) - OUTPUT: artifacts + PR: ((pr)) + OUTPUT: artifact run: - path: .ci/util/run.sh - dir: repo + path: repo/.ci/util/run.sh args: - - .ci/tasks/build.sh + - repo/.ci/tasks/build.sh - build/((target)) - "Build for the target platform: ((target))" diff --git a/.ci/tasks/upload.sh b/.ci/tasks/upload.sh deleted file mode 100755 index 5e1103e..0000000 --- a/.ci/tasks/upload.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -set -eu - -artifacts="$PWD/artifacts" -repo="$PWD/repo" - -base_url="${GITEA_URL}/api/packages/${GITEA_USER}/generic" - -cd "$repo" - -if [ -n "$PR" ]; then - echo "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" - ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short HEAD 2>/dev/null || echo 'manual')" -else - ref=$(git describe --tags) -fi - -echo "ref: $ref" - -# TODO: If this is a tag, check the tag name to determine which -# binary was affected and only upload that. -for f in dtmt dtmt.exe dtmm dtmm.exe; do - if [ -f "$artifacts/$f" ]; then - url="$base_url/$(basename -s .exe $f)/$ref/$f" - curl -i -X 'PUT' \ - --user "concourse:$GITEA_API_KEY" \ - --upload-file "$artifacts/$f" \ - "$url" - fi -done diff --git a/.ci/tasks/upload.yml b/.ci/tasks/upload.yml deleted file mode 100644 index de22cd2..0000000 --- a/.ci/tasks/upload.yml +++ /dev/null @@ -1,24 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/cappyzawa/concourse-pipeline-jsonschema/master/concourse_jsonschema.json#/definitions/TaskConfig ---- -platform: linux - -image_resource: - name: python-script - type: registry-image - source: - repository: registry.local:5000/python-script - tag: latest - -inputs: -- name: repo -- name: ((input)) - -params: - CI: "true" - GITEA_API_KEY: ((gitea_api_key)) - GITEA_URL: ((gitea_url)) - GITEA_USER: ((user)) - PR: ((pr)) - -run: - path: repo/.ci/tasks/upload.sh diff --git a/Justfile b/Justfile index 09ce0a3..964dc1f 100644 --- a/Justfile +++ b/Justfile @@ -1,3 +1,5 @@ +fly_target := "main" + ci-build: ci-build-msvc ci-build-linux ci-build-msvc: @@ -25,3 +27,26 @@ ci-image-linux: docker build -t dtmt-ci-base-linux -f .ci/image/Dockerfile.linux .ci/image docker tag dtmt-ci-base-linux registry.sclu1034.dev/dtmt-ci-base-linux docker push registry.sclu1034.dev/dtmt-ci-base-linux + +set-base-pipeline: + fly -t ((fly_target)) set-pipeline \ + --pipeline dtmt-prs \ + --config .ci/pipelines/base-pipeline.yml \ + -v gitea_api_key=${GITEA_API_KEY} \ + -v owner=bitsquid_dt \ + -v repo=dtmt + +set-pr-pipeline pr: + curl \ + -H "Authorization: ${GITEA_API_KEY}" \ + -H 'Accept: application/json' \ + 'https://git.sclu1034.dev/api/v1/repos/bitsquid_dt/dtmt/pulls/{{pr}}' \ + | yq -y '.' - > 'pr-{{pr}}.yaml' + fly -t main set-pipeline \ + --pipeline dtmt-pr \ + --config .ci/pipelines/pr.yml \ + -v gitea_api_key=${GITEA_API_KEY} \ + -i n={{pr}} \ + -y branch="$(yq -y '.head.ref' 'pr-{{pr}}.yaml')" \ + -y pr="$(cat 'pr-{{pr}}.yaml')" + From edac52e73f68b07f196908ecc8a26485c8826fb5 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 11:44:44 +0100 Subject: [PATCH 146/335] ci: Use proper resource for commit statuses --- .ci/pipelines/pr.yml | 121 ++++++++++++++++++++++++++++++++++++++----- .ci/tasks/build.yml | 7 +-- .ci/tasks/clippy.yml | 7 +-- 3 files changed, 111 insertions(+), 24 deletions(-) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index d818490..267db09 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -8,12 +8,19 @@ resource_types: source: repository: registry.local:5000/gitea-package +- name: gitea-status + type: registry-image + source: + repository: registry.local:5000/gitea-status + + resources: - name: repo type: git source: uri: http://forgejo:3000/bitsquid_dt/dtmt branch: ((pr.head.ref)) + - name: gitea-package type: gitea-package source: @@ -23,38 +30,109 @@ resources: type: generic name: dtmt +- name: pr-status-lint-clippy + type: gitea-status + source: + access_token: ((gitea_api_key)) + url: http://forgejo:3000 + owner: bitsquid_dt + repo: dtmt + sha: ((pr.head.sha)) + context: lint/clippy + description: Checking for common mistakes and opportunities for code improvement + +- name: pr-status-build-msvc + type: gitea-status + source: + access_token: ((gitea_api_key)) + url: http://forgejo:3000 + owner: bitsquid_dt + repo: dtmt + sha: ((pr.head.sha)) + context: build/msvc + description: "Build for the target platform: msvc" + +- name: pr-status-build-linux + type: gitea-status + source: + access_token: ((gitea_api_key)) + url: http://forgejo:3000 + owner: bitsquid_dt + repo: dtmt + sha: ((pr.head.sha)) + context: build/linux + description: "Build for the target platform: linux" + + jobs: - name: clippy + on_success: + put: state-success + resource: pr-status-lint-clippy + no_get: true + params: + state: success + + on_failure: + put: state-success + resource: pr-status-lint-clippy + no_get: true + params: + state: failure + plan: + - put: state-pending + resource: pr-status-lint-clippy + no_get: true + params: + state: pending + - get: repo trigger: true - - load_var: ref - format: trim - file: repo/.git/ref + - task: check file: repo/.ci/tasks/clippy.yml vars: - ref: ((.:ref)) gitea_api_key: ((gitea_api_key)) + - name: build-msvc + on_success: + put: state-success + resource: pr-status-build-msvc + no_get: true + params: + state: success + + on_failure: + put: state-success + resource: pr-status-build-msvc + no_get: true + params: + state: failure + plan: + - put: state-pending + resource: pr-status-build-msvc + no_get: true + params: + state: pending + - get: repo trigger: true - - load_var: ref - format: trim - file: repo/.git/ref + - task: build file: repo/.ci/tasks/build.yml vars: target: msvc - ref: ((.:ref)) pr: ((pr)) gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) + - load_var: version_number reveal: true file: artifact/version + - put: package resource: gitea-package no_get: true @@ -70,23 +148,42 @@ jobs: - artifact/*.exe - name: build-linux + on_success: + put: state-success + resource: pr-status-build-linux + no_get: true + params: + state: success + + on_failure: + put: state-success + resource: pr-status-build-linux + no_get: true + params: + state: failure + plan: + - put: state-pending + resource: pr-status-build-linux + no_get: true + params: + state: pending + - get: repo trigger: true - - load_var: ref - reveal: true - file: repo/.git/ref + - task: build file: repo/.ci/tasks/build.yml vars: target: linux - ref: ((.:ref)) pr: ((pr)) gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) + - load_var: version_number reveal: true file: artifact/version + - put: package resource: gitea-package no_get: true diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml index 1a561f4..dce44d0 100644 --- a/.ci/tasks/build.yml +++ b/.ci/tasks/build.yml @@ -23,13 +23,8 @@ params: CI: "true" TARGET: ((target)) GITEA_API_KEY: ((gitea_api_key)) - REF: ((ref)) PR: ((pr)) OUTPUT: artifact run: - path: repo/.ci/util/run.sh - args: - - repo/.ci/tasks/build.sh - - build/((target)) - - "Build for the target platform: ((target))" + path: repo/.ci/tasks/build.sh diff --git a/.ci/tasks/clippy.yml b/.ci/tasks/clippy.yml index cea0e76..483cb30 100644 --- a/.ci/tasks/clippy.yml +++ b/.ci/tasks/clippy.yml @@ -19,13 +19,8 @@ caches: params: CI: "true" GITEA_API_KEY: ((gitea_api_key)) - REF: ((ref)) run: - path: .ci/util/run.sh + path: .ci/tasks/clippy.sh dir: repo - args: - - .ci/tasks/clippy.sh - - lint/clippy - - "Checking for common mistakes and opportunities for code improvement" From 96f3625b7a58d98c0feb05eae11e1a27e0526249 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 13:16:03 +0100 Subject: [PATCH 147/335] Fix submodule refs --- .gitmodules | 1 + lib/color-eyre | 2 +- lib/luajit2-sys | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 9398bb8..a03eebf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,6 +7,7 @@ [submodule "lib/color-eyre"] path = lib/color-eyre url = https://github.com/sclu1034/color-eyre.git + branch = "fork" [submodule "lib/ansi-parser"] path = lib/ansi-parser url = https://gitlab.com/lschwiderski/ansi-parser.git diff --git a/lib/color-eyre b/lib/color-eyre index 55f8c6b..0fa05eb 160000 --- a/lib/color-eyre +++ b/lib/color-eyre @@ -1 +1 @@ -Subproject commit 55f8c6b7481d462e50ee4a03a43253d80d648ae2 +Subproject commit 0fa05eba9954be223b06468c8760b97e660f9941 diff --git a/lib/luajit2-sys b/lib/luajit2-sys index 24da35e..1912016 160000 --- a/lib/luajit2-sys +++ b/lib/luajit2-sys @@ -1 +1 @@ -Subproject commit 24da35e631099e914d6fc1bcc863228c48e540ec +Subproject commit 19120166f9fc7838b98c71fc348791abc820e323 From d956e75146c45e22aae10e11e7e99c73feaf7008 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 13:19:00 +0100 Subject: [PATCH 148/335] Fix step names --- .ci/pipelines/pr.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index 267db09..146f987 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -74,7 +74,7 @@ jobs: state: success on_failure: - put: state-success + put: state-failure resource: pr-status-lint-clippy no_get: true params: @@ -105,7 +105,7 @@ jobs: state: success on_failure: - put: state-success + put: state-failure resource: pr-status-build-msvc no_get: true params: @@ -156,7 +156,7 @@ jobs: state: success on_failure: - put: state-success + put: state-failure resource: pr-status-build-linux no_get: true params: From 4c96bcf5ba25d8b6d73e128a6358d7551b478ef2 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 13:29:12 +0100 Subject: [PATCH 149/335] Improve CI log output --- .ci/tasks/build.sh | 25 ++++++++++++++----------- .ci/tasks/clippy.sh | 10 +++++++++- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 226f137..4c70a10 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -12,39 +12,42 @@ title() { printf "\033[1m%s\033[0m\n" "$1" } +cd "repo" + if [ -n "${PR:-}" ]; then title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" - ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short HEAD 2>/dev/null || echo 'manual')" + ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short $(cat .git/ref || echo "HEAD")) 2>/dev/null || echo 'manual')" else ref=$(git describe --tags) fi -title "Version is '$ref'" +title "Version: '$ref'" echo "$ref" > "$OUTPUT/version" -cd "repo" case "$TARGET" in msvc) cp /src/*.lib ./lib/oodle/ - title "Build project for target $TARGET" + title "Building project for target $TARGET" cargo build --color always --locked --release --target x86_64-pc-windows-msvc -Zbuild-std title "Install artifacts" - install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmt.exe - install -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmm.exe + install -v -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmt.exe + install -v -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmm.exe ;; linux) cp /src/*.a ./lib/oodle/ - title "Build project for target $TARGET" + title "Building project for target $TARGET" cargo build --color always --locked --profile release-lto - title "Install artifacts" - install -t "$OUTPUT/" target/release-lto/dtmt - install -t "$OUTPUT/" target/release-lto/dtmm + title "Installing artifacts" + install -v -t "$OUTPUT/" target/release-lto/dtmt + install -v -t "$OUTPUT/" target/release-lto/dtmm ;; *) - echo "Env var 'TARGET' must either be 'msvc' or 'linux'. Got '$TARGET'." >&2 + echo -e "\033[31;1mEnv var 'TARGET' must either be 'msvc' or 'linux'. Got '$TARGET'.\033[0m" >&2 exit 1 esac + +title "Done" diff --git a/.ci/tasks/clippy.sh b/.ci/tasks/clippy.sh index a7b6e11..33901a9 100755 --- a/.ci/tasks/clippy.sh +++ b/.ci/tasks/clippy.sh @@ -1,7 +1,15 @@ #!/bin/sh -set -eux +set -eu +title() { + printf "\033[1m%s\033[0m\n" "$1" +} + +title "Install clippy" rustup component add clippy +title "Run clippy" cargo clippy --color always --no-deps + +title "Done" From b86ea337d1d24e0fe258780fcb9b70828b7c6ede Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 15:23:02 +0100 Subject: [PATCH 150/335] Fix build script --- .ci/image/Dockerfile.linux | 1 + .ci/tasks/build.sh | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.ci/image/Dockerfile.linux b/.ci/image/Dockerfile.linux index 4927810..df10059 100644 --- a/.ci/image/Dockerfile.linux +++ b/.ci/image/Dockerfile.linux @@ -5,6 +5,7 @@ RUN set -eux; \ apt-get install --no-install-recommends -y \ build-essential \ curl \ + git \ gpg \ jq \ libatk1.0-dev \ diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 4c70a10..9d294b8 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -eu @@ -16,7 +16,7 @@ cd "repo" if [ -n "${PR:-}" ]; then title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" - ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short $(cat .git/ref || echo "HEAD")) 2>/dev/null || echo 'manual')" + ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$(cat .git/ref || echo "HEAD")" 2>/dev/null || echo 'manual')" else ref=$(git describe --tags) fi From 92546f6f5edddc5909d8ad938278aab905f41350 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 15:23:40 +0100 Subject: [PATCH 151/335] Fix pipeline status Since the PR pipeline is not re-applied on new commits, the `pr.head.ref` variable doesn't get updated, and isn't suitable for the status reporting. --- .ci/pipelines/base-pipeline.yml | 2 +- .ci/pipelines/pr.yml | 39 +++++++++++++++++++++++---------- Justfile | 4 ++-- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/.ci/pipelines/base-pipeline.yml b/.ci/pipelines/base-pipeline.yml index ae9e323..f231fb7 100644 --- a/.ci/pipelines/base-pipeline.yml +++ b/.ci/pipelines/base-pipeline.yml @@ -40,4 +40,4 @@ jobs: pr: ((.:pr)) gitea_api_key: ((gitea_api_key)) instance_vars: - n: ((.:pr.number)) + number: ((.:pr.number)) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index 146f987..a9060f9 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -37,7 +37,6 @@ resources: url: http://forgejo:3000 owner: bitsquid_dt repo: dtmt - sha: ((pr.head.sha)) context: lint/clippy description: Checking for common mistakes and opportunities for code improvement @@ -48,7 +47,6 @@ resources: url: http://forgejo:3000 owner: bitsquid_dt repo: dtmt - sha: ((pr.head.sha)) context: build/msvc description: "Build for the target platform: msvc" @@ -59,7 +57,6 @@ resources: url: http://forgejo:3000 owner: bitsquid_dt repo: dtmt - sha: ((pr.head.sha)) context: build/linux description: "Build for the target platform: linux" @@ -72,6 +69,7 @@ jobs: no_get: true params: state: success + sha: ((.:git_sha)) on_failure: put: state-failure @@ -79,16 +77,21 @@ jobs: no_get: true params: state: failure + sha: ((.:git_sha)) plan: + - get: repo + trigger: true + + - load_var: git_sha + file: repo/.git/ref + - put: state-pending resource: pr-status-lint-clippy no_get: true params: state: pending - - - get: repo - trigger: true + sha: ((.:git_sha)) - task: check file: repo/.ci/tasks/clippy.yml @@ -103,6 +106,7 @@ jobs: no_get: true params: state: success + sha: ((.:git_sha)) on_failure: put: state-failure @@ -110,16 +114,21 @@ jobs: no_get: true params: state: failure + sha: ((.:git_sha)) plan: + - get: repo + trigger: true + + - load_var: git_sha + file: repo/.git/ref + - put: state-pending resource: pr-status-build-msvc no_get: true params: state: pending - - - get: repo - trigger: true + sha: ((.:git_sha)) - task: build file: repo/.ci/tasks/build.yml @@ -154,6 +163,7 @@ jobs: no_get: true params: state: success + sha: ((.:git_sha)) on_failure: put: state-failure @@ -161,16 +171,21 @@ jobs: no_get: true params: state: failure + sha: ((.:git_sha)) plan: + - get: repo + trigger: true + + - load_var: git_sha + file: repo/.git/ref + - put: state-pending resource: pr-status-build-linux no_get: true params: state: pending - - - get: repo - trigger: true + sha: ((.:git_sha)) - task: build file: repo/.ci/tasks/build.yml diff --git a/Justfile b/Justfile index 964dc1f..70af61a 100644 --- a/Justfile +++ b/Justfile @@ -29,7 +29,7 @@ ci-image-linux: docker push registry.sclu1034.dev/dtmt-ci-base-linux set-base-pipeline: - fly -t ((fly_target)) set-pipeline \ + fly -t {{fly_target}} set-pipeline \ --pipeline dtmt-prs \ --config .ci/pipelines/base-pipeline.yml \ -v gitea_api_key=${GITEA_API_KEY} \ @@ -46,7 +46,7 @@ set-pr-pipeline pr: --pipeline dtmt-pr \ --config .ci/pipelines/pr.yml \ -v gitea_api_key=${GITEA_API_KEY} \ - -i n={{pr}} \ + -i number={{pr}} \ -y branch="$(yq -y '.head.ref' 'pr-{{pr}}.yaml')" \ -y pr="$(cat 'pr-{{pr}}.yaml')" From 0cb86efe910fedd6887f58d534ad0af6f9f80d84 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 19:40:09 +0100 Subject: [PATCH 152/335] Use correct owner for packages --- .ci/pipelines/pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index a9060f9..a59e6f9 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -26,7 +26,7 @@ resources: source: access_token: ((gitea_api_key)) url: http://forgejo:3000 - owner: concourse + owner: bitsquid_dt type: generic name: dtmt From fa3f517aed6e426144581cac5f5c40e3e7106fb9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 23 Nov 2023 19:40:27 +0100 Subject: [PATCH 153/335] Generate checksums for artifacts Closes #138. --- .ci/pipelines/pr.yml | 3 +++ .ci/tasks/build.sh | 13 +++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index a59e6f9..bca0ebb 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -155,6 +155,7 @@ jobs: - artifact/dtmt - artifact/dtmm - artifact/*.exe + - artifact/*.sha256 - name: build-linux on_success: @@ -212,3 +213,5 @@ jobs: - artifact/dtmt - artifact/dtmm - artifact/*.exe + - artifact/*.sha256 + diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 9d294b8..5e60d7f 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -12,6 +12,11 @@ title() { printf "\033[1m%s\033[0m\n" "$1" } +install_artifact() { + install -v -t "$OUTPUT/" "$1" + sha256sum "$1" | cut -d' ' -f1 > "$OUTPUT/$(basename "$1").sha256" +} + cd "repo" if [ -n "${PR:-}" ]; then @@ -32,8 +37,8 @@ case "$TARGET" in cargo build --color always --locked --release --target x86_64-pc-windows-msvc -Zbuild-std title "Install artifacts" - install -v -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmt.exe - install -v -t "$OUTPUT/" target/x86_64-pc-windows-msvc/release/dtmm.exe + install_artifact target/x86_64-pc-windows-msvc/release/dtmt.exe + install_artifact target/x86_64-pc-windows-msvc/release/dtmm.exe ;; linux) cp /src/*.a ./lib/oodle/ @@ -42,8 +47,8 @@ case "$TARGET" in cargo build --color always --locked --profile release-lto title "Installing artifacts" - install -v -t "$OUTPUT/" target/release-lto/dtmt - install -v -t "$OUTPUT/" target/release-lto/dtmm + install_artifact target/release-lto/dtmt + install_artifact target/release-lto/dtmm ;; *) echo -e "\033[31;1mEnv var 'TARGET' must either be 'msvc' or 'linux'. Got '$TARGET'.\033[0m" >&2 From 6f0fdc50862f9a281536a3b0ac7d3ed2c535ef11 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 24 Nov 2023 10:13:59 +0100 Subject: [PATCH 154/335] dtmm: Document error dialog --- crates/dtmm/src/ui/window/dialog.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/ui/window/dialog.rs b/crates/dtmm/src/ui/window/dialog.rs index 06a6c02..11df4d5 100644 --- a/crates/dtmm/src/ui/window/dialog.rs +++ b/crates/dtmm/src/ui/window/dialog.rs @@ -7,11 +7,14 @@ use crate::ui::widget::button::Button; const WINDOW_SIZE: (f64, f64) = (600., 250.); +/// Show an error dialog. +/// The title and message are extracted from the error chain in the given `Report`. pub fn error(err: Report, _parent: WindowHandle) -> WindowDesc { let (title, msg) = { let count = err.chain().count(); if count == 1 { + // If there is only one error, that's all we can show. ( String::from("An error occurred!"), err.root_cause().to_string(), @@ -20,13 +23,20 @@ pub fn error(err: Report, _parent: WindowHandle) -> WindowDesc { let first = err.chain().next().unwrap(); let root = err.root_cause(); + // If there is more than one error in the chain we want to show + // - The first one: This will describe the overall operation that failed + // - The root cause: The actual thing that failed (e.g. 'No such file or directory') + // - The one before the root cause: With diligent `wrap_err` usage, this will provide + // context to the root cause (e.g. the file name we failed to access) + // + // If there are only two errors, the first one is also the context to the root cause. if count > 2 { // The second to last one, the context to the root cause let context = err.chain().nth(count - 2).unwrap(); (format!("{first}!"), format!("{}: {}", context, root)) } else { - (format!("{first}!"), root.to_string()) + ("An error occurred!".to_string(), format!("{}: {}", first, root)) } } }; From 6ab514c4280f5244829705e23c45f75a724d22d9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 24 Nov 2023 11:07:40 +0100 Subject: [PATCH 155/335] dtmm: Filter file dialog error log Druid's implementation makes it so that cancelling the file dialog logs an error tracing event. Closes #133. --- crates/dtmm/src/util/log.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/util/log.rs b/crates/dtmm/src/util/log.rs index 09fc9a6..fa4a643 100644 --- a/crates/dtmm/src/util/log.rs +++ b/crates/dtmm/src/util/log.rs @@ -56,7 +56,7 @@ impl std::io::Write for ChannelWriter { } pub fn create_tracing_subscriber(tx: UnboundedSender>, level: Option) { - let env_layer = if let Some(level) = level { + let mut env_layer = if let Some(level) = level { EnvFilter::from(level) } else if cfg!(debug_assertions) { EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")) @@ -64,6 +64,18 @@ pub fn create_tracing_subscriber(tx: UnboundedSender>, level: Option Date: Wed, 8 Nov 2023 15:04:32 +0100 Subject: [PATCH 156/335] Add mod config option for loose files Just the field in the config file, for now. --- crates/dtmm/src/state/data.rs | 2 ++ crates/dtmt/src/cmd/migrate.rs | 1 + lib/dtmt-shared/src/lib.rs | 13 +++++++++++++ 3 files changed, 16 insertions(+) diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 55774aa..c43e973 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -109,6 +109,7 @@ pub(crate) struct ModInfo { #[data(ignore)] pub resources: ModResourceInfo, pub depends: Vector, + pub bundle: bool, #[data(ignore)] pub nexus: Option, } @@ -129,6 +130,7 @@ impl ModInfo { version: cfg.version, enabled: false, packages, + bundle: cfg.bundle, image, categories: cfg.categories.into_iter().collect(), resources: ModResourceInfo { diff --git a/crates/dtmt/src/cmd/migrate.rs b/crates/dtmt/src/cmd/migrate.rs index 1a4d605..8ecd648 100644 --- a/crates/dtmt/src/cmd/migrate.rs +++ b/crates/dtmt/src/cmd/migrate.rs @@ -350,6 +350,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> localization: mod_file.localization, }, depends: vec![ModDependency::ID(String::from("DMF"))], + bundle: true, }; tracing::debug!(?dtmt_cfg); diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index 3907928..a162d3b 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -30,6 +30,17 @@ pub enum ModDependency { Config { id: String, order: ModOrder }, } +// A bit dumb, but serde doesn't support literal values with the +// `default` attribute, only paths. +fn default_true() -> bool { + true +} + +// Similarly dumb, as the `skip_serializing_if` attribute needs a function +fn is_true(val: &bool) -> bool { + *val +} + #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct ModConfig { #[serde(skip)] @@ -51,6 +62,8 @@ pub struct ModConfig { pub resources: ModConfigResources, #[serde(default)] pub depends: Vec, + #[serde(default = "default_true", skip_serializing_if = "is_true")] + pub bundle: bool, } pub const STEAMAPP_ID: u32 = 1361210; From 871a54020ec1914e952ffadfdc9f3e72f426271f Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 8 Nov 2023 15:06:53 +0100 Subject: [PATCH 157/335] Apply formatting --- crates/dtmm/src/controller/game.rs | 8 ++++---- crates/dtmt/src/cmd/dictionary.rs | 5 ++++- lib/nexusmods/src/lib.rs | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index a5f6fa8..5520c4a 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -583,12 +583,12 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { }, async { let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); - match read_sjson_file::<_, DeploymentData>(path) - .await - { + match read_sjson_file::<_, DeploymentData>(path).await { Ok(data) => Ok(Some(data)), Err(err) => { - if let Some(err) = err.downcast_ref::() && err.kind() == ErrorKind::NotFound { + if let Some(err) = err.downcast_ref::() + && err.kind() == ErrorKind::NotFound + { Ok(None) } else { Err(err).wrap_err("Failed to read deployment data") diff --git a/crates/dtmt/src/cmd/dictionary.rs b/crates/dtmt/src/cmd/dictionary.rs index 0400519..62a5295 100644 --- a/crates/dtmt/src/cmd/dictionary.rs +++ b/crates/dtmt/src/cmd/dictionary.rs @@ -145,7 +145,10 @@ pub(crate) async fn run(mut ctx: sdk::Context, matches: &ArgMatches) -> Result<( .get_one::("group") .expect("required argument not found"); - let r: BufReader> = if let Some(name) = path.file_name() && name == "-" { + let r: BufReader> = if let Some(name) = + path.file_name() + && name == "-" + { let f = tokio::io::stdin(); BufReader::new(Box::new(f)) } else { diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index a0700ae..3dbbecd 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -215,7 +215,7 @@ impl Api { }; let user_id = query.get("user_id").and_then(|id| id.parse().ok()); - let Some(user_id) = user_id else { + let Some(user_id) = user_id else { return Err(Error::InvalidNXM("Missing 'user_id'", nxm)); }; From e633a571b5ebc5bef2db094d0424af890246ab39 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 9 Nov 2023 19:48:05 +0100 Subject: [PATCH 158/335] Apply clippy lints --- lib/nexusmods/src/lib.rs | 2 +- lib/sdk/src/bundle/mod.rs | 11 ++++------- lib/sdk/src/filetype/lua.rs | 4 ++-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index 3dbbecd..c305db0 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -154,7 +154,7 @@ impl Api { self.mods_download_link(nxm.mod_id, nxm.file_id, nxm.key, nxm.expires) )?; - let Some(download_url) = download_info.get(0).map(|i| i.uri.clone()) else { + let Some(download_url) = download_info.first().map(|i| i.uri.clone()) else { return Err(Error::InvalidNXM("no download link", url)); }; diff --git a/lib/sdk/src/bundle/mod.rs b/lib/sdk/src/bundle/mod.rs index 96a2ec2..813df06 100644 --- a/lib/sdk/src/bundle/mod.rs +++ b/lib/sdk/src/bundle/mod.rs @@ -227,13 +227,10 @@ impl Bundle { let _enter = span.enter(); tracing::trace!(num_files = self.files.len()); - self.files - .iter() - .fold(Ok::, Report>(Vec::new()), |data, file| { - let mut data = data?; - data.append(&mut file.to_binary()?); - Ok(data) - })? + self.files.iter().try_fold(Vec::new(), |mut data, file| { + data.append(&mut file.to_binary()?); + Ok::<_, Report>(data) + })? }; // Ceiling division (or division toward infinity) to calculate diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index 2458dec..bfe6de7 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -53,8 +53,8 @@ where let mut buf = vec![0u8; length]; r.read_exact(&mut buf)?; - let mut s = String::from_utf8(buf) - .wrap_err_with(|| format!("Invalid byte sequence for LuaJIT bytecode name"))?; + let mut s = + String::from_utf8(buf).wrap_err("Invalid byte sequence for LuaJIT bytecode name")?; // Remove the leading `@` s.remove(0); s From bd6c2366551c90bdb7c6add12e399fe45f960dea Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 9 Nov 2023 20:10:54 +0100 Subject: [PATCH 159/335] Implement deploying non-bundled mods Closes #113. --- Cargo.lock | 50 +- Cargo.toml | 5 +- crates/dtmm/Cargo.toml | 4 +- crates/dtmm/src/controller/app.rs | 170 +----- crates/dtmm/src/controller/deploy.rs | 875 +++++++++++++++++++++++++++ crates/dtmm/src/controller/game.rs | 622 +------------------ crates/dtmm/src/controller/import.rs | 475 +++++++++++++++ crates/dtmm/src/controller/mod.rs | 2 + crates/dtmm/src/controller/worker.rs | 2 + crates/dtmm/src/main.rs | 1 + crates/dtmm/src/state/data.rs | 4 +- crates/dtmm/src/ui/window/main.rs | 2 +- crates/dtmt/src/cmd/migrate.rs | 2 +- lib/dtmt-shared/src/lib.rs | 2 +- 14 files changed, 1396 insertions(+), 820 deletions(-) create mode 100644 crates/dtmm/src/controller/deploy.rs create mode 100644 crates/dtmm/src/controller/import.rs diff --git a/Cargo.lock b/Cargo.lock index a7885f2..011fa6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,12 +39,10 @@ dependencies = [ [[package]] name = "ansi-parser" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcb2392079bf27198570d6af79ecbd9ec7d8f16d3ec6b60933922fdb66287127" +version = "0.9.0" dependencies = [ "heapless", - "nom 4.2.3", + "nom", ] [[package]] @@ -381,7 +379,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom 7.1.3", + "nom", ] [[package]] @@ -896,6 +894,7 @@ name = "dtmm" version = "0.1.0" dependencies = [ "ansi-parser", + "async-recursion", "bitflags 1.3.2", "clap", "color-eyre", @@ -906,6 +905,7 @@ dependencies = [ "dtmt-shared", "futures", "lazy_static", + "luajit2-sys", "nexusmods", "oodle", "path-slash", @@ -1390,7 +1390,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -1614,12 +1614,12 @@ checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heapless" -version = "0.5.6" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74911a68a1658cfcfb61bc0ccfbd536e3b6e906f8c2f7883ee50157e3e2184f1" +checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422" dependencies = [ "as-slice", - "generic-array 0.13.3", + "generic-array 0.14.7", "hash32", "stable_deref_trait", ] @@ -1747,7 +1747,7 @@ dependencies = [ "serde", "sized-chunks", "typenum", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -2175,16 +2175,6 @@ dependencies = [ "memoffset 0.6.5", ] -[[package]] -name = "nom" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -dependencies = [ - "memchr", - "version_check 0.1.5", -] - [[package]] name = "nom" version = "7.1.3" @@ -2203,7 +2193,7 @@ checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" dependencies = [ "bytecount", "memchr", - "nom 7.1.3", + "nom", ] [[package]] @@ -2673,7 +2663,7 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -2684,7 +2674,7 @@ checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", - "version_check 0.9.4", + "version_check", ] [[package]] @@ -3102,7 +3092,7 @@ dependencies = [ name = "serde_sjson" version = "1.0.0" dependencies = [ - "nom 7.1.3", + "nom", "nom_locate", "serde", ] @@ -3832,7 +3822,7 @@ version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" dependencies = [ - "version_check 0.9.4", + "version_check", ] [[package]] @@ -3966,12 +3956,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" -[[package]] -name = "version_check" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" - [[package]] name = "version_check" version = "0.9.4" @@ -4384,7 +4368,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[patch.unused]] -name = "ansi-parser" -version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 123b2ef..451cd09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,10 +7,7 @@ members = [ "lib/oodle", "lib/sdk", "lib/serde_sjson", -] -exclude = [ - "lib/color-eyre", - "lib/ansi-parser", + "lib/luajit2-sys", ] [patch.crates-io] diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 86444be..1842a62 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -31,5 +31,7 @@ lazy_static = "1.4.0" colors-transform = "0.2.11" usvg = "0.25.0" druid-widget-nursery = "0.1" -ansi-parser = "0.8.0" +ansi-parser = "0.9.0" string_template = "0.2.1" +luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } +async-recursion = "1.0.5" diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index 5c5d980..cb6e2f0 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -1,18 +1,17 @@ use std::collections::HashMap; -use std::io::{Cursor, ErrorKind, Read}; +use std::io::ErrorKind; use std::path::{Path, PathBuf}; use std::sync::Arc; use color_eyre::eyre::{self, Context}; use color_eyre::{Help, Report, Result}; use druid::im::Vector; -use druid::{FileInfo, ImageBuf}; +use druid::ImageBuf; use dtmt_shared::ModConfig; use nexusmods::Api as NexusApi; use tokio::fs::{self, DirEntry, File}; use tokio_stream::wrappers::ReadDirStream; use tokio_stream::StreamExt; -use zip::ZipArchive; use crate::state::{ActionState, InitialLoadResult, ModInfo, ModOrder, NexusInfo, PackageInfo}; use crate::util; @@ -20,161 +19,6 @@ use crate::util::config::{ConfigSerialize, LoadOrderEntry}; use super::read_sjson_file; -#[tracing::instrument(skip(state))] -pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result { - let data = fs::read(&info.path) - .await - .wrap_err_with(|| format!("Failed to read file {}", info.path.display()))?; - let data = Cursor::new(data); - - let nexus = if let Some((_, id, _, _)) = info - .path - .file_name() - .and_then(|s| s.to_str()) - .and_then(NexusApi::parse_file_name) - { - if !state.nexus_api_key.is_empty() { - let api = NexusApi::new(state.nexus_api_key.to_string())?; - let mod_info = api - .mods_id(id) - .await - .wrap_err_with(|| format!("Failed to query mod {} from Nexus", id))?; - Some(NexusInfo::from(mod_info)) - } else { - None - } - } else { - None - }; - - let mut archive = ZipArchive::new(data).wrap_err("Failed to open ZIP archive")?; - - if tracing::enabled!(tracing::Level::DEBUG) { - let names = archive.file_names().fold(String::new(), |mut s, name| { - s.push('\n'); - s.push_str(name); - s - }); - tracing::debug!("Archive contents:{}", names); - } - - let dir_name = { - let f = archive.by_index(0).wrap_err("Archive is empty")?; - - if !f.is_dir() { - let err = eyre::eyre!("archive does not have a top-level directory"); - return Err(err).with_suggestion(|| "Use 'dtmt build' to create the mod archive."); - } - - let name = f.name(); - // The directory name is returned with a trailing slash, which we don't want - name[..(name.len().saturating_sub(1))].to_string() - }; - - tracing::info!("Importing mod {}", dir_name); - - let names: Vec<_> = archive.file_names().map(|s| s.to_string()).collect(); - - let mod_cfg: ModConfig = { - let name = names - .iter() - .find(|name| name.ends_with("dtmt.cfg")) - .ok_or_else(|| eyre::eyre!("archive does not contain mod config"))?; - - let mut f = archive - .by_name(name) - .wrap_err("Failed to read mod config from archive")?; - - let mut buf = Vec::with_capacity(f.size() as usize); - f.read_to_end(&mut buf) - .wrap_err("Failed to read mod config from archive")?; - - let data = String::from_utf8(buf).wrap_err("Mod config is not valid UTF-8")?; - - serde_sjson::from_str(&data).wrap_err("Failed to deserialize mod config")? - }; - - tracing::debug!(?mod_cfg); - - let files: HashMap> = { - let name = names - .iter() - .find(|name| name.ends_with("files.sjson")) - .ok_or_else(|| eyre::eyre!("archive does not contain file index"))?; - - let mut f = archive - .by_name(name) - .wrap_err("Failed to read file index from archive")?; - let mut buf = Vec::with_capacity(f.size() as usize); - f.read_to_end(&mut buf) - .wrap_err("Failed to read file index from archive")?; - - let data = String::from_utf8(buf).wrap_err("File index is not valid UTF-8")?; - - serde_sjson::from_str(&data).wrap_err("Failed to deserialize file index")? - }; - - tracing::trace!(?files); - - let image = if let Some(path) = &mod_cfg.image { - let name = names - .iter() - .find(|name| name.ends_with(&path.display().to_string())) - .ok_or_else(|| eyre::eyre!("archive does not contain configured image file"))?; - - let mut f = archive - .by_name(name) - .wrap_err("Failed to read image file from archive")?; - let mut buf = Vec::with_capacity(f.size() as usize); - f.read_to_end(&mut buf) - .wrap_err("Failed to read file index from archive")?; - - // Druid somehow doesn't return an error compatible with eyre, here. - // So we have to wrap through `Display` manually. - let img = match ImageBuf::from_data(&buf) { - Ok(img) => img, - Err(err) => { - let err = Report::msg(err.to_string()).wrap_err("Invalid image data"); - return Err(err).with_suggestion(|| { - "Supported formats are: PNG, JPEG, Bitmap and WebP".to_string() - }); - } - }; - - Some(img) - } else { - None - }; - - let mod_dir = state.mod_dir; - - tracing::trace!("Creating mods directory {}", mod_dir.display()); - fs::create_dir_all(Arc::as_ref(&mod_dir)) - .await - .wrap_err_with(|| format!("Failed to create data directory {}", mod_dir.display()))?; - - tracing::trace!("Extracting mod archive to {}", mod_dir.display()); - archive - .extract(Arc::as_ref(&mod_dir)) - .wrap_err_with(|| format!("Failed to extract archive to {}", mod_dir.display()))?; - - if let Some(nexus) = &nexus { - let data = serde_sjson::to_string(nexus).wrap_err("Failed to serialize Nexus info")?; - let path = mod_dir.join(&mod_cfg.id).join("nexus.sjson"); - fs::write(&path, data.as_bytes()) - .await - .wrap_err_with(|| format!("Failed to write Nexus info to '{}'", path.display()))?; - } - - let packages = files - .into_iter() - .map(|(name, files)| Arc::new(PackageInfo::new(name, files.into_iter().collect()))) - .collect(); - let info = ModInfo::new(mod_cfg, packages, image, nexus); - - Ok(info) -} - #[tracing::instrument(skip(state))] pub(crate) async fn delete_mod(state: ActionState, info: &ModInfo) -> Result<()> { let mod_dir = state.mod_dir.join(&info.id); @@ -229,9 +73,13 @@ async fn read_mod_dir_entry(res: Result) -> Result { Err(err) => return Err(err), }; - let files: HashMap> = read_sjson_file(&index_path) - .await - .wrap_err_with(|| format!("Failed to read file index '{}'", index_path.display()))?; + let files: HashMap> = if cfg.bundled { + read_sjson_file(&index_path) + .await + .wrap_err_with(|| format!("Failed to read file index '{}'", index_path.display()))? + } else { + Default::default() + }; let image = if let Some(path) = &cfg.image { let path = entry.path().join(path); diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs new file mode 100644 index 0000000..6a804e7 --- /dev/null +++ b/crates/dtmm/src/controller/deploy.rs @@ -0,0 +1,875 @@ +use std::collections::HashMap; +use std::io::{Cursor, ErrorKind}; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::sync::Arc; + +use color_eyre::eyre::Context; +use color_eyre::{eyre, Help, Report, Result}; +use futures::StreamExt; +use futures::{stream, TryStreamExt}; +use path_slash::PathBufExt; +use sdk::filetype::lua; +use sdk::filetype::package::Package; +use sdk::murmur::Murmur64; +use sdk::{ + Bundle, BundleDatabase, BundleFile, BundleFileType, BundleFileVariant, FromBinary, ToBinary, +}; +use serde::{Deserialize, Serialize}; +use string_template::Template; +use time::OffsetDateTime; +use tokio::fs::{self, DirEntry}; +use tokio::io::AsyncWriteExt; +use tracing::Instrument; + +use super::read_sjson_file; +use crate::controller::app::check_mod_order; +use crate::state::{ActionState, PackageInfo}; + +pub const MOD_BUNDLE_NAME: &str = "packages/mods"; +pub const BOOT_BUNDLE_NAME: &str = "packages/boot"; +pub const DML_BUNDLE_NAME: &str = "packages/dml"; +pub const BUNDLE_DATABASE_NAME: &str = "bundle_database.data"; +pub const MOD_BOOT_SCRIPT: &str = "scripts/mod_main"; +pub const MOD_DATA_SCRIPT: &str = "scripts/mods/mod_data"; +pub const SETTINGS_FILE_PATH: &str = "application_settings/settings_common.ini"; +pub const DEPLOYMENT_DATA_PATH: &str = "dtmm-deployment.sjson"; + +#[derive(Debug, Serialize, Deserialize)] +pub struct DeploymentData { + pub bundles: Vec, + pub mod_folders: Vec, + #[serde(with = "time::serde::iso8601")] + pub timestamp: OffsetDateTime, +} + +#[tracing::instrument] +async fn read_file_with_backup

(path: P) -> Result> +where + P: AsRef + std::fmt::Debug, +{ + let path = path.as_ref(); + let backup_path = { + let mut p = PathBuf::from(path); + let ext = if let Some(ext) = p.extension() { + ext.to_string_lossy().to_string() + ".bak" + } else { + String::from("bak") + }; + p.set_extension(ext); + p + }; + + let file_name = path + .file_name() + .map(|s| s.to_string_lossy().to_string()) + .unwrap_or_else(|| String::from("file")); + + let bin = match fs::read(&backup_path).await { + Ok(bin) => bin, + Err(err) if err.kind() == ErrorKind::NotFound => { + // TODO: This doesn't need to be awaited here, yet. + // I only need to make sure it has finished before writing the changed bundle. + tracing::debug!( + "Backup does not exist. Backing up original {} to '{}'", + file_name, + backup_path.display() + ); + fs::copy(path, &backup_path).await.wrap_err_with(|| { + format!( + "Failed to back up {} '{}' to '{}'", + file_name, + path.display(), + backup_path.display() + ) + })?; + + tracing::debug!("Reading {} from original '{}'", file_name, path.display()); + fs::read(path).await.wrap_err_with(|| { + format!("Failed to read {} file: {}", file_name, path.display()) + })? + } + Err(err) => { + return Err(err).wrap_err_with(|| { + format!( + "Failed to read {} from backup '{}'", + file_name, + backup_path.display() + ) + }); + } + }; + Ok(bin) +} + +#[tracing::instrument(skip_all)] +async fn patch_game_settings(state: Arc) -> Result<()> { + let settings_path = state.game_dir.join("bundle").join(SETTINGS_FILE_PATH); + + let settings = read_file_with_backup(&settings_path) + .await + .wrap_err("Failed to read settings.ini")?; + let settings = String::from_utf8(settings).wrap_err("Settings.ini is not valid UTF-8")?; + + let mut f = fs::File::create(&settings_path) + .await + .wrap_err_with(|| format!("Failed to open {}", settings_path.display()))?; + + let Some(i) = settings.find("boot_script =") else { + eyre::bail!("couldn't find 'boot_script' field"); + }; + + f.write_all(settings[0..i].as_bytes()).await?; + f.write_all(b"boot_script = \"scripts/mod_main\"").await?; + + let Some(j) = settings[i..].find('\n') else { + eyre::bail!("couldn't find end of 'boot_script' field"); + }; + + f.write_all(settings[(i + j)..].as_bytes()).await?; + + Ok(()) +} + +#[tracing::instrument(skip_all, fields(package = info.name))] +fn make_package(info: &PackageInfo) -> Result { + let mut pkg = Package::new(info.name.clone(), PathBuf::new()); + + for f in &info.files { + let mut it = f.rsplit('.'); + let file_type = it + .next() + .ok_or_else(|| eyre::eyre!("missing file extension")) + .and_then(BundleFileType::from_str) + .wrap_err("Invalid file name in package info")?; + let name: String = it.collect(); + pkg.add_file(file_type, name); + } + + Ok(pkg) +} + +#[tracing::instrument] +async fn copy_recursive( + from: impl Into + std::fmt::Debug, + to: impl AsRef + std::fmt::Debug, +) -> Result<()> { + let to = to.as_ref(); + + #[tracing::instrument] + async fn handle_dir(from: PathBuf) -> Result> { + let mut dir = fs::read_dir(&from) + .await + .wrap_err("Failed to read directory")?; + let mut entries = Vec::new(); + + while let Some(entry) = dir.next_entry().await? { + let meta = entry.metadata().await.wrap_err_with(|| { + format!("Failed to get metadata for '{}'", entry.path().display()) + })?; + entries.push((meta.is_dir(), entry)); + } + + Ok(entries) + } + + let base = from.into(); + stream::unfold(vec![base.clone()], |mut state| async { + let from = state.pop()?; + let inner = match handle_dir(from).await { + Ok(entries) => { + for (is_dir, entry) in &entries { + if *is_dir { + state.push(entry.path()); + } + } + stream::iter(entries).map(Ok).left_stream() + } + Err(e) => stream::once(async { Err(e) }).right_stream(), + }; + + Some((inner, state)) + }) + .flatten() + .try_for_each(|(is_dir, entry)| { + let path = entry.path(); + let dest = path + .strip_prefix(&base) + .map(|suffix| to.join(suffix)) + .expect("all entries are relative to the directory we are walking"); + + async move { + if is_dir { + tracing::trace!("Creating directory '{}'", dest.display()); + fs::create_dir(&dest) + .await + .map(|_| ()) + .wrap_err_with(|| format!("Failed to create directory '{}'", dest.display())) + } else { + tracing::trace!("Copying file '{}' -> '{}'", path.display(), dest.display()); + fs::copy(&path, &dest).await.map(|_| ()).wrap_err_with(|| { + format!( + "Failed to copy file '{}' -> '{}'", + path.display(), + dest.display() + ) + }) + } + } + }) + .await + .map(|_| ()) +} + +#[tracing::instrument(skip(state))] +async fn copy_mod_folders(state: Arc) -> Result> { + let bundle_dir = Arc::new(state.game_dir.join("bundle")); + + let mut tasks = Vec::new(); + + for mod_info in state + .mods + .iter() + .filter(|m| m.id != "dml" && m.enabled && !m.bundled) + { + let span = tracing::trace_span!("copying legacy mod", name = mod_info.name); + let _enter = span.enter(); + + let mod_id = mod_info.id.clone(); + let mod_dir = Arc::clone(&state.mod_dir); + let bundle_dir = Arc::clone(&bundle_dir); + + let task = async move { + let from = mod_dir.join(&mod_id); + let to = bundle_dir.join("mods").join(&mod_id); + + tracing::debug!(from = %from.display(), to = %to.display(), "Copying legacy mod '{}'", mod_id); + let _ = fs::create_dir_all(&to).await; + copy_recursive(&from, &to).await.wrap_err_with(|| { + format!( + "Failed to copy legacy mod from '{}' to '{}'", + from.display(), + to.display() + ) + })?; + + Ok::<_, Report>(mod_id) + }; + tasks.push(task); + } + + let ids = futures::future::try_join_all(tasks).await?; + Ok(ids) +} + +fn build_mod_data_lua(state: Arc) -> String { + let mut lua = String::from("return {\n"); + + // DMF is handled explicitely by the loading procedures, as it actually drives most of that + // and should therefore not show up in the load order. + for mod_info in state.mods.iter().filter(|m| m.id != "dml" && m.enabled) { + lua.push_str(" {\n name = \""); + lua.push_str(&mod_info.name); + + lua.push_str("\",\n id = \""); + lua.push_str(&mod_info.id); + + lua.push_str("\",\n bundled = \""); + if mod_info.bundled { + lua.push_str("true"); + } else { + lua.push_str("false"); + } + + lua.push_str("\",\n run = function()\n"); + + let resources = &mod_info.resources; + if resources.data.is_some() || resources.localization.is_some() { + lua.push_str(" new_mod(\""); + lua.push_str(&mod_info.id); + lua.push_str("\", {\n mod_script = \""); + lua.push_str(&resources.init.to_slash_lossy()); + + if let Some(data) = resources.data.as_ref() { + lua.push_str("\",\n mod_data = \""); + lua.push_str(&data.to_slash_lossy()); + } + + if let Some(localization) = &resources.localization { + lua.push_str("\",\n mod_localization = \""); + lua.push_str(&localization.to_slash_lossy()); + } + + lua.push_str("\",\n })\n"); + } else { + lua.push_str(" return dofile(\""); + lua.push_str(&resources.init.to_slash_lossy()); + lua.push_str("\")\n"); + } + + lua.push_str(" end,\n packages = {\n"); + + for pkg_info in &mod_info.packages { + lua.push_str(" \""); + lua.push_str(&pkg_info.name); + lua.push_str("\",\n"); + } + + lua.push_str(" },\n },\n"); + } + + lua.push('}'); + + tracing::debug!("mod_data_lua:\n{}", lua); + + lua +} + +#[tracing::instrument(skip_all)] +async fn build_bundles(state: Arc) -> Result> { + let mut mod_bundle = Bundle::new(MOD_BUNDLE_NAME.to_string()); + let mut tasks = Vec::new(); + + let bundle_dir = Arc::new(state.game_dir.join("bundle")); + + let mut bundles = Vec::new(); + + { + tracing::trace!("Building mod data script"); + + let span = tracing::debug_span!("Building mod data script"); + let _enter = span.enter(); + + let lua = build_mod_data_lua(state.clone()); + + tracing::trace!("Compiling mod data script"); + + let file = + lua::compile(MOD_DATA_SCRIPT, lua).wrap_err("Failed to compile mod data Lua file")?; + + tracing::trace!("Compile mod data script"); + + mod_bundle.add_file(file); + } + + tracing::trace!("Preparing tasks to deploy bundle files"); + + for mod_info in state + .mods + .iter() + .filter(|m| m.id != "dml" && m.enabled && m.bundled) + { + let span = tracing::trace_span!("building mod packages", name = mod_info.name); + let _enter = span.enter(); + + let mod_dir = state.mod_dir.join(&mod_info.id); + for pkg_info in &mod_info.packages { + let span = tracing::trace_span!("building package", name = pkg_info.name); + let _enter = span.enter(); + + tracing::trace!( + "Building package {} for mod {}", + pkg_info.name, + mod_info.name + ); + + let pkg = make_package(pkg_info).wrap_err("Failed to make package")?; + let mut variant = BundleFileVariant::new(); + let bin = pkg + .to_binary() + .wrap_err("Failed to serialize package to binary")?; + variant.set_data(bin); + let mut file = BundleFile::new(pkg_info.name.clone(), BundleFileType::Package); + file.add_variant(variant); + + tracing::trace!( + "Compiled package {} for mod {}", + pkg_info.name, + mod_info.name + ); + + mod_bundle.add_file(file); + + let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); + let src = mod_dir.join(&bundle_name); + let dest = bundle_dir.join(&bundle_name); + let pkg_name = pkg_info.name.clone(); + let mod_name = mod_info.name.clone(); + + // Explicitely drop the guard, so that we can move the span + // into the async operation + drop(_enter); + + let ctx = state.ctx.clone(); + + let task = async move { + let bundle = { + let bin = fs::read(&src).await.wrap_err_with(|| { + format!("Failed to read bundle file '{}'", src.display()) + })?; + let name = Bundle::get_name_from_path(&ctx, &src); + Bundle::from_binary(&ctx, name, bin) + .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))? + }; + + tracing::debug!( + src = %src.display(), + dest = %dest.display(), + "Copying bundle '{}' for mod '{}'", + pkg_name, + mod_name, + ); + // We attempt to remove any previous file, so that the hard link can be created. + // We can reasonably ignore errors here, as a 'NotFound' is actually fine, the copy + // may be possible despite an error here, or the error will be reported by it anyways. + // TODO: There is a chance that we delete an actual game bundle, but with 64bit + // hashes, it's low enough for now, and the setup required to detect + // "game bundle vs mod bundle" is non-trivial. + let _ = fs::remove_file(&dest).await; + fs::copy(&src, &dest).await.wrap_err_with(|| { + format!( + "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", + src.display(), + dest.display() + ) + })?; + + Ok::(bundle) + } + .instrument(span); + + tasks.push(task); + } + } + + tracing::debug!("Copying {} mod bundles", tasks.len()); + + let mut tasks = stream::iter(tasks).buffer_unordered(10); + + while let Some(res) = tasks.next().await { + let bundle = res?; + bundles.push(bundle); + } + + { + let path = bundle_dir.join(format!("{:x}", mod_bundle.name().to_murmur64())); + tracing::trace!("Writing mod bundle to '{}'", path.display()); + fs::write(&path, mod_bundle.to_binary()?) + .await + .wrap_err_with(|| format!("Failed to write bundle to '{}'", path.display()))?; + } + + bundles.push(mod_bundle); + + Ok(bundles) +} + +#[tracing::instrument(skip_all)] +async fn patch_boot_bundle(state: Arc) -> Result> { + let bundle_dir = Arc::new(state.game_dir.join("bundle")); + let bundle_path = bundle_dir.join(format!("{:x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes()))); + + let mut bundles = Vec::with_capacity(2); + + let mut boot_bundle = async { + let bin = read_file_with_backup(&bundle_path) + .await + .wrap_err("Failed to read boot bundle")?; + + Bundle::from_binary(&state.ctx, BOOT_BUNDLE_NAME.to_string(), bin) + .wrap_err("Failed to parse boot bundle") + } + .instrument(tracing::trace_span!("read boot bundle")) + .await + .wrap_err_with(|| format!("Failed to read bundle '{}'", BOOT_BUNDLE_NAME))?; + + { + tracing::trace!("Adding mod package file to boot bundle"); + let span = tracing::trace_span!("create mod package file"); + let _enter = span.enter(); + + let mut pkg = Package::new(MOD_BUNDLE_NAME.to_string(), PathBuf::new()); + + for mod_info in &state.mods { + for pkg_info in &mod_info.packages { + pkg.add_file(BundleFileType::Package, &pkg_info.name); + } + } + + pkg.add_file(BundleFileType::Lua, MOD_DATA_SCRIPT); + + let mut variant = BundleFileVariant::new(); + variant.set_data(pkg.to_binary()?); + let mut f = BundleFile::new(MOD_BUNDLE_NAME.to_string(), BundleFileType::Package); + f.add_variant(variant); + + boot_bundle.add_file(f); + } + + { + tracing::trace!("Handling DML packages and bundle"); + let span = tracing::trace_span!("handle DML"); + let _enter = span.enter(); + + let mut variant = BundleFileVariant::new(); + + let mod_info = state + .mods + .iter() + .find(|m| m.id == "dml") + .ok_or_else(|| eyre::eyre!("DML not found in mod list"))?; + let pkg_info = mod_info + .packages + .get(0) + .ok_or_else(|| eyre::eyre!("invalid mod package for DML")) + .with_suggestion(|| "Re-download and import the newest version.".to_string())?; + let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); + let src = state.mod_dir.join(&mod_info.id).join(&bundle_name); + + { + let bin = fs::read(&src) + .await + .wrap_err_with(|| format!("Failed to read bundle file '{}'", src.display()))?; + let name = Bundle::get_name_from_path(&state.ctx, &src); + + let dml_bundle = Bundle::from_binary(&state.ctx, name, bin) + .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))?; + + bundles.push(dml_bundle); + }; + + { + let dest = bundle_dir.join(&bundle_name); + let pkg_name = pkg_info.name.clone(); + let mod_name = mod_info.name.clone(); + + tracing::debug!( + "Copying bundle {} for mod {}: {} -> {}", + pkg_name, + mod_name, + src.display(), + dest.display() + ); + // We attempt to remove any previous file, so that the hard link can be created. + // We can reasonably ignore errors here, as a 'NotFound' is actually fine, the copy + // may be possible despite an error here, or the error will be reported by it anyways. + // TODO: There is a chance that we delete an actual game bundle, but with 64bit + // hashes, it's low enough for now, and the setup required to detect + // "game bundle vs mod bundle" is non-trivial. + let _ = fs::remove_file(&dest).await; + fs::copy(&src, &dest).await.wrap_err_with(|| { + format!( + "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", + src.display(), + dest.display() + ) + })?; + } + + let pkg = make_package(pkg_info).wrap_err("Failed to create package file for dml")?; + variant.set_data(pkg.to_binary()?); + + let mut f = BundleFile::new(DML_BUNDLE_NAME.to_string(), BundleFileType::Package); + f.add_variant(variant); + + boot_bundle.add_file(f); + } + + { + let span = tracing::debug_span!("Importing mod main script"); + let _enter = span.enter(); + + let is_io_enabled = format!("{}", state.is_io_enabled); + let mut data = HashMap::new(); + data.insert("is_io_enabled", is_io_enabled.as_str()); + + let tmpl = include_str!("../../assets/mod_main.lua"); + let lua = Template::new(tmpl).render(&data); + tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); + let file = + lua::compile(MOD_BOOT_SCRIPT, lua).wrap_err("Failed to compile mod main Lua file")?; + + boot_bundle.add_file(file); + } + + async { + let bin = boot_bundle + .to_binary() + .wrap_err("Failed to serialize boot bundle")?; + fs::write(&bundle_path, bin) + .await + .wrap_err_with(|| format!("Failed to write main bundle: {}", bundle_path.display())) + } + .instrument(tracing::trace_span!("write boot bundle")) + .await?; + + bundles.push(boot_bundle); + + Ok(bundles) +} + +#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] +async fn patch_bundle_database(state: Arc, bundles: B) -> Result<()> +where + B: AsRef<[Bundle]>, +{ + let bundle_dir = Arc::new(state.game_dir.join("bundle")); + let database_path = bundle_dir.join(BUNDLE_DATABASE_NAME); + + let mut db = { + let bin = read_file_with_backup(&database_path) + .await + .wrap_err("Failed to read bundle database")?; + let mut r = Cursor::new(bin); + let db = BundleDatabase::from_binary(&mut r).wrap_err("Failed to parse bundle database")?; + tracing::trace!("Finished parsing bundle database"); + db + }; + + for bundle in bundles.as_ref() { + tracing::trace!("Adding '{}' to bundle database", bundle.name().display()); + db.add_bundle(bundle); + } + + { + let bin = db + .to_binary() + .wrap_err("Failed to serialize bundle database")?; + fs::write(&database_path, bin).await.wrap_err_with(|| { + format!( + "failed to write bundle database to '{}'", + database_path.display() + ) + })?; + } + + Ok(()) +} + +#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] +async fn write_deployment_data( + state: Arc, + bundles: B, + mod_folders: Vec, +) -> Result<()> +where + B: AsRef<[Bundle]>, +{ + let info = DeploymentData { + timestamp: OffsetDateTime::now_utc(), + bundles: bundles + .as_ref() + .iter() + .map(|bundle| format!("{:x}", bundle.name().to_murmur64())) + .collect(), + // TODO: + mod_folders, + }; + let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); + let data = serde_sjson::to_string(&info).wrap_err("Failed to serizalie deployment data")?; + + fs::write(&path, &data) + .await + .wrap_err_with(|| format!("Failed to write deployment data to '{}'", path.display()))?; + + Ok(()) +} + +#[tracing::instrument(skip_all, fields( + game_dir = %state.game_dir.display(), + mods = state.mods.len() +))] +pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { + let state = Arc::new(state); + let bundle_dir = state.game_dir.join("bundle"); + let boot_bundle_path = format!("{:016x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes())); + + if fs::metadata(bundle_dir.join(format!("{boot_bundle_path}.patch_999"))) + .await + .is_ok() + { + let err = eyre::eyre!("Found dtkit-patch-based mod installation."); + return Err(err) + .with_suggestion(|| { + "If you're a mod author and saved projects directly in 'mods/', \ + use DTMT to migrate them to the new project structure." + .to_string() + }) + .with_suggestion(|| { + "Click 'Reset Game' to remove the previous mod installation.".to_string() + }); + } + + let (_, game_info, deployment_info) = tokio::try_join!( + async { + fs::metadata(&bundle_dir) + .await + .wrap_err("Failed to open game bundle directory") + .with_suggestion(|| "Double-check 'Game Directory' in the Settings tab.") + }, + async { + tokio::task::spawn_blocking(dtmt_shared::collect_game_info) + .await + .map_err(Report::new) + }, + async { + let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); + match read_sjson_file::<_, DeploymentData>(path).await { + Ok(data) => Ok(Some(data)), + Err(err) => { + if let Some(err) = err.downcast_ref::() + && err.kind() == ErrorKind::NotFound + { + Ok(None) + } else { + Err(err).wrap_err("Failed to read deployment data") + } + } + } + } + ) + .wrap_err("Failed to gather deployment information")?; + + tracing::debug!(?game_info, ?deployment_info); + + if let Some(game_info) = game_info { + if deployment_info + .as_ref() + .map(|i| game_info.last_updated > i.timestamp) + .unwrap_or(false) + { + tracing::warn!( + "Game was updated since last mod deployment. \ + Attempting to reconcile game files." + ); + + tokio::try_join!( + async { + let path = bundle_dir.join(BUNDLE_DATABASE_NAME); + let backup_path = path.with_extension("data.bak"); + + fs::copy(&path, &backup_path) + .await + .wrap_err("Failed to re-create backup for bundle database.") + }, + async { + let path = bundle_dir.join(boot_bundle_path); + let backup_path = path.with_extension("bak"); + + fs::copy(&path, &backup_path) + .await + .wrap_err("Failed to re-create backup for boot bundle") + } + ) + .with_suggestion(|| { + "Reset the game using 'Reset Game', then verify game files.".to_string() + })?; + + tracing::info!( + "Successfully re-created game file backups. \ + Continuing mod deployment." + ); + } + } + + check_mod_order(&state)?; + + tracing::info!( + "Deploying {} mods to '{}'.", + state.mods.iter().filter(|i| i.enabled).count(), + bundle_dir.display() + ); + + tracing::info!("Copy legacy mod folders"); + let mod_folders = copy_mod_folders(state.clone()) + .await + .wrap_err("Failed to copy mod folders")?; + + tracing::info!("Build mod bundles"); + let mut bundles = build_bundles(state.clone()) + .await + .wrap_err("Failed to build mod bundles")?; + + tracing::info!("Patch boot bundle"); + let mut boot_bundles = patch_boot_bundle(state.clone()) + .await + .wrap_err("Failed to patch boot bundle")?; + bundles.append(&mut boot_bundles); + + if let Some(info) = &deployment_info { + let bundle_dir = Arc::new(bundle_dir); + // Remove bundles from the previous deployment that don't match the current one. + // I.e. mods that used to be installed/enabled but aren't anymore. + { + let tasks = info.bundles.iter().cloned().filter_map(|file_name| { + let is_being_deployed = bundles.iter().any(|b2| { + let name = format!("{:016x}", b2.name()); + file_name == name + }); + + if !is_being_deployed { + let bundle_dir = bundle_dir.clone(); + let task = async move { + let path = bundle_dir.join(&file_name); + + tracing::debug!("Removing unused bundle '{}'", file_name); + + if let Err(err) = fs::remove_file(&path).await.wrap_err_with(|| { + format!("Failed to remove unused bundle '{}'", path.display()) + }) { + tracing::error!("{:?}", err); + } + }; + Some(task) + } else { + None + } + }); + + futures::future::join_all(tasks).await; + } + + // Do the same thing for mod folders + { + let tasks = info.mod_folders.iter().filter_map(|mod_id| { + let is_being_deployed = mod_folders.iter().any(|id| id == mod_id); + + if !is_being_deployed { + let path = bundle_dir.join("mods").join(mod_id); + tracing::debug!("Removing unused mod folder '{}'", path.display()); + + let task = async move { + if let Err(err) = fs::remove_dir_all(&path).await.wrap_err_with(|| { + format!("Failed to remove unused legacy mod '{}'", path.display()) + }) { + tracing::error!("{:?}", err); + } + }; + + Some(task) + } else { + None + } + }); + futures::future::join_all(tasks).await; + } + } + + tracing::info!("Patch game settings"); + patch_game_settings(state.clone()) + .await + .wrap_err("Failed to patch game settings")?; + + tracing::info!("Patching bundle database"); + patch_bundle_database(state.clone(), &bundles) + .await + .wrap_err("Failed to patch bundle database")?; + + tracing::info!("Writing deployment data"); + write_deployment_data(state.clone(), &bundles, mod_folders) + .await + .wrap_err("Failed to write deployment data")?; + + tracing::info!("Finished deploying mods"); + Ok(()) +} diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 5520c4a..6b91169 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -1,46 +1,19 @@ -use std::collections::HashMap; -use std::io::{self, Cursor, ErrorKind}; +use std::io::{self, ErrorKind}; use std::path::{Path, PathBuf}; -use std::str::FromStr; use std::sync::Arc; use color_eyre::eyre::Context; -use color_eyre::{eyre, Help, Report, Result}; -use futures::stream; -use futures::StreamExt; -use path_slash::PathBufExt; -use sdk::filetype::lua; -use sdk::filetype::package::Package; +use color_eyre::{eyre, Result}; use sdk::murmur::Murmur64; -use sdk::{ - Bundle, BundleDatabase, BundleFile, BundleFileType, BundleFileVariant, FromBinary, ToBinary, -}; -use serde::{Deserialize, Serialize}; -use string_template::Template; -use time::OffsetDateTime; -use tokio::fs; +use tokio::fs::{self}; use tokio::io::AsyncWriteExt; -use tracing::Instrument; -use super::read_sjson_file; -use crate::controller::app::check_mod_order; -use crate::state::{ActionState, PackageInfo}; +use crate::controller::deploy::{ + DeploymentData, BOOT_BUNDLE_NAME, BUNDLE_DATABASE_NAME, DEPLOYMENT_DATA_PATH, +}; +use crate::state::ActionState; -const MOD_BUNDLE_NAME: &str = "packages/mods"; -const BOOT_BUNDLE_NAME: &str = "packages/boot"; -const DML_BUNDLE_NAME: &str = "packages/dml"; -const BUNDLE_DATABASE_NAME: &str = "bundle_database.data"; -const MOD_BOOT_SCRIPT: &str = "scripts/mod_main"; -const MOD_DATA_SCRIPT: &str = "scripts/mods/mod_data"; -const SETTINGS_FILE_PATH: &str = "application_settings/settings_common.ini"; -const DEPLOYMENT_DATA_PATH: &str = "dtmm-deployment.sjson"; - -#[derive(Debug, Serialize, Deserialize)] -struct DeploymentData { - bundles: Vec, - #[serde(with = "time::serde::iso8601")] - timestamp: OffsetDateTime, -} +use super::deploy::SETTINGS_FILE_PATH; #[tracing::instrument] async fn read_file_with_backup

(path: P) -> Result> @@ -130,585 +103,6 @@ async fn patch_game_settings(state: Arc) -> Result<()> { Ok(()) } -#[tracing::instrument(skip_all, fields(package = info.name))] -fn make_package(info: &PackageInfo) -> Result { - let mut pkg = Package::new(info.name.clone(), PathBuf::new()); - - for f in &info.files { - let mut it = f.rsplit('.'); - let file_type = it - .next() - .ok_or_else(|| eyre::eyre!("missing file extension")) - .and_then(BundleFileType::from_str) - .wrap_err("Invalid file name in package info")?; - let name: String = it.collect(); - pkg.add_file(file_type, name); - } - - Ok(pkg) -} - -fn build_mod_data_lua(state: Arc) -> String { - let mut lua = String::from("return {\n"); - - // DMF is handled explicitely by the loading procedures, as it actually drives most of that - // and should therefore not show up in the load order. - for mod_info in state.mods.iter().filter(|m| m.id != "dml" && m.enabled) { - lua.push_str(" {\n name = \""); - lua.push_str(&mod_info.name); - - lua.push_str("\",\n id = \""); - lua.push_str(&mod_info.id); - - lua.push_str("\",\n run = function()\n"); - - let resources = &mod_info.resources; - if resources.data.is_some() || resources.localization.is_some() { - lua.push_str(" new_mod(\""); - lua.push_str(&mod_info.id); - lua.push_str("\", {\n mod_script = \""); - lua.push_str(&resources.init.to_slash_lossy()); - - if let Some(data) = resources.data.as_ref() { - lua.push_str("\",\n mod_data = \""); - lua.push_str(&data.to_slash_lossy()); - } - - if let Some(localization) = &resources.localization { - lua.push_str("\",\n mod_localization = \""); - lua.push_str(&localization.to_slash_lossy()); - } - - lua.push_str("\",\n })\n"); - } else { - lua.push_str(" return dofile(\""); - lua.push_str(&resources.init.to_slash_lossy()); - lua.push_str("\")\n"); - } - - lua.push_str(" end,\n packages = {\n"); - - for pkg_info in &mod_info.packages { - lua.push_str(" \""); - lua.push_str(&pkg_info.name); - lua.push_str("\",\n"); - } - - lua.push_str(" },\n },\n"); - } - - lua.push('}'); - - tracing::debug!("mod_data_lua:\n{}", lua); - - lua -} - -#[tracing::instrument(skip_all)] -async fn build_bundles(state: Arc) -> Result> { - let mut mod_bundle = Bundle::new(MOD_BUNDLE_NAME.to_string()); - let mut tasks = Vec::new(); - - let bundle_dir = Arc::new(state.game_dir.join("bundle")); - - let mut bundles = Vec::new(); - - { - tracing::trace!("Building mod data script"); - - let span = tracing::debug_span!("Building mod data script"); - let _enter = span.enter(); - - let lua = build_mod_data_lua(state.clone()); - - tracing::trace!("Compiling mod data script"); - - let file = - lua::compile(MOD_DATA_SCRIPT, &lua).wrap_err("Failed to compile mod data Lua file")?; - - tracing::trace!("Compile mod data script"); - - mod_bundle.add_file(file); - } - - tracing::trace!("Preparing tasks to deploy bundle files"); - - for mod_info in state.mods.iter().filter(|m| m.id != "dml" && m.enabled) { - let span = tracing::trace_span!("building mod packages", name = mod_info.name); - let _enter = span.enter(); - - let mod_dir = state.mod_dir.join(&mod_info.id); - for pkg_info in &mod_info.packages { - let span = tracing::trace_span!("building package", name = pkg_info.name); - let _enter = span.enter(); - - tracing::trace!( - "Building package {} for mod {}", - pkg_info.name, - mod_info.name - ); - - let pkg = make_package(pkg_info).wrap_err("Failed to make package")?; - let mut variant = BundleFileVariant::new(); - let bin = pkg - .to_binary() - .wrap_err("Failed to serialize package to binary")?; - variant.set_data(bin); - let mut file = BundleFile::new(pkg_info.name.clone(), BundleFileType::Package); - file.add_variant(variant); - - tracing::trace!( - "Compiled package {} for mod {}", - pkg_info.name, - mod_info.name - ); - - mod_bundle.add_file(file); - - let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); - let src = mod_dir.join(&bundle_name); - let dest = bundle_dir.join(&bundle_name); - let pkg_name = pkg_info.name.clone(); - let mod_name = mod_info.name.clone(); - - // Explicitely drop the guard, so that we can move the span - // into the async operation - drop(_enter); - - let ctx = state.ctx.clone(); - - let task = async move { - let bundle = { - let bin = fs::read(&src).await.wrap_err_with(|| { - format!("Failed to read bundle file '{}'", src.display()) - })?; - let name = Bundle::get_name_from_path(&ctx, &src); - Bundle::from_binary(&ctx, name, bin) - .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))? - }; - - tracing::debug!( - src = %src.display(), - dest = %dest.display(), - "Copying bundle '{}' for mod '{}'", - pkg_name, - mod_name, - ); - // We attempt to remove any previous file, so that the hard link can be created. - // We can reasonably ignore errors here, as a 'NotFound' is actually fine, the copy - // may be possible despite an error here, or the error will be reported by it anyways. - // TODO: There is a chance that we delete an actual game bundle, but with 64bit - // hashes, it's low enough for now, and the setup required to detect - // "game bundle vs mod bundle" is non-trivial. - let _ = fs::remove_file(&dest).await; - fs::copy(&src, &dest).await.wrap_err_with(|| { - format!( - "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", - src.display(), - dest.display() - ) - })?; - - Ok::(bundle) - } - .instrument(span); - - tasks.push(task); - } - } - - tracing::debug!("Copying {} mod bundles", tasks.len()); - - let mut tasks = stream::iter(tasks).buffer_unordered(10); - - while let Some(res) = tasks.next().await { - let bundle = res?; - bundles.push(bundle); - } - - { - let path = bundle_dir.join(format!("{:x}", mod_bundle.name().to_murmur64())); - tracing::trace!("Writing mod bundle to '{}'", path.display()); - fs::write(&path, mod_bundle.to_binary()?) - .await - .wrap_err_with(|| format!("Failed to write bundle to '{}'", path.display()))?; - } - - bundles.push(mod_bundle); - - Ok(bundles) -} - -#[tracing::instrument(skip_all)] -async fn patch_boot_bundle(state: Arc) -> Result> { - let bundle_dir = Arc::new(state.game_dir.join("bundle")); - let bundle_path = bundle_dir.join(format!("{:x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes()))); - - let mut bundles = Vec::with_capacity(2); - - let mut boot_bundle = async { - let bin = read_file_with_backup(&bundle_path) - .await - .wrap_err("Failed to read boot bundle")?; - - Bundle::from_binary(&state.ctx, BOOT_BUNDLE_NAME.to_string(), bin) - .wrap_err("Failed to parse boot bundle") - } - .instrument(tracing::trace_span!("read boot bundle")) - .await - .wrap_err_with(|| format!("Failed to read bundle '{}'", BOOT_BUNDLE_NAME))?; - - { - tracing::trace!("Adding mod package file to boot bundle"); - let span = tracing::trace_span!("create mod package file"); - let _enter = span.enter(); - - let mut pkg = Package::new(MOD_BUNDLE_NAME.to_string(), PathBuf::new()); - - for mod_info in &state.mods { - for pkg_info in &mod_info.packages { - pkg.add_file(BundleFileType::Package, &pkg_info.name); - } - } - - pkg.add_file(BundleFileType::Lua, MOD_DATA_SCRIPT); - - let mut variant = BundleFileVariant::new(); - variant.set_data(pkg.to_binary()?); - let mut f = BundleFile::new(MOD_BUNDLE_NAME.to_string(), BundleFileType::Package); - f.add_variant(variant); - - boot_bundle.add_file(f); - } - - { - tracing::trace!("Handling DML packages and bundle"); - let span = tracing::trace_span!("handle DML"); - let _enter = span.enter(); - - let mut variant = BundleFileVariant::new(); - - let mod_info = state - .mods - .iter() - .find(|m| m.id == "dml") - .ok_or_else(|| eyre::eyre!("DML not found in mod list"))?; - let pkg_info = mod_info - .packages - .get(0) - .ok_or_else(|| eyre::eyre!("invalid mod package for DML")) - .with_suggestion(|| "Re-download and import the newest version.".to_string())?; - let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); - let src = state.mod_dir.join(&mod_info.id).join(&bundle_name); - - { - let bin = fs::read(&src) - .await - .wrap_err_with(|| format!("Failed to read bundle file '{}'", src.display()))?; - let name = Bundle::get_name_from_path(&state.ctx, &src); - - let dml_bundle = Bundle::from_binary(&state.ctx, name, bin) - .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))?; - - bundles.push(dml_bundle); - }; - - { - let dest = bundle_dir.join(&bundle_name); - let pkg_name = pkg_info.name.clone(); - let mod_name = mod_info.name.clone(); - - tracing::debug!( - "Copying bundle {} for mod {}: {} -> {}", - pkg_name, - mod_name, - src.display(), - dest.display() - ); - // We attempt to remove any previous file, so that the hard link can be created. - // We can reasonably ignore errors here, as a 'NotFound' is actually fine, the copy - // may be possible despite an error here, or the error will be reported by it anyways. - // TODO: There is a chance that we delete an actual game bundle, but with 64bit - // hashes, it's low enough for now, and the setup required to detect - // "game bundle vs mod bundle" is non-trivial. - let _ = fs::remove_file(&dest).await; - fs::copy(&src, &dest).await.wrap_err_with(|| { - format!( - "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", - src.display(), - dest.display() - ) - })?; - } - - let pkg = make_package(pkg_info).wrap_err("Failed to create package file for dml")?; - variant.set_data(pkg.to_binary()?); - - let mut f = BundleFile::new(DML_BUNDLE_NAME.to_string(), BundleFileType::Package); - f.add_variant(variant); - - boot_bundle.add_file(f); - } - - { - let span = tracing::debug_span!("Importing mod main script"); - let _enter = span.enter(); - - let is_io_enabled = format!("{}", state.is_io_enabled); - let mut data = HashMap::new(); - data.insert("is_io_enabled", is_io_enabled.as_str()); - - let tmpl = include_str!("../../assets/mod_main.lua"); - let lua = Template::new(tmpl).render(&data); - tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); - let file = - lua::compile(MOD_BOOT_SCRIPT, lua).wrap_err("Failed to compile mod main Lua file")?; - - boot_bundle.add_file(file); - } - - async { - let bin = boot_bundle - .to_binary() - .wrap_err("Failed to serialize boot bundle")?; - fs::write(&bundle_path, bin) - .await - .wrap_err_with(|| format!("Failed to write main bundle: {}", bundle_path.display())) - } - .instrument(tracing::trace_span!("write boot bundle")) - .await?; - - bundles.push(boot_bundle); - - Ok(bundles) -} - -#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] -async fn patch_bundle_database(state: Arc, bundles: B) -> Result<()> -where - B: AsRef<[Bundle]>, -{ - let bundle_dir = Arc::new(state.game_dir.join("bundle")); - let database_path = bundle_dir.join(BUNDLE_DATABASE_NAME); - - let mut db = { - let bin = read_file_with_backup(&database_path) - .await - .wrap_err("Failed to read bundle database")?; - let mut r = Cursor::new(bin); - let db = BundleDatabase::from_binary(&mut r).wrap_err("Failed to parse bundle database")?; - tracing::trace!("Finished parsing bundle database"); - db - }; - - for bundle in bundles.as_ref() { - tracing::trace!("Adding '{}' to bundle database", bundle.name().display()); - db.add_bundle(bundle); - } - - { - let bin = db - .to_binary() - .wrap_err("Failed to serialize bundle database")?; - fs::write(&database_path, bin).await.wrap_err_with(|| { - format!( - "failed to write bundle database to '{}'", - database_path.display() - ) - })?; - } - - Ok(()) -} - -#[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] -async fn write_deployment_data(state: Arc, bundles: B) -> Result<()> -where - B: AsRef<[Bundle]>, -{ - let info = DeploymentData { - timestamp: OffsetDateTime::now_utc(), - bundles: bundles - .as_ref() - .iter() - .map(|bundle| format!("{:x}", bundle.name().to_murmur64())) - .collect(), - }; - let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); - let data = serde_sjson::to_string(&info).wrap_err("Failed to serizalie deployment data")?; - - fs::write(&path, &data) - .await - .wrap_err_with(|| format!("Failed to write deployment data to '{}'", path.display()))?; - - Ok(()) -} - -#[tracing::instrument(skip_all, fields( - game_dir = %state.game_dir.display(), - mods = state.mods.len() -))] -pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { - let state = Arc::new(state); - let bundle_dir = state.game_dir.join("bundle"); - let boot_bundle_path = format!("{:016x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes())); - - if fs::metadata(bundle_dir.join(format!("{boot_bundle_path}.patch_999"))) - .await - .is_ok() - { - let err = eyre::eyre!("Found dtkit-patch-based mod installation."); - return Err(err) - .with_suggestion(|| { - "If you're a mod author and saved projects directly in 'mods/', \ - use DTMT to migrate them to the new project structure." - .to_string() - }) - .with_suggestion(|| { - "Click 'Reset Game' to remove the previous mod installation.".to_string() - }); - } - - let (_, game_info, deployment_info) = tokio::try_join!( - async { - fs::metadata(&bundle_dir) - .await - .wrap_err("Failed to open game bundle directory") - .with_suggestion(|| "Double-check 'Game Directory' in the Settings tab.") - }, - async { - tokio::task::spawn_blocking(dtmt_shared::collect_game_info) - .await - .map_err(Report::new) - }, - async { - let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); - match read_sjson_file::<_, DeploymentData>(path).await { - Ok(data) => Ok(Some(data)), - Err(err) => { - if let Some(err) = err.downcast_ref::() - && err.kind() == ErrorKind::NotFound - { - Ok(None) - } else { - Err(err).wrap_err("Failed to read deployment data") - } - } - } - } - ) - .wrap_err("Failed to gather deployment information")?; - - tracing::debug!(?game_info, ?deployment_info); - - if let Some(game_info) = game_info { - if deployment_info - .as_ref() - .map(|i| game_info.last_updated > i.timestamp) - .unwrap_or(false) - { - tracing::warn!( - "Game was updated since last mod deployment. \ - Attempting to reconcile game files." - ); - - tokio::try_join!( - async { - let path = bundle_dir.join(BUNDLE_DATABASE_NAME); - let backup_path = path.with_extension("data.bak"); - - fs::copy(&path, &backup_path) - .await - .wrap_err("Failed to re-create backup for bundle database.") - }, - async { - let path = bundle_dir.join(boot_bundle_path); - let backup_path = path.with_extension("bak"); - - fs::copy(&path, &backup_path) - .await - .wrap_err("Failed to re-create backup for boot bundle") - } - ) - .with_suggestion(|| { - "Reset the game using 'Reset Game', then verify game files.".to_string() - })?; - - tracing::info!( - "Successfully re-created game file backups. \ - Continuing mod deployment." - ); - } - } - - check_mod_order(&state)?; - - tracing::info!( - "Deploying {} mods to '{}'.", - state.mods.iter().filter(|i| i.enabled).count(), - bundle_dir.display() - ); - - tracing::info!("Build mod bundles"); - let mut bundles = build_bundles(state.clone()) - .await - .wrap_err("Failed to build mod bundles")?; - - tracing::info!("Patch boot bundle"); - let mut more_bundles = patch_boot_bundle(state.clone()) - .await - .wrap_err("Failed to patch boot bundle")?; - bundles.append(&mut more_bundles); - - if let Some(info) = &deployment_info { - let bundle_dir = Arc::new(bundle_dir); - let tasks = info.bundles.iter().cloned().filter_map(|file_name| { - let contains = bundles.iter().any(|b2| { - let name = format!("{:016x}", b2.name()); - file_name == name - }); - - if !contains { - let bundle_dir = bundle_dir.clone(); - let task = async move { - let path = bundle_dir.join(&file_name); - - tracing::debug!("Removing unused bundle '{}'", file_name); - - if let Err(err) = fs::remove_file(&path).await.wrap_err_with(|| { - format!("Failed to remove unused bundle '{}'", path.display()) - }) { - tracing::error!("{:?}", err); - } - }; - Some(task) - } else { - None - } - }); - - futures::future::join_all(tasks).await; - } - - tracing::info!("Patch game settings"); - patch_game_settings(state.clone()) - .await - .wrap_err("Failed to patch game settings")?; - - tracing::info!("Patching bundle database"); - patch_bundle_database(state.clone(), &bundles) - .await - .wrap_err("Failed to patch bundle database")?; - - tracing::info!("Writing deployment data"); - write_deployment_data(state.clone(), &bundles) - .await - .wrap_err("Failed to write deployment data")?; - - tracing::info!("Finished deploying mods"); - Ok(()) -} - #[tracing::instrument(skip_all)] async fn reset_dtkit_patch(state: ActionState) -> Result<()> { let bundle_dir = state.game_dir.join("bundle"); diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs new file mode 100644 index 0000000..3e17d4c --- /dev/null +++ b/crates/dtmm/src/controller/import.rs @@ -0,0 +1,475 @@ +use std::collections::HashMap; +use std::ffi::CStr; +use std::io::{Cursor, Read, Seek, Write}; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +use color_eyre::eyre::{self, Context}; +use color_eyre::{Help, Report, Result}; +use druid::im::Vector; +use druid::{FileInfo, ImageBuf}; +use dtmt_shared::{ModConfig, ModConfigResources}; +use luajit2_sys as lua; +use nexusmods::Api as NexusApi; +use tokio::fs; +use zip::ZipArchive; + +use crate::state::{ActionState, ModInfo, NexusInfo, PackageInfo}; + +fn find_archive_file( + archive: &ZipArchive, + name: impl AsRef, +) -> Option { + let path = archive + .file_names() + .find(|path| path.ends_with(name.as_ref())) + .map(|s| s.to_string()); + path +} + +// Runs the content of a `.mod` file to extract what data we can get +// from legacy mods. +// 1. Create a global function `new_mod` that stores +// the relevant bits in global variables. +// 2. Run the `.mod` file, which will merely return a table. +// 3. Run the `run` function from that table. +// 4. Access the global variables from #1. +#[tracing::instrument] +fn parse_mod_id_file(data: &str) -> Result<(String, ModConfigResources)> { + tracing::debug!("Parsing mod file:\n{}", data); + + let ret = unsafe { + let state = lua::luaL_newstate(); + lua::luaL_openlibs(state); + + let run = b" +function fassert() end +function new_mod(id, resources) + _G.id = id + _G.script = resources.mod_script + _G.data = resources.mod_data + _G.localization = resources.mod_localization +end +\0"; + match lua::luaL_loadstring(state, run.as_ptr() as _) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRSYNTAX => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Invalid syntax: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to create `new_mod`") + } + _ => unreachable!(), + } + + match lua::lua_pcall(state, 0, 0, 0) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRRUN => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Failed to run buffer: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to run buffer") + } + // We don't use an error handler function, so this should be unreachable + lua::LUA_ERRERR => unreachable!(), + _ => unreachable!(), + } + + let name = b".mod\0"; + match lua::luaL_loadbuffer( + state, + data.as_ptr() as _, + data.len() as _, + name.as_ptr() as _, + ) as u32 + { + lua::LUA_OK => {} + lua::LUA_ERRSYNTAX => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Invalid syntax: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to load `.mod` file buffer") + } + _ => unreachable!(), + } + + match lua::lua_pcall(state, 0, 1, 0) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRRUN => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Failed to run `.mod` file: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to run `.mod` file") + } + // We don't use an error handler function, so this should be unreachable + lua::LUA_ERRERR => unreachable!(), + _ => unreachable!(), + } + + let key = b"run\0"; + lua::lua_pushstring(state, key.as_ptr() as _); + lua::lua_gettable(state, -2); + + match lua::lua_pcall(state, 0, 0, 0) as u32 { + lua::LUA_OK => {} + lua::LUA_ERRRUN => { + let err = lua::lua_tostring(state, -1); + let err = CStr::from_ptr(err).to_string_lossy().to_string(); + + lua::lua_close(state); + + eyre::bail!("Failed to run `.mod.run`: {}", err); + } + lua::LUA_ERRMEM => { + lua::lua_close(state); + eyre::bail!("Failed to allocate sufficient memory to run `.mod.run`") + } + // We don't use an error handler function, so this should be unreachable + lua::LUA_ERRERR => unreachable!(), + _ => unreachable!(), + } + + let get_global = |state, key: &[u8]| { + lua::lua_getglobal(state, key.as_ptr() as _); + + if lua::lua_isnil(state, -1) != 0 { + return Ok(None); + } + + let s = lua::lua_tostring(state, -1); + + if s.is_null() { + eyre::bail!("Expected string, got NULL"); + } + + let ret = CStr::from_ptr(s).to_string_lossy().to_string(); + lua::lua_pop(state, 1); + Ok(Some(ret)) + }; + + let mod_id = get_global(state, b"id\0") + .and_then(|s| s.ok_or_else(|| eyre::eyre!("Got `nil`"))) + .wrap_err("Failed to get `id`")?; + + let resources = ModConfigResources { + init: get_global(state, b"script\0") + .and_then(|s| s.map(PathBuf::from).ok_or_else(|| eyre::eyre!("Got `nil`"))) + .wrap_err("Failed to get `script`.")?, + data: get_global(state, b"data\0") + .wrap_err("Failed to get `data`.")? + .map(PathBuf::from), + localization: get_global(state, b"localization\0") + .wrap_err("Failed to get `localization`")? + .map(PathBuf::from), + }; + + lua::lua_close(state); + + (mod_id, resources) + }; + + Ok(ret) +} + +// Extracts the mod configuration from the mod archive. +// This may either be a proper `dtmt.cfg`, or the legacy `.mod` ID file. +// +// It also returns the directory where this file was found, used as root path. This +// allows flexibility in what the directory structure is exactly, since many people +// still end up creating tarbombs and Nexus does its own re-packaging. +#[tracing::instrument(skip(archive))] +fn extract_mod_config(archive: &mut ZipArchive) -> Result<(ModConfig, String)> { + if let Some(name) = find_archive_file(archive, "dtmt.cfg") { + let mut f = archive + .by_name(&name) + .wrap_err("Failed to read mod config from archive")?; + + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err("Failed to read mod config from archive")?; + + let data = String::from_utf8(buf).wrap_err("Mod config is not valid UTF-8")?; + + let cfg = serde_sjson::from_str(&data).wrap_err("Failed to deserialize mod config")?; + let root = name + .strip_suffix("dtmt.cfg") + .expect("String must end with that suffix") + .to_string(); + + Ok((cfg, root)) + } else if let Some(name) = find_archive_file(archive, ".mod") { + let (mod_id, resources) = { + let mut f = archive + .by_name(&name) + .wrap_err("Failed to read `.mod` file from archive")?; + + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err("Failed to read `.mod` file from archive")?; + + let data = String::from_utf8(buf).wrap_err("`.mod` file is not valid UTF-8")?; + parse_mod_id_file(&data).wrap_err("Invalid `.mod` file")? + }; + + let cfg = ModConfig { + bundled: false, + dir: PathBuf::new(), + id: mod_id.clone(), + name: mod_id, + summary: String::new(), + version: String::new(), + description: None, + author: None, + image: None, + categories: Vec::new(), + packages: Vec::new(), + resources, + depends: Vec::new(), + }; + let root = if let Some(index) = name.rfind('/') { + name[..index].to_string() + } else { + String::new() + }; + + Ok((cfg, root)) + } else { + eyre::bail!( + "Mod needs either a config file or `.mod` file. \ + Please get in touch with the author to provide a properly packaged mod." + ); + } +} + +#[tracing::instrument(skip(archive))] +fn extract_bundled_mod( + archive: &mut ZipArchive, + root: String, + dest: impl AsRef + std::fmt::Debug, +) -> Result>> { + let files: HashMap> = { + let name = archive + .file_names() + .find(|name| name.ends_with("files.sjson")) + .map(|s| s.to_string()) + .ok_or_else(|| eyre::eyre!("archive does not contain file index"))?; + + let mut f = archive + .by_name(&name) + .wrap_err("Failed to read file index from archive")?; + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err("Failed to read file index from archive")?; + + let data = String::from_utf8(buf).wrap_err("File index is not valid UTF-8")?; + serde_sjson::from_str(&data).wrap_err("Failed to deserialize file index")? + }; + + tracing::trace!(?files); + + let dest = dest.as_ref(); + tracing::trace!("Extracting mod archive to {}", dest.display()); + archive + .extract(dest) + .wrap_err_with(|| format!("Failed to extract archive to {}", dest.display()))?; + + let packages = files + .into_iter() + .map(|(name, files)| Arc::new(PackageInfo::new(name, files.into_iter().collect()))) + .collect(); + + tracing::trace!(?packages); + + Ok(packages) +} + +#[tracing::instrument(skip(archive))] +fn extract_legacy_mod( + archive: &mut ZipArchive, + root: String, + dest: impl Into + std::fmt::Debug, +) -> Result<()> { + let dest = dest.into(); + let file_count = archive.len(); + + for i in 0..file_count { + let mut f = archive + .by_index(i) + .wrap_err_with(|| format!("Failed to get file at index {}", i))?; + + let Some(name) = f.enclosed_name().map(|p| p.to_path_buf()) else { + let err = eyre::eyre!("File name in archive is not a safe path value."); + return Err(err).with_suggestion(|| { + "Only use well-known applications to create the ZIP archive, \ + and don't create paths that point outside the archive directory." + }); + }; + + let Ok(suffix) = name.strip_prefix(&root) else { + tracing::warn!( + "Skipping file outside of the mod root directory: {}", + name.display() + ); + continue; + }; + let name = dest.join(suffix); + + if f.is_dir() { + // The majority of errors will actually be "X already exists". + // But rather than filter them invidually, we just ignore all of them. + // If there is a legitimate error of "couldn't create X", it will eventually fail when + // we try to put a file in there. + tracing::trace!("Creating directory '{}'", name.display()); + let _ = std::fs::create_dir_all(&name); + } else { + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err_with(|| format!("Failed to read file '{}'", name.display()))?; + + tracing::trace!("Writing file '{}'", name.display()); + let mut out = std::fs::OpenOptions::new() + .write(true) + .create(true) + .open(&name) + .wrap_err_with(|| format!("Failed to open file '{}'", name.display()))?; + + out.write_all(&buf) + .wrap_err_with(|| format!("Failed to write to '{}'", name.display()))?; + } + } + + Ok(()) +} + +#[tracing::instrument(skip(state))] +pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result { + let data = fs::read(&info.path) + .await + .wrap_err_with(|| format!("Failed to read file {}", info.path.display()))?; + let data = Cursor::new(data); + + let nexus = if let Some((_, id, _, _)) = info + .path + .file_name() + .and_then(|s| s.to_str()) + .and_then(NexusApi::parse_file_name) + { + if !state.nexus_api_key.is_empty() { + let api = NexusApi::new(state.nexus_api_key.to_string())?; + let mod_info = api + .mods_id(id) + .await + .wrap_err_with(|| format!("Failed to query mod {} from Nexus", id))?; + Some(NexusInfo::from(mod_info)) + } else { + None + } + } else { + None + }; + + let mut archive = ZipArchive::new(data).wrap_err("Failed to open ZIP archive")?; + + if tracing::enabled!(tracing::Level::DEBUG) { + let names = archive.file_names().fold(String::new(), |mut s, name| { + s.push('\n'); + s.push_str(name); + s + }); + tracing::debug!("Archive contents:{}", names); + } + + let (mod_cfg, root) = + extract_mod_config(&mut archive).wrap_err("Failed to extract mod configuration")?; + tracing::info!("Importing mod {} ({})", mod_cfg.name, mod_cfg.id); + tracing::debug!(root, ?mod_cfg); + + let image = if let Some(path) = &mod_cfg.image { + let name = archive + .file_names() + .find(|name| name.ends_with(&path.display().to_string())) + .map(|s| s.to_string()) + .ok_or_else(|| eyre::eyre!("archive does not contain configured image file"))?; + + let mut f = archive + .by_name(&name) + .wrap_err("Failed to read image file from archive")?; + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err("Failed to read file index from archive")?; + + // Druid somehow doesn't return an error compatible with eyre, here. + // So we have to wrap through `Display` manually. + let img = match ImageBuf::from_data(&buf) { + Ok(img) => img, + Err(err) => { + let err = Report::msg(err.to_string()).wrap_err("Invalid image data"); + return Err(err).with_suggestion(|| { + "Supported formats are: PNG, JPEG, Bitmap and WebP".to_string() + }); + } + }; + + Some(img) + } else { + None + }; + + tracing::trace!(?image); + + let mod_dir = state.data_dir.join(state.mod_dir.as_ref()); + let dest = mod_dir.join(&mod_cfg.id); + + tracing::trace!("Creating mods directory {}", dest.display()); + fs::create_dir_all(&dest) + .await + .wrap_err_with(|| format!("Failed to create data directory '{}'", dest.display()))?; + + let packages = if mod_cfg.bundled { + extract_bundled_mod(&mut archive, root, &mod_dir).wrap_err("Failed to extract mod")? + } else { + extract_legacy_mod(&mut archive, root, &dest).wrap_err("Failed to extract legacy mod")?; + + let data = serde_sjson::to_string(&mod_cfg).wrap_err("Failed to serialize mod config")?; + fs::write(dest.join("dtmt.cfg"), &data) + .await + .wrap_err("Failed to write mod config")?; + + Default::default() + }; + + if let Some(nexus) = &nexus { + let data = serde_sjson::to_string(nexus).wrap_err("Failed to serialize Nexus info")?; + let path = dest.join("nexus.sjson"); + fs::write(&path, data.as_bytes()) + .await + .wrap_err_with(|| format!("Failed to write Nexus info to '{}'", path.display()))?; + } + + let info = ModInfo::new(mod_cfg, packages, image, nexus); + Ok(info) +} diff --git a/crates/dtmm/src/controller/mod.rs b/crates/dtmm/src/controller/mod.rs index eacc3ef..9c75e84 100644 --- a/crates/dtmm/src/controller/mod.rs +++ b/crates/dtmm/src/controller/mod.rs @@ -5,7 +5,9 @@ use serde::Deserialize; use tokio::fs; pub mod app; +pub mod deploy; pub mod game; +pub mod import; pub mod worker; #[tracing::instrument] diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index 238b3f3..518c0a8 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -13,7 +13,9 @@ use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::RwLock; use crate::controller::app::*; +use crate::controller::deploy::deploy_mods; use crate::controller::game::*; +use crate::controller::import::import_mod; use crate::state::AsyncAction; use crate::state::ACTION_FINISH_CHECK_UPDATE; use crate::state::ACTION_FINISH_LOAD_INITIAL; diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 9ba838c..8069e6d 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -1,6 +1,7 @@ #![recursion_limit = "256"] #![feature(let_chains)] #![feature(arc_unwrap_or_clone)] +#![feature(iterator_try_collect)] #![windows_subsystem = "windows"] use std::path::PathBuf; diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index c43e973..8a461bd 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -109,7 +109,7 @@ pub(crate) struct ModInfo { #[data(ignore)] pub resources: ModResourceInfo, pub depends: Vector, - pub bundle: bool, + pub bundled: bool, #[data(ignore)] pub nexus: Option, } @@ -130,7 +130,7 @@ impl ModInfo { version: cfg.version, enabled: false, packages, - bundle: cfg.bundle, + bundled: cfg.bundled, image, categories: cfg.categories.into_iter().collect(), resources: ModResourceInfo { diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 7aa2819..7de4418 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -145,7 +145,7 @@ fn build_mod_list() -> impl Widget { let tree = theme::icons::recolor_icon(tree, true, COLOR_YELLOW_LIGHT); - Svg::new(Arc::new(tree)).fix_height(druid::theme::TEXT_SIZE_NORMAL) + Svg::new(tree).fix_height(druid::theme::TEXT_SIZE_NORMAL) }; Either::new( diff --git a/crates/dtmt/src/cmd/migrate.rs b/crates/dtmt/src/cmd/migrate.rs index 8ecd648..2eacded 100644 --- a/crates/dtmt/src/cmd/migrate.rs +++ b/crates/dtmt/src/cmd/migrate.rs @@ -350,7 +350,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> localization: mod_file.localization, }, depends: vec![ModDependency::ID(String::from("DMF"))], - bundle: true, + bundled: true, }; tracing::debug!(?dtmt_cfg); diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index a162d3b..28e4694 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -63,7 +63,7 @@ pub struct ModConfig { #[serde(default)] pub depends: Vec, #[serde(default = "default_true", skip_serializing_if = "is_true")] - pub bundle: bool, + pub bundled: bool, } pub const STEAMAPP_ID: u32 = 1361210; From 15498cc2e0f6ee5288f97a61aa1e14def69c5ff1 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 10 Nov 2023 11:13:08 +0100 Subject: [PATCH 160/335] Move deployment directory for legacy mods This moves it back to its original place at `$game_dir/mods`. --- crates/dtmm/src/controller/deploy.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index 6a804e7..ded840e 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -223,7 +223,7 @@ async fn copy_recursive( #[tracing::instrument(skip(state))] async fn copy_mod_folders(state: Arc) -> Result> { - let bundle_dir = Arc::new(state.game_dir.join("bundle")); + let game_dir = Arc::clone(&state.game_dir); let mut tasks = Vec::new(); @@ -237,11 +237,11 @@ async fn copy_mod_folders(state: Arc) -> Result> { let mod_id = mod_info.id.clone(); let mod_dir = Arc::clone(&state.mod_dir); - let bundle_dir = Arc::clone(&bundle_dir); + let game_dir = Arc::clone(&game_dir); let task = async move { let from = mod_dir.join(&mod_id); - let to = bundle_dir.join("mods").join(&mod_id); + let to = game_dir.join("mods").join(&mod_id); tracing::debug!(from = %from.display(), to = %to.display(), "Copying legacy mod '{}'", mod_id); let _ = fs::create_dir_all(&to).await; From 13e77a209784ddc62eecd90c70171e7bdc74d8c0 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 12 Nov 2023 23:20:10 +0100 Subject: [PATCH 161/335] Use template engine to build `mod_data.lua` The string-building version became too complex to maintain properly. --- Cargo.lock | 10 +++ crates/dtmm/Cargo.toml | 1 + crates/dtmm/assets/mod_data.lua.j2 | 27 ++++++ crates/dtmm/assets/mod_main.lua | 10 +-- crates/dtmm/src/controller/deploy.rs | 119 +++++++++++++-------------- 5 files changed, 99 insertions(+), 68 deletions(-) create mode 100644 crates/dtmm/assets/mod_data.lua.j2 diff --git a/Cargo.lock b/Cargo.lock index 011fa6b..8385e67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -906,6 +906,7 @@ dependencies = [ "futures", "lazy_static", "luajit2-sys", + "minijinja", "nexusmods", "oodle", "path-slash", @@ -2084,6 +2085,15 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minijinja" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "208758577ef2c86cf5dd3e85730d161413ec3284e2d73b2ef65d9a24d9971bcb" +dependencies = [ + "serde", +] + [[package]] name = "minimal-lexical" version = "0.2.1" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 1842a62..5f60220 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -35,3 +35,4 @@ ansi-parser = "0.9.0" string_template = "0.2.1" luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } async-recursion = "1.0.5" +minijinja = "1.0.10" diff --git a/crates/dtmm/assets/mod_data.lua.j2 b/crates/dtmm/assets/mod_data.lua.j2 new file mode 100644 index 0000000..9f87ad1 --- /dev/null +++ b/crates/dtmm/assets/mod_data.lua.j2 @@ -0,0 +1,27 @@ +return { +{% for mod in mods %} +{ + id = "{{ mod.id }}", + name = "{{ mod.name }}", + bundled = {{ mod.bundled }}, + packages = { + {% for pkg in mod.packages %} + "{{ pkg }}", + {% endfor %} + }, + run = function() + {% if mod.data is none %} + return dofile("{{ mod.init }}") + {% else %} + new_mod("{{ mod.id }}", { + mod_script = "{{ mod.init }}", + mod_data = "{{ mod.data }}", + {% if not mod.localization is none %} + mod_localization = "{{ mod.localization }}", + {% endif %} + }) + {% endif %} + end, +}, +{% endfor %} +} diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua index 6dbf3e2..a17f5be 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua @@ -61,14 +61,14 @@ local function patch_mod_loading_state() if state == "load_package" and package_manager:update() then log("StateBootLoadMods", "Packages loaded, loading mods") self._state = "load_mods" - local ModLoader = require("scripts/mods/dml/init") + local DML = require("scripts/mods/dml/init") local mod_data = require("scripts/mods/mod_data") - local mod_loader = ModLoader:new(mod_data, self._parent:gui()) + local mod_loader = DML.create_loader(mod_data, self._parent:gui()) - self._mod_loader = mod_loader + self._dml = DML Managers.mod = mod_loader - elseif state == "load_mods" and self._mod_loader:update(dt) then + elseif state == "load_mods" and self._dml.update(Managers.mod, dt) then log("StateBootLoadMods", "Mods loaded, exiting") return true, false end @@ -112,7 +112,7 @@ local require_store = {} -- This token is treated as a string template and filled by DTMM during deployment. -- This allows hiding unsafe I/O functions behind a setting. -- It's also a valid table definition, thereby degrading gracefully when not replaced. -local is_io_enabled = { { is_io_enabled } } -- luacheck: ignore 113 +local is_io_enabled = {{ is_io_enabled }} -- luacheck: ignore 113 local lua_libs = { debug = debug, os = { diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index ded840e..53c4ef1 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -8,7 +8,7 @@ use color_eyre::eyre::Context; use color_eyre::{eyre, Help, Report, Result}; use futures::StreamExt; use futures::{stream, TryStreamExt}; -use path_slash::PathBufExt; +use minijinja::Environment; use sdk::filetype::lua; use sdk::filetype::package::Package; use sdk::murmur::Murmur64; @@ -201,10 +201,10 @@ async fn copy_recursive( async move { if is_dir { tracing::trace!("Creating directory '{}'", dest.display()); - fs::create_dir(&dest) - .await - .map(|_| ()) - .wrap_err_with(|| format!("Failed to create directory '{}'", dest.display())) + // Instead of trying to filter "already exists" errors out explicitly, + // we just ignore all. It'll fail eventually with the next copy operation. + let _ = fs::create_dir(&dest).await; + Ok(()) } else { tracing::trace!("Copying file '{}' -> '{}'", path.display(), dest.display()); fs::copy(&path, &dest).await.map(|_| ()).wrap_err_with(|| { @@ -262,67 +262,60 @@ async fn copy_mod_folders(state: Arc) -> Result> { Ok(ids) } -fn build_mod_data_lua(state: Arc) -> String { - let mut lua = String::from("return {\n"); - - // DMF is handled explicitely by the loading procedures, as it actually drives most of that - // and should therefore not show up in the load order. - for mod_info in state.mods.iter().filter(|m| m.id != "dml" && m.enabled) { - lua.push_str(" {\n name = \""); - lua.push_str(&mod_info.name); - - lua.push_str("\",\n id = \""); - lua.push_str(&mod_info.id); - - lua.push_str("\",\n bundled = \""); - if mod_info.bundled { - lua.push_str("true"); - } else { - lua.push_str("false"); - } - - lua.push_str("\",\n run = function()\n"); - - let resources = &mod_info.resources; - if resources.data.is_some() || resources.localization.is_some() { - lua.push_str(" new_mod(\""); - lua.push_str(&mod_info.id); - lua.push_str("\", {\n mod_script = \""); - lua.push_str(&resources.init.to_slash_lossy()); - - if let Some(data) = resources.data.as_ref() { - lua.push_str("\",\n mod_data = \""); - lua.push_str(&data.to_slash_lossy()); - } - - if let Some(localization) = &resources.localization { - lua.push_str("\",\n mod_localization = \""); - lua.push_str(&localization.to_slash_lossy()); - } - - lua.push_str("\",\n })\n"); - } else { - lua.push_str(" return dofile(\""); - lua.push_str(&resources.init.to_slash_lossy()); - lua.push_str("\")\n"); - } - - lua.push_str(" end,\n packages = {\n"); - - for pkg_info in &mod_info.packages { - lua.push_str(" \""); - lua.push_str(&pkg_info.name); - lua.push_str("\",\n"); - } - - lua.push_str(" },\n },\n"); +fn build_mod_data_lua(state: Arc) -> Result { + #[derive(Serialize)] + struct TemplateDataMod { + id: String, + name: String, + bundled: bool, + init: String, + data: Option, + localization: Option, + packages: Vec, } - lua.push('}'); + let mut env = Environment::new(); + env.add_template("mod_data.lua", include_str!("../../assets/mod_data.lua.j2")) + .wrap_err("Failed to compile template for `mod_data.lua`")?; + let tmpl = env + .get_template("mod_data.lua") + .wrap_err("Failed to get template `mod_data.lua`")?; - tracing::debug!("mod_data_lua:\n{}", lua); + let data: Vec = state + .mods + .iter() + .filter_map(|m| { + if m.id == "dml" || !m.enabled { + return None; + } - lua + Some(TemplateDataMod { + id: m.id.clone(), + name: m.name.clone(), + bundled: m.bundled, + init: m.resources.init.to_string_lossy().to_string(), + data: m + .resources + .data + .as_ref() + .map(|p| p.to_string_lossy().to_string()), + localization: m + .resources + .localization + .as_ref() + .map(|p| p.to_string_lossy().to_string()), + packages: m.packages.iter().map(|p| p.name.clone()).collect(), + }) + }) + .collect(); + + let lua = tmpl + .render(minijinja::context!(mods => data)) + .wrap_err("Failed to render template `mod_data.lua`")?; + + tracing::debug!("mod_data.lua:\n{}", lua); + + Ok(lua) } #[tracing::instrument(skip_all)] @@ -340,7 +333,7 @@ async fn build_bundles(state: Arc) -> Result> { let span = tracing::debug_span!("Building mod data script"); let _enter = span.enter(); - let lua = build_mod_data_lua(state.clone()); + let lua = build_mod_data_lua(state.clone()).wrap_err("Failed to build Lua mod data")?; tracing::trace!("Compiling mod data script"); From 3af631348d3950d760f8829811e435a8a2e16f68 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Nov 2023 15:06:19 +0100 Subject: [PATCH 162/335] Fix missing `Mods.original_require` --- crates/dtmm/assets/mod_main.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua index a17f5be..2b329bf 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua @@ -138,7 +138,8 @@ Mods = { -- Fatshark's code scrubs them. -- The loader can then decide to pass them on to mods, or ignore them lua = setmetatable({}, { __index = lua_libs }), - require_store = require_store + require_store = require_store, + original_require = require, } local can_insert = function(filepath, new_result) From 6c9472399562a6613e12da726ede4e45a249bad7 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Nov 2023 15:07:07 +0100 Subject: [PATCH 163/335] Fix Nexusmods API key not being loaded from config --- crates/dtmm/src/controller/import.rs | 5 ++++- crates/dtmm/src/state/delegate.rs | 1 + crates/dtmm/src/util/config.rs | 8 ++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 3e17d4c..05feba9 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -384,7 +384,10 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result for Delegate { state.config_path = Arc::new(config.path); state.data_dir = Arc::new(config.data_dir); state.game_dir = Arc::new(config.game_dir.unwrap_or_default()); + state.nexus_api_key = Arc::new(config.nexus_api_key.unwrap_or_default()); state.is_io_enabled = config.unsafe_io; } diff --git a/crates/dtmm/src/util/config.rs b/crates/dtmm/src/util/config.rs index 3a0d0b2..9affbb6 100644 --- a/crates/dtmm/src/util/config.rs +++ b/crates/dtmm/src/util/config.rs @@ -125,6 +125,9 @@ where .wrap_err_with(|| format!("Invalid config file {}", path.display()))?; cfg.path = path; + + tracing::debug!("Read config file '{}': {:?}", cfg.path.display(), cfg); + Ok(cfg) } Err(err) if err.kind() == ErrorKind::NotFound => { @@ -133,6 +136,11 @@ where .wrap_err_with(|| format!("Failed to read config file {}", path.display()))?; } + tracing::debug!( + "Config file not found at '{}', creating default.", + path.display() + ); + { let parent = default_path .parent() From a228ea465253c0f1b3647343b914fcd9a90e1d13 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Nov 2023 15:38:51 +0100 Subject: [PATCH 164/335] Use mod name from Nexus if necessary Non-bundled mods come without `dtmt.cfg` and therefore no way to provide a user friendly name. Similar to the other fields, use the one from Nexus in that case. --- crates/dtmm/src/state/data.rs | 2 ++ crates/dtmm/src/ui/window/main.rs | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 8a461bd..64fdd28 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -73,6 +73,7 @@ impl From for ModDependency { #[derive(Clone, Data, Debug, Lens, serde::Serialize, serde::Deserialize)] pub(crate) struct NexusInfo { pub id: u64, + pub name: String, pub version: String, pub author: String, pub summary: Arc, @@ -83,6 +84,7 @@ impl From for NexusInfo { fn from(value: NexusMod) -> Self { Self { id: value.mod_id, + name: value.name, version: value.version, author: value.author, summary: Arc::new(value.summary), diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 7de4418..baa8e22 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -135,8 +135,13 @@ fn build_mod_list() -> impl Widget { }) .lens(lens!((usize, Arc, bool), 1).then(ModInfo::enabled.in_arc())); - let name = - Label::raw().lens(lens!((usize, Arc, bool), 1).then(ModInfo::name.in_arc())); + let name = Label::dynamic(|info: &Arc, _| { + info.nexus + .as_ref() + .map(|n| n.name.clone()) + .unwrap_or_else(|| info.name.clone()) + }) + .lens(lens!((usize, Arc, bool), 1)); let version = { let icon = { From 3cbf383b189937c1909590a83e20317e575e6720 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 Nov 2023 16:19:07 +0100 Subject: [PATCH 165/335] Use version number from Nexus import Non-bundled mods come without a `dtmt.cfg`, and therefore without a version number. But we need a version number at import to compare to for the Nexus update check. --- crates/dtmm/src/controller/import.rs | 17 ++++++++++++----- lib/nexusmods/src/lib.rs | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 05feba9..c5b5948 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -372,7 +372,7 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result Result Result Result Result Date: Thu, 16 Nov 2023 00:06:16 +0100 Subject: [PATCH 166/335] Prevent excessive debug logs --- crates/dtmm/src/controller/worker.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index 518c0a8..152030f 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -38,7 +38,9 @@ async fn handle_action( action_queue: Arc>>, ) { while let Some(action) = action_queue.write().await.recv().await { - tracing::debug!(?action); + if cfg!(debug_assertions) && !matches!(action, AsyncAction::Log(_)) { + tracing::debug!(?action); + } let event_sink = event_sink.clone(); match action { From 845b0114bbd6d728489dba308a188a9860bab3cb Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 Nov 2023 14:29:06 +0100 Subject: [PATCH 167/335] Delay mod loading The initial implementation of DML ended up loading mods quite late, which did give it the benefit of all `Manager`s being available. This change therefore moves mod loading until after those are initialized. But contrary to old DML, we still create a separate game state to make sure the game doesn't advance until mods are loaded. This avoids race conditions like the one where LogMeIn needs to come early in the load order. --- crates/dtmm/assets/mod_main.lua | 186 ++++++++++++++++---------------- 1 file changed, 92 insertions(+), 94 deletions(-) diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua index 2b329bf..e4006f6 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua @@ -11,100 +11,6 @@ local log = function(category, format, ...) end end --- Patch `GameStateMachine.init` to add our own state for loading mods. --- In the future, Fatshark might provide us with a dedicated way to do this. -local function patch_mod_loading_state() - local StateBootSubStateBase = require("scripts/game_states/boot/state_boot_sub_state_base") - - -- A necessary override. - -- The original does not proxy `dt` to `_state_update`, but we need that. - StateBootSubStateBase.update = function(self, dt) - local done, error = self:_state_update(dt) - local params = self._params - - if error then - return StateError, { error } - elseif done then - local next_index = params.sub_state_index + 1 - params.sub_state_index = next_index - local next_state_data = params.states[next_index] - - if next_state_data then - return next_state_data[1], self._params - else - self._parent:sub_states_done() - end - end - end - - local StateBootLoadMods = class("StateBootLoadMods", "StateBootSubStateBase") - - StateBootLoadMods.on_enter = function(self, parent, params) - log("StateBootLoadMods", "Entered") - StateBootLoadMods.super.on_enter(self, parent, params) - - local state_params = self:_state_params() - local package_manager = state_params.package_manager - - self._state = "load_package" - self._package_manager = package_manager - self._package_handles = { - ["packages/mods"] = package_manager:load("packages/mods", "StateBootLoadMods", nil), - ["packages/dml"] = package_manager:load("packages/dml", "StateBootLoadMods", nil), - } - end - - StateBootLoadMods._state_update = function(self, dt) - local state = self._state - local package_manager = self._package_manager - - if state == "load_package" and package_manager:update() then - log("StateBootLoadMods", "Packages loaded, loading mods") - self._state = "load_mods" - local DML = require("scripts/mods/dml/init") - - local mod_data = require("scripts/mods/mod_data") - local mod_loader = DML.create_loader(mod_data, self._parent:gui()) - - self._dml = DML - Managers.mod = mod_loader - elseif state == "load_mods" and self._dml.update(Managers.mod, dt) then - log("StateBootLoadMods", "Mods loaded, exiting") - return true, false - end - - return false, false - end - - local GameStateMachine = require("scripts/foundation/utilities/game_state_machine") - - local patched = false - - local GameStateMachine_init = GameStateMachine.init - GameStateMachine.init = function(self, parent, start_state, params, ...) - if not patched then - log("mod_main", "Injecting mod loading state") - patched = true - - -- Hardcoded position after `StateRequireScripts`. - -- We do want to wait until then, so that most of the game's core - -- systems are at least loaded and can be hooked, even if they aren't - -- running, yet. - local pos = 4 - table.insert(params.states, pos, { - StateBootLoadMods, - { - package_manager = params.package_manager, - }, - }) - end - - GameStateMachine_init(self, parent, start_state, params, ...) - end - - log("mod_main", "Mod patching complete") -end - log("mod_main", "Initializing mods...") local require_store = {} @@ -199,6 +105,98 @@ end require("scripts/main") log("mod_main", "'scripts/main' loaded") +-- Inject our state into the game. The state needs to run after `StateGame._init_managers`, +-- since some parts of DMF, and presumably other mods, depend on some of those managers to exist. +local function patch_mod_loading_state() + local StateBootSubStateBase = require("scripts/game_states/boot/state_boot_sub_state_base") + local StateBootLoadDML = class("StateBootLoadDML", "StateBootSubStateBase") + local StateGameLoadMods = class("StateGameLoadMods") + + StateBootLoadDML.on_enter = function(self, parent, params) + log("StateBootLoadDML", "Entered") + StateBootLoadDML.super.on_enter(self, parent, params) + + local state_params = self:_state_params() + local package_manager = state_params.package_manager + + self._package_manager = package_manager + self._package_handles = { + ["packages/mods"] = package_manager:load("packages/mods", "StateBootDML", nil), + ["packages/dml"] = package_manager:load("packages/dml", "StateBootDML", nil), + } + end + + StateBootLoadDML._state_update = function(self, dt) + local package_manager = self._package_manager + + if package_manager:update() then + local DML = require("scripts/mods/dml/init") + local mod_data = require("scripts/mods/mod_data") + local mod_loader = DML.create_loader(mod_data) + Managers.mod = mod_loader + log("StateBootLoadDML", "DML loaded, exiting") + return true, false + end + + return false, false + end + + + function StateGameLoadMods:on_enter(_, params) + log("StateGameLoadMods", "Entered") + self._next_state = require("scripts/game_states/game/state_splash") + self._next_state_params = params + end + + function StateGameLoadMods:update(main_dt) + local state = self._loading_state + + -- We're relying on the fact that DML internally makes sure + -- that `Managers.mod:update()` is being called appropriately. + -- The implementation as of this writing is to hook `StateGame.update`. + if Managers.mod:all_mods_loaded() then + Log.info("StateGameLoadMods", "Mods loaded, exiting") + return self._next_state, self._next_state_params + end + end + + local GameStateMachine = require("scripts/foundation/utilities/game_state_machine") + local GameStateMachine_init = GameStateMachine.init + GameStateMachine.init = function(self, parent, start_state, params, creation_context, state_change_callbacks, name) + if name == "Main" then + log("mod_main", "Injecting StateBootLoadDML") + + -- Hardcoded position after `StateRequireScripts`. + -- We need to wait until then to even begin most of our stuff, + -- so that most of the game's core systems are at least loaded and can be hooked, + -- even if they aren't running, yet. + local pos = 4 + table.insert(params.states, pos, { + StateBootLoadDML, + { + package_manager = params.package_manager, + }, + }) + + GameStateMachine_init(self, parent, start_state, params, creation_context, state_change_callbacks, name) + elseif name == "Game" then + log("mod_main", "Injection StateGameLoadMods") + -- The second time around, we want to be the first, so we pass our own + -- 'start_state'. + -- We can't just have the state machine be initialized and then change its `_next_state`, as by the end of + -- `init`, a bunch of stuff will already be initialized. + GameStateMachine_init(self, parent, StateGameLoadMods, params, creation_context, state_change_callbacks, name) + -- And since we're done now, we can revert the function to its original + GameStateMachine.init = GameStateMachine_init + + return + else + -- In all other cases, simply call the original + GameStateMachine_init(self, parent, start_state, params, creation_context, state_change_callbacks, name) + end + end +end + -- Override `init` to run our injection function init() patch_mod_loading_state() From 8ecca087dea5de0f1bb473f5580ddd968486e7f8 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 24 Nov 2023 00:50:56 +0100 Subject: [PATCH 168/335] dtmm: Use `dtmt.cfg` for non-bundled mods Closes #144. --- crates/dtmm/src/controller/import.rs | 106 +++++++++++++++------------ 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index c5b5948..131d01f 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -31,7 +31,7 @@ fn find_archive_file( // from legacy mods. // 1. Create a global function `new_mod` that stores // the relevant bits in global variables. -// 2. Run the `.mod` file, which will merely return a table. +// 2. Run the `.mod` file, which will return a table. // 3. Run the `run` function from that table. // 4. Access the global variables from #1. #[tracing::instrument] @@ -203,6 +203,37 @@ end // still end up creating tarbombs and Nexus does its own re-packaging. #[tracing::instrument(skip(archive))] fn extract_mod_config(archive: &mut ZipArchive) -> Result<(ModConfig, String)> { + let legacy_mod_data = if let Some(name) = find_archive_file(archive, ".mod") { + let (mod_id, resources) = { + let mut f = archive + .by_name(&name) + .wrap_err("Failed to read `.mod` file from archive")?; + + let mut buf = Vec::with_capacity(f.size() as usize); + f.read_to_end(&mut buf) + .wrap_err("Failed to read `.mod` file from archive")?; + + let data = String::from_utf8(buf).wrap_err("`.mod` file is not valid UTF-8")?; + parse_mod_id_file(&data) + .wrap_err("Invalid `.mod` file") + .note( + "The `.mod` file's `run` function may not contain any additional logic \ + besides the default.", + ) + .suggestion("Contact the mod author to fix this.")? + }; + + let root = if let Some(index) = name.rfind('/') { + name[..index].to_string() + } else { + String::new() + }; + + Some((mod_id, resources, root)) + } else { + None + }; + if let Some(name) = find_archive_file(archive, "dtmt.cfg") { let mut f = archive .by_name(&name) @@ -214,52 +245,30 @@ fn extract_mod_config(archive: &mut ZipArchive) -> Result<(Mo let data = String::from_utf8(buf).wrap_err("Mod config is not valid UTF-8")?; - let cfg = serde_sjson::from_str(&data).wrap_err("Failed to deserialize mod config")?; - let root = name - .strip_suffix("dtmt.cfg") - .expect("String must end with that suffix") - .to_string(); + let mut cfg: ModConfig = serde_sjson::from_str(&data) + .wrap_err("Failed to deserialize mod config") + .suggestion("Contact the mod author to fix this.")?; - Ok((cfg, root)) - } else if let Some(name) = find_archive_file(archive, ".mod") { - let (mod_id, resources) = { - let mut f = archive - .by_name(&name) - .wrap_err("Failed to read `.mod` file from archive")?; + if let Some((mod_id, resources, root)) = legacy_mod_data { + if cfg.id != mod_id { + let err = eyre::eyre!("Mod ID in `dtmt.cfg` does not match mod ID in `.mod` file"); + return Err(err).suggestion("Contact the mod author to fix this."); + } - let mut buf = Vec::with_capacity(f.size() as usize); - f.read_to_end(&mut buf) - .wrap_err("Failed to read `.mod` file from archive")?; + cfg.resources = resources; - let data = String::from_utf8(buf).wrap_err("`.mod` file is not valid UTF-8")?; - parse_mod_id_file(&data).wrap_err("Invalid `.mod` file")? - }; - - let cfg = ModConfig { - bundled: false, - dir: PathBuf::new(), - id: mod_id.clone(), - name: mod_id, - summary: String::new(), - version: String::new(), - description: None, - author: None, - image: None, - categories: Vec::new(), - packages: Vec::new(), - resources, - depends: Vec::new(), - }; - let root = if let Some(index) = name.rfind('/') { - name[..index].to_string() + Ok((cfg, root)) } else { - String::new() - }; + let root = name + .strip_suffix("dtmt.cfg") + .expect("String must end with that suffix") + .to_string(); - Ok((cfg, root)) + Ok((cfg, root)) + } } else { eyre::bail!( - "Mod needs either a config file or `.mod` file. \ + "Mod needs a config file or `.mod` file. \ Please get in touch with the author to provide a properly packaged mod." ); } @@ -322,11 +331,11 @@ fn extract_legacy_mod( .wrap_err_with(|| format!("Failed to get file at index {}", i))?; let Some(name) = f.enclosed_name().map(|p| p.to_path_buf()) else { - let err = eyre::eyre!("File name in archive is not a safe path value."); - return Err(err).with_suggestion(|| { + let err = eyre::eyre!("File name in archive is not a safe path value.").suggestion( "Only use well-known applications to create the ZIP archive, \ - and don't create paths that point outside the archive directory." - }); + and don't create paths that point outside the archive directory.", + ); + return Err(err); }; let Ok(suffix) = name.strip_prefix(&root) else { @@ -430,10 +439,11 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result img, Err(err) => { - let err = Report::msg(err.to_string()).wrap_err("Invalid image data"); - return Err(err).with_suggestion(|| { - "Supported formats are: PNG, JPEG, Bitmap and WebP".to_string() - }); + let err = Report::msg(err.to_string()) + .wrap_err("Invalid image data") + .note("Supported formats are: PNG, JPEG, Bitmap and WebP") + .suggestion("Contact the mod author to fix this"); + return Err(err); } }; From 0b3c92d19e9d1ffe6bc09c7e901dd654c2b87154 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 24 Nov 2023 11:49:11 +0100 Subject: [PATCH 169/335] dtmm: Fetch mod image from Nexus Closes #129. --- crates/dtmm/src/controller/import.rs | 56 ++++++++++++++++++++-------- crates/dtmm/src/state/data.rs | 2 + lib/nexusmods/src/lib.rs | 12 +++++- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 131d01f..68c54a5 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -27,6 +27,16 @@ fn find_archive_file( path } +fn image_data_to_buffer(data: impl AsRef<[u8]>) -> Result { + // Druid somehow doesn't return an error compatible with eyre, here. + // So we have to wrap through `Display` manually. + ImageBuf::from_data(data.as_ref()).map_err(|err| { + Report::msg(err.to_string()) + .wrap_err("Invalid image data") + .suggestion("Supported formats are: PNG, JPEG, Bitmap and WebP") + }) +} + // Runs the content of a `.mod` file to extract what data we can get // from legacy mods. // 1. Create a global function `new_mod` that stores @@ -420,6 +430,9 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result img, - Err(err) => { - let err = Report::msg(err.to_string()) - .wrap_err("Invalid image data") - .note("Supported formats are: PNG, JPEG, Bitmap and WebP") - .suggestion("Contact the mod author to fix this"); - return Err(err); - } - }; - + let img = image_data_to_buffer(buf)?; Some(img) + } else if let Some((nexus, _)) = &nexus { + let api = NexusApi::new(state.nexus_api_key.to_string())?; + let url = nexus.picture_url.as_ref(); + let data = api + .picture(url) + .await + .wrap_err_with(|| format!("Failed to download Nexus image from '{}'", url))?; + + let img = image_data_to_buffer(&data)?; + + let name = "image.bin"; + let path = dest.join(name); + match fs::write(&path, &data).await { + Ok(_) => { + mod_cfg.image = Some(name.into()); + Some(img) + } + Err(err) => { + let err = Report::new(err).wrap_err(format!( + "Failed to write Nexus picture to file '{}'", + path.display() + )); + tracing::error!("{:?}", err); + None + } + } } else { None }; tracing::trace!(?image); - let mod_dir = state.data_dir.join(state.mod_dir.as_ref()); - let dest = mod_dir.join(&mod_cfg.id); - tracing::trace!("Creating mods directory {}", dest.display()); fs::create_dir_all(&dest) .await diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 64fdd28..e5b70c4 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -78,6 +78,7 @@ pub(crate) struct NexusInfo { pub author: String, pub summary: Arc, pub description: Arc, + pub picture_url: Arc, } impl From for NexusInfo { @@ -89,6 +90,7 @@ impl From for NexusInfo { author: value.author, summary: Arc::new(value.summary), description: Arc::new(value.description), + picture_url: Arc::new(value.picture_url.into()), } } } diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index 145435d..1407fca 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -4,7 +4,7 @@ use std::convert::Infallible; use lazy_static::lazy_static; use regex::Regex; use reqwest::header::{HeaderMap, HeaderValue, InvalidHeaderValue}; -use reqwest::{Client, RequestBuilder, Url}; +use reqwest::{Client, IntoUrl, RequestBuilder, Url}; use serde::Deserialize; use thiserror::Error; @@ -102,6 +102,16 @@ impl Api { self.send(req).await } + #[tracing::instrument(skip(self))] + pub async fn picture(&self, url: impl IntoUrl + std::fmt::Debug) -> Result> { + let res = self.client.get(url).send().await?.error_for_status()?; + + res.bytes() + .await + .map(|bytes| bytes.to_vec()) + .map_err(From::from) + } + pub fn parse_file_name>( name: S, ) -> Option<(String, u64, String, OffsetDateTime)> { From 246564d00f032ba75ec1de7a9d3377d4c65df529 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 24 Nov 2023 13:54:19 +0100 Subject: [PATCH 170/335] Fetch file version from Nexus When importing an archive file downloaded from Nexus, the file name does include a version field. But, presumably for compatibility reasons, Nexus replaces special characters with `-`, so that this field doesn't match common schemes like `1.0.0`. So instead we use the also included update timestamp to find the corresponding file info from Nexus and use the version data from that. Closes #131. --- crates/dtmm/src/controller/import.rs | 20 ++++++++++++++++--- lib/nexusmods/src/lib.rs | 24 +++++++++++++++++++++++ lib/nexusmods/src/types.rs | 29 ++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 131d01f..522d94a 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -381,7 +381,7 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result version, + Err(err) => { + let err = Report::new(err); + tracing::warn!( + "Failed to fetch version for Nexus download. \ + Falling back to file name:\n{:?}", + err + ); + version + } + }; + + let info = NexusInfo::from(mod_info); + tracing::debug!(version, ?info); + Some((info, version)) } else { None diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index 145435d..0cca768 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -39,6 +39,8 @@ pub enum Error { Infallible(#[from] Infallible), #[error("invalid NXM URL '{}': {0}", .1.as_str())] InvalidNXM(&'static str, Url), + #[error("{0}")] + Custom(String), } pub type Result = std::result::Result; @@ -102,6 +104,28 @@ impl Api { self.send(req).await } + #[tracing::instrument(skip(self))] + pub async fn file_version(&self, id: u64, timestamp: T) -> Result + where + T: std::fmt::Debug, + OffsetDateTime: PartialEq, + { + let url = BASE_URL_GAME.join(&format!("mods/{id}/files.json"))?; + let req = self.client.get(url); + let files: FileList = self.send(req).await?; + + let Some(file) = files + .files + .into_iter() + .find(|file| file.updated_timestamp == timestamp) + else { + let err = Error::Custom("Timestamp does not match any file".into()); + return Err(err); + }; + + Ok(file.version) + } + pub fn parse_file_name>( name: S, ) -> Option<(String, u64, String, OffsetDateTime)> { diff --git a/lib/nexusmods/src/types.rs b/lib/nexusmods/src/types.rs index b0dffd5..b88911e 100644 --- a/lib/nexusmods/src/types.rs +++ b/lib/nexusmods/src/types.rs @@ -64,6 +64,35 @@ pub struct Mod { // pub contains_adult_content: bool, } +#[derive(Debug, Deserialize)] +pub struct File { + pub id: Vec, + pub uid: u64, + pub file_id: u64, + pub name: String, + pub version: String, + pub category_id: u64, + pub category_name: String, + pub is_primary: bool, + pub size: u64, + pub file_name: String, + #[serde(with = "time::serde::timestamp")] + pub updated_timestamp: OffsetDateTime, + pub mod_version: String, + pub external_virus_scan_url: String, + pub description: String, + pub size_kb: u64, + pub size_in_bytes: u64, + pub changelog_html: String, + pub content_preview_link: String, +} + +#[derive(Debug, Deserialize)] +pub struct FileList { + pub files: Vec, + // pub file_updates: Vec, +} + #[derive(Debug, Deserialize)] pub struct DownloadLink { pub name: String, From 440d0f505bf4390c3d70c46eea3ae6f3fb821fb9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 27 Nov 2023 15:38:40 +0100 Subject: [PATCH 171/335] Add changelog entry --- CHANGELOG.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 4d1861c..31910d3 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -16,6 +16,7 @@ - dtmt: add utility to migrate mod projects - dtmm: reset dtkit-patch installations - sdk: implement decompiling Lua files +- dtmm: fetch file version for Nexus mods === Fixed From edd363c3a621d705776ac8b8eeb9b97920504b5d Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 27 Nov 2023 15:40:03 +0100 Subject: [PATCH 172/335] Add changelog entry --- CHANGELOG.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 4d1861c..ee02b6c 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -16,6 +16,7 @@ - dtmt: add utility to migrate mod projects - dtmm: reset dtkit-patch installations - sdk: implement decompiling Lua files +- dtmm: fetch cover image for Nexus mods === Fixed From 316a5aaa33cc650fb8fce8e3be10b20229d586d9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 27 Nov 2023 16:12:11 +0100 Subject: [PATCH 173/335] ci: Fix undefined variable --- .ci/tasks/build.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 5e60d7f..bb96775 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -19,7 +19,9 @@ install_artifact() { cd "repo" -if [ -n "${PR:-}" ]; then +PR=${PR:-} + +if [ -n "$PR" ]; then title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$(cat .git/ref || echo "HEAD")" 2>/dev/null || echo 'manual')" else From b64ff9043c661c690bf23e5e8f7c89c793bcd8cb Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 27 Nov 2023 16:12:41 +0100 Subject: [PATCH 174/335] ci: Create build artifacts for commits on master --- .ci/pipelines/base-pipeline.yml | 43 ------- .ci/pipelines/base.yml | 204 ++++++++++++++++++++++++++++++++ Justfile | 4 +- 3 files changed, 206 insertions(+), 45 deletions(-) delete mode 100644 .ci/pipelines/base-pipeline.yml create mode 100644 .ci/pipelines/base.yml diff --git a/.ci/pipelines/base-pipeline.yml b/.ci/pipelines/base-pipeline.yml deleted file mode 100644 index f231fb7..0000000 --- a/.ci/pipelines/base-pipeline.yml +++ /dev/null @@ -1,43 +0,0 @@ ---- - -# The base pipeline that runs continuously, checks for branches and -# creates a new pipeline instance for each of them. - -resource_types: -- name: gitea-pr - type: registry-image - source: - repository: registry.local:5000/gitea-pr - -resources: -- name: repo-pr - type: gitea-pr - source: - access_token: ((gitea_api_key)) - owner: ((owner)) - repo: ((repo)) - url: https://git.sclu1034.dev -- name: repo - type: git - source: - uri: https://git.sclu1034.dev/bitsquid_dt/dtmt - -jobs: - - name: set-pipelines - plan: - - in_parallel: - - get: repo-pr - trigger: true - - get: repo - - load_var: prs - file: repo-pr/prs.json - - across: - - var: pr - values: ((.:prs)) - set_pipeline: dtmt-pr - file: repo/.ci/pipelines/pr.yml - vars: - pr: ((.:pr)) - gitea_api_key: ((gitea_api_key)) - instance_vars: - number: ((.:pr.number)) diff --git a/.ci/pipelines/base.yml b/.ci/pipelines/base.yml new file mode 100644 index 0000000..a731222 --- /dev/null +++ b/.ci/pipelines/base.yml @@ -0,0 +1,204 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/cappyzawa/concourse-pipeline-jsonschema/master/concourse_jsonschema.json#/definitions/Config +--- + +# The actual CI pipeline that is run per branch +resource_types: +- name: gitea-package + type: registry-image + source: + repository: registry.local:5000/gitea-package + +- name: gitea-status + type: registry-image + source: + repository: registry.local:5000/gitea-status + +- name: gitea-pr + type: registry-image + source: + repository: registry.local:5000/gitea-pr + + +resources: +- name: repo + type: git + source: + uri: http://forgejo:3000/bitsquid_dt/dtmt + branch: master + +- name: repo-pr + type: gitea-pr + source: + access_token: ((gitea_api_key)) + owner: ((owner)) + repo: ((repo)) + url: https://git.sclu1034.dev + +- name: gitea-package + type: gitea-package + source: + access_token: ((gitea_api_key)) + url: http://forgejo:3000 + owner: bitsquid_dt + type: generic + name: dtmt + + +- name: status-build-msvc + type: gitea-status + source: + access_token: ((gitea_api_key)) + url: http://forgejo:3000 + owner: bitsquid_dt + repo: dtmt + context: build/msvc + description: "Build for the target platform: msvc" + +- name: status-build-linux + type: gitea-status + source: + access_token: ((gitea_api_key)) + url: http://forgejo:3000 + owner: bitsquid_dt + repo: dtmt + context: build/linux + description: "Build for the target platform: linux" + + +jobs: +- name: set-pipelines + plan: + - in_parallel: + - get: repo-pr + trigger: true + + - get: repo + + - load_var: prs + file: repo-pr/prs.json + + - across: + - var: pr + values: ((.:prs)) + set_pipeline: dtmt-pr + file: repo/.ci/pipelines/pr.yml + vars: + pr: ((.:pr)) + gitea_api_key: ((gitea_api_key)) + instance_vars: + number: ((.:pr.number)) + + +- name: build-msvc + on_success: + put: state-success + resource: status-build-msvc + no_get: true + params: + state: success + sha: ((.:git_sha)) + + on_failure: + put: state-failure + resource: status-build-msvc + no_get: true + params: + state: failure + sha: ((.:git_sha)) + + plan: + - get: repo + trigger: true + + - load_var: git_sha + file: repo/.git/ref + + - put: state-pending + resource: status-build-msvc + no_get: true + params: + state: pending + sha: ((.:git_sha)) + + - task: build + file: repo/.ci/tasks/build.yml + vars: + target: msvc + gitea_url: http://forgejo:3000 + gitea_api_key: ((gitea_api_key)) + + - load_var: version_number + reveal: true + file: artifact/version + + - put: package + resource: gitea-package + no_get: true + inputs: + - artifact + params: + version: ((.:version_number)) + fail_fast: true + override: true + globs: + - artifact/dtmt + - artifact/dtmm + - artifact/*.exe + - artifact/*.sha256 + +- name: build-linux + on_success: + put: state-success + resource: status-build-linux + no_get: true + params: + state: success + sha: ((.:git_sha)) + + on_failure: + put: state-failure + resource: status-build-linux + no_get: true + params: + state: failure + sha: ((.:git_sha)) + + plan: + - get: repo + trigger: true + + - load_var: git_sha + file: repo/.git/ref + + - put: state-pending + resource: status-build-linux + no_get: true + params: + state: pending + sha: ((.:git_sha)) + + - task: build + file: repo/.ci/tasks/build.yml + vars: + target: linux + gitea_url: http://forgejo:3000 + gitea_api_key: ((gitea_api_key)) + + - load_var: version_number + reveal: true + file: artifact/version + + - put: package + resource: gitea-package + no_get: true + inputs: + - artifact + params: + version: ((.:version_number)) + fail_fast: true + override: true + globs: + - artifact/dtmt + - artifact/dtmm + - artifact/*.exe + - artifact/*.sha256 diff --git a/Justfile b/Justfile index 70af61a..12fde29 100644 --- a/Justfile +++ b/Justfile @@ -30,8 +30,8 @@ ci-image-linux: set-base-pipeline: fly -t {{fly_target}} set-pipeline \ - --pipeline dtmt-prs \ - --config .ci/pipelines/base-pipeline.yml \ + --pipeline dtmt \ + --config .ci/pipelines/base.yml \ -v gitea_api_key=${GITEA_API_KEY} \ -v owner=bitsquid_dt \ -v repo=dtmt From d95f5dfe1f26ae2c2f81d588ad358ea82cb0d405 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Nov 2023 21:51:29 +0100 Subject: [PATCH 175/335] ci: Improve caching setup for image building The `RUN --mount` flag is much easier to maintain than having to set up all the crates. --- .ci/Dockerfile.linux | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/.ci/Dockerfile.linux b/.ci/Dockerfile.linux index 9e93e44..b46f7ff 100644 --- a/.ci/Dockerfile.linux +++ b/.ci/Dockerfile.linux @@ -1,35 +1,7 @@ FROM dtmt-ci-base-linux -# Create dummy crates and copy their Cargo.toml, so that dependencies can be cached -RUN set -e; \ - cargo new --bin crates/dtmt; \ - cargo new --bin crates/dtmm; \ - cargo new --lib lib/dtmt-shared; \ - cargo new --lib lib/nexusmods; \ - cargo new --lib lib/sdk; \ - cargo new --lib lib/serde_sjson; \ - cargo new --lib lib/ansi-parser - -COPY Cargo.toml Cargo.lock /src/dtmt/ -COPY crates/dtmt/Cargo.toml /src/dtmt/crates/dtmt/ -COPY crates/dtmm/Cargo.toml /src/dtmt/crates/dtmm/ -COPY lib/dtmt-shared/Cargo.toml /src/dtmt/lib/dtmt-shared/ -COPY lib/nexusmods/Cargo.toml /src/dtmt/lib/nexusmods/ -COPY lib/sdk/Cargo.toml /src/dtmt/lib/sdk/ -COPY lib/serde_sjson/Cargo.toml /src/dtmt/lib/serde_sjson/ -COPY lib/ansi-parser/Cargo.toml /src/dtmt/lib/ansi-parser/ - -# Crates with build scripts cannot be split that way, but they shouldn't change too often -COPY lib/luajit2-sys /src/dtmt/lib/luajit2-sys -COPY lib/oodle /src/dtmt/lib/oodle -# color-eyre needs to be copied, too, then, as it's used by `oodle` -COPY lib/color-eyre /src/dtmt/lib/color-eyre -COPY --from=dtmt-ci-base-linux /src/*.lib /src/dtmt/lib/oodle/ - -RUN cargo build --release --locked -RUN rm -r crates lib - COPY . /src/dtmt COPY --from=dtmt-ci-base-linux /src/*.lib /src/*.so /src/dtmt/lib/oodle/ - -RUN cargo build --release --locked +RUN --mount=type=cache,id=cargo-registry,target=/cargo/registry \ + --mount=type=cache,id=cargo-target,target=/src/dtmt/target \ + cargo build --release --locked From 227dff03efa67006393614bcd0fa1a7b1fbdd3f5 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Nov 2023 21:52:55 +0100 Subject: [PATCH 176/335] ci: Fix base pipeline Concourse doesn't allow variables to be undefined. --- .ci/pipelines/base.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.ci/pipelines/base.yml b/.ci/pipelines/base.yml index a731222..474c090 100644 --- a/.ci/pipelines/base.yml +++ b/.ci/pipelines/base.yml @@ -123,6 +123,7 @@ jobs: - task: build file: repo/.ci/tasks/build.yml vars: + pr: "" target: msvc gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) @@ -180,6 +181,7 @@ jobs: - task: build file: repo/.ci/tasks/build.yml vars: + pr: "" target: linux gitea_url: http://forgejo:3000 gitea_api_key: ((gitea_api_key)) From 3555fc83d227052eda16bfbb6f81aa4c7aeede97 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Nov 2023 21:53:33 +0100 Subject: [PATCH 177/335] ci: Rework MSVC image building This moves process of downloading and setting up the Windows libraries to a separate build step, freeing up the layers of the final image. --- .ci/image/Dockerfile.linux | 6 ++--- .ci/image/Dockerfile.msvc | 51 ++++++++++++++++++++++++++------------ Justfile | 6 ++--- 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/.ci/image/Dockerfile.linux b/.ci/image/Dockerfile.linux index df10059..5cbaf5d 100644 --- a/.ci/image/Dockerfile.linux +++ b/.ci/image/Dockerfile.linux @@ -8,9 +8,9 @@ RUN set -eux; \ git \ gpg \ jq \ - libatk1.0-dev \ + libatk1.0-dev \ libclang-13-dev \ - libglib2.0-dev \ + libglib2.0-dev \ libgtk-3-dev \ libpango1.0-dev \ libssl-dev \ @@ -22,4 +22,4 @@ RUN set -eux; \ WORKDIR /src/dtmt -COPY *.so *.a /src/ +COPY lib/oodle/*.so lib/oodle/*.a /src/ diff --git a/.ci/image/Dockerfile.msvc b/.ci/image/Dockerfile.msvc index a9eab62..864dc73 100644 --- a/.ci/image/Dockerfile.msvc +++ b/.ci/image/Dockerfile.msvc @@ -1,13 +1,40 @@ # https://jake-shadle.github.io/xwin/ -FROM dtmt-ci-base-linux +FROM debian:bullseye-slim as xwin + +ARG XWIN_VERSION=0.5.0 +ARG XWIN_PREFIX="xwin-$XWIN_VERSION-x86_64-unknown-linux-musl" +ADD https://github.com/Jake-Shadle/xwin/releases/download/$XWIN_VERSION/$XWIN_PREFIX.tar.gz /root/$XWIN_PREFIX.tar.gz + +RUN set -eux; \ + apt-get update; \ + apt-get install --no-install-recommends -y \ + tar \ + ; \ + # Install xwin to cargo/bin via github release. Note you could also just use `cargo install xwin`. + tar -xzv -f /root/$XWIN_PREFIX.tar.gz -C /usr/bin --strip-components=1 $XWIN_PREFIX/xwin; \ + apt-get remove -y --auto-remove; \ + rm -rf \ + /var/lib/apt/lists/* \ + /root/$XWIN_PREFIX.tar.gz; + +RUN set -eux; \ + # Splat the CRT and SDK files to /xwin/crt and /xwin/sdk respectively + xwin \ + --log-level debug \ + --cache-dir /root/.xwin-cache \ + --manifest-version 16 \ + --accept-license \ + splat \ + --output /xwin; \ + rm -rf \ + /root/.xwin-cache; + +FROM dtmt-ci-base-linux as final ENV KEYRINGS /usr/local/share/keyrings -ARG XWIN_VERSION=0.2.11 -ARG XWIN_PREFIX="xwin-$XWIN_VERSION-x86_64-unknown-linux-musl" ADD https://apt.llvm.org/llvm-snapshot.gpg.key /root/llvm-snapshot.gpg.key ADD https://dl.winehq.org/wine-builds/winehq.key /root/winehq.key -ADD https://github.com/Jake-Shadle/xwin/releases/download/$XWIN_VERSION/$XWIN_PREFIX.tar.gz /root/$XWIN_PREFIX.tar.gz RUN set -eux; \ mkdir -p $KEYRINGS; \ @@ -26,7 +53,7 @@ RUN set -eux; \ llvm-13 \ lld-13 \ winehq-staging \ - tar; \ + ; \ # ensure that clang/clang++ are callable directly ln -s clang-13 /usr/bin/clang && ln -s clang /usr/bin/clang++ && ln -s lld-13 /usr/bin/ld.lld; \ # We also need to setup symlinks ourselves for the MSVC shims because they aren't in the debian packages @@ -44,19 +71,15 @@ RUN set -eux; \ update-alternatives --install /usr/bin/ld ld /usr/bin/ld.lld 100; \ rustup target add x86_64-pc-windows-msvc; \ rustup component add rust-src; \ - # Install xwin to cargo/bin via github release. Note you could also just use `cargo install xwin`. - tar -xzv -f /root/$XWIN_PREFIX.tar.gz -C /usr/local/cargo/bin --strip-components=1 $XWIN_PREFIX/xwin; \ - # Splat the CRT and SDK files to /xwin/crt and /xwin/sdk respectively - xwin --accept-license splat --output /xwin; \ # Remove unneeded files to reduce image size apt-get remove -y --auto-remove; \ rm -rf \ - .xwin-cache \ - /usr/local/cargo/bin/xwin \ - /root/$XWIN_PREFIX.tar.gz \ /var/lib/apt/lists/* \ /root/*.key; +COPY lib/oodle/*.lib /src +COPY --from=xwin /xwin /xwin + # Note that we're using the full target triple for each variable instead of the # simple CC/CXX/AR shorthands to avoid issues when compiling any C/C++ code for # build dependencies that need to compile and execute in the host environment @@ -83,7 +106,3 @@ ENV CFLAGS_x86_64_pc_windows_msvc="$CL_FLAGS" \ # Run wineboot just to setup the default WINEPREFIX so we don't do it every # container run RUN wine wineboot --init - -WORKDIR /src/dtmt - -COPY *.lib /src diff --git a/Justfile b/Justfile index 12fde29..967619b 100644 --- a/Justfile +++ b/Justfile @@ -18,13 +18,13 @@ build-image-linux: ci-image: ci-image-msvc ci-image-linux -ci-image-msvc: ci-image-linux - docker build -t dtmt-ci-base-msvc -f .ci/image/Dockerfile.msvc .ci/image +ci-image-msvc: + docker build -t dtmt-ci-base-msvc -f .ci/image/Dockerfile.msvc . docker tag dtmt-ci-base-msvc registry.sclu1034.dev/dtmt-ci-base-msvc docker push registry.sclu1034.dev/dtmt-ci-base-msvc ci-image-linux: - docker build -t dtmt-ci-base-linux -f .ci/image/Dockerfile.linux .ci/image + docker build -t dtmt-ci-base-linux -f .ci/image/Dockerfile.linux . docker tag dtmt-ci-base-linux registry.sclu1034.dev/dtmt-ci-base-linux docker push registry.sclu1034.dev/dtmt-ci-base-linux From 4be37f6e5e89640c4bca763fb216d6a9f21753ae Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 28 Nov 2023 23:23:09 +0100 Subject: [PATCH 178/335] ci: Combine Dockerfiles into multi-stage build Closes #134. --- .ci/image/{Dockerfile.msvc => Dockerfile} | 34 +++++++++++++++++++++-- .ci/image/Dockerfile.linux | 25 ----------------- Justfile | 15 +++++----- 3 files changed, 38 insertions(+), 36 deletions(-) rename .ci/image/{Dockerfile.msvc => Dockerfile} (85%) delete mode 100644 .ci/image/Dockerfile.linux diff --git a/.ci/image/Dockerfile.msvc b/.ci/image/Dockerfile similarity index 85% rename from .ci/image/Dockerfile.msvc rename to .ci/image/Dockerfile index 864dc73..4e3433b 100644 --- a/.ci/image/Dockerfile.msvc +++ b/.ci/image/Dockerfile @@ -26,10 +26,38 @@ RUN set -eux; \ --accept-license \ splat \ --output /xwin; \ - rm -rf \ - /root/.xwin-cache; + # Even though this build step only exists temporary, to copy the + # final data out of, it still generates a cache entry on the Docker host. + # And to keep that to a minimum, we still delete the stuff we don't need. + rm -rf /root/.xwin-cache; -FROM dtmt-ci-base-linux as final +FROM rust:slim-bullseye as linux + +RUN set -eux; \ + apt-get update; \ + apt-get install --no-install-recommends -y \ + build-essential \ + curl \ + git \ + gpg \ + jq \ + libatk1.0-dev \ + libclang-13-dev \ + libglib2.0-dev \ + libgtk-3-dev \ + libpango1.0-dev \ + libssl-dev \ + libzstd-dev \ + pkg-config; \ + apt-get remove -y --auto-remove; \ + rm -rf /var/lib/apt/lists/*; \ + rustup default nightly + +WORKDIR /src/dtmt + +COPY lib/oodle/*.so lib/oodle/*.a /src/ + +FROM linux as msvc ENV KEYRINGS /usr/local/share/keyrings diff --git a/.ci/image/Dockerfile.linux b/.ci/image/Dockerfile.linux deleted file mode 100644 index 5cbaf5d..0000000 --- a/.ci/image/Dockerfile.linux +++ /dev/null @@ -1,25 +0,0 @@ -FROM rust:slim-bullseye - -RUN set -eux; \ - apt-get update; \ - apt-get install --no-install-recommends -y \ - build-essential \ - curl \ - git \ - gpg \ - jq \ - libatk1.0-dev \ - libclang-13-dev \ - libglib2.0-dev \ - libgtk-3-dev \ - libpango1.0-dev \ - libssl-dev \ - libzstd-dev \ - pkg-config; \ - apt-get remove -y --auto-remove; \ - rm -rf /var/lib/apt/lists/*; \ - rustup default nightly - -WORKDIR /src/dtmt - -COPY lib/oodle/*.so lib/oodle/*.a /src/ diff --git a/Justfile b/Justfile index 967619b..f9b37bc 100644 --- a/Justfile +++ b/Justfile @@ -16,16 +16,15 @@ build-image-msvc: build-image-linux: docker build -f .ci/Dockerfile.linux . -ci-image: ci-image-msvc ci-image-linux - -ci-image-msvc: - docker build -t dtmt-ci-base-msvc -f .ci/image/Dockerfile.msvc . +ci-image: + # The MSVC image depends on the Linux image. So by building that first, + # we actually build both, and cache them, so that "building" the + # Linux image afterwards merely needs to pull the cache. + docker build --target msvc -t dtmt-ci-base-msvc -f .ci/image/Dockerfile . + docker build --target linux -t dtmt-ci-base-linux -f .ci/image/Dockerfile . docker tag dtmt-ci-base-msvc registry.sclu1034.dev/dtmt-ci-base-msvc - docker push registry.sclu1034.dev/dtmt-ci-base-msvc - -ci-image-linux: - docker build -t dtmt-ci-base-linux -f .ci/image/Dockerfile.linux . docker tag dtmt-ci-base-linux registry.sclu1034.dev/dtmt-ci-base-linux + docker push registry.sclu1034.dev/dtmt-ci-base-msvc docker push registry.sclu1034.dev/dtmt-ci-base-linux set-base-pipeline: From b3305e87b8231a42f1e70faa29e1db9d9ef7e996 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 29 Nov 2023 15:21:07 +0100 Subject: [PATCH 179/335] dtmm: Fix importing from `.mod` file --- crates/dtmm/src/controller/import.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 68c54a5..c5bbce7 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -244,6 +244,8 @@ fn extract_mod_config(archive: &mut ZipArchive) -> Result<(Mo None }; + tracing::debug!(?legacy_mod_data); + if let Some(name) = find_archive_file(archive, "dtmt.cfg") { let mut f = archive .by_name(&name) @@ -276,6 +278,24 @@ fn extract_mod_config(archive: &mut ZipArchive) -> Result<(Mo Ok((cfg, root)) } + } else if let Some((mod_id, resources, root)) = legacy_mod_data { + let cfg = ModConfig { + bundled: false, + dir: PathBuf::new(), + id: mod_id.clone(), + name: mod_id, + summary: "A mod for the game Warhammer 40,000: Darktide".into(), + version: "N/A".into(), + description: None, + author: None, + image: None, + categories: Vec::new(), + packages: Vec::new(), + resources, + depends: Vec::new(), + }; + + Ok((cfg, root)) } else { eyre::bail!( "Mod needs a config file or `.mod` file. \ From 61e78e97185907e4300c7bb4fd399a37e62dcc86 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 29 Nov 2023 15:21:34 +0100 Subject: [PATCH 180/335] dtmm: Fix writing Nexus image to disk --- crates/dtmm/src/controller/import.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index c5bbce7..5ca4068 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -434,6 +434,8 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result Result Date: Wed, 29 Nov 2023 15:21:07 +0100 Subject: [PATCH 181/335] dtmm: Fix importing from `.mod` file --- crates/dtmm/src/controller/import.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 522d94a..68f2b05 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -234,6 +234,8 @@ fn extract_mod_config(archive: &mut ZipArchive) -> Result<(Mo None }; + tracing::debug!(?legacy_mod_data); + if let Some(name) = find_archive_file(archive, "dtmt.cfg") { let mut f = archive .by_name(&name) @@ -266,6 +268,24 @@ fn extract_mod_config(archive: &mut ZipArchive) -> Result<(Mo Ok((cfg, root)) } + } else if let Some((mod_id, resources, root)) = legacy_mod_data { + let cfg = ModConfig { + bundled: false, + dir: PathBuf::new(), + id: mod_id.clone(), + name: mod_id, + summary: "A mod for the game Warhammer 40,000: Darktide".into(), + version: "N/A".into(), + description: None, + author: None, + image: None, + categories: Vec::new(), + packages: Vec::new(), + resources, + depends: Vec::new(), + }; + + Ok((cfg, root)) } else { eyre::bail!( "Mod needs a config file or `.mod` file. \ From a0fe5d3f816396d586f443dfc5e881f6e374ee9d Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 29 Nov 2023 15:37:37 +0100 Subject: [PATCH 182/335] nexusmods: Fix File type --- lib/nexusmods/src/lib.rs | 2 +- lib/nexusmods/src/types.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index 0cca768..06c1f01 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -117,7 +117,7 @@ impl Api { let Some(file) = files .files .into_iter() - .find(|file| file.updated_timestamp == timestamp) + .find(|file| file.uploaded_timestamp == timestamp) else { let err = Error::Custom("Timestamp does not match any file".into()); return Err(err); diff --git a/lib/nexusmods/src/types.rs b/lib/nexusmods/src/types.rs index b88911e..db0f624 100644 --- a/lib/nexusmods/src/types.rs +++ b/lib/nexusmods/src/types.rs @@ -77,13 +77,13 @@ pub struct File { pub size: u64, pub file_name: String, #[serde(with = "time::serde::timestamp")] - pub updated_timestamp: OffsetDateTime, + pub uploaded_timestamp: OffsetDateTime, pub mod_version: String, pub external_virus_scan_url: String, pub description: String, pub size_kb: u64, pub size_in_bytes: u64, - pub changelog_html: String, + pub changelog_html: Option, pub content_preview_link: String, } From 6f848bb837c028e090278b605ed6be0aba54149f Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 27 Nov 2023 15:32:39 +0100 Subject: [PATCH 183/335] dtmm: Implement NXM URI handler Closes #31. --- CHANGELOG.adoc | 1 + Cargo.lock | 30 ++++++++ crates/dtmm/Cargo.toml | 34 +++++---- crates/dtmm/src/controller/import.rs | 29 +++++++- crates/dtmm/src/controller/worker.rs | 26 ++++++- crates/dtmm/src/main.rs | 105 +++++++++++++++++++++++++-- crates/dtmm/src/state/delegate.rs | 19 +++++ crates/dtmm/src/util/log.rs | 16 ++-- lib/nexusmods/src/lib.rs | 34 ++++++--- 9 files changed, 252 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 1fd318f..358bc33 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -18,6 +18,7 @@ - sdk: implement decompiling Lua files - dtmm: fetch cover image for Nexus mods - dtmm: fetch file version for Nexus mods +- dtmm: handle `nxm://` URIs via IPC and import the corresponding mod === Fixed diff --git a/Cargo.lock b/Cargo.lock index 8385e67..1817853 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -218,6 +218,15 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.64.0" @@ -895,6 +904,7 @@ version = "0.1.0" dependencies = [ "ansi-parser", "async-recursion", + "bincode", "bitflags 1.3.2", "clap", "color-eyre", @@ -904,6 +914,7 @@ dependencies = [ "druid-widget-nursery", "dtmt-shared", "futures", + "interprocess", "lazy_static", "luajit2-sys", "minijinja", @@ -1833,6 +1844,19 @@ dependencies = [ "web-sys", ] +[[package]] +name = "interprocess" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" +dependencies = [ + "cfg-if", + "libc", + "rustc_version", + "to_method", + "winapi", +] + [[package]] name = "intl-memoizer" version = "0.5.1" @@ -3517,6 +3541,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "to_method" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" + [[package]] name = "tokio" version = "1.33.0" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 5f60220..c159295 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -6,33 +6,35 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +ansi-parser = "0.9.0" +async-recursion = "1.0.5" +bincode = "1.3.3" bitflags = "1.3.2" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } color-eyre = "0.6.2" +colors-transform = "0.2.11" confy = "0.5.1" druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] } +druid-widget-nursery = "0.1" dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } futures = "0.3.25" -oodle = { path = "../../lib/oodle", version = "*" } -sdk = { path = "../../lib/sdk", version = "*" } +interprocess = { version = "1.2.1", default-features = false } +lazy_static = "1.4.0" +luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } +minijinja = "1.0.10" nexusmods = { path = "../../lib/nexusmods", version = "*" } -serde_sjson = { path = "../../lib/serde_sjson", version = "*" } +oodle = { path = "../../lib/oodle", version = "*" } +path-slash = "0.2.1" +sdk = { path = "../../lib/sdk", version = "*" } serde = { version = "1.0.152", features = ["derive", "rc"] } +serde_sjson = { path = "../../lib/serde_sjson", version = "*" } +string_template = "0.2.1" +strip-ansi-escapes = "0.1.1" +time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] } tokio = { version = "1.23.0", features = ["rt", "fs", "tracing", "sync"] } +tokio-stream = { version = "0.1.12", features = ["fs"] } tracing = "0.1.37" tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } -zip = "0.6.4" -tokio-stream = { version = "0.1.12", features = ["fs"] } -path-slash = "0.2.1" -time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] } -strip-ansi-escapes = "0.1.1" -lazy_static = "1.4.0" -colors-transform = "0.2.11" usvg = "0.25.0" -druid-widget-nursery = "0.1" -ansi-parser = "0.9.0" -string_template = "0.2.1" -luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } -async-recursion = "1.0.5" -minijinja = "1.0.10" +zip = "0.6.4" diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index cef7ee4..0a7a6ce 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -405,11 +405,10 @@ fn extract_legacy_mod( } #[tracing::instrument(skip(state))] -pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result { +pub(crate) async fn import_from_file(state: ActionState, info: FileInfo) -> Result { let data = fs::read(&info.path) .await .wrap_err_with(|| format!("Failed to read file {}", info.path.display()))?; - let data = Cursor::new(data); let nexus = if let Some((_, id, version, timestamp)) = info .path @@ -450,6 +449,32 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result { + let url = uri + .parse() + .wrap_err_with(|| format!("Invalid Uri '{}'", uri))?; + + let api = NexusApi::new(state.nexus_api_key.to_string())?; + let (mod_info, file_info, data) = api + .handle_nxm(url) + .await + .wrap_err_with(|| format!("Failed to download mod from NXM uri '{}'", uri))?; + + let nexus = NexusInfo::from(mod_info); + import_mod(state, Some((nexus, file_info.version)), data).await +} + +#[tracing::instrument(skip(state))] +pub(crate) async fn import_mod( + state: ActionState, + nexus: Option<(NexusInfo, String)>, + data: Vec, +) -> Result { + let data = Cursor::new(data); let mut archive = ZipArchive::new(data).wrap_err("Failed to open ZIP archive")?; if tracing::enabled!(tracing::Level::DEBUG) { diff --git a/crates/dtmm/src/controller/worker.rs b/crates/dtmm/src/controller/worker.rs index 152030f..6ee498f 100644 --- a/crates/dtmm/src/controller/worker.rs +++ b/crates/dtmm/src/controller/worker.rs @@ -15,7 +15,7 @@ use tokio::sync::RwLock; use crate::controller::app::*; use crate::controller::deploy::deploy_mods; use crate::controller::game::*; -use crate::controller::import::import_mod; +use crate::controller::import::*; use crate::state::AsyncAction; use crate::state::ACTION_FINISH_CHECK_UPDATE; use crate::state::ACTION_FINISH_LOAD_INITIAL; @@ -57,7 +57,7 @@ async fn handle_action( .expect("failed to send command"); }), AsyncAction::AddMod(state, info) => tokio::spawn(async move { - match import_mod(state, info) + match import_from_file(state, info) .await .wrap_err("Failed to import mod") { @@ -186,6 +186,28 @@ async fn handle_action( let _ = f.write_all(&line).await; } }), + AsyncAction::NxmDownload(state, uri) => tokio::spawn(async move { + match import_from_nxm(state, uri) + .await + .wrap_err("Failed to handle NXM URI") + { + Ok(mod_info) => { + event_sink + .write() + .await + .submit_command( + ACTION_FINISH_ADD_MOD, + SingleUse::new(Arc::new(mod_info)), + Target::Auto, + ) + .expect("failed to send command"); + } + Err(err) => { + tracing::error!("{:?}", err); + send_error(event_sink.clone(), err).await; + } + } + }), }; } } diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 8069e6d..b9c1daf 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -10,12 +10,13 @@ use std::sync::Arc; use clap::parser::ValueSource; use clap::{command, value_parser, Arg}; use color_eyre::eyre::{self, Context}; -use color_eyre::{Report, Result}; +use color_eyre::{Report, Result, Section}; use druid::AppLauncher; +use interprocess::local_socket::{LocalSocketListener, LocalSocketStream}; use tokio::sync::RwLock; use crate::controller::worker::work_thread; -use crate::state::AsyncAction; +use crate::state::{AsyncAction, ACTION_HANDLE_NXM}; use crate::state::{Delegate, State}; use crate::ui::theme; use crate::util::log::LogLevel; @@ -29,6 +30,37 @@ mod util { } mod ui; +// As explained in https://docs.rs/interprocess/latest/interprocess/local_socket/enum.NameTypeSupport.html +// namespaces are supported on both platforms we care about: Windows and Linux. +const IPC_ADDRESS: &str = "@dtmm.sock"; + +#[tracing::instrument] +fn notify_nxm_download( + uri: impl AsRef + std::fmt::Debug, + level: Option, +) -> Result<()> { + util::log::create_tracing_subscriber(level, None); + + tracing::debug!("Received Uri '{}', sending to main process.", uri.as_ref()); + + let mut stream = LocalSocketStream::connect(IPC_ADDRESS) + .wrap_err_with(|| format!("Failed to connect to '{}'", IPC_ADDRESS)) + .suggestion("Make sure the main window is open.")?; + + tracing::debug!("Connected to main process at '{}'", IPC_ADDRESS); + + bincode::serialize_into(&mut stream, uri.as_ref()).wrap_err("Failed to send URI")?; + + // We don't really care what the message is, we just need an acknowledgement. + let _: String = bincode::deserialize_from(&mut stream).wrap_err("Failed to receive reply")?; + + tracing::info!( + "Notified DTMM with uri '{}'. Check the main window.", + uri.as_ref() + ); + Ok(()) +} + #[tracing::instrument] fn main() -> Result<()> { color_eyre::install()?; @@ -53,15 +85,25 @@ fn main() -> Result<()> { .value_parser(value_parser!(LogLevel)) .default_value("info"), ) + .arg( + Arg::new("nxm") + .help("An `nxm://` URI to download") + .required(false), + ) .get_matches(); - let (log_tx, log_rx) = tokio::sync::mpsc::unbounded_channel(); let level = if matches.value_source("log-level") == Some(ValueSource::DefaultValue) { None } else { matches.get_one::("log-level").cloned() }; - util::log::create_tracing_subscriber(log_tx, level); + + if let Some(uri) = matches.get_one::("nxm") { + return notify_nxm_download(uri, level).wrap_err("Failed to send NXM Uri to main window."); + } + + let (log_tx, log_rx) = tokio::sync::mpsc::unbounded_channel(); + util::log::create_tracing_subscriber(level, Some(log_tx)); let (action_tx, action_rx) = tokio::sync::mpsc::unbounded_channel(); @@ -84,6 +126,59 @@ fn main() -> Result<()> { let event_sink = launcher.get_external_handle(); + { + let span = tracing::info_span!(IPC_ADDRESS, "nxm-socket"); + let _guard = span.enter(); + + let event_sink = event_sink.clone(); + let server = + LocalSocketListener::bind(IPC_ADDRESS).wrap_err("Failed to create IPC listener")?; + + tracing::debug!("IPC server listening on '{}'", IPC_ADDRESS); + + // Drop the guard here, so that we can re-enter the same span in the thread. + drop(_guard); + + std::thread::Builder::new() + .name("nxm-socket".into()) + .spawn(move || { + let _guard = span.enter(); + + loop { + let res = server.accept().wrap_err_with(|| { + format!("IPC server failed to listen on '{}'", IPC_ADDRESS) + }); + + match res { + Ok(mut stream) => { + let res = bincode::deserialize_from(&mut stream) + .wrap_err("Failed to read message") + .and_then(|uri: String| { + tracing::trace!(uri, "Received NXM uri"); + + event_sink + .submit_command(ACTION_HANDLE_NXM, uri, druid::Target::Auto) + .wrap_err("Failed to start NXM download") + }); + match res { + Ok(()) => { + let _ = bincode::serialize_into(&mut stream, "Ok"); + } + Err(err) => { + tracing::error!("{:?}", err); + let _ = bincode::serialize_into(&mut stream, "Error"); + } + } + } + Err(err) => { + tracing::error!("Failed to receive client connection: {:?}", err) + } + } + } + }) + .wrap_err("Failed to create thread")?; + } + std::thread::Builder::new() .name("work-thread".into()) .spawn(move || { @@ -97,7 +192,7 @@ fn main() -> Result<()> { } } }) - .wrap_err("Work thread panicked")?; + .wrap_err("Failed to create thread")?; launcher.launch(State::new()).map_err(Report::new) } diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index 47d56e8..e30b878 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -32,6 +32,7 @@ pub(crate) const ACTION_START_RESET_DEPLOYMENT: Selector = pub(crate) const ACTION_FINISH_RESET_DEPLOYMENT: Selector = Selector::new("dtmm.action.finish-reset-deployment"); +pub(crate) const ACTION_HANDLE_NXM: Selector = Selector::new("dtmm.action.handle-nxm"); pub(crate) const ACTION_ADD_MOD: Selector = Selector::new("dtmm.action.add-mod"); pub(crate) const ACTION_FINISH_ADD_MOD: Selector>> = Selector::new("dtmm.action.finish-add-mod"); @@ -97,6 +98,7 @@ pub(crate) enum AsyncAction { CheckUpdates(ActionState), LoadInitial((PathBuf, bool)), Log((ActionState, Vec)), + NxmDownload(ActionState, String), } impl std::fmt::Debug for AsyncAction { @@ -116,6 +118,9 @@ impl std::fmt::Debug for AsyncAction { path, is_default ), AsyncAction::Log(_) => write!(f, "AsyncAction::Log(_)"), + AsyncAction::NxmDownload(_, uri) => { + write!(f, "AsyncAction::NxmDownload(_state, {})", uri) + } } } } @@ -250,6 +255,20 @@ impl AppDelegate for Delegate { Handled::Yes } + cmd if cmd.is(ACTION_HANDLE_NXM) => { + let uri = cmd + .get(ACTION_HANDLE_NXM) + .expect("command type match but didn't contain the expected value"); + + if self + .sender + .send(AsyncAction::NxmDownload(state.clone().into(), uri.clone())) + .is_err() + { + tracing::error!("Failed to queue action to download NXM mod"); + } + Handled::Yes + } cmd if cmd.is(ACTION_ADD_MOD) => { let info = cmd .get(ACTION_ADD_MOD) diff --git a/crates/dtmm/src/util/log.rs b/crates/dtmm/src/util/log.rs index fa4a643..4b7c15a 100644 --- a/crates/dtmm/src/util/log.rs +++ b/crates/dtmm/src/util/log.rs @@ -8,7 +8,7 @@ use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::prelude::*; use tracing_subscriber::EnvFilter; -#[derive(Clone, Copy, ValueEnum)] +#[derive(Clone, Copy, Debug, ValueEnum)] pub enum LogLevel { Trace, Debug, @@ -55,7 +55,7 @@ impl std::io::Write for ChannelWriter { } } -pub fn create_tracing_subscriber(tx: UnboundedSender>, level: Option) { +pub fn create_tracing_subscriber(level: Option, tx: Option>>) { let mut env_layer = if let Some(level) = level { EnvFilter::from(level) } else if cfg!(debug_assertions) { @@ -78,11 +78,13 @@ pub fn create_tracing_subscriber(tx: UnboundedSender>, level: Option Result { + let url = BASE_URL_GAME.join(&format!("mods/{mod_id}/files/{file_id}.json"))?; + let req = self.client.get(url); + self.send(req).await + } + pub fn parse_file_name>( name: S, ) -> Option<(String, u64, String, OffsetDateTime)> { @@ -174,7 +181,7 @@ impl Api { self.send(req).await } - pub async fn handle_nxm(&self, url: Url) -> Result<(Mod, Vec)> { + pub async fn handle_nxm(&self, url: Url) -> Result<(Mod, File, Vec)> { let nxm = Self::parse_nxm(url.clone())?; let user = self.user_validate().await?; @@ -183,8 +190,9 @@ impl Api { return Err(Error::InvalidNXM("user_id mismtach", url)); } - let (mod_data, download_info) = futures::try_join!( + let (mod_data, file_info, download_info) = futures::try_join!( self.mods_id(nxm.mod_id), + self.get_file_by_id(nxm.mod_id, nxm.file_id), self.mods_download_link(nxm.mod_id, nxm.file_id, nxm.key, nxm.expires) )?; @@ -195,7 +203,7 @@ impl Api { let req = self.client.get(download_url); let data = req.send().await?.bytes().await?; - Ok((mod_data, data.to_vec())) + Ok((mod_data, file_info, data.to_vec())) } pub fn parse_nxm(nxm: Url) -> Result { @@ -204,17 +212,20 @@ impl Api { } // Now it makes sense, why Nexus calls this field `game_domain_name`, when it's just - // another path segmentin the regular API calls. + // another path segment in the regular API calls. if nxm.host_str() != Some(GAME_ID) { return Err(Error::InvalidNXM("Invalid game domain name", nxm)); } let Some(mut segments) = nxm.path_segments() else { - return Err(Error::InvalidNXM("Cannot be a base", nxm)); + return Err(Error::InvalidNXM("Missing path segments", nxm)); }; if segments.next() != Some("mods") { - return Err(Error::InvalidNXM("Unexpected path segment", nxm)); + return Err(Error::InvalidNXM( + "Unexpected path segment, expected 'mods'", + nxm, + )); } let Some(mod_id) = segments.next().and_then(|id| id.parse().ok()) else { @@ -222,7 +233,10 @@ impl Api { }; if segments.next() != Some("files") { - return Err(Error::InvalidNXM("Unexpected path segment", nxm)); + return Err(Error::InvalidNXM( + "Unexpected path segment, expected 'files'", + nxm, + )); } let Some(file_id) = segments.next().and_then(|id| id.parse().ok()) else { @@ -237,7 +251,7 @@ impl Api { } let Some(key) = query.get("key") else { - return Err(Error::InvalidNXM("Missing 'key'", nxm)); + return Err(Error::InvalidNXM("Missing query field 'key'", nxm)); }; let expires = query @@ -245,12 +259,12 @@ impl Api { .and_then(|expires| expires.parse().ok()) .and_then(|expires| OffsetDateTime::from_unix_timestamp(expires).ok()); let Some(expires) = expires else { - return Err(Error::InvalidNXM("Missing 'expires'", nxm)); + return Err(Error::InvalidNXM("Missing query field 'expires'", nxm)); }; let user_id = query.get("user_id").and_then(|id| id.parse().ok()); let Some(user_id) = user_id else { - return Err(Error::InvalidNXM("Missing 'user_id'", nxm)); + return Err(Error::InvalidNXM("Missing query field 'user_id'", nxm)); }; Ok(Nxm { From 031c03480dd6661ae705a1a7a8936655489f55d8 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 30 Nov 2023 17:50:31 +0100 Subject: [PATCH 184/335] dtmm: Add .desktop file A basic Desktop Entry file for the Linux, which includes the configuration for the `nxm://` scheme handling. --- crates/dtmm/assets/dtmm.desktop | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 crates/dtmm/assets/dtmm.desktop diff --git a/crates/dtmm/assets/dtmm.desktop b/crates/dtmm/assets/dtmm.desktop new file mode 100644 index 0000000..4c2e0a9 --- /dev/null +++ b/crates/dtmm/assets/dtmm.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=DTMM +GenericName=Mod Manager +Comment=A graphical mod manager for Warhammer 40,000: Darktide +Exec=dtmm %u +Type=Application +Keywords=Mod; +StartupNotify=true +Categories=Utility; +MimeType=x-scheme-handler/nxm; From 4c6ad1aaed7472e71a39e4d4173dc1013b2480a1 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 30 Nov 2023 18:11:53 +0100 Subject: [PATCH 185/335] Reduce debug verbosity Prevent binary buffers inflating the log output. --- crates/dtmm/src/controller/import.rs | 2 +- crates/dtmm/src/state/data.rs | 35 +++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 0a7a6ce..27aebdf 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -468,7 +468,7 @@ pub(crate) async fn import_from_nxm(state: ActionState, uri: String) -> Result, diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index e5b70c4..6b5a706 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -95,7 +95,7 @@ impl From for NexusInfo { } } -#[derive(Clone, Data, Debug, Lens)] +#[derive(Clone, Data, Lens)] pub(crate) struct ModInfo { pub id: String, pub name: String, @@ -118,6 +118,39 @@ pub(crate) struct ModInfo { pub nexus: Option, } +impl std::fmt::Debug for ModInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ModInfo") + .field("id", &self.id) + .field("name", &self.name) + .field("summary", &self.summary) + .field( + "description", + &(match &self.description { + Some(desc) => format!("Some(String[0..{}])", desc.len()), + None => "None".to_string(), + }), + ) + .field("categories", &self.categories) + .field("author", &self.author) + .field( + "image", + &(match &self.image { + Some(image) => format!("Some(ImageBuf[{}x{}])", image.width(), image.height()), + None => "None".to_string(), + }), + ) + .field("version", &self.version) + .field("enabled", &self.enabled) + .field("packages", &format!("Vec[0..{}]", self.packages.len())) + .field("resources", &self.resources) + .field("depends", &self.depends) + .field("bundled", &self.bundled) + .field("nexus", &self.nexus) + .finish() + } +} + impl ModInfo { pub fn new( cfg: ModConfig, From 5278041ddbb83127f44e896cab95b8841eda4067 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 4 Dec 2023 13:40:00 +0100 Subject: [PATCH 186/335] Use Nexus mod name when available Fixes #154. --- crates/dtmm/src/ui/window/main.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index baa8e22..ad808cb 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -307,13 +307,11 @@ fn build_mod_details_info() -> impl Widget { // Force the label to take up the entire details' pane width, // so that we can center-align it. .expand_width() - .lens(ModInfo::name.in_arc()); + .lens(NexusInfoLens::new(NexusInfo::name, ModInfo::name).in_arc()); let summary = Label::raw() .with_line_break_mode(LineBreaking::WordWrap) .lens(NexusInfoLens::new(NexusInfo::summary, ModInfo::summary).in_arc()); - // TODO: Image/icon? - let version_line = Label::dynamic(|info: &Arc, _| { let author = info .nexus @@ -366,8 +364,6 @@ fn build_mod_details_info() -> impl Widget { .must_fill_main_axis(true) .cross_axis_alignment(CrossAxisAlignment::Start) .with_child(image) - // .with_spacer(4.) - // .with_flex_child(details, 1.) .with_child(details) }, Flex::column, From 57771617ffb7ffbb137025aa95cc249198f817d9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 4 Dec 2023 16:47:09 +0100 Subject: [PATCH 187/335] dtmm: Add link to open mod on Nexus Closes #157. --- CHANGELOG.adoc | 1 + Cargo.lock | 37 ++++++++++++++++++++++++++++++ crates/dtmm/Cargo.toml | 1 + crates/dtmm/src/state/delegate.rs | 16 +++++++++++++ crates/dtmm/src/ui/theme/colors.rs | 1 + crates/dtmm/src/ui/window/main.rs | 31 ++++++++++++++++++++++--- 6 files changed, 84 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 358bc33..db30865 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -19,6 +19,7 @@ - dtmm: fetch cover image for Nexus mods - dtmm: fetch file version for Nexus mods - dtmm: handle `nxm://` URIs via IPC and import the corresponding mod +- dtmm: Add button to open mod on nexusmods.com === Fixed diff --git a/Cargo.lock b/Cargo.lock index 1817853..93568af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -920,6 +920,7 @@ dependencies = [ "minijinja", "nexusmods", "oodle", + "open", "path-slash", "sdk", "serde", @@ -1882,6 +1883,25 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "itoa" version = "1.0.9" @@ -2340,6 +2360,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "open" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90878fb664448b54c4e592455ad02831e23a3f7e157374a8b95654731aac7349" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + [[package]] name = "openssl" version = "0.10.59" @@ -2478,6 +2509,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e91099d4268b0e11973f036e885d652fb0b21fedcf69738c627f94db6a44f42" +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "pbkdf2" version = "0.11.0" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index c159295..05dc160 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -24,6 +24,7 @@ luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } minijinja = "1.0.10" nexusmods = { path = "../../lib/nexusmods", version = "*" } oodle = { path = "../../lib/oodle", version = "*" } +open = "5.0.1" path-slash = "0.2.1" sdk = { path = "../../lib/sdk", version = "*" } serde = { version = "1.0.152", features = ["derive", "rc"] } diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index e30b878..f3c4711 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -61,6 +61,8 @@ pub(crate) type InitialLoadResult = (Config, Vector>); pub(crate) const ACTION_FINISH_LOAD_INITIAL: Selector>> = Selector::new("dtmm.action.finish-load-initial"); +pub(crate) const ACTION_OPEN_LINK: Selector> = Selector::new("dtmm.action.open-link"); + // A sub-selection of `State`'s fields that are required in `AsyncAction`s and that are // `Send + Sync` pub(crate) struct ActionState { @@ -438,6 +440,20 @@ impl AppDelegate for Delegate { Handled::Yes } + cmd if cmd.is(ACTION_OPEN_LINK) => { + let url = cmd + .get(ACTION_OPEN_LINK) + .expect("command type matched but didn't contain the expected value"); + + if let Err(err) = open::that_detached(Arc::as_ref(url)) { + tracing::error!( + "{:?}", + Report::new(err).wrap_err(format!("Failed to open url '{}'", url)) + ); + } + + Handled::Yes + } _ => Handled::No, } } diff --git a/crates/dtmm/src/ui/theme/colors.rs b/crates/dtmm/src/ui/theme/colors.rs index 078dab0..2c2bf78 100644 --- a/crates/dtmm/src/ui/theme/colors.rs +++ b/crates/dtmm/src/ui/theme/colors.rs @@ -17,6 +17,7 @@ macro_rules! make_color { } make_color!(TOP_BAR_BACKGROUND_COLOR, COLOR_BG1); +make_color!(LINK_COLOR, COLOR_ACCENT); #[allow(dead_code)] pub mod gruvbox_dark { diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index baa8e22..4116932 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -2,6 +2,7 @@ use std::str::FromStr; use std::sync::Arc; use druid::im::Vector; +use druid::text::RichTextBuilder; use druid::widget::{ Checkbox, CrossAxisAlignment, Either, Flex, Image, Label, LineBreaking, List, MainAxisAlignment, Maybe, Scroll, SizedBox, Split, Svg, SvgData, TextBox, ViewSwitcher, @@ -16,9 +17,10 @@ use druid_widget_nursery::WidgetExt as _; use lazy_static::lazy_static; use crate::state::{ - ModInfo, NexusInfo, NexusInfoLens, State, View, ACTION_ADD_MOD, ACTION_SELECTED_MOD_DOWN, - ACTION_SELECTED_MOD_UP, ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE, ACTION_START_CHECK_UPDATE, - ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT, + ModInfo, NexusInfo, NexusInfoLens, State, View, ACTION_ADD_MOD, ACTION_OPEN_LINK, + ACTION_SELECTED_MOD_DOWN, ACTION_SELECTED_MOD_UP, ACTION_SELECT_MOD, ACTION_SET_WINDOW_HANDLE, + ACTION_START_CHECK_UPDATE, ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY, + ACTION_START_RESET_DEPLOYMENT, }; use crate::ui::theme::{self, ColorExt, COLOR_YELLOW_LIGHT}; use crate::ui::widget::border::Border; @@ -345,6 +347,28 @@ fn build_mod_details_info() -> impl Widget { } }); + let nexus_link = Maybe::or_empty(|| { + let link = Label::raw().lens(NexusInfo::id.map( + |id| { + let url = format!("https://nexusmods.com/warhammer40kdarktide/mods/{}", id); + let mut builder = RichTextBuilder::new(); + builder + .push("Open on Nexusmods") + .underline(true) + .text_color(theme::LINK_COLOR) + .link(ACTION_OPEN_LINK.with(Arc::new(url))); + builder.build() + }, + |_, _| {}, + )); + Flex::column() + .cross_axis_alignment(CrossAxisAlignment::Start) + .main_axis_alignment(MainAxisAlignment::Start) + .with_child(link) + .with_spacer(4.) + }) + .lens(ModInfo::nexus.in_arc()); + let details = Flex::column() .cross_axis_alignment(CrossAxisAlignment::Start) .main_axis_alignment(MainAxisAlignment::Start) @@ -352,6 +376,7 @@ fn build_mod_details_info() -> impl Widget { .with_spacer(4.) .with_child(summary) .with_spacer(4.) + .with_child(nexus_link) .with_child(version_line) .with_spacer(4.) .with_child(categories) From 0fb10d9d69387a3fcb539fad5a3a68ef99c7fa20 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 5 Dec 2023 10:11:51 +0100 Subject: [PATCH 188/335] dtmm: Enforce skipping packages for non-bundled mods Fixes #161. --- crates/dtmm/src/controller/import.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 27aebdf..8e47d23 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -269,6 +269,10 @@ fn extract_mod_config(archive: &mut ZipArchive) -> Result<(Mo cfg.resources = resources; + // Enforce that packages are skipped + cfg.bundled = false; + cfg.packages = vec![]; + Ok((cfg, root)) } else { let root = name From a0791cba412942c4d68d4c356b0359441e0d4912 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 5 Dec 2023 10:40:29 +0100 Subject: [PATCH 189/335] dtmm: Extend NexusInfo This provides forward compatibility, in case I ever want to use those fields. If I only added them at the time when they are needed, I would need to come up with a process to load the `nexus.sjson` with missing fields. Closes #130. --- crates/dtmm/src/state/data.rs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/crates/dtmm/src/state/data.rs b/crates/dtmm/src/state/data.rs index 6b5a706..23a4ae0 100644 --- a/crates/dtmm/src/state/data.rs +++ b/crates/dtmm/src/state/data.rs @@ -72,25 +72,35 @@ impl From for ModDependency { #[derive(Clone, Data, Debug, Lens, serde::Serialize, serde::Deserialize)] pub(crate) struct NexusInfo { + pub author: String, + pub category_id: u64, + pub created_timestamp: i64, + pub description: Arc, pub id: u64, pub name: String, - pub version: String, - pub author: String, - pub summary: Arc, - pub description: Arc, pub picture_url: Arc, + pub summary: Arc, + pub uid: u64, + pub updated_timestamp: i64, + pub uploaded_by: String, + pub version: String, } impl From for NexusInfo { fn from(value: NexusMod) -> Self { Self { + author: value.author, + category_id: value.category_id, + created_timestamp: value.created_timestamp.unix_timestamp(), + description: Arc::new(value.description), id: value.mod_id, name: value.name, - version: value.version, - author: value.author, - summary: Arc::new(value.summary), - description: Arc::new(value.description), picture_url: Arc::new(value.picture_url.into()), + summary: Arc::new(value.summary), + uid: value.uid, + updated_timestamp: value.updated_timestamp.unix_timestamp(), + uploaded_by: value.uploaded_by, + version: value.version, } } } @@ -106,14 +116,14 @@ pub(crate) struct ModInfo { pub image: Option, pub version: String, pub enabled: bool, + pub depends: Vector, + pub bundled: bool, #[lens(ignore)] #[data(ignore)] pub packages: Vector>, #[lens(ignore)] #[data(ignore)] pub resources: ModResourceInfo, - pub depends: Vector, - pub bundled: bool, #[data(ignore)] pub nexus: Option, } From 103775e03290d81fcc505a8116e6eb5a6a5a721c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 5 Dec 2023 14:28:12 +0100 Subject: [PATCH 190/335] dtmm: Replace icon for mod update notification Closes #158. --- crates/dtmm/assets/tabler-icons/cloud-download.svg | 8 ++++++++ crates/dtmm/src/ui/theme/icons.rs | 2 +- crates/dtmm/src/ui/window/main.rs | 6 +++--- 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 crates/dtmm/assets/tabler-icons/cloud-download.svg diff --git a/crates/dtmm/assets/tabler-icons/cloud-download.svg b/crates/dtmm/assets/tabler-icons/cloud-download.svg new file mode 100644 index 0000000..5b62734 --- /dev/null +++ b/crates/dtmm/assets/tabler-icons/cloud-download.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/crates/dtmm/src/ui/theme/icons.rs b/crates/dtmm/src/ui/theme/icons.rs index 8d82fc1..50ecbe3 100644 --- a/crates/dtmm/src/ui/theme/icons.rs +++ b/crates/dtmm/src/ui/theme/icons.rs @@ -4,7 +4,7 @@ use usvg::{ }; pub static ALERT_CIRCLE: &str = include_str!("../../../assets/tabler-icons/alert-circle.svg"); -pub static ALERT_TRIANGLE: &str = include_str!("../../../assets/tabler-icons/alert-triangle.svg"); +pub static CLOUD_DOWNLOAD: &str = include_str!("../../../assets/tabler-icons/cloud-download.svg"); pub fn parse_svg(svg: &str) -> Result { let opt = Options::default(); diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index e3ca4b0..022a780 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -22,7 +22,7 @@ use crate::state::{ ACTION_START_CHECK_UPDATE, ACTION_START_DELETE_SELECTED_MOD, ACTION_START_DEPLOY, ACTION_START_RESET_DEPLOYMENT, }; -use crate::ui::theme::{self, ColorExt, COLOR_YELLOW_LIGHT}; +use crate::ui::theme::{self, ColorExt, COLOR_GREEN_LIGHT}; use crate::ui::widget::border::Border; use crate::ui::widget::button::Button; use crate::ui::widget::controller::{ @@ -148,9 +148,9 @@ fn build_mod_list() -> impl Widget { let version = { let icon = { let tree = - theme::icons::parse_svg(theme::icons::ALERT_TRIANGLE).expect("invalid SVG"); + theme::icons::parse_svg(theme::icons::CLOUD_DOWNLOAD).expect("invalid SVG"); - let tree = theme::icons::recolor_icon(tree, true, COLOR_YELLOW_LIGHT); + let tree = theme::icons::recolor_icon(tree, true, COLOR_GREEN_LIGHT); Svg::new(tree).fix_height(druid::theme::TEXT_SIZE_NORMAL) }; From 4ad30a8a12425bd73d59955b6ec5eebc3d704e4c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sat, 11 May 2024 17:44:44 +0200 Subject: [PATCH 191/335] Update color-eyre We no longer need to patch `ansi-parser`. --- Cargo.lock | 2 ++ Cargo.toml | 1 - lib/color-eyre | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 93568af..0faafbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,8 @@ dependencies = [ [[package]] name = "ansi-parser" version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad5bd94a775101bd68c2de2bb28ca2eccd69f395ae3aec4ac4f6da3c1cd2c6a" dependencies = [ "heapless", "nom", diff --git a/Cargo.toml b/Cargo.toml index 451cd09..4bab0cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ members = [ [patch.crates-io] color-eyre = { path = "lib/color-eyre" } -ansi-parser = { path = "lib/ansi-parser" } [profile.dev.package.backtrace] opt-level = 3 diff --git a/lib/color-eyre b/lib/color-eyre index 0fa05eb..8a3d454 160000 --- a/lib/color-eyre +++ b/lib/color-eyre @@ -1 +1 @@ -Subproject commit 0fa05eba9954be223b06468c8760b97e660f9941 +Subproject commit 8a3d4544046f956e8e28c906553dc54d325368c1 From cfee6d91214b0926b4ab07e1773e0131ee2c3852 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sat, 11 May 2024 19:15:45 +0200 Subject: [PATCH 192/335] Fix clippy lints --- Cargo.toml | 1 + crates/dtmm/src/main.rs | 1 - crates/dtmm/src/state/lens.rs | 1 + crates/dtmm/src/ui/theme/colors.rs | 13 ------------- crates/dtmm/src/ui/widget/mod.rs | 5 ----- lib/color-eyre | 2 +- 6 files changed, 3 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4bab0cd..11831e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "lib/serde_sjson", "lib/luajit2-sys", ] +exclude = ["lib/color-eyre"] [patch.crates-io] color-eyre = { path = "lib/color-eyre" } diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index b9c1daf..aa223f0 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -1,6 +1,5 @@ #![recursion_limit = "256"] #![feature(let_chains)] -#![feature(arc_unwrap_or_clone)] #![feature(iterator_try_collect)] #![windows_subsystem = "windows"] diff --git a/crates/dtmm/src/state/lens.rs b/crates/dtmm/src/state/lens.rs index c26c298..983cb2e 100644 --- a/crates/dtmm/src/state/lens.rs +++ b/crates/dtmm/src/state/lens.rs @@ -42,6 +42,7 @@ impl Lens>> for SelectedModLens { /// A Lens that maps an `im::Vector` to `im::Vector<(usize, T)>`, /// where each element in the destination vector includes its index in the /// source vector. +#[allow(dead_code)] pub(crate) struct IndexedVectorLens; impl Lens, Vector<(usize, T)>> for IndexedVectorLens { diff --git a/crates/dtmm/src/ui/theme/colors.rs b/crates/dtmm/src/ui/theme/colors.rs index 2c2bf78..1051539 100644 --- a/crates/dtmm/src/ui/theme/colors.rs +++ b/crates/dtmm/src/ui/theme/colors.rs @@ -69,23 +69,10 @@ pub mod gruvbox_dark { } pub trait ColorExt { - fn lighten(&self, fac: f32) -> Self; fn darken(&self, fac: f32) -> Self; } impl ColorExt for Color { - fn lighten(&self, fac: f32) -> Self { - let (r, g, b, a) = self.as_rgba(); - let rgb = Rgb::from(r as f32, g as f32, b as f32); - let rgb = rgb.lighten(fac); - Self::rgba( - rgb.get_red() as f64, - rgb.get_green() as f64, - rgb.get_blue() as f64, - a, - ) - } - fn darken(&self, fac: f32) -> Self { let (r, g, b, a) = self.as_rgba(); let rgb = Rgb::from(r as f32, g as f32, b as f32); diff --git a/crates/dtmm/src/ui/widget/mod.rs b/crates/dtmm/src/ui/widget/mod.rs index 05c91c7..06ccedd 100644 --- a/crates/dtmm/src/ui/widget/mod.rs +++ b/crates/dtmm/src/ui/widget/mod.rs @@ -2,16 +2,11 @@ use std::path::PathBuf; use std::sync::Arc; use druid::text::Formatter; -use druid::{Data, Widget}; pub mod border; pub mod button; pub mod controller; -pub trait ExtraWidgetExt: Widget + Sized + 'static {} - -impl + 'static> ExtraWidgetExt for W {} - pub(crate) struct PathBufFormatter; impl PathBufFormatter { diff --git a/lib/color-eyre b/lib/color-eyre index 8a3d454..b40962a 160000 --- a/lib/color-eyre +++ b/lib/color-eyre @@ -1 +1 @@ -Subproject commit 8a3d4544046f956e8e28c906553dc54d325368c1 +Subproject commit b40962a61c748756d7da293d9fff26aca019603e From 86ed5c327fc20e1291421ee5a8abfabf1e694752 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 12 May 2024 20:57:09 +0200 Subject: [PATCH 193/335] Update crates `steamlocate` changed its API again. `shlex` deprecated `quote`, but that will be addressed later. --- Cargo.lock | 1055 ++++++++++++++------------ crates/dtmm/src/controller/app.rs | 14 +- crates/dtmm/src/controller/deploy.rs | 8 + lib/dtmt-shared/src/lib.rs | 37 +- 4 files changed, 608 insertions(+), 506 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0faafbc..4da0fb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", @@ -30,9 +30,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -58,57 +58,58 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "arrayref" @@ -148,13 +149,13 @@ checksum = "46016233fc1bb55c23b856fe556b7db6ccd05119a0a392e04f0b3b7c79058f16" [[package]] name = "async-recursion" -version = "1.0.5" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] @@ -183,15 +184,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -210,9 +211,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64ct" @@ -259,9 +260,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bitmaps" @@ -287,34 +288,23 @@ dependencies = [ "generic-array 0.14.7", ] -[[package]] -name = "bstr" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" -dependencies = [ - "memchr", - "regex-automata 0.4.3", - "serde", -] - [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytecount" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" [[package]] name = "byteorder" @@ -324,9 +314,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "bzip2" @@ -376,12 +366,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" dependencies = [ "jobserver", "libc", + "once_cell", ] [[package]] @@ -395,9 +386,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.5" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03915af431787e6ffdcc74c645077518c6b6e01f80b761e0fbbfa288536311b3" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ "smallvec", "target-lexicon", @@ -421,20 +412,20 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", - "libloading", + "libloading 0.8.3", ] [[package]] name = "clap" -version = "4.4.7" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -442,9 +433,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.7" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", @@ -456,21 +447,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "cli-table" @@ -550,9 +541,9 @@ dependencies = [ [[package]] name = "color-spantrace" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" dependencies = [ "once_cell", "owo-colors", @@ -568,9 +559,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "colors-transform" @@ -608,9 +599,9 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -618,9 +609,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "core-graphics" @@ -637,9 +628,9 @@ dependencies = [ [[package]] name = "core-graphics-types" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -660,40 +651,51 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] -name = "crc32fast" -version = "1.3.2" +name = "crc" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crypto-common" @@ -707,11 +709,10 @@ dependencies = [ [[package]] name = "csv-async" -version = "1.2.6" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71933d3f2d0481d5111cb2817b15b6961961458ec58adf8008194e6c850046f4" +checksum = "d37fe5b0d07f4a8260ce1e9a81413e88f459af0f2dfc55c15e96868a2f99c0f0" dependencies = [ - "bstr", "cfg-if", "csv-core", "futures", @@ -739,9 +740,9 @@ checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", "serde", @@ -764,16 +765,16 @@ version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" dependencies = [ - "dirs-sys", + "dirs-sys 0.3.7", ] [[package]] name = "dirs" -version = "3.0.2" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys", + "dirs-sys 0.4.1", ] [[package]] @@ -797,6 +798,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -816,7 +829,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] @@ -953,7 +966,7 @@ dependencies = [ "futures", "futures-util", "glob", - "libloading", + "libloading 0.7.4", "luajit2-sys", "nanorand", "notify", @@ -1004,15 +1017,15 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -1031,12 +1044,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1051,9 +1064,9 @@ dependencies = [ [[package]] name = "eyre" -version = "0.6.8" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -1070,9 +1083,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fd-lock" @@ -1087,9 +1100,9 @@ dependencies = [ [[package]] name = "fdeflate" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d6dafc854908ff5da46ff3f8f473c6984119a2876a383a860246dd7841a868" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" dependencies = [ "simd-adler32", ] @@ -1100,27 +1113,27 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset 0.9.0", + "memoffset 0.9.1", "rustc_version", ] [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "redox_syscall", + "windows-sys 0.52.0", ] [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -1134,16 +1147,16 @@ checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" [[package]] name = "fluent-bundle" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd" +checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493" dependencies = [ "fluent-langneg", "fluent-syntax", "intl-memoizer", "intl_pluralrules", "rustc-hash", - "self_cell", + "self_cell 0.10.3", "smallvec", "unic-langid", ] @@ -1159,9 +1172,9 @@ dependencies = [ [[package]] name = "fluent-syntax" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78" +checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" dependencies = [ "thiserror", ] @@ -1174,11 +1187,11 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fontconfig-parser" -version = "0.5.3" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "674e258f4b5d2dcd63888c01c68413c51f565e8af99d2f7701c7b81d79ef41c4" +checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d" dependencies = [ - "roxmltree 0.18.1", + "roxmltree 0.19.0", ] [[package]] @@ -1210,9 +1223,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -1234,9 +1247,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -1249,9 +1262,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -1259,15 +1272,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -1276,38 +1289,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -1410,9 +1423,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -1431,9 +1444,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "gio" @@ -1497,7 +1510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb1a9325847aa46f1e96ffea37611b9d51fc4827e67f79e7de502a297560a67b" dependencies = [ "anyhow", - "heck", + "heck 0.4.1", "proc-macro-crate", "proc-macro-error", "proc-macro2", @@ -1589,9 +1602,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.21" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -1599,7 +1612,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 1.9.3", + "indexmap", "slab", "tokio", "tokio-util", @@ -1617,15 +1630,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heapless" @@ -1646,10 +1653,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] -name = "hermit-abi" -version = "0.3.3" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hmac" @@ -1662,18 +1675,18 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "http" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1682,9 +1695,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", "http", @@ -1705,9 +1718,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -1720,7 +1733,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2", "tokio", "tower-service", "tracing", @@ -1742,9 +1755,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1767,15 +1780,14 @@ dependencies = [ [[package]] name = "image" -version = "0.24.7" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", "color_quant", "jpeg-decoder", - "num-rational", "num-traits", "png", ] @@ -1788,22 +1800,12 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.9.3" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown", ] [[package]] @@ -1862,9 +1864,9 @@ dependencies = [ [[package]] name = "intl-memoizer" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f" +checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda" dependencies = [ "type-map", "unic-langid", @@ -1905,31 +1907,37 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.9" +name = "is_terminal_polyfill" +version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.27" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" dependencies = [ "libc", ] [[package]] name = "jpeg-decoder" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.65" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -1945,9 +1953,9 @@ dependencies = [ [[package]] name = "keyvalues-parser" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d990301996c856ea07a84bc291e76f1273db52683663efc05c8d355976897e5" +checksum = "7e4c8354918309196302015ac9cae43362f1a13d0d5c5539a33b4c2fd2cd6d25" dependencies = [ "pest", "pest_derive", @@ -1956,14 +1964,11 @@ dependencies = [ [[package]] name = "keyvalues-serde" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da419ac133bb3ddf0dbf9c12fcc0ce01d994fcb65f6f1713faf15cc689320b5f" +checksum = "0447866c47c00f8bd1949618e8f63017cf93e985b4684dc28d784527e2882390" dependencies = [ "keyvalues-parser", - "once_cell", - "paste", - "regex", "serde", "thiserror", ] @@ -2021,9 +2026,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libloading" @@ -2036,27 +2041,36 @@ dependencies = [ ] [[package]] -name = "libredox" -version = "0.0.1" +name = "libloading" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ - "bitflags 2.4.1", + "cfg-if", + "windows-targets 0.52.5", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.5.0", "libc", - "redox_syscall 0.4.1", ] [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "luajit2-sys" @@ -2094,9 +2108,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memmap2" @@ -2118,9 +2132,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] @@ -2133,9 +2147,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minijinja" -version = "1.0.10" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "208758577ef2c86cf5dd3e85730d161413ec3284e2d73b2ef65d9a24d9971bcb" +checksum = "55e877d961d4f96ce13615862322df7c0b6d169d40cab71a7ef3f9b9e594451e" dependencies = [ "serde", ] @@ -2148,9 +2162,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", "simd-adler32", @@ -2158,9 +2172,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -2281,31 +2295,16 @@ dependencies = [ ] [[package]] -name = "num-integer" -version = "0.1.45" +name = "num-conv" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -2322,9 +2321,9 @@ dependencies = [ [[package]] name = "num_threads" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] @@ -2340,18 +2339,18 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oodle" @@ -2364,9 +2363,9 @@ dependencies = [ [[package]] name = "open" -version = "5.0.1" +version = "5.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90878fb664448b54c4e592455ad02831e23a3f7e157374a8b95654731aac7349" +checksum = "449f0ff855d85ddbf1edd5b646d65249ead3f5e422aaa86b7d2d0b049b103e32" dependencies = [ "is-wsl", "libc", @@ -2375,11 +2374,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.59" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "cfg-if", "foreign-types", "libc", @@ -2396,7 +2395,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] @@ -2407,9 +2406,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.95" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -2417,6 +2416,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "overload" version = "0.1.1" @@ -2493,12 +2498,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - [[package]] name = "path-clean" version = "1.0.1" @@ -2537,15 +2536,15 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" dependencies = [ "memchr", "thiserror", @@ -2554,9 +2553,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" +checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" dependencies = [ "pest", "pest_generator", @@ -2564,22 +2563,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" +checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] name = "pest_meta" -version = "2.7.5" +version = "2.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" +checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" dependencies = [ "once_cell", "pest", @@ -2681,9 +2680,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -2693,15 +2692,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "png" -version = "0.17.10" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -2752,9 +2751,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -2770,9 +2769,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -2808,15 +2807,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -2828,9 +2818,9 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom", "libredox", @@ -2839,14 +2829,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", - "regex-syntax 0.8.2", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", ] [[package]] @@ -2860,13 +2850,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -2877,17 +2867,17 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", @@ -2905,9 +2895,11 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", @@ -2957,18 +2949,15 @@ dependencies = [ [[package]] name = "roxmltree" -version = "0.18.1" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862340e351ce1b271a378ec53f304a5558f7db87f3769dc655a8f6ecbb68b302" -dependencies = [ - "xmlparser", -] +checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -2987,15 +2976,24 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", ] [[package]] @@ -3040,9 +3038,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -3055,11 +3053,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3081,7 +3079,7 @@ dependencies = [ "futures", "futures-util", "glob", - "libloading", + "libloading 0.7.4", "luajit2-sys", "nanorand", "oodle", @@ -3097,11 +3095,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -3110,9 +3108,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -3120,41 +3118,50 @@ dependencies = [ [[package]] name = "self_cell" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" +checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d" +dependencies = [ + "self_cell 1.0.4", +] + +[[package]] +name = "self_cell" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" [[package]] name = "semver" -version = "1.0.20" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.190" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.190" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -3172,9 +3179,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] @@ -3224,15 +3231,15 @@ dependencies = [ [[package]] name = "shlex" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -3279,28 +3286,18 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.4.10" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" -dependencies = [ - "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3311,15 +3308,16 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "steamlocate" -version = "2.0.0-alpha.0" +version = "2.0.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b1568c4a70a26c4373fe1131ffa4eff055459631b6e40c6bc118615f2d870c3" +checksum = "c3b6a4810c4e7fecb0123a9a8ba99b335c17d92e636c265ef99108ee4734c812" dependencies = [ + "crc", "dirs", "keyvalues-parser", "keyvalues-serde", "serde", - "winreg 0.10.1", + "winreg 0.51.0", ] [[package]] @@ -3357,9 +3355,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" @@ -3399,15 +3397,21 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.39" +version = "2.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "9f660c3bfcefb88c538776b6685a0c472e3128b51e74d48793dc2a488196e8eb" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-configuration" version = "0.5.1" @@ -3431,70 +3435,69 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.2.0" +version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ "cfg-expr", - "heck", + "heck 0.5.0", "pkg-config", - "toml 0.8.6", + "toml 0.8.12", "version-compare", ] [[package]] name = "target-lexicon" -version = "0.12.12" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "tempfile" -version = "3.8.1" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.0.1", - "redox_syscall 0.4.1", + "fastrand 2.1.0", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "termcolor" -version = "1.3.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -3502,13 +3505,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.30" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "libc", + "num-conv", "num_threads", "powerfmt", "serde", @@ -3524,10 +3528,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.15" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -3558,9 +3563,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0e245e80bdc9b4e5356fc45a72184abbc3861992603f515270e9340f5a219" +checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece" dependencies = [ "displaydoc", ] @@ -3588,9 +3593,9 @@ checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" [[package]] name = "tokio" -version = "1.33.0" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -3599,7 +3604,7 @@ dependencies = [ "num_cpus", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2", "tokio-macros", "tracing", "windows-sys 0.48.0", @@ -3607,13 +3612,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] @@ -3628,9 +3633,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -3639,16 +3644,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -3662,14 +3666,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.6" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff9e3abce27ee2c9a37f9ad37238c1bdd4e789c84ba37df76aa4d528f5072cc" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.20.7", + "toml_edit 0.22.12", ] [[package]] @@ -3687,22 +3691,22 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.20.7" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ - "indexmap 2.1.0", + "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.8", ] [[package]] @@ -3730,7 +3734,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", ] [[package]] @@ -3755,9 +3759,9 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" dependencies = [ "log", "once_cell", @@ -3766,9 +3770,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", "nu-ansi-term", @@ -3795,9 +3799,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "ttf-parser" @@ -3807,9 +3811,9 @@ checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" [[package]] name = "type-map" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46" +checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" dependencies = [ "rustc-hash", ] @@ -3859,18 +3863,18 @@ checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" [[package]] name = "unic-langid" -version = "0.9.1" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "398f9ad7239db44fd0f80fe068d12ff22d78354080332a5077dc6f52f14dcf2f" +checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44" dependencies = [ "unic-langid-impl", ] [[package]] name = "unic-langid-impl" -version = "0.9.1" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff" +checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5" dependencies = [ "tinystr", ] @@ -3906,9 +3910,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-bidi-mirroring" @@ -3936,24 +3940,24 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] [[package]] name = "unicode-script" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" +checksum = "ad8d71f5726e5f285a935e9fe8edfd53f0491eb6e9a5774097fdabee7cd8c9cd" [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-vo" @@ -3963,15 +3967,15 @@ checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -4031,9 +4035,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version-compare" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" @@ -4064,9 +4068,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -4089,9 +4093,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4099,24 +4103,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.38" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -4126,9 +4130,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4136,28 +4140,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.39", + "syn 2.0.62", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.88" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.65" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -4165,9 +4169,9 @@ dependencies = [ [[package]] name = "weezl" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "which" @@ -4199,11 +4203,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -4230,6 +4234,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -4260,6 +4273,22 @@ dependencies = [ "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -4272,6 +4301,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -4284,6 +4319,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -4296,6 +4337,18 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -4308,6 +4361,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -4320,6 +4379,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -4332,6 +4397,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -4345,21 +4416,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] -name = "winnow" -version = "0.5.19" +name = "windows_x86_64_msvc" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] -name = "winreg" -version = "0.10.1" +name = "winnow" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" dependencies = [ - "winapi", + "memchr", ] [[package]] @@ -4372,6 +4449,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "937f3df7948156640f46aacef17a70db0de5917bda9c92b0f751f3a955b588fc" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wio" version = "0.2.2" @@ -4440,9 +4527,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.10+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" dependencies = [ "cc", "pkg-config", diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index cb6e2f0..7ac703f 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -286,12 +286,18 @@ pub(crate) async fn load_initial(path: PathBuf, is_default: bool) -> Result game_info, + Err(err) => { + tracing::error!("Failed to collect game info: {:?}", err); + None } + }; + + if config.game_dir.is_none() && game_info.is_none() { + tracing::error!("No Game Directory set. Head to the 'Settings' tab to set it manually",); } let mod_dir = config.data_dir.join("mods"); diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index 53c4ef1..4d5f831 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -723,6 +723,14 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { ) .wrap_err("Failed to gather deployment information")?; + let game_info = match game_info { + Ok(game_info) => game_info, + Err(err) => { + tracing::error!("Failed to collect game info: {:#?}", err); + None + } + }; + tracing::debug!(?game_info, ?deployment_info); if let Some(game_info) = game_info { diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index 28e4694..3c9530e 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -1,12 +1,15 @@ use std::path::PathBuf; -mod log; - -pub use log::*; +use color_eyre::eyre::{OptionExt as _, WrapErr as _}; +use color_eyre::Result; use serde::{Deserialize, Serialize}; use steamlocate::SteamDir; use time::OffsetDateTime; +pub use log::*; + +mod log; + #[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct ModConfigResources { pub init: PathBuf, @@ -74,25 +77,23 @@ pub struct GameInfo { pub last_updated: OffsetDateTime, } -pub fn collect_game_info() -> Option { - let mut dir = if let Some(dir) = SteamDir::locate() { - dir - } else { - tracing::debug!("Failed to locate Steam installation"); - return None; - }; +pub fn collect_game_info() -> Result> { + let dir = SteamDir::locate().wrap_err("Failed to locate Steam installation")?; let found = dir - .app(&STEAMAPP_ID) - .and_then(|app| app.last_updated.map(|v| (app.path.clone(), v))); + .find_app(STEAMAPP_ID) + .wrap_err("Failed to look up game by Steam app ID")?; - let Some((path, last_updated)) = found else { - tracing::debug!("Found Steam, but failed to find game installation"); - return None; + let Some((app, _)) = found else { + return Ok(None); }; - Some(GameInfo { - path, + let last_updated = app + .last_updated + .ok_or_eyre("Missing field 'last_updated'")?; + + Ok(Some(GameInfo { + path: app.install_dir.into(), last_updated: last_updated.into(), - }) + })) } From ec578f4953e8757523919d6a63a7784b98c0ed28 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 15:30:54 +0200 Subject: [PATCH 194/335] Update CI image Updates for - Rust - LLVM - Xwin --- .ci/image/Dockerfile | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.ci/image/Dockerfile b/.ci/image/Dockerfile index 4e3433b..7ab9c0c 100644 --- a/.ci/image/Dockerfile +++ b/.ci/image/Dockerfile @@ -1,7 +1,7 @@ # https://jake-shadle.github.io/xwin/ FROM debian:bullseye-slim as xwin -ARG XWIN_VERSION=0.5.0 +ARG XWIN_VERSION=0.5.2 ARG XWIN_PREFIX="xwin-$XWIN_VERSION-x86_64-unknown-linux-musl" ADD https://github.com/Jake-Shadle/xwin/releases/download/$XWIN_VERSION/$XWIN_PREFIX.tar.gz /root/$XWIN_PREFIX.tar.gz @@ -59,6 +59,7 @@ COPY lib/oodle/*.so lib/oodle/*.a /src/ FROM linux as msvc +ARG LLVM_VERSION=18 ENV KEYRINGS /usr/local/share/keyrings ADD https://apt.llvm.org/llvm-snapshot.gpg.key /root/llvm-snapshot.gpg.key @@ -70,22 +71,22 @@ RUN set -eux; \ gpg --dearmor > $KEYRINGS/llvm.gpg < /root/llvm-snapshot.gpg.key; \ # wine gpg --dearmor > $KEYRINGS/winehq.gpg < /root/winehq.key; \ - echo "deb [signed-by=$KEYRINGS/llvm.gpg] http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-13 main" > /etc/apt/sources.list.d/llvm.list; \ + echo "deb [signed-by=$KEYRINGS/llvm.gpg] http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-${LLVM_VERSION} main" > /etc/apt/sources.list.d/llvm.list; \ echo "deb [signed-by=$KEYRINGS/winehq.gpg] https://dl.winehq.org/wine-builds/debian/ bullseye main" > /etc/apt/sources.list.d/winehq.list; \ dpkg --add-architecture i386; \ apt-get update; \ apt-get install --no-install-recommends -y \ - libclang-13-dev \ + libclang-${LLVM_VERSION}-dev \ gcc-mingw-w64-x86-64 \ - clang-13 \ - llvm-13 \ - lld-13 \ + clang-${LLVM_VERSION} \ + llvm-${LLVM_VERSION} \ + lld-${LLVM_VERSION} \ winehq-staging \ ; \ # ensure that clang/clang++ are callable directly - ln -s clang-13 /usr/bin/clang && ln -s clang /usr/bin/clang++ && ln -s lld-13 /usr/bin/ld.lld; \ + ln -s clang-${LLVM_VERSION} /usr/bin/clang && ln -s clang /usr/bin/clang++ && ln -s lld-${LLVM_VERSION} /usr/bin/ld.lld; \ # We also need to setup symlinks ourselves for the MSVC shims because they aren't in the debian packages - ln -s clang-13 /usr/bin/clang-cl && ln -s llvm-ar-13 /usr/bin/llvm-lib && ln -s lld-link-13 /usr/bin/lld-link; \ + ln -s clang-${LLVM_VERSION} /usr/bin/clang-cl && ln -s llvm-ar-${LLVM_VERSION} /usr/bin/llvm-lib && ln -s lld-link-${LLVM_VERSION} /usr/bin/lld-link; \ # Verify the symlinks are correct clang++ -v; \ ld.lld -v; \ From 535a30a7ca6f77195da6bf54ab4d9b9120ff3334 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 14 May 2024 00:50:01 +0200 Subject: [PATCH 195/335] Add simpler shell parser This obsoletes `shlex`. The quoting turned out unnecessary, and the splitting supported a lot more than we need. It also forced unncessary allocations: The splitting doesn't add any characters and keeps UTF-8 intact, so returning slices from the input is perfectly possible. Though this particular implementation will only come to use in the future, as `CmdLine` still requires that the slices are cloned. Still, the custom implementation performs about 3x faster. --- crates/dtmt/Cargo.toml | 5 +- crates/dtmt/src/cmd/bundle/extract.rs | 32 +++-- crates/dtmt/src/main.rs | 2 + crates/dtmt/src/shell_parse.rs | 189 ++++++++++++++++++++++++++ 4 files changed, 214 insertions(+), 14 deletions(-) create mode 100644 crates/dtmt/src/shell_parse.rs diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index 69bbc31..688066f 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -33,7 +33,10 @@ path-slash = "0.2.1" async-recursion = "1.0.2" notify = "5.1.0" luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } -shlex = "1.2.0" +shlex = { version = "1.2.0", optional = true } [dev-dependencies] tempfile = "3.3.0" + +[features] +shlex-bench = ["dep:shlex"] diff --git a/crates/dtmt/src/cmd/bundle/extract.rs b/crates/dtmt/src/cmd/bundle/extract.rs index 5e1c03b..9a0f1dd 100644 --- a/crates/dtmt/src/cmd/bundle/extract.rs +++ b/crates/dtmt/src/cmd/bundle/extract.rs @@ -3,7 +3,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use clap::{value_parser, Arg, ArgAction, ArgMatches, Command}; -use color_eyre::eyre::{self, Context, Result}; +use color_eyre::eyre::{self, bail, Context, Result}; use color_eyre::{Help, Report}; use futures::future::try_join_all; use futures::StreamExt; @@ -12,7 +12,9 @@ use sdk::{Bundle, BundleFile, CmdLine}; use tokio::fs; use crate::cmd::util::resolve_bundle_paths; +use crate::shell_parse::ShellParser; +#[inline] fn parse_glob_pattern(s: &str) -> Result { match Pattern::new(s) { Ok(p) => Ok(p), @@ -20,6 +22,7 @@ fn parse_glob_pattern(s: &str) -> Result { } } +#[inline] fn flatten_name(s: &str) -> String { s.replace('/', "_") } @@ -131,26 +134,29 @@ async fn parse_command_line_template(tmpl: &String) -> Result { let mut cmd = if matches!(fs::try_exists(tmpl).await, Ok(true)) { let path = PathBuf::from(tmpl); if path.file_name() == Some(OsStr::new("main.py")) { - let arg = path.display().to_string(); let mut cmd = CmdLine::new("python"); - cmd.arg(shlex::quote(&arg).to_string()); + cmd.arg(path); cmd } else { CmdLine::new(path) } } else { - let Some(args) = shlex::split(tmpl) else { - eyre::bail!("Invalid shell syntax"); - }; + let mut parsed = ShellParser::new(tmpl.as_bytes()); + // Safety: The initial `tmpl` was a `&String` (i.e. valid UTF-8), and `shlex` does not + // insert or remove characters, nor does it split UTF-8 characters. + // So the resulting byte stream is still valid UTF-8. + let mut cmd = CmdLine::new(unsafe { + let bytes = parsed.next().expect("Template is not empty"); + String::from_utf8_unchecked(bytes.to_vec()) + }); - // We already checked that the template is not empty - let mut cmd = CmdLine::new(args[0].clone()); - let mut it = args.iter(); - // Skip the first one, that's the command name - it.next(); + while let Some(arg) = parsed.next() { + // Safety: See above. + cmd.arg(unsafe { String::from_utf8_unchecked(arg.to_vec()) }); + } - for arg in it { - cmd.arg(arg); + if parsed.errored { + bail!("Invalid command line template"); } cmd diff --git a/crates/dtmt/src/main.rs b/crates/dtmt/src/main.rs index bd419e7..2e10b17 100644 --- a/crates/dtmt/src/main.rs +++ b/crates/dtmt/src/main.rs @@ -1,6 +1,7 @@ #![feature(io_error_more)] #![feature(let_chains)] #![feature(result_flattening)] +#![feature(test)] #![windows_subsystem = "console"] use std::path::PathBuf; @@ -27,6 +28,7 @@ mod cmd { mod util; pub mod watch; } +mod shell_parse; #[derive(Default, Deserialize, Serialize)] struct GlobalConfig { diff --git a/crates/dtmt/src/shell_parse.rs b/crates/dtmt/src/shell_parse.rs new file mode 100644 index 0000000..6f35a5f --- /dev/null +++ b/crates/dtmt/src/shell_parse.rs @@ -0,0 +1,189 @@ +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +enum ParserState { + Start, + Word, + SingleQuote, + DoubleQuote, +} + +pub struct ShellParser<'a> { + bytes: &'a [u8], + offset: usize, + pub errored: bool, +} + +impl<'a> ShellParser<'a> { + pub fn new(bytes: &'a [u8]) -> Self { + Self { + bytes, + offset: 0, + errored: false, + } + } + + fn parse_word(&mut self) -> Option<&'a [u8]> { + // The start of the current word. Certain leading characters should be ignored, + // so this might change. + let mut start = self.offset; + let mut state = ParserState::Start; + + while self.offset < self.bytes.len() { + let c = self.bytes[self.offset]; + self.offset += 1; + + match state { + ParserState::Start => match c { + // Ignore leading whitespace + b' ' | b'\t' | b'\n' => start += 1, + b'\'' => { + state = ParserState::SingleQuote; + start += 1; + } + b'"' => { + state = ParserState::DoubleQuote; + start += 1; + } + _ => { + state = ParserState::Word; + } + }, + ParserState::Word => match c { + // Unquoted whitespace ends the current word + b' ' | b'\t' | b'\n' => { + return Some(&self.bytes[start..self.offset - 1]); + } + _ => {} + }, + ParserState::SingleQuote => match c { + b'\'' => { + return Some(&self.bytes[start..(self.offset - 1)]); + } + _ => {} + }, + ParserState::DoubleQuote => match c { + b'"' => { + return Some(&self.bytes[start..(self.offset - 1)]); + } + _ => {} + }, + } + } + + match state { + ParserState::Start => None, + ParserState::Word => Some(&self.bytes[start..self.offset]), + ParserState::SingleQuote | ParserState::DoubleQuote => { + self.errored = true; + None + } + } + } +} + +impl<'a> Iterator for ShellParser<'a> { + type Item = &'a [u8]; + + fn next(&mut self) -> Option { + self.parse_word() + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_one_word() { + let mut it = ShellParser::new(b"hello"); + assert_eq!(it.next(), Some("hello".as_bytes())); + assert_eq!(it.next(), None); + } + + #[test] + fn test_one_single() { + let mut it = ShellParser::new(b"'hello'"); + assert_eq!(it.next(), Some("hello".as_bytes())); + assert_eq!(it.next(), None); + } + + #[test] + fn test_open_quote() { + let mut it = ShellParser::new(b"'hello"); + assert_eq!(it.next(), None); + assert!(it.errored) + } + + #[test] + fn test_ww2ogg() { + let mut it = ShellParser::new( + b"ww2ogg.exe --pcb \"/usr/share/ww2ogg/packed_cookbook_aoTuV_603.bin\"", + ); + assert_eq!(it.next(), Some("ww2ogg.exe".as_bytes())); + assert_eq!(it.next(), Some("--pcb".as_bytes())); + assert_eq!( + it.next(), + Some("/usr/share/ww2ogg/packed_cookbook_aoTuV_603.bin".as_bytes()) + ); + assert_eq!(it.next(), None); + } +} + +#[cfg(test)] +mod bench { + extern crate test; + + use super::*; + #[cfg(feature = "shlex-bench")] + use shlex::bytes::Shlex; + use test::Bencher; + + mod ww2ogg { + use super::*; + + #[bench] + fn custom(b: &mut Bencher) { + let val = test::black_box( + b"ww2ogg.exe --pcb \"/usr/share/ww2ogg/packed_cookbook_aoTuV_603.bin\"", + ); + b.iter(|| { + let it = ShellParser::new(val); + let _: Vec<_> = test::black_box(it.collect()); + }) + } + + #[cfg(feature = "shlex-bench")] + #[bench] + fn shlex(b: &mut Bencher) { + let val = test::black_box( + b"ww2ogg.exe --pcb \"/usr/share/ww2ogg/packed_cookbook_aoTuV_603.bin\"", + ); + b.iter(|| { + let it = Shlex::new(val); + let _: Vec<_> = test::black_box(it.collect()); + }) + } + } + + mod one_single { + use super::*; + + #[bench] + fn custom(b: &mut Bencher) { + let val = test::black_box(b"'hello'"); + b.iter(|| { + let it = ShellParser::new(val); + let _: Vec<_> = test::black_box(it.collect()); + }) + } + + #[cfg(feature = "shlex-bench")] + #[bench] + fn shlex(b: &mut Bencher) { + let val = test::black_box(b"'hello'"); + b.iter(|| { + let it = Shlex::new(val); + let _: Vec<_> = test::black_box(it.collect()); + }) + } + } +} From dfd51513dade1eb4ad39f5bfecb19469f7b34fee Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 16:30:39 +0200 Subject: [PATCH 196/335] Update strip-ansi-escapes Removes duplicate dependency of arrayvec. --- Cargo.lock | 21 +++++++-------------- crates/dtmm/Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4da0fb9..ed230d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,12 +117,6 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "arrayvec" version = "0.7.4" @@ -1999,7 +1993,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449" dependencies = [ - "arrayvec 0.7.4", + "arrayvec", ] [[package]] @@ -2008,7 +2002,7 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" dependencies = [ - "arrayvec 0.7.4", + "arrayvec", "serde", ] @@ -3346,9 +3340,9 @@ dependencies = [ [[package]] name = "strip-ansi-escapes" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8" +checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" dependencies = [ "vte", ] @@ -3543,7 +3537,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" dependencies = [ "arrayref", - "arrayvec 0.7.4", + "arrayvec", "bytemuck", "cfg-if", "png", @@ -4047,11 +4041,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vte" -version = "0.10.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" +checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" dependencies = [ - "arrayvec 0.5.2", "utf8parse", "vte_generate_state_changes", ] diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 05dc160..9e4960a 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -30,7 +30,7 @@ sdk = { path = "../../lib/sdk", version = "*" } serde = { version = "1.0.152", features = ["derive", "rc"] } serde_sjson = { path = "../../lib/serde_sjson", version = "*" } string_template = "0.2.1" -strip-ansi-escapes = "0.1.1" +strip-ansi-escapes = "0.2.0" time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] } tokio = { version = "1.23.0", features = ["rt", "fs", "tracing", "sync"] } tokio-stream = { version = "0.1.12", features = ["fs"] } From 6030917ade914885b3ef413e9fa222908ead8e5c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 16:37:28 +0200 Subject: [PATCH 197/335] Update steamlocate The actual update already happened, but `cargo oudated` cannot handle the suffix, so we must update the `Cargo.toml` as well. --- lib/dtmt-shared/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dtmt-shared/Cargo.toml b/lib/dtmt-shared/Cargo.toml index 57b4a8c..b547dbe 100644 --- a/lib/dtmt-shared/Cargo.toml +++ b/lib/dtmt-shared/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" ansi_term = "0.12.1" color-eyre = "0.6.2" serde = "1.0.152" -steamlocate = "2.0.0-alpha.0" +steamlocate = "2.0.0-beta.2" time = { version = "0.3.19", features = ["formatting", "local-offset", "macros"] } tracing = "0.1.37" tracing-error = "0.2.0" From 21df6cfc5c9a2276620f8b78ec7318c8b769da75 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 18:52:58 +0200 Subject: [PATCH 198/335] Update reqwest --- Cargo.lock | 205 ++++++++++++++++++++++++++++++++------- Cargo.toml | 1 + lib/nexusmods/Cargo.toml | 2 +- 3 files changed, 173 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed230d8..d6dd126 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,9 +205,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" @@ -524,13 +524,20 @@ dependencies = [ name = "color-eyre" version = "0.6.2" dependencies = [ + "ansi-parser", "backtrace", "color-spantrace", "eyre", "indenter", "once_cell", "owo-colors", + "pretty_assertions", + "thiserror", + "tracing", "tracing-error", + "tracing-subscriber", + "url", + "wasm-bindgen-test", ] [[package]] @@ -742,6 +749,12 @@ dependencies = [ "serde", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.10.7" @@ -1596,9 +1609,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.26" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" dependencies = [ "bytes", "fnv", @@ -1678,9 +1691,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -1689,12 +1702,24 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", "pin-project-lite", ] @@ -1704,47 +1729,60 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - [[package]] name = "hyper" -version = "0.14.28" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", "h2", "http", "http-body", "httparse", - "httpdate", "itoa", "pin-project-lite", - "socket2", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", + "http-body-util", "hyper", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", ] [[package]] @@ -2672,6 +2710,26 @@ dependencies = [ "xi-unicode", ] +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.62", +] + [[package]] name = "pin-project-lite" version = "0.2.14" @@ -2709,6 +2767,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -2867,11 +2935,11 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", "encoding_rs", "futures-core", @@ -2879,8 +2947,10 @@ dependencies = [ "h2", "http", "http-body", + "http-body-util", "hyper", "hyper-tls", + "hyper-util", "ipnet", "js-sys", "log", @@ -2902,7 +2972,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg 0.50.0", + "winreg 0.52.0", ] [[package]] @@ -2983,13 +3053,20 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", + "rustls-pki-types", ] +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + [[package]] name = "rustybuzz" version = "0.6.0" @@ -3054,6 +3131,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -3703,6 +3786,28 @@ dependencies = [ "winnow 0.6.8", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -3715,6 +3820,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -4150,6 +4256,31 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-bindgen-test" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.62", +] + [[package]] name = "web-sys" version = "0.3.69" @@ -4434,9 +4565,9 @@ dependencies = [ [[package]] name = "winreg" -version = "0.50.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "937f3df7948156640f46aacef17a70db0de5917bda9c92b0f751f3a955b588fc" dependencies = [ "cfg-if", "windows-sys 0.48.0", @@ -4444,9 +4575,9 @@ dependencies = [ [[package]] name = "winreg" -version = "0.51.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "937f3df7948156640f46aacef17a70db0de5917bda9c92b0f751f3a955b588fc" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" dependencies = [ "cfg-if", "windows-sys 0.48.0", @@ -4479,6 +4610,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + [[package]] name = "zip" version = "0.6.6" diff --git a/Cargo.toml b/Cargo.toml index 11831e4..d44a5a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "lib/sdk", "lib/serde_sjson", "lib/luajit2-sys", + "lib/color-eyre", ] exclude = ["lib/color-eyre"] diff --git a/lib/nexusmods/Cargo.toml b/lib/nexusmods/Cargo.toml index d7967ef..2f90a46 100644 --- a/lib/nexusmods/Cargo.toml +++ b/lib/nexusmods/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" futures = "0.3.26" lazy_static = "1.4.0" regex = "1.7.1" -reqwest = { version = "0.11.14" } +reqwest = { version = "0.12.4" } serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.94" thiserror = "1.0.39" From bac75e1c9aad1f9a3737e7e78c3e8d5e15b901db Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 18:58:08 +0200 Subject: [PATCH 199/335] Update confy --- Cargo.lock | 36 ++++++++---------------------------- crates/dtmm/Cargo.toml | 2 +- crates/dtmt/Cargo.toml | 2 +- 3 files changed, 10 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d6dd126..7f3772a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -572,14 +572,14 @@ checksum = "9226dbc05df4fb986f48d730b001532580883c4c06c5d1c213f4b34c1c157178" [[package]] name = "confy" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37668cb35145dcfaa1931a5f37fde375eeae8068b4c0d2f289da28a270b2d2c" +checksum = "45b1f4c00870f07dc34adcac82bb6a72cc5aabca8536ba1797e01df51d2ce9a0" dependencies = [ "directories", "serde", "thiserror", - "toml 0.5.11", + "toml", ] [[package]] @@ -768,11 +768,11 @@ dependencies = [ [[package]] name = "directories" -version = "4.0.1" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51c5d4ddabd36886dd3e1438cb358cdcb0d7c499cb99cb4ac2e38e18b5cb210" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" dependencies = [ - "dirs-sys 0.3.7", + "dirs-sys", ] [[package]] @@ -781,7 +781,7 @@ version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ - "dirs-sys 0.4.1", + "dirs-sys", ] [[package]] @@ -794,17 +794,6 @@ dependencies = [ "dirs-sys-next", ] -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - [[package]] name = "dirs-sys" version = "0.4.1" @@ -3519,7 +3508,7 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.12", + "toml", "version-compare", ] @@ -3732,15 +3721,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - [[package]] name = "toml" version = "0.8.12" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 9e4960a..627981b 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -13,7 +13,7 @@ bitflags = "1.3.2" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } color-eyre = "0.6.2" colors-transform = "0.2.11" -confy = "0.5.1" +confy = "0.6.1" druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] } druid-widget-nursery = "0.1" dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index 688066f..4a98244 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "unicode"] } cli-table = { version = "0.4.7", default-features = false, features = ["derive"] } color-eyre = "0.6.2" -confy = "0.5.1" +confy = "0.6.1" csv-async = { version = "1.2.4", features = ["tokio", "serde"] } dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } futures = "0.3.25" From ecd235be058233677a767663c60d0db9615380a6 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 19:14:07 +0200 Subject: [PATCH 200/335] Update ansi-parser Patched to update heapless while waiting for the merge request. --- Cargo.lock | 49 ++++++++----------------------------------------- Cargo.toml | 1 + 2 files changed, 9 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7f3772a..7411d5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,8 +40,7 @@ dependencies = [ [[package]] name = "ansi-parser" version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad5bd94a775101bd68c2de2bb28ca2eccd69f395ae3aec4ac4f6da3c1cd2c6a" +source = "git+https://gitlab.com/lschwiderski/ansi-parser.git?branch=issue/outdated-heapless#af1951d16951a37101139763593be44103e3a477" dependencies = [ "heapless", "nom", @@ -123,18 +122,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" -[[package]] -name = "as-slice" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" -dependencies = [ - "generic-array 0.12.4", - "generic-array 0.13.3", - "generic-array 0.14.7", - "stable_deref_trait", -] - [[package]] name = "associative-cache" version = "1.0.1" @@ -279,7 +266,7 @@ version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "generic-array 0.14.7", + "generic-array", ] [[package]] @@ -704,7 +691,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.7", + "generic-array", "typenum", ] @@ -1389,24 +1376,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" -dependencies = [ - "typenum", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -1617,9 +1586,9 @@ dependencies = [ [[package]] name = "hash32" -version = "0.1.1" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" dependencies = [ "byteorder", ] @@ -1632,12 +1601,10 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heapless" -version = "0.6.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ - "as-slice", - "generic-array 0.14.7", "hash32", "stable_deref_trait", ] @@ -1855,7 +1822,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ - "generic-array 0.14.7", + "generic-array", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d44a5a6..54cf161 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ exclude = ["lib/color-eyre"] [patch.crates-io] color-eyre = { path = "lib/color-eyre" } +ansi-parser = { git = "https://gitlab.com/lschwiderski/ansi-parser.git", branch = "issue/outdated-heapless" } [profile.dev.package.backtrace] opt-level = 3 From 647cb1b8bdaf75e6404b623ec2839953475e23f4 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 19:16:34 +0200 Subject: [PATCH 201/335] Update fastrand --- Cargo.lock | 13 ++----------- lib/sdk/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7411d5e..660e7cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1055,15 +1055,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "fastrand" version = "2.1.0" @@ -3108,7 +3099,7 @@ dependencies = [ "byteorder", "color-eyre", "csv-async", - "fastrand 1.9.0", + "fastrand", "futures", "futures-util", "glob", @@ -3492,7 +3483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.1.0", + "fastrand", "rustix", "windows-sys 0.52.0", ] diff --git a/lib/sdk/Cargo.toml b/lib/sdk/Cargo.toml index 63f789b..6745ff4 100644 --- a/lib/sdk/Cargo.toml +++ b/lib/sdk/Cargo.toml @@ -8,7 +8,7 @@ bitflags = "1.3.2" byteorder = "1.4.3" color-eyre = "0.6.2" csv-async = { version = "1.2.4", features = ["tokio", "serde"] } -fastrand = "1.8.0" +fastrand = "2.1.0" futures = "0.3.25" futures-util = "0.3.24" glob = "0.3.0" From ae30499a4901d9ef5ce0ad182d6ea81ed9519b24 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 19:19:54 +0200 Subject: [PATCH 202/335] Remove unused dependency --- Cargo.lock | 14 +------------- crates/dtmt/Cargo.toml | 1 - lib/sdk/Cargo.toml | 1 - 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 660e7cc..336f957 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,7 +399,7 @@ checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", - "libloading 0.8.3", + "libloading", ] [[package]] @@ -949,7 +949,6 @@ dependencies = [ "futures", "futures-util", "glob", - "libloading 0.7.4", "luajit2-sys", "nanorand", "notify", @@ -2009,16 +2008,6 @@ version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - [[package]] name = "libloading" version = "0.8.3" @@ -3103,7 +3092,6 @@ dependencies = [ "futures", "futures-util", "glob", - "libloading 0.7.4", "luajit2-sys", "nanorand", "oodle", diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index 4a98244..60cd70c 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -13,7 +13,6 @@ dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } futures = "0.3.25" futures-util = "0.3.24" glob = "0.3.0" -libloading = "0.7.4" nanorand = "0.7.0" oodle = { path = "../../lib/oodle", version = "*" } pin-project-lite = "0.2.9" diff --git a/lib/sdk/Cargo.toml b/lib/sdk/Cargo.toml index 6745ff4..f060b4b 100644 --- a/lib/sdk/Cargo.toml +++ b/lib/sdk/Cargo.toml @@ -12,7 +12,6 @@ fastrand = "2.1.0" futures = "0.3.25" futures-util = "0.3.24" glob = "0.3.0" -libloading = "0.7.4" nanorand = "0.7.0" pin-project-lite = "0.2.9" serde = { version = "1.0.147", features = ["derive"] } From 0c4c078b100b71f2226f42bbc9398dae2593c328 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 19:24:57 +0200 Subject: [PATCH 203/335] Update dependencies --- Cargo.lock | 62 +++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 336f957..88c8664 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,7 +136,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -283,9 +283,9 @@ checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" [[package]] name = "byteorder" @@ -435,7 +435,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -812,7 +812,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -1274,7 +1274,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -2363,7 +2363,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -2539,7 +2539,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -2663,7 +2663,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -3151,22 +3151,22 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -3191,9 +3191,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -3409,9 +3409,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.62" +version = "2.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f660c3bfcefb88c538776b6685a0c472e3128b51e74d48793dc2a488196e8eb" +checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" dependencies = [ "proc-macro2", "quote", @@ -3502,7 +3502,7 @@ checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -3630,7 +3630,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -3669,21 +3669,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.12", + "toml_edit 0.22.13", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] @@ -3701,9 +3701,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.12" +version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" dependencies = [ "indexmap", "serde", @@ -3760,7 +3760,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] @@ -4137,7 +4137,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", "wasm-bindgen-shared", ] @@ -4171,7 +4171,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4204,7 +4204,7 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.62", + "syn 2.0.63", ] [[package]] From 4bc5777a4b921a28bbc1d5dc675503144f04e9c3 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 20:04:47 +0200 Subject: [PATCH 204/335] Update notify --- Cargo.lock | 75 +++--------------------------------------- crates/dtmt/Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 71 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88c8664..8e0f2f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2236,20 +2236,21 @@ dependencies = [ [[package]] name = "notify" -version = "5.2.0" +version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "crossbeam-channel", "filetime", "fsevent-sys", "inotify", "kqueue", "libc", + "log", "mio", "walkdir", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -4266,15 +4267,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -4293,21 +4285,6 @@ dependencies = [ "windows-targets 0.52.5", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.48.5" @@ -4339,12 +4316,6 @@ dependencies = [ "windows_x86_64_msvc 0.52.5", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -4357,12 +4328,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -4375,12 +4340,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -4399,12 +4358,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -4417,12 +4370,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -4435,12 +4382,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -4453,12 +4394,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index 60cd70c..baa21f8 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -30,7 +30,7 @@ zip = "0.6.3" path-clean = "1.0.1" path-slash = "0.2.1" async-recursion = "1.0.2" -notify = "5.1.0" +notify = "6.1.1" luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } shlex = { version = "1.2.0", optional = true } From c7ff0b7a908806f2ef3045eb5f5b1bb7b78b762c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 21:57:04 +0200 Subject: [PATCH 205/335] Update bindgen --- Cargo.toml | 2 +- build.rs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 23f0cd3..1f48fa3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,6 @@ links = "luajit" libc = "0.2" [build-dependencies] -bindgen = "0.64.0" +bindgen = "0.69.4" cc = "1" fs_extra = "1.1.0" diff --git a/build.rs b/build.rs index 6e7e5a4..d291dd7 100644 --- a/build.rs +++ b/build.rs @@ -192,11 +192,10 @@ fn main() { .impl_debug(true) .use_core() .detect_include_paths(true) - // Make it pretty - .rustfmt_bindings(true) + .formatter(bindgen::Formatter::Rustfmt) .sort_semantically(true) .merge_extern_blocks(true) - .parse_callbacks(Box::new(bindgen::CargoCallbacks)); + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())); let bindings = if env::var("CARGO_CFG_WINDOWS").is_ok() { bindings From 3546bc8faa243605015768f3bdf25621ac40397a Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 21:57:45 +0200 Subject: [PATCH 206/335] Update bindgen --- Cargo.lock | 36 +++++++++++++++++++++++++----------- lib/luajit2-sys | 2 +- lib/oodle/Cargo.toml | 2 +- lib/oodle/build.rs | 4 +--- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8e0f2f1..a4be1c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,23 +213,24 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.64.0" +version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "cexpr", "clang-sys", + "itertools", "lazy_static", "lazycell", "log", - "peeking_take_while", + "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 1.0.109", + "syn 2.0.63", "which", ] @@ -1890,6 +1891,15 @@ version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -2497,12 +2507,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "percent-encoding" version = "2.3.1" @@ -2714,6 +2718,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn 2.0.63", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" diff --git a/lib/luajit2-sys b/lib/luajit2-sys index 1912016..5d1a075 160000 --- a/lib/luajit2-sys +++ b/lib/luajit2-sys @@ -1 +1 @@ -Subproject commit 19120166f9fc7838b98c71fc348791abc820e323 +Subproject commit 5d1a075742395f767c79d9c0d7466c6fb442f106 diff --git a/lib/oodle/Cargo.toml b/lib/oodle/Cargo.toml index 3283592..6fc5039 100644 --- a/lib/oodle/Cargo.toml +++ b/lib/oodle/Cargo.toml @@ -10,4 +10,4 @@ color-eyre = "0.6.2" tracing = "0.1.37" [build-dependencies] -bindgen = "0.64.0" +bindgen = "0.69.4" diff --git a/lib/oodle/build.rs b/lib/oodle/build.rs index e33c7d5..1a1d4e9 100644 --- a/lib/oodle/build.rs +++ b/lib/oodle/build.rs @@ -1,5 +1,3 @@ -extern crate bindgen; - use std::env; use std::path::PathBuf; @@ -33,7 +31,7 @@ fn main() { .blocklist_file("stdlib.h") // Tell cargo to invalidate the built crate whenever any of the // included header files changed. - .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) // Finish the builder and generate the bindings. .generate() // Unwrap the Result and panic on failure. From b8ac80562ad6514af17220764f25cb8e8ebb0e97 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 22:46:21 +0200 Subject: [PATCH 207/335] Update zip --- Cargo.lock | 135 ++++++++------------------------- Cargo.toml | 3 + crates/dtmm/Cargo.toml | 2 +- crates/dtmt/Cargo.toml | 2 +- crates/dtmt/src/cmd/package.rs | 28 ++----- 5 files changed, 43 insertions(+), 127 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a4be1c4..bd1f426 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,17 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -110,6 +99,15 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "arrayref" version = "0.3.7" @@ -196,12 +194,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - [[package]] name = "bincode" version = "1.3.3" @@ -382,16 +374,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "clang-sys" version = "1.7.0" @@ -580,12 +562,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "core-foundation" version = "0.9.4" @@ -737,6 +713,17 @@ dependencies = [ "serde", ] +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.63", +] + [[package]] name = "diff" version = "0.1.13" @@ -751,7 +738,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", - "subtle", ] [[package]] @@ -1618,15 +1604,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - [[package]] name = "home" version = "0.5.9" @@ -1807,15 +1784,6 @@ dependencies = [ "libc", ] -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array", -] - [[package]] name = "instant" version = "0.1.12" @@ -2466,17 +2434,6 @@ dependencies = [ "system-deps", ] -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core", - "subtle", -] - [[package]] name = "path-clean" version = "1.0.1" @@ -2495,18 +2452,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest", - "hmac", - "password-hash", - "sha2", -] - [[package]] name = "percent-encoding" version = "2.3.1" @@ -3225,17 +3170,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "sha2" version = "0.10.8" @@ -3386,12 +3320,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - [[package]] name = "svgfilters" version = "0.4.0" @@ -4493,40 +4421,37 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zip" -version = "0.6.6" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +checksum = "f1f4a27345eb6f7aa7bd015ba7eb4175fa4e1b462a29874b779e0bbcf96c6ac7" dependencies = [ - "aes", - "byteorder", + "arbitrary", "bzip2", - "constant_time_eq", "crc32fast", "crossbeam-utils", + "displaydoc", "flate2", - "hmac", - "pbkdf2", - "sha1", + "indexmap", + "thiserror", "time", "zstd", ] [[package]] name = "zstd" -version = "0.11.2+zstd.1.5.2" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" +version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" dependencies = [ - "libc", "zstd-sys", ] diff --git a/Cargo.toml b/Cargo.toml index 54cf161..39d8f38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,9 @@ members = [ ] exclude = ["lib/color-eyre"] +[workspace.dependencies] +zip = { version = "1.3.0", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } + [patch.crates-io] color-eyre = { path = "lib/color-eyre" } ansi-parser = { git = "https://gitlab.com/lschwiderski/ansi-parser.git", branch = "issue/outdated-heapless" } diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 627981b..ef862f3 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -38,4 +38,4 @@ tracing = "0.1.37" tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } usvg = "0.25.0" -zip = "0.6.4" +zip = { workspace = true } diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index baa21f8..d836a50 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -26,7 +26,7 @@ tokio = { version = "1.21.2", features = ["rt-multi-thread", "fs", "process", "m tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tracing = { version = "0.1.37", features = ["async-await"] } -zip = "0.6.3" +zip = { workspace = true } path-clean = "1.0.1" path-slash = "0.2.1" async-recursion = "1.0.2" diff --git a/crates/dtmt/src/cmd/package.rs b/crates/dtmt/src/cmd/package.rs index e922b6e..5ded885 100644 --- a/crates/dtmt/src/cmd/package.rs +++ b/crates/dtmt/src/cmd/package.rs @@ -1,6 +1,5 @@ use std::io::{Cursor, Write}; use std::path::{Path, PathBuf}; -use std::sync::Arc; use clap::{value_parser, Arg, ArgMatches, Command}; use color_eyre::eyre::{Context, Result}; @@ -8,9 +7,9 @@ use color_eyre::Help; use dtmt_shared::ModConfig; use path_slash::{PathBufExt, PathExt}; use tokio::fs; -use tokio::sync::Mutex; use tokio_stream::wrappers::ReadDirStream; use tokio_stream::StreamExt; +use zip::write::SimpleFileOptions; use zip::ZipWriter; use crate::cmd::build::read_project_config; @@ -51,11 +50,7 @@ pub(crate) fn command_definition() -> Command { } #[async_recursion::async_recursion] -async fn process_directory( - zip: Arc>>, - path: P1, - prefix: P2, -) -> Result<()> +async fn process_directory(zip: &mut ZipWriter, path: P1, prefix: P2) -> Result<()> where P1: AsRef + std::marker::Send, P2: AsRef + std::marker::Send, @@ -64,9 +59,7 @@ where let path = path.as_ref(); let prefix = prefix.as_ref(); - zip.lock() - .await - .add_directory(prefix.to_slash_lossy(), Default::default())?; + zip.add_directory(prefix.to_slash_lossy(), SimpleFileOptions::default())?; let read_dir = fs::read_dir(&path) .await @@ -87,12 +80,11 @@ where .await .wrap_err_with(|| format!("Failed to read '{}'", in_path.display()))?; { - let mut zip = zip.lock().await; - zip.start_file(out_path.to_slash_lossy(), Default::default())?; + zip.start_file(out_path.to_slash_lossy(), SimpleFileOptions::default())?; zip.write_all(&data)?; } } else if t.is_dir() { - process_directory(zip.clone(), in_path, out_path).await?; + process_directory(zip, in_path, out_path).await?; } } @@ -107,16 +99,12 @@ where let path = path.as_ref(); let dest = dest.as_ref(); - let data = Cursor::new(Vec::new()); - let zip = ZipWriter::new(data); - let zip = Arc::new(Mutex::new(zip)); + let mut zip = ZipWriter::new(Cursor::new(Vec::with_capacity(1024))); - process_directory(zip.clone(), path, PathBuf::from(&cfg.id)) + process_directory(&mut zip, path, PathBuf::from(&cfg.id)) .await .wrap_err("Failed to add directory to archive")?; - let mut zip = zip.lock().await; - { let name = PathBuf::from(&cfg.id).join("dtmt.cfg"); let path = cfg.dir.join("dtmt.cfg"); @@ -125,7 +113,7 @@ where .await .wrap_err_with(|| format!("Failed to read mod config at {}", path.display()))?; - zip.start_file(name.to_slash_lossy(), Default::default())?; + zip.start_file(name.to_slash_lossy(), SimpleFileOptions::default())?; zip.write_all(&data)?; } From 189c3199a0aec79e6ca2cba6ee9bd3ad455e0e32 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 15 May 2024 22:53:39 +0200 Subject: [PATCH 208/335] Update bitflags --- Cargo.lock | 4 ++-- crates/dtmm/Cargo.toml | 2 +- lib/sdk/Cargo.toml | 2 +- lib/sdk/src/bundle/file.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd1f426..d103e9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -890,7 +890,7 @@ dependencies = [ "ansi-parser", "async-recursion", "bincode", - "bitflags 1.3.2", + "bitflags 2.5.0", "clap", "color-eyre", "colors-transform", @@ -3044,7 +3044,7 @@ name = "sdk" version = "0.3.0" dependencies = [ "async-recursion", - "bitflags 1.3.2", + "bitflags 2.5.0", "byteorder", "color-eyre", "csv-async", diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index ef862f3..c1184ac 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" ansi-parser = "0.9.0" async-recursion = "1.0.5" bincode = "1.3.3" -bitflags = "1.3.2" +bitflags = "2.5.0" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } color-eyre = "0.6.2" colors-transform = "0.2.11" diff --git a/lib/sdk/Cargo.toml b/lib/sdk/Cargo.toml index f060b4b..b164d47 100644 --- a/lib/sdk/Cargo.toml +++ b/lib/sdk/Cargo.toml @@ -4,7 +4,7 @@ version = "0.3.0" edition = "2021" [dependencies] -bitflags = "1.3.2" +bitflags = "2.5.0" byteorder = "1.4.3" color-eyre = "0.6.2" csv-async = { version = "1.2.4", features = ["tokio", "serde"] } diff --git a/lib/sdk/src/bundle/file.rs b/lib/sdk/src/bundle/file.rs index ab59884..4d6c56e 100644 --- a/lib/sdk/src/bundle/file.rs +++ b/lib/sdk/src/bundle/file.rs @@ -501,7 +501,7 @@ impl BundleFileVariant { } bitflags! { - #[derive(Default)] + #[derive(Default, Clone, Copy, Debug)] pub struct Properties: u32 { const DATA = 0b100; } From 9577d704235da55e96ad901df74ecc9a2eb5963d Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 May 2024 10:58:03 +0200 Subject: [PATCH 209/335] Add missing build tools to CI image --- .ci/image/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/image/Dockerfile b/.ci/image/Dockerfile index 7ab9c0c..f115929 100644 --- a/.ci/image/Dockerfile +++ b/.ci/image/Dockerfile @@ -37,6 +37,7 @@ RUN set -eux; \ apt-get update; \ apt-get install --no-install-recommends -y \ build-essential \ + cmake \ curl \ git \ gpg \ From ef4c2a1d94b316cc52640358cb9521e0c0dd8dd2 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 16 May 2024 11:06:05 +0200 Subject: [PATCH 210/335] Update interprocess --- Cargo.lock | 33 +++++++++++++++++++-------------- crates/dtmm/Cargo.toml | 4 ++-- crates/dtmm/src/main.rs | 26 ++++++++++++++++++-------- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d103e9c..7cbb4b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1798,15 +1798,14 @@ dependencies = [ [[package]] name = "interprocess" -version = "1.2.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb" +checksum = "7b4d0250d41da118226e55b3d50ca3f0d9e0a0f6829b92f543ac0054aeea1572" dependencies = [ - "cfg-if", "libc", - "rustc_version", - "to_method", - "winapi", + "recvmsg", + "widestring", + "windows-sys 0.52.0", ] [[package]] @@ -2093,9 +2092,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minijinja" -version = "1.0.21" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e877d961d4f96ce13615862322df7c0b6d169d40cab71a7ef3f9b9e594451e" +checksum = "7165d0e94806d52ad5295e4b54a95176d831814840bc067298ca647e1c956338" dependencies = [ "serde", ] @@ -2765,6 +2764,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" +[[package]] +name = "recvmsg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" + [[package]] name = "redox_syscall" version = "0.4.1" @@ -3540,12 +3545,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "to_method" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8" - [[package]] name = "tokio" version = "1.37.0" @@ -4178,6 +4177,12 @@ dependencies = [ "rustix", ] +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index c1184ac..f57d0c8 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -18,10 +18,10 @@ druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "b druid-widget-nursery = "0.1" dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } futures = "0.3.25" -interprocess = { version = "1.2.1", default-features = false } +interprocess = "2.1.0" lazy_static = "1.4.0" luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } -minijinja = "1.0.10" +minijinja = { version = "2.0.1", default-features = false } nexusmods = { path = "../../lib/nexusmods", version = "*" } oodle = { path = "../../lib/oodle", version = "*" } open = "5.0.1" diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index aa223f0..6c0bb48 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -11,7 +11,7 @@ use clap::{command, value_parser, Arg}; use color_eyre::eyre::{self, Context}; use color_eyre::{Report, Result, Section}; use druid::AppLauncher; -use interprocess::local_socket::{LocalSocketListener, LocalSocketStream}; +use interprocess::local_socket::{prelude::*, GenericNamespaced, ListenerOptions}; use tokio::sync::RwLock; use crate::controller::worker::work_thread; @@ -29,9 +29,9 @@ mod util { } mod ui; -// As explained in https://docs.rs/interprocess/latest/interprocess/local_socket/enum.NameTypeSupport.html +// As explained in https://docs.rs/interprocess/2.1.0/interprocess/local_socket/struct.Name.html // namespaces are supported on both platforms we care about: Windows and Linux. -const IPC_ADDRESS: &str = "@dtmm.sock"; +const IPC_ADDRESS: &str = "dtmm.sock"; #[tracing::instrument] fn notify_nxm_download( @@ -42,9 +42,13 @@ fn notify_nxm_download( tracing::debug!("Received Uri '{}', sending to main process.", uri.as_ref()); - let mut stream = LocalSocketStream::connect(IPC_ADDRESS) - .wrap_err_with(|| format!("Failed to connect to '{}'", IPC_ADDRESS)) - .suggestion("Make sure the main window is open.")?; + let mut stream = LocalSocketStream::connect( + IPC_ADDRESS + .to_ns_name::() + .expect("Invalid socket name"), + ) + .wrap_err_with(|| format!("Failed to connect to '{}'", IPC_ADDRESS)) + .suggestion("Make sure the main window is open.")?; tracing::debug!("Connected to main process at '{}'", IPC_ADDRESS); @@ -130,8 +134,14 @@ fn main() -> Result<()> { let _guard = span.enter(); let event_sink = event_sink.clone(); - let server = - LocalSocketListener::bind(IPC_ADDRESS).wrap_err("Failed to create IPC listener")?; + let server = ListenerOptions::new() + .name( + IPC_ADDRESS + .to_ns_name::() + .expect("Invalid socket name"), + ) + .create_sync() + .wrap_err("Failed to create IPC listener")?; tracing::debug!("IPC server listening on '{}'", IPC_ADDRESS); From 96a7eeb1e0cf1f1af484febb8278a9bb679c0586 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 17 May 2024 14:51:17 +0200 Subject: [PATCH 211/335] Implement faster hex string parsing --- Cargo.toml | 6 + Justfile | 8 + lib/sdk/src/lib.rs | 2 + lib/sdk/src/murmur/mod.rs | 377 +----------------------------------- lib/sdk/src/murmur/types.rs | 365 ++++++++++++++++++++++++++++++++++ lib/sdk/src/murmur/util.rs | 132 +++++++++++++ 6 files changed, 518 insertions(+), 372 deletions(-) create mode 100644 lib/sdk/src/murmur/types.rs create mode 100644 lib/sdk/src/murmur/util.rs diff --git a/Cargo.toml b/Cargo.toml index 39d8f38..8cbe52c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,3 +31,9 @@ strip = "debuginfo" [profile.release-lto] inherits = "release" lto = true + +[profile.perf] +inherits = "release" +strip = false +lto = true +debug = "line-tables-only" diff --git a/Justfile b/Justfile index f9b37bc..dbecc22 100644 --- a/Justfile +++ b/Justfile @@ -1,5 +1,13 @@ +set positional-arguments + fly_target := "main" +build-perf-dtmt: + cargo build --profile perf --bin dtmt + +perf-dtmt *args='': build-perf-dtmt + perf record --call-graph dwarf ./target/perf/dtmt "$@" + ci-build: ci-build-msvc ci-build-linux ci-build-msvc: diff --git a/lib/sdk/src/lib.rs b/lib/sdk/src/lib.rs index 37a4d67..a24b3bd 100644 --- a/lib/sdk/src/lib.rs +++ b/lib/sdk/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(test)] + mod binary; mod bundle; mod context; diff --git a/lib/sdk/src/murmur/mod.rs b/lib/sdk/src/murmur/mod.rs index a2a9ef3..87a8473 100644 --- a/lib/sdk/src/murmur/mod.rs +++ b/lib/sdk/src/murmur/mod.rs @@ -1,15 +1,16 @@ use std::fmt; use color_eyre::eyre::Context; -use color_eyre::Report; +use color_eyre::{Report, Result}; use serde::de::Visitor; -use serde::{Deserialize, Serialize}; -use serde::{Deserializer, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; mod dictionary; // Currently unused // mod murmurhash32; mod murmurhash64; +mod types; +mod util; pub const SEED: u32 = 0; @@ -18,372 +19,4 @@ pub use murmurhash64::hash; pub use murmurhash64::hash32; pub use murmurhash64::hash_inverse as inverse; -fn _swap_bytes_u32(value: u32) -> u32 { - u32::from_le_bytes(value.to_be_bytes()) -} - -fn _swap_bytes_u64(value: u64) -> u64 { - u64::from_le_bytes(value.to_be_bytes()) -} - -#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] -pub struct Murmur64(u64); - -impl Murmur64 { - pub fn hash(s: B) -> Self - where - B: AsRef<[u8]>, - { - hash(s.as_ref(), SEED as u64).into() - } -} - -impl From for Murmur64 { - fn from(value: u64) -> Self { - Self(value) - } -} - -impl From for u64 { - fn from(value: Murmur64) -> Self { - value.0 - } -} - -impl TryFrom<&str> for Murmur64 { - type Error = Report; - - fn try_from(value: &str) -> Result { - u64::from_str_radix(value, 16) - .map(Self) - .wrap_err_with(|| format!("Failed to convert value to Murmur64: {value}")) - } -} - -impl fmt::UpperHex for Murmur64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) - } -} - -impl fmt::LowerHex for Murmur64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::LowerHex::fmt(&self.0, f) - } -} - -impl fmt::Display for Murmur64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) - } -} - -impl<'de> Visitor<'de> for Murmur64 { - type Value = Self; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str( - "an usigned 64 bit integer \ - or a string in hexadecimal format encoding such an integer", - ) - } - - fn visit_f64(self, value: f64) -> Result - where - E: serde::de::Error, - { - let bytes = value.to_le_bytes(); - Ok(Self::from(u64::from_le_bytes(bytes))) - } - - fn visit_u64(self, value: u64) -> Result - where - E: serde::de::Error, - { - Ok(Self::from(value)) - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - match Murmur64::try_from(value) { - Ok(hash) => Ok(hash), - Err(err) => Err(E::custom(format!( - "failed to convert '{value}' to Murmur64: {err}" - ))), - } - } -} - -impl<'de> Deserialize<'de> for Murmur64 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_any(Self(0)) - } -} - -impl Serialize for Murmur64 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{self:016X}")) - } -} - -#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] -pub struct Murmur32(u32); - -impl Murmur32 { - pub fn hash(s: B) -> Self - where - B: AsRef<[u8]>, - { - hash32(s.as_ref(), SEED).into() - } -} - -impl From for Murmur32 { - fn from(value: u32) -> Self { - Self(value) - } -} - -impl From for u32 { - fn from(value: Murmur32) -> Self { - value.0 - } -} - -impl TryFrom<&str> for Murmur32 { - type Error = Report; - - fn try_from(value: &str) -> Result { - u32::from_str_radix(value, 16) - .map(Self) - .wrap_err_with(|| format!("Failed to convert value to Murmur32: {value}")) - } -} - -impl fmt::UpperHex for Murmur32 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) - } -} - -impl fmt::Display for Murmur32 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) - } -} - -impl Serialize for Murmur32 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&format!("{self:08X}")) - } -} - -impl<'de> Visitor<'de> for Murmur32 { - type Value = Self; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str( - "an usigned 32 bit integer \ - or a string in hexadecimal format encoding such an integer", - ) - } - - fn visit_f64(self, value: f64) -> Result - where - E: serde::de::Error, - { - let bytes = value.to_le_bytes(); - self.visit_u32(u64::from_le_bytes(bytes) as u32) - } - - fn visit_u64(self, value: u64) -> Result - where - E: serde::de::Error, - { - self.visit_u32(value as u32) - } - - fn visit_u32(self, value: u32) -> Result - where - E: serde::de::Error, - { - Ok(Self::from(value)) - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - match Murmur32::try_from(value) { - Ok(hash) => Ok(hash), - Err(err) => Err(E::custom(format!( - "failed to convert '{value}' to Murmur32: {err}" - ))), - } - } -} - -impl<'de> Deserialize<'de> for Murmur32 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_any(Self(0)) - } -} - -// This type encodes the fact that when reading in a bundle, we don't always have a dictionary -// entry for every hash in there. So we do want to have the real string available when needed, -// but at the same time retain the original hash information for when we don't. -// This is especially important when wanting to write back the read bundle, as the hashes need to -// stay the same. -// The previous system of always turning hashes into strings worked well for the purpose of -// displaying hashes, but would have made it very hard to turn a stringyfied hash back into -// an actual hash. -#[derive(Clone, Debug, Eq)] -pub enum IdString64 { - Hash(Murmur64), - String(String), -} - -impl IdString64 { - pub fn to_murmur64(&self) -> Murmur64 { - match self { - Self::Hash(hash) => *hash, - Self::String(s) => Murmur64::hash(s.as_bytes()), - } - } - - pub fn display(&self) -> IdString64Display { - let s = match self { - IdString64::Hash(hash) => hash.to_string(), - IdString64::String(s) => s.clone(), - }; - - IdString64Display(s) - } - - pub fn is_string(&self) -> bool { - match self { - IdString64::Hash(_) => false, - IdString64::String(_) => true, - } - } - - pub fn is_hash(&self) -> bool { - match self { - IdString64::Hash(_) => true, - IdString64::String(_) => false, - } - } -} - -impl> From for IdString64 { - fn from(value: S) -> Self { - Self::String(value.into()) - } -} - -impl From for IdString64 { - fn from(value: Murmur64) -> Self { - Self::Hash(value) - } -} - -impl From for Murmur64 { - fn from(value: IdString64) -> Self { - value.to_murmur64() - } -} - -impl PartialEq for IdString64 { - fn eq(&self, other: &Self) -> bool { - self.to_murmur64() == other.to_murmur64() - } -} - -impl std::hash::Hash for IdString64 { - fn hash(&self, state: &mut H) { - state.write_u64(self.to_murmur64().into()); - } -} - -impl serde::Serialize for IdString64 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_u64(self.to_murmur64().into()) - } -} - -struct IdString64Visitor; - -impl<'de> serde::de::Visitor<'de> for IdString64Visitor { - type Value = IdString64; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an u64 or a string") - } - - fn visit_u64(self, value: u64) -> Result - where - E: serde::de::Error, - { - Ok(IdString64::Hash(value.into())) - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - Ok(IdString64::String(v.to_string())) - } - - fn visit_string(self, v: String) -> Result - where - E: serde::de::Error, - { - Ok(IdString64::String(v)) - } -} - -impl<'de> serde::Deserialize<'de> for IdString64 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_u64(IdString64Visitor) - } -} - -pub struct IdString64Display(String); - -impl std::fmt::Display for IdString64Display { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl std::fmt::UpperHex for IdString64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - std::fmt::UpperHex::fmt(&self.to_murmur64(), f) - } -} - -impl std::fmt::LowerHex for IdString64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - std::fmt::LowerHex::fmt(&self.to_murmur64(), f) - } -} +pub use types::*; diff --git a/lib/sdk/src/murmur/types.rs b/lib/sdk/src/murmur/types.rs new file mode 100644 index 0000000..1146494 --- /dev/null +++ b/lib/sdk/src/murmur/types.rs @@ -0,0 +1,365 @@ +use self::util::{parse_hex32, parse_hex64}; + +use super::*; + +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub struct Murmur64(u64); + +impl Murmur64 { + pub fn hash(s: B) -> Self + where + B: AsRef<[u8]>, + { + hash(s.as_ref(), SEED as u64).into() + } +} + +impl From for Murmur64 { + fn from(value: u64) -> Self { + Self(value) + } +} + +impl From for u64 { + fn from(value: Murmur64) -> Self { + value.0 + } +} + +impl TryFrom<&str> for Murmur64 { + type Error = Report; + + fn try_from(value: &str) -> Result { + parse_hex64(value) + .map(Self) + .wrap_err_with(|| format!("Failed to convert value to Murmur64: {value}")) + } +} + +impl fmt::UpperHex for Murmur64 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + +impl fmt::LowerHex for Murmur64 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::LowerHex::fmt(&self.0, f) + } +} + +impl fmt::Display for Murmur64 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + +impl<'de> Visitor<'de> for Murmur64 { + type Value = Self; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str( + "an usigned 64 bit integer \ + or a string in hexadecimal format encoding such an integer", + ) + } + + fn visit_f64(self, value: f64) -> Result + where + E: serde::de::Error, + { + let bytes = value.to_le_bytes(); + Ok(Self::from(u64::from_le_bytes(bytes))) + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + Ok(Self::from(value)) + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match Murmur64::try_from(value) { + Ok(hash) => Ok(hash), + Err(err) => Err(E::custom(format!( + "failed to convert '{value}' to Murmur64: {err}" + ))), + } + } +} + +impl<'de> Deserialize<'de> for Murmur64 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_any(Self(0)) + } +} + +impl Serialize for Murmur64 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("{self:016X}")) + } +} + +#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] +pub struct Murmur32(u32); + +impl Murmur32 { + pub fn hash(s: B) -> Self + where + B: AsRef<[u8]>, + { + hash32(s.as_ref(), SEED).into() + } +} + +impl From for Murmur32 { + fn from(value: u32) -> Self { + Self(value) + } +} + +impl From for u32 { + fn from(value: Murmur32) -> Self { + value.0 + } +} + +impl TryFrom<&str> for Murmur32 { + type Error = Report; + + fn try_from(value: &str) -> Result { + parse_hex32(value) + .map(Self) + .wrap_err_with(|| format!("Failed to convert value to Murmur32: {value}")) + } +} + +impl fmt::UpperHex for Murmur32 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + +impl fmt::Display for Murmur32 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + +impl Serialize for Murmur32 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("{self:08X}")) + } +} + +impl<'de> Visitor<'de> for Murmur32 { + type Value = Self; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str( + "an usigned 32 bit integer \ + or a string in hexadecimal format encoding such an integer", + ) + } + + fn visit_f64(self, value: f64) -> Result + where + E: serde::de::Error, + { + let bytes = value.to_le_bytes(); + self.visit_u32(u64::from_le_bytes(bytes) as u32) + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + self.visit_u32(value as u32) + } + + fn visit_u32(self, value: u32) -> Result + where + E: serde::de::Error, + { + Ok(Self::from(value)) + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match Murmur32::try_from(value) { + Ok(hash) => Ok(hash), + Err(err) => Err(E::custom(format!( + "failed to convert '{value}' to Murmur32: {err}" + ))), + } + } +} + +impl<'de> Deserialize<'de> for Murmur32 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_any(Self(0)) + } +} + +// This type encodes the fact that when reading in a bundle, we don't always have a dictionary +// entry for every hash in there. So we do want to have the real string available when needed, +// but at the same time retain the original hash information for when we don't. +// This is especially important when wanting to write back the read bundle, as the hashes need to +// stay the same. +// The previous system of always turning hashes into strings worked well for the purpose of +// displaying hashes, but would have made it very hard to turn a stringyfied hash back into +// an actual hash. +#[derive(Clone, Debug, Eq)] +pub enum IdString64 { + Hash(Murmur64), + String(String), +} + +impl IdString64 { + pub fn to_murmur64(&self) -> Murmur64 { + match self { + Self::Hash(hash) => *hash, + Self::String(s) => Murmur64::hash(s.as_bytes()), + } + } + + pub fn display(&self) -> IdString64Display { + let s = match self { + IdString64::Hash(hash) => hash.to_string(), + IdString64::String(s) => s.clone(), + }; + + IdString64Display(s) + } + + pub fn is_string(&self) -> bool { + match self { + IdString64::Hash(_) => false, + IdString64::String(_) => true, + } + } + + pub fn is_hash(&self) -> bool { + match self { + IdString64::Hash(_) => true, + IdString64::String(_) => false, + } + } +} + +impl> From for IdString64 { + fn from(value: S) -> Self { + Self::String(value.into()) + } +} + +impl From for IdString64 { + fn from(value: Murmur64) -> Self { + Self::Hash(value) + } +} + +impl From for Murmur64 { + fn from(value: IdString64) -> Self { + value.to_murmur64() + } +} + +impl PartialEq for IdString64 { + fn eq(&self, other: &Self) -> bool { + self.to_murmur64() == other.to_murmur64() + } +} + +impl std::hash::Hash for IdString64 { + fn hash(&self, state: &mut H) { + state.write_u64(self.to_murmur64().into()); + } +} + +impl serde::Serialize for IdString64 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u64(self.to_murmur64().into()) + } +} + +struct IdString64Visitor; + +impl<'de> serde::de::Visitor<'de> for IdString64Visitor { + type Value = IdString64; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an u64 or a string") + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + Ok(IdString64::Hash(value.into())) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + Ok(IdString64::String(v.to_string())) + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + Ok(IdString64::String(v)) + } +} + +impl<'de> serde::Deserialize<'de> for IdString64 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_u64(IdString64Visitor) + } +} + +pub struct IdString64Display(String); + +impl std::fmt::Display for IdString64Display { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::fmt::UpperHex for IdString64 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::UpperHex::fmt(&self.to_murmur64(), f) + } +} + +impl std::fmt::LowerHex for IdString64 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::LowerHex::fmt(&self.to_murmur64(), f) + } +} diff --git a/lib/sdk/src/murmur/util.rs b/lib/sdk/src/murmur/util.rs new file mode 100644 index 0000000..134c4e7 --- /dev/null +++ b/lib/sdk/src/murmur/util.rs @@ -0,0 +1,132 @@ +use color_eyre::eyre::bail; +use color_eyre::Result; + +// Generates tables similar to these: +// https://github.com/zbjornson/fast-hex/blob/a3487bca95127634a61bfeae8f8bfc8f0e5baa3f/src/hex.cc#L20-L89 +// `upper` determines upper vs. lower bits (first character is `upper`). +const fn generate_byte_map(upper: bool) -> [u8; 256] { + let mut out = [0u8; 256]; + let factor = if upper { 16 } else { 1 }; + + let mut i = 0; + + while i < 256 { + match i { + 0x30..=0x39 => out[i] = factor * (i as u8 - 0x30), + 0x41..=0x46 => out[i] = factor * (9 + i as u8 - 0x40), + 0x61..=0x66 => out[i] = factor * (9 + i as u8 - 0x60), + _ => out[i] = u8::MAX, + } + i += 1; + } + + out +} + +const BYTE_MAP_UPPER: [u8; 256] = generate_byte_map(true); +const BYTE_MAP_LOWER: [u8; 256] = generate_byte_map(false); + +macro_rules! make_parse_hex { + ($name:ident, $ty:ty, $len:expr) => { + #[inline] + pub fn $name(s: impl AsRef) -> Result<$ty> { + // For the string to be valid hex characters, it needs to be ASCII. + // So we can simply treat it as a byte stream. + let s = s.as_ref().as_bytes(); + + if s.len() != $len { + bail!( + "String length doesn't match. Expected {}, got {}", + $len, + s.len() + ); + } + + let n = $len / 2; + let mut out: $ty = 0; + let mut i = 0; + + while i < n { + let j = i * 2; + + let c1 = BYTE_MAP_UPPER[s[j] as usize]; + if c1 == u8::MAX { + bail!("Invalid character '{:?}' ({})", char::from(c1), c1); + } + + let c2 = BYTE_MAP_LOWER[s[j + 1] as usize]; + if c2 == u8::MAX { + bail!("Invalid character '{:?}' ({})", char::from(c2), c2); + } + + out |= ((c1 + c2) as $ty) << (n - i - 1) * 8; + + i += 1; + } + + Ok(out) + } + }; +} + +make_parse_hex!(parse_hex64, u64, 16); +make_parse_hex!(parse_hex32, u32, 8); + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn parse_32() { + let hash = "A14E8DFA"; + assert_eq!(parse_hex32(hash).unwrap(), 0xA14E8DFA); + } + + #[test] + fn parse_64() { + let hash = "A14E8DFA2CD117E2"; + assert_eq!(parse_hex64(hash).unwrap(), 0xA14E8DFA2CD117E2); + } + + #[test] + fn std_from_radix_32() { + let hash = "A14E8DFA"; + assert_eq!(u32::from_str_radix(hash, 16).unwrap(), 0xA14E8DFA); + } + + #[test] + fn std_from_radix_64() { + let hash = "A14E8DFA2CD117E2"; + assert_eq!(u64::from_str_radix(hash, 16).unwrap(), 0xA14E8DFA2CD117E2); + } +} + +#[cfg(test)] +mod bench { + use super::{parse_hex32, parse_hex64}; + + extern crate test; + + const HASH32: &str = "A14E8DFA"; + const HASH64: &str = "A14E8DFA2CD117E2"; + + #[bench] + fn custom_32(b: &mut test::Bencher) { + b.iter(|| test::black_box(parse_hex32(test::black_box(HASH32)))) + } + + #[bench] + fn std_32(b: &mut test::Bencher) { + b.iter(|| test::black_box(u32::from_str_radix(test::black_box(HASH32), 16))) + } + + #[bench] + fn custom_64(b: &mut test::Bencher) { + b.iter(|| test::black_box(parse_hex64(test::black_box(HASH64)))) + } + + #[bench] + fn std_64(b: &mut test::Bencher) { + b.iter(|| test::black_box(u64::from_str_radix(test::black_box(HASH64), 16))) + } +} From b40375122877d62c6715ad7ff24b9177a97c8287 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 8 Jul 2024 16:55:32 +0200 Subject: [PATCH 212/335] Update ansi-parser --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7cbb4b5..2df8581 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,8 +28,8 @@ dependencies = [ [[package]] name = "ansi-parser" -version = "0.9.0" -source = "git+https://gitlab.com/lschwiderski/ansi-parser.git?branch=issue/outdated-heapless#af1951d16951a37101139763593be44103e3a477" +version = "0.9.1" +source = "git+https://gitlab.com/lschwiderski/ansi-parser.git?branch=issue/outdated-heapless#bfcd2689677fa93ce72c55833e891af36c65b2aa" dependencies = [ "heapless", "nom", diff --git a/Cargo.toml b/Cargo.toml index 8cbe52c..8c0e7c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ zip = { version = "1.3.0", default-features = false, features = ["deflate", "bzi [patch.crates-io] color-eyre = { path = "lib/color-eyre" } -ansi-parser = { git = "https://gitlab.com/lschwiderski/ansi-parser.git", branch = "issue/outdated-heapless" } +ansi-parser = { git = "https://gitlab.com/lschwiderski/ansi-parser.git", branch = "issue/outdated-heapless", version = "0.9.1" } [profile.dev.package.backtrace] opt-level = 3 From 94b64b461917ad17c2137a01ad0ce2b09700f152 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 10 Jul 2024 18:40:52 +0200 Subject: [PATCH 213/335] Update zip --- Cargo.lock | 42 ++++++++++++++++++++++++++++++++---------- Cargo.toml | 2 +- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2df8581..faad8f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -640,9 +640,9 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -658,9 +658,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crypto-common" @@ -2011,6 +2011,12 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + [[package]] name = "log" version = "0.4.21" @@ -3435,18 +3441,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", @@ -4426,9 +4432,9 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zip" -version = "1.3.0" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f4a27345eb6f7aa7bd015ba7eb4175fa4e1b462a29874b779e0bbcf96c6ac7" +checksum = "775a2b471036342aa69bc5a602bc889cb0a06cda00477d0c69566757d5553d39" dependencies = [ "arbitrary", "bzip2", @@ -4437,11 +4443,27 @@ dependencies = [ "displaydoc", "flate2", "indexmap", + "memchr", "thiserror", "time", + "zopfli", "zstd", ] +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", +] + [[package]] name = "zstd" version = "0.13.1" diff --git a/Cargo.toml b/Cargo.toml index 8c0e7c5..f38ac22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ members = [ exclude = ["lib/color-eyre"] [workspace.dependencies] -zip = { version = "1.3.0", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } +zip = { version = "2.1.3", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } [patch.crates-io] color-eyre = { path = "lib/color-eyre" } From 0f14834e2d162fe6b734407ce6359cbc49127e91 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 10 Jul 2024 18:41:38 +0200 Subject: [PATCH 214/335] Remove string_template Use minijinja for all templates --- Cargo.lock | 1 - crates/dtmm/Cargo.toml | 1 - .../assets/{mod_main.lua => mod_main.lua.j2} | 4 +++- crates/dtmm/src/controller/deploy.rs | 24 ++++++++++++------- 4 files changed, 18 insertions(+), 12 deletions(-) rename crates/dtmm/assets/{mod_main.lua => mod_main.lua.j2} (98%) diff --git a/Cargo.lock b/Cargo.lock index faad8f6..55cdf0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -910,7 +910,6 @@ dependencies = [ "sdk", "serde", "serde_sjson", - "string_template", "strip-ansi-escapes", "time", "tokio", diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index f57d0c8..661eb17 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -29,7 +29,6 @@ path-slash = "0.2.1" sdk = { path = "../../lib/sdk", version = "*" } serde = { version = "1.0.152", features = ["derive", "rc"] } serde_sjson = { path = "../../lib/serde_sjson", version = "*" } -string_template = "0.2.1" strip-ansi-escapes = "0.2.0" time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] } tokio = { version = "1.23.0", features = ["rt", "fs", "tracing", "sync"] } diff --git a/crates/dtmm/assets/mod_main.lua b/crates/dtmm/assets/mod_main.lua.j2 similarity index 98% rename from crates/dtmm/assets/mod_main.lua rename to crates/dtmm/assets/mod_main.lua.j2 index e4006f6..c1131be 100644 --- a/crates/dtmm/assets/mod_main.lua +++ b/crates/dtmm/assets/mod_main.lua.j2 @@ -17,7 +17,7 @@ local require_store = {} -- This token is treated as a string template and filled by DTMM during deployment. -- This allows hiding unsafe I/O functions behind a setting. --- It's also a valid table definition, thereby degrading gracefully when not replaced. +-- When not replaced, it's also a valid table definition, thereby degrading gracefully. local is_io_enabled = {{ is_io_enabled }} -- luacheck: ignore 113 local lua_libs = { debug = debug, @@ -207,3 +207,5 @@ function init() Main:init() end + +-- vim: ft=lua diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index 4d5f831..a59a1fe 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::io::{Cursor, ErrorKind}; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -16,7 +15,6 @@ use sdk::{ Bundle, BundleDatabase, BundleFile, BundleFileType, BundleFileVariant, FromBinary, ToBinary, }; use serde::{Deserialize, Serialize}; -use string_template::Template; use time::OffsetDateTime; use tokio::fs::{self, DirEntry}; use tokio::io::AsyncWriteExt; @@ -572,12 +570,17 @@ async fn patch_boot_bundle(state: Arc) -> Result> { let span = tracing::debug_span!("Importing mod main script"); let _enter = span.enter(); - let is_io_enabled = format!("{}", state.is_io_enabled); - let mut data = HashMap::new(); - data.insert("is_io_enabled", is_io_enabled.as_str()); + let mut env = Environment::new(); + env.add_template("mod_main.lua", include_str!("../../assets/mod_main.lua.j2")) + .wrap_err("Failed to compile template for `mod_main.lua`")?; + let tmpl = env + .get_template("mod_main.lua") + .wrap_err("Failed to get template `mod_main.lua`")?; + + let lua = tmpl + .render(minijinja::context!(is_io_enabled => if state.is_io_enabled { "true" } else {"false"})) + .wrap_err("Failed to render template `mod_main.lua`")?; - let tmpl = include_str!("../../assets/mod_main.lua"); - let lua = Template::new(tmpl).render(&data); tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); let file = lua::compile(MOD_BOOT_SCRIPT, lua).wrap_err("Failed to compile mod main Lua file")?; @@ -707,7 +710,7 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { }, async { let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); - match read_sjson_file::<_, DeploymentData>(path).await { + match read_sjson_file::<_, DeploymentData>(&path).await { Ok(data) => Ok(Some(data)), Err(err) => { if let Some(err) = err.downcast_ref::() @@ -715,7 +718,10 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { { Ok(None) } else { - Err(err).wrap_err("Failed to read deployment data") + Err(err).wrap_err(format!( + "Failed to read deployment data from: {}", + path.display() + )) } } } From 05df72635a440a08ce307a2a3f5fa3396bcb7d28 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sat, 2 Dec 2023 19:48:03 +0100 Subject: [PATCH 215/335] dtmm: Pull ModLoader in Closes #155. --- crates/dtmm/assets/init.lua | 70 +++++ crates/dtmm/assets/mod_loader.lua | 405 +++++++++++++++++++++++++++ crates/dtmm/assets/mod_main.lua.j2 | 25 +- crates/dtmm/src/controller/app.rs | 8 - crates/dtmm/src/controller/deploy.rs | 112 ++------ 5 files changed, 509 insertions(+), 111 deletions(-) create mode 100644 crates/dtmm/assets/init.lua create mode 100644 crates/dtmm/assets/mod_loader.lua diff --git a/crates/dtmm/assets/init.lua b/crates/dtmm/assets/init.lua new file mode 100644 index 0000000..e2acdbd --- /dev/null +++ b/crates/dtmm/assets/init.lua @@ -0,0 +1,70 @@ +local StateGame = require("scripts/game_states/state_game") +local StateSplash = require("scripts/game_states/game/state_splash") +local GameStateMachine = require("scripts/foundation/utilities/game_state_machine") + +local function hook(obj, fn_name, cb) + local orig = obj[fn_name] + + obj[fn_name] = function(...) + return cb(orig, ...) + end +end + +function init(mod_data, boot_gui) + local ModLoader = require("scripts/mods/mod_loader") + local mod_loader = ModLoader:new(mod_data, boot_gui) + + -- The mod loader needs to remain active during game play, to + -- enable reloads + hook(StateGame, "update", function(func, dt, ...) + mod_loader:update(dt) + return func(dt, ...) + end) + + -- Skip splash view + hook(StateSplash, "on_enter", function(func, self, ...) + local result = func(self, ...) + + self._should_skip = true + self._continue = true + + return result + end) + + -- Trigger state change events + hook(GameStateMachine, "_change_state", function(func, self, ...) + local old_state = self._state + local old_state_name = old_state and self:current_state_name() + + if old_state_name then + mod_loader:on_game_state_changed("exit", old_state_name, old_state) + end + + local result = func(self, ...) + + local new_state = self._state + local new_state_name = new_state and self:current_state_name() + + if new_state_name then + mod_loader:on_game_state_changed("enter", new_state_name, new_state) + end + + return result + end) + + -- Trigger ending state change event + hook(GameStateMachine, "destroy", function(func, self, ...) + local old_state = self._state + local old_state_name = old_state and self:current_state_name() + + if old_state_name then + mod_loader:on_game_state_changed("exit", old_state_name) + end + + return func(self, ...) + end) + + return mod_loader +end + +return init diff --git a/crates/dtmm/assets/mod_loader.lua b/crates/dtmm/assets/mod_loader.lua new file mode 100644 index 0000000..4da08a4 --- /dev/null +++ b/crates/dtmm/assets/mod_loader.lua @@ -0,0 +1,405 @@ +-- Copyright on this file is owned by Fatshark. +-- It is extracted, used and modified with permission only for +-- the purpose of loading mods within Warhammer 40,000: Darktide. +local ModLoader = class("ModLoader") + +local table_unpack = table.unpack or unpack +local table_pack = table.pack or pack + +local ScriptGui = require("scripts/foundation/utilities/script_gui") + +local FONT_MATERIAL = "content/ui/fonts/arial" + +local LOG_LEVELS = { + spew = 4, + info = 3, + warning = 2, + error = 1 +} +local DEFAULT_SETTINGS = { + log_level = LOG_LEVELS.error, + developer_mode = false +} + +local Keyboard = Keyboard +local BUTTON_INDEX_R = Keyboard.button_index("r") +local BUTTON_INDEX_LEFT_SHIFT = Keyboard.button_index("left shift") +local BUTTON_INDEX_LEFT_CTRL = Keyboard.button_index("left ctrl") + +ModLoader.init = function(self, mod_data, boot_gui) + table.dump(mod_data, nil, 5, function(...) Log.info("ModLoader", ...) end) + + self._mod_data = mod_data + self._gui = boot_gui + + self._settings = Application.user_setting("mod_settings") or DEFAULT_SETTINGS + + self._mods = {} + self._num_mods = nil + self._chat_print_buffer = {} + self._reload_data = {} + self._ui_time = 0 + + self._state = "scanning" +end + +ModLoader.developer_mode_enabled = function(self) + return self._settings.developer_mode +end + +ModLoader.set_developer_mode = function(self, enabled) + self._settings.developer_mode = enabled +end + +ModLoader._draw_state_to_gui = function(self, gui, dt) + local state = self._state + local t = self._ui_time + dt + self._ui_time = t + local status_str = "Loading mods" + + if state == "scanning" then + status_str = "Scanning for mods" + elseif state == "loading" or state == "initializing" then + local mod = self._mods[self._mod_load_index] + status_str = string.format("Loading mod %q", mod.name) + end + + local msg = status_str .. string.rep(".", (2 * t) % 4) + ScriptGui.text(gui, msg, FONT_MATERIAL, 25, Vector3(20, 30, 1), Color.white()) +end + +ModLoader.remove_gui = function(self) + self._gui = nil +end + +ModLoader.mod_data = function(self, id) + -- Since this primarily exists for DMF, + -- we can optimize the search for its use case of looking for the + -- mod currently being loaded + local mod_data = self._mods[self._mod_load_index] + + if mod_data.id ~= id then + mod_data = nil + + for _, v in ipairs(self._mods) do + if v.id == id then + mod_data = v + end + end + end + + return mod_data +end + +ModLoader._check_reload = function() + return Keyboard.pressed(BUTTON_INDEX_R) and + Keyboard.button(BUTTON_INDEX_LEFT_SHIFT) + + Keyboard.button(BUTTON_INDEX_LEFT_CTRL) == 2 +end + +ModLoader.update = function(self, dt) + local chat_print_buffer = self._chat_print_buffer + local num_delayed_prints = #chat_print_buffer + + if num_delayed_prints > 0 and Managers.chat then + for i = 1, num_delayed_prints, 1 do + -- TODO: Use new chat system + -- Managers.chat:add_local_system_message(1, chat_print_buffer[i], true) + + chat_print_buffer[i] = nil + end + end + + local old_state = self._state + + if self._settings.developer_mode and self:_check_reload() then + self._reload_requested = true + end + + if self._reload_requested and old_state == "done" then + self:_reload_mods() + end + + if old_state == "done" then + self:_run_callbacks("update", dt) + elseif old_state == "scanning" then + Log.info("ModLoader", "Scanning for mods") + self:_build_mod_table() + + self._state = self:_load_mod(1) + self._ui_time = 0 + elseif old_state == "loading" then + local handle = self._loading_resource_handle + + if ResourcePackage.has_loaded(handle) then + ResourcePackage.flush(handle) + + local mod = self._mods[self._mod_load_index] + local next_index = mod.package_index + 1 + local mod_data = mod.data + + if next_index <= #mod_data.packages then + self:_load_package(mod, next_index) + else + self._state = "initializing" + end + end + elseif old_state == "initializing" then + local mod = self._mods[self._mod_load_index] + local mod_data = mod.data + + Log.info("ModLoader", "Initializing mod %q", mod.name) + + mod.state = "running" + local ok, object = xpcall(mod_data.run, function(err) + if type(err) == "string" then + return err .. "\n" .. Script.callstack() + else + return err + end + end) + + if not ok then + if object.error then + object = string.format( + "%s\n<>\n%s\n<>\n<>\n%s\n<>\n<>\n%s\n<>", + object.error, object.traceback, object.locals, object.self) + end + + Log.error("ModLoader", "Failed 'run' for %q: %s", mod.name, object) + end + + mod.object = object or {} + + self:_run_callback(mod, "init", self._reload_data[mod.id]) + + Log.info("ModLoader", "Finished loading %q", mod.name) + + self._state = self:_load_mod(self._mod_load_index + 1) + end + + local gui = self._gui + if gui then + self:_draw_state_to_gui(gui, dt) + end + + if old_state ~= self._state then + Log.info("ModLoader", "%s -> %s", old_state, self._state) + end +end + +ModLoader.all_mods_loaded = function(self) + return self._state == "done" +end + +ModLoader.destroy = function(self) + self:_run_callbacks("on_destroy") + self:unload_all_mods() +end + +ModLoader._run_callbacks = function(self, callback_name, ...) + for i = 1, self._num_mods, 1 do + local mod = self._mods[i] + + if mod and not mod.callbacks_disabled then + self:_run_callback(mod, callback_name, ...) + end + end +end + +ModLoader._run_callback = function(self, mod, callback_name, ...) + local object = mod.object + local cb = object[callback_name] + + if not cb then + return + end + + local args = table_pack(...) + + local success, val = xpcall( + function() return cb(object, table_unpack(args)) end, + function(err) + if type(err) == "string" then + return err .. "\n" .. Script.callstack() + else + return err + end + end + ) + + if success then + return val + else + Log.error("ModLoader", "Failed to run callback %q for mod %q with id %q. Disabling callbacks until reload.", + callback_name, mod.name, mod.id) + if val.error then + Log.error("ModLoader", + "Error: %s\n<>\n%s<>\n<>\n%s<>\n<>\n%s<>", + val.error, val.traceback, val.locals, val.self) + else + Log.error("ModLoader", "Error: %s", val or "[unknown error]") + end + + mod.callbacks_disabled = true + end +end + +ModLoader._start_scan = function(self) + Log.info("ModLoader", "Starting mod scan") + self._state = "scanning" +end + +ModLoader._build_mod_table = function(self) + fassert(table.is_empty(self._mods), "Trying to add mods to non-empty mod table") + + for i, mod_data in ipairs(self._mod_data) do + Log.info("ModLoader", "mods[%d] = id=%q | name=%q | bundled=%s", i, mod_data.id, mod_data.name, + tostring(mod_data.bundled)) + + self._mods[i] = { + id = mod_data.id, + state = "not_loaded", + callbacks_disabled = false, + name = mod_data.name, + loaded_packages = {}, + packages = mod_data.packages, + data = mod_data, + bundled = mod_data.bundled or false, + } + end + + self._num_mods = #self._mods + + Log.info("ModLoader", "Found %i mods", self._num_mods) +end + +ModLoader._load_mod = function(self, index) + self._ui_time = 0 + local mods = self._mods + local mod = mods[index] + + if not mod then + table.clear(self._reload_data) + + return "done" + end + + Log.info("ModLoader", "Loading mod %q", mod.id) + + mod.state = "loading" + + Crashify.print_property(string.format("Mod:%s:%s", mod.id, mod.name), true) + + self._mod_load_index = index + + if mod.bundled and mod.packages[1] then + self:_load_package(mod, 1) + return "loading" + else + return "initializing" + end +end + +ModLoader._load_package = function(self, mod, index) + mod.package_index = index + local package_name = mod.packages[index] + + if not package_name then + return + end + + Log.info("ModLoader", "Loading package %q", package_name) + + local resource_handle = Application.resource_package(package_name) + self._loading_resource_handle = resource_handle + + ResourcePackage.load(resource_handle) + + table.insert(mod.loaded_packages, resource_handle) +end + +ModLoader.unload_all_mods = function(self) + if self._state ~= "done" then + Log.error("ModLoader", "Mods can't be unloaded, mod state is not \"done\". current: %q", self._state) + + return + end + + Log.info("ModLoader", "Unload all mod packages") + + for i = self._num_mods, 1, -1 do + local mod = self._mods[i] + + if mod then + self:unload_mod(i) + end + + self._mods[i] = nil + end + + self._num_mods = nil + self._state = "unloaded" +end + +ModLoader.unload_mod = function(self, index) + local mod = self._mods[index] + + if mod then + Log.info("ModLoader", "Unloading %q.", mod.name) + + for _, handle in ipairs(mod.loaded_packages) do + ResourcePackage.unload(handle) + Application.release_resource_package(handle) + end + + mod.state = "not_loaded" + else + Log.error("ModLoader", "Mod index %i can't be unloaded, has not been loaded", index) + end +end + +ModLoader._reload_mods = function(self) + Log.info("ModLoader", "reloading mods") + + for i = 1, self._num_mods, 1 do + local mod = self._mods[i] + + if mod and mod.state == "running" then + Log.info("ModLoader", "reloading %s", mod.name) + + self._reload_data[mod.id] = self:_run_callback(mod, "on_reload") + else + Log.info("ModLoader", "not reloading mod, state: %s", mod.state) + end + end + + self:unload_all_mods() + self:_start_scan() + + self._reload_requested = false +end + +ModLoader.on_game_state_changed = function(self, status, state_name, state_object) + if self._state == "done" then + self:_run_callbacks("on_game_state_changed", status, state_name, state_object) + else + Log.warning("ModLoader", "Ignored on_game_state_changed call due to being in state %q", self._state) + end +end + +ModLoader.print = function(self, level, str, ...) + local f = Log[level] + if f then + f("ModLoader", str, ...) + else + local message = string.format("[ModLoader][" .. level .. "] " .. str, ...) + local log_level = LOG_LEVELS[level] or 99 + + if log_level <= 2 then + print(message) + end + end +end + +return ModLoader diff --git a/crates/dtmm/assets/mod_main.lua.j2 b/crates/dtmm/assets/mod_main.lua.j2 index c1131be..c93a75c 100644 --- a/crates/dtmm/assets/mod_main.lua.j2 +++ b/crates/dtmm/assets/mod_main.lua.j2 @@ -18,7 +18,7 @@ local require_store = {} -- This token is treated as a string template and filled by DTMM during deployment. -- This allows hiding unsafe I/O functions behind a setting. -- When not replaced, it's also a valid table definition, thereby degrading gracefully. -local is_io_enabled = {{ is_io_enabled }} -- luacheck: ignore 113 +local is_io_enabled = { { is_io_enabled } } -- luacheck: ignore 113 local lua_libs = { debug = debug, os = { @@ -105,8 +105,15 @@ end require("scripts/main") log("mod_main", "'scripts/main' loaded") --- Inject our state into the game. The state needs to run after `StateGame._init_managers`, --- since some parts of DMF, and presumably other mods, depend on some of those managers to exist. +-- We need to inject two states into two different state machines: +-- First, we inject one into the `"Main"` state machine at a specific location, so that we're +-- still early in the process, but right after `StateRequireScripts` where most game files +-- are already available to `require` and hook. +-- This is where the `ModLoader` is created initially. +-- Then, we inject into the very first position of the `"Game"` state machine. This runs right +-- after `StateGame._init_managers`, at which point all the parts needed for DMF and other mods +-- have been initialized. +-- This is where `ModLoader` will finally start loading mods. local function patch_mod_loading_state() local StateBootSubStateBase = require("scripts/game_states/boot/state_boot_sub_state_base") local StateBootLoadDML = class("StateBootLoadDML", "StateBootSubStateBase") @@ -121,8 +128,7 @@ local function patch_mod_loading_state() self._package_manager = package_manager self._package_handles = { - ["packages/mods"] = package_manager:load("packages/mods", "StateBootDML", nil), - ["packages/dml"] = package_manager:load("packages/dml", "StateBootDML", nil), + ["packages/mods"] = package_manager:load("packages/mods", "StateBootLoadDML", nil), } end @@ -130,10 +136,13 @@ local function patch_mod_loading_state() local package_manager = self._package_manager if package_manager:update() then - local DML = require("scripts/mods/dml/init") local mod_data = require("scripts/mods/mod_data") - local mod_loader = DML.create_loader(mod_data) + + local create_mod_loader = require("scripts/mods/init") + local mod_loader = create_mod_loader(mod_data) + Managers.mod = mod_loader + log("StateBootLoadDML", "DML loaded, exiting") return true, false end @@ -188,8 +197,6 @@ local function patch_mod_loading_state() GameStateMachine_init(self, parent, StateGameLoadMods, params, creation_context, state_change_callbacks, name) -- And since we're done now, we can revert the function to its original GameStateMachine.init = GameStateMachine_init - - return else -- In all other cases, simply call the original GameStateMachine_init(self, parent, start_state, params, creation_context, state_change_callbacks, name) diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index 7ac703f..44842e0 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -161,14 +161,6 @@ where } pub(crate) fn check_mod_order(state: &ActionState) -> Result<()> { - { - let first = state.mods.get(0); - if first.is_none() || !(first.unwrap().id == "dml" && first.unwrap().enabled) { - // TODO: Add a suggestion where to get it, once that's published - eyre::bail!("'Darktide Mod Loader' needs to be installed, enabled and at the top of the load order"); - } - } - if tracing::enabled!(tracing::Level::DEBUG) { let order = state.mods.iter().filter(|i| i.enabled).enumerate().fold( String::new(), diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index a59a1fe..f0cfe96 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -26,7 +26,6 @@ use crate::state::{ActionState, PackageInfo}; pub const MOD_BUNDLE_NAME: &str = "packages/mods"; pub const BOOT_BUNDLE_NAME: &str = "packages/boot"; -pub const DML_BUNDLE_NAME: &str = "packages/dml"; pub const BUNDLE_DATABASE_NAME: &str = "bundle_database.data"; pub const MOD_BOOT_SCRIPT: &str = "scripts/mod_main"; pub const MOD_DATA_SCRIPT: &str = "scripts/mods/mod_data"; @@ -225,11 +224,7 @@ async fn copy_mod_folders(state: Arc) -> Result> { let mut tasks = Vec::new(); - for mod_info in state - .mods - .iter() - .filter(|m| m.id != "dml" && m.enabled && !m.bundled) - { + for mod_info in state.mods.iter().filter(|m| m.enabled && !m.bundled) { let span = tracing::trace_span!("copying legacy mod", name = mod_info.name); let _enter = span.enter(); @@ -283,7 +278,7 @@ fn build_mod_data_lua(state: Arc) -> Result { .mods .iter() .filter_map(|m| { - if m.id == "dml" || !m.enabled { + if !m.enabled { return None; } @@ -325,31 +320,29 @@ async fn build_bundles(state: Arc) -> Result> { let mut bundles = Vec::new(); - { - tracing::trace!("Building mod data script"); - - let span = tracing::debug_span!("Building mod data script"); + let mut add_lua_asset = |name, data: &str| { + let span = tracing::info_span!("Compiling Lua", name, data_len = data.len()); let _enter = span.enter(); - let lua = build_mod_data_lua(state.clone()).wrap_err("Failed to build Lua mod data")?; - - tracing::trace!("Compiling mod data script"); - - let file = - lua::compile(MOD_DATA_SCRIPT, lua).wrap_err("Failed to compile mod data Lua file")?; - - tracing::trace!("Compile mod data script"); + let file = lua::compile(name, data).wrap_err("Failed to compile Lua")?; mod_bundle.add_file(file); - } + + Ok::<_, Report>(()) + }; + + build_mod_data_lua(state.clone()) + .wrap_err("Failed to build 'mod_data.lua'") + .and_then(|data| add_lua_asset(MOD_DATA_SCRIPT, &data))?; + add_lua_asset("scripts/mods/init", include_str!("../../assets/init.lua"))?; + add_lua_asset( + "scripts/mods/mod_loader", + include_str!("../../assets/mod_loader.lua"), + )?; tracing::trace!("Preparing tasks to deploy bundle files"); - for mod_info in state - .mods - .iter() - .filter(|m| m.id != "dml" && m.enabled && m.bundled) - { + for mod_info in state.mods.iter().filter(|m| m.enabled && m.bundled) { let span = tracing::trace_span!("building mod packages", name = mod_info.name); let _enter = span.enter(); @@ -497,75 +490,6 @@ async fn patch_boot_bundle(state: Arc) -> Result> { boot_bundle.add_file(f); } - { - tracing::trace!("Handling DML packages and bundle"); - let span = tracing::trace_span!("handle DML"); - let _enter = span.enter(); - - let mut variant = BundleFileVariant::new(); - - let mod_info = state - .mods - .iter() - .find(|m| m.id == "dml") - .ok_or_else(|| eyre::eyre!("DML not found in mod list"))?; - let pkg_info = mod_info - .packages - .get(0) - .ok_or_else(|| eyre::eyre!("invalid mod package for DML")) - .with_suggestion(|| "Re-download and import the newest version.".to_string())?; - let bundle_name = format!("{:016x}", Murmur64::hash(&pkg_info.name)); - let src = state.mod_dir.join(&mod_info.id).join(&bundle_name); - - { - let bin = fs::read(&src) - .await - .wrap_err_with(|| format!("Failed to read bundle file '{}'", src.display()))?; - let name = Bundle::get_name_from_path(&state.ctx, &src); - - let dml_bundle = Bundle::from_binary(&state.ctx, name, bin) - .wrap_err_with(|| format!("Failed to parse bundle '{}'", src.display()))?; - - bundles.push(dml_bundle); - }; - - { - let dest = bundle_dir.join(&bundle_name); - let pkg_name = pkg_info.name.clone(); - let mod_name = mod_info.name.clone(); - - tracing::debug!( - "Copying bundle {} for mod {}: {} -> {}", - pkg_name, - mod_name, - src.display(), - dest.display() - ); - // We attempt to remove any previous file, so that the hard link can be created. - // We can reasonably ignore errors here, as a 'NotFound' is actually fine, the copy - // may be possible despite an error here, or the error will be reported by it anyways. - // TODO: There is a chance that we delete an actual game bundle, but with 64bit - // hashes, it's low enough for now, and the setup required to detect - // "game bundle vs mod bundle" is non-trivial. - let _ = fs::remove_file(&dest).await; - fs::copy(&src, &dest).await.wrap_err_with(|| { - format!( - "Failed to copy bundle {pkg_name} for mod {mod_name}. Src: {}, dest: {}", - src.display(), - dest.display() - ) - })?; - } - - let pkg = make_package(pkg_info).wrap_err("Failed to create package file for dml")?; - variant.set_data(pkg.to_binary()?); - - let mut f = BundleFile::new(DML_BUNDLE_NAME.to_string(), BundleFileType::Package); - f.add_variant(variant); - - boot_bundle.add_file(f); - } - { let span = tracing::debug_span!("Importing mod main script"); let _enter = span.enter(); From 91651a8467f850f2ea45cad177736be34f507042 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 10 Jul 2024 19:53:46 +0200 Subject: [PATCH 216/335] Apply linter fixes in mod_main.lua --- crates/dtmm/assets/mod_main.lua.j2 | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/assets/mod_main.lua.j2 b/crates/dtmm/assets/mod_main.lua.j2 index c93a75c..4dd2787 100644 --- a/crates/dtmm/assets/mod_main.lua.j2 +++ b/crates/dtmm/assets/mod_main.lua.j2 @@ -18,7 +18,7 @@ local require_store = {} -- This token is treated as a string template and filled by DTMM during deployment. -- This allows hiding unsafe I/O functions behind a setting. -- When not replaced, it's also a valid table definition, thereby degrading gracefully. -local is_io_enabled = { { is_io_enabled } } -- luacheck: ignore 113 +local is_io_enabled = {{ is_io_enabled }} -- luacheck: ignore 113 local lua_libs = { debug = debug, os = { @@ -115,7 +115,6 @@ log("mod_main", "'scripts/main' loaded") -- have been initialized. -- This is where `ModLoader` will finally start loading mods. local function patch_mod_loading_state() - local StateBootSubStateBase = require("scripts/game_states/boot/state_boot_sub_state_base") local StateBootLoadDML = class("StateBootLoadDML", "StateBootSubStateBase") local StateGameLoadMods = class("StateGameLoadMods") @@ -132,7 +131,7 @@ local function patch_mod_loading_state() } end - StateBootLoadDML._state_update = function(self, dt) + StateBootLoadDML._state_update = function(self, _) local package_manager = self._package_manager if package_manager:update() then @@ -157,9 +156,7 @@ local function patch_mod_loading_state() self._next_state_params = params end - function StateGameLoadMods:update(main_dt) - local state = self._loading_state - + function StateGameLoadMods:update(_) -- We're relying on the fact that DML internally makes sure -- that `Managers.mod:update()` is being called appropriately. -- The implementation as of this writing is to hook `StateGame.update`. From e6f1e7c117e6160458a23e5ad2120c5ba76d7ae1 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 10 Jul 2024 21:54:51 +0200 Subject: [PATCH 217/335] Fix load order verification With `.enumerate()` after `.filter()`, the resulting indices didn't properly map back to the overall mod list anymore. But the checks afterwards relied on that. Moving the `.enumerate()` before the `.filter()` makes sure that the indices are correct. --- crates/dtmm/src/controller/app.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/crates/dtmm/src/controller/app.rs b/crates/dtmm/src/controller/app.rs index 44842e0..d5b397c 100644 --- a/crates/dtmm/src/controller/app.rs +++ b/crates/dtmm/src/controller/app.rs @@ -162,18 +162,20 @@ where pub(crate) fn check_mod_order(state: &ActionState) -> Result<()> { if tracing::enabled!(tracing::Level::DEBUG) { - let order = state.mods.iter().filter(|i| i.enabled).enumerate().fold( - String::new(), - |mut s, (i, info)| { + let order = state + .mods + .iter() + .enumerate() + .filter(|(_, i)| i.enabled) + .fold(String::new(), |mut s, (i, info)| { s.push_str(&format!("{}: {} - {}\n", i, info.id, info.name)); s - }, - ); + }); tracing::debug!("Mod order:\n{}", order); } - for (i, mod_info) in state.mods.iter().filter(|i| i.enabled).enumerate() { + for (i, mod_info) in state.mods.iter().enumerate().filter(|(_, i)| i.enabled) { for dep in &mod_info.depends { let dep_info = state.mods.iter().enumerate().find(|(_, m)| m.id == dep.id); From 84606814fdd1fd3fae8987e7aafd21a0d70a1bd8 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sat, 11 May 2024 16:54:57 +0200 Subject: [PATCH 218/335] Add application icon --- .gitattributes | 6 +++++ Cargo.lock | 23 ++++++++++++++++++-- crates/dtmm/Cargo.toml | 9 ++++++++ crates/dtmm/assets/DTMM_logo.xcf | 3 +++ crates/dtmm/assets/DTMM_logo_256.png | 3 +++ crates/dtmm/assets/DTMM_logo_48.png | 3 +++ crates/dtmm/assets/DTMM_logo_64.png | 3 +++ crates/dtmm/assets/DTMM_logo_border.png | 3 +++ crates/dtmm/assets/DTMM_logo_faint_glow.png | 3 +++ crates/dtmm/assets/DTMM_logo_small.png | 3 +++ crates/dtmm/assets/dtmm.desktop | 1 + crates/dtmm/assets/dtmm.ico | 3 +++ crates/dtmm/build.rs | 7 ++++++ docs/screenshots/dtmm.png | Bin 58994 -> 130 bytes 14 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 .gitattributes create mode 100644 crates/dtmm/assets/DTMM_logo.xcf create mode 100644 crates/dtmm/assets/DTMM_logo_256.png create mode 100644 crates/dtmm/assets/DTMM_logo_48.png create mode 100644 crates/dtmm/assets/DTMM_logo_64.png create mode 100644 crates/dtmm/assets/DTMM_logo_border.png create mode 100644 crates/dtmm/assets/DTMM_logo_faint_glow.png create mode 100644 crates/dtmm/assets/DTMM_logo_small.png create mode 100644 crates/dtmm/assets/dtmm.ico create mode 100644 crates/dtmm/build.rs diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9a16e3d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +* text=auto + +*.xcf filter=lfs diff=lfs merge=lfs -text +*.ico filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text diff --git a/Cargo.lock b/Cargo.lock index 55cdf0d..a251de9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -549,7 +549,7 @@ dependencies = [ "directories", "serde", "thiserror", - "toml", + "toml 0.8.13", ] [[package]] @@ -918,6 +918,7 @@ dependencies = [ "tracing-error", "tracing-subscriber", "usvg", + "winres", "zip", ] @@ -3407,7 +3408,7 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml", + "toml 0.8.13", "version-compare", ] @@ -3614,6 +3615,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "toml" version = "0.8.13" @@ -4396,6 +4406,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winres" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" +dependencies = [ + "toml 0.5.11", +] + [[package]] name = "wio" version = "0.2.2" diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index 661eb17..f947fc6 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -2,6 +2,12 @@ name = "dtmm" version = "0.1.0" edition = "2021" +authors = ["Lucas Schwiderski "] +description = "DTMM is a GUI application to install and manage mods for the game." +documentation = "https://git.sclu1034.dev/bitsquid_dt/dtmt/wiki" +repository = "https://git.sclu1034.dev/bitsquid_dt/dtmt" +homepage = "https://git.sclu1034.dev/bitsquid_dt/dtmt" +license-file = "LICENSE" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -38,3 +44,6 @@ tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } usvg = "0.25.0" zip = { workspace = true } + +[build-dependencies] +winres = "0.1.12" diff --git a/crates/dtmm/assets/DTMM_logo.xcf b/crates/dtmm/assets/DTMM_logo.xcf new file mode 100644 index 0000000..00de67d --- /dev/null +++ b/crates/dtmm/assets/DTMM_logo.xcf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:144903129d56235895e433435ecee90528ceb5c4db98f5b02e637a215dde1881 +size 17736337 diff --git a/crates/dtmm/assets/DTMM_logo_256.png b/crates/dtmm/assets/DTMM_logo_256.png new file mode 100644 index 0000000..e53931f --- /dev/null +++ b/crates/dtmm/assets/DTMM_logo_256.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:62096b0f6e3798820c9ad04ba61fdff45522b7c67c4b254dc8fd11bdde984c76 +size 39017 diff --git a/crates/dtmm/assets/DTMM_logo_48.png b/crates/dtmm/assets/DTMM_logo_48.png new file mode 100644 index 0000000..33c1d11 --- /dev/null +++ b/crates/dtmm/assets/DTMM_logo_48.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b0524e05b8f2c6ca061aa7edc6f7e62efb8bcddf347e4e9344187efb437436c +size 3082 diff --git a/crates/dtmm/assets/DTMM_logo_64.png b/crates/dtmm/assets/DTMM_logo_64.png new file mode 100644 index 0000000..e5d5407 --- /dev/null +++ b/crates/dtmm/assets/DTMM_logo_64.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e0bf9431d5f46f4437c21fe38ebbd86e8b3872acaf3a13f0bc8f4a9e8e78e118 +size 4287 diff --git a/crates/dtmm/assets/DTMM_logo_border.png b/crates/dtmm/assets/DTMM_logo_border.png new file mode 100644 index 0000000..bf610e4 --- /dev/null +++ b/crates/dtmm/assets/DTMM_logo_border.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cefec60ffe91eb4d827e1c7c9b4bfebdec528236809e02ccc9f15b15ee290442 +size 537707 diff --git a/crates/dtmm/assets/DTMM_logo_faint_glow.png b/crates/dtmm/assets/DTMM_logo_faint_glow.png new file mode 100644 index 0000000..1066370 --- /dev/null +++ b/crates/dtmm/assets/DTMM_logo_faint_glow.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d579be0297e78ef0c9cfb4ce357dd61ed13cc65084d5a38c322913cdcdbe5b99 +size 605023 diff --git a/crates/dtmm/assets/DTMM_logo_small.png b/crates/dtmm/assets/DTMM_logo_small.png new file mode 100644 index 0000000..0020520 --- /dev/null +++ b/crates/dtmm/assets/DTMM_logo_small.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be2a3cb6a94828b3df9368bcf9ea335ad08590a69b128a15a92fb1cf751d06b6 +size 502425 diff --git a/crates/dtmm/assets/dtmm.desktop b/crates/dtmm/assets/dtmm.desktop index 4c2e0a9..cb9185c 100644 --- a/crates/dtmm/assets/dtmm.desktop +++ b/crates/dtmm/assets/dtmm.desktop @@ -8,3 +8,4 @@ Keywords=Mod; StartupNotify=true Categories=Utility; MimeType=x-scheme-handler/nxm; +Icon=dtmm diff --git a/crates/dtmm/assets/dtmm.ico b/crates/dtmm/assets/dtmm.ico new file mode 100644 index 0000000..b862839 --- /dev/null +++ b/crates/dtmm/assets/dtmm.ico @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02d4bea2d7af89a86c3f052240a2acd137a0df5e9b6e85ecfe3f163032010652 +size 37519 diff --git a/crates/dtmm/build.rs b/crates/dtmm/build.rs new file mode 100644 index 0000000..9e551d4 --- /dev/null +++ b/crates/dtmm/build.rs @@ -0,0 +1,7 @@ +fn main() { + if cfg!(target_os = "windows") { + let mut res = winres::WindowsResource::new(); + res.set_icon("assets/dtmm.ico"); + res.compile().unwrap(); + } +} diff --git a/docs/screenshots/dtmm.png b/docs/screenshots/dtmm.png index af2a9805d643bfc8d38e348d4ad09aa5f10760a1..d51c253f997dbdb4b526f67e2f7ee34566e11863 100644 GIT binary patch literal 130 zcmWN{xe>!45J1tKDrmrhlj5Fog_4J0_hgS)#7?h-z;3|bN+i_80ObqUETF`cXjvEHNgsU5-2YSUckV>ph!uIzK4N%#sdQbJB9!U zO>v%W*M$D-_E1)LdT-!LX6FDjwXilJb8@#cAv1BaFol6}o3~NWLVfxQ;SPcYfGtxI zh&Z&3Su|k;oxBo~>FW`t;t|O4GAM{|i%EAYwp!Vi;KrwYu5nNi;J* zjI3C^p%DJ_l8TL_Q6z9g*OUwEgapo!RVJwE&5eoZT$1aihkW)|aT&#S4!je_0-c2m zB*;@$^Wt^N#@c&K?v&-$Ier-@7B)O~X^!^R`X4MEF;V0I2HW25L{qLWzK(o~g6!%V zA{f;_xM5(Pe6kP`QIHZ5`C|{%id63?fMk~fR)D-xE;)9P3%dPh^|XU5@(9fUa%F@X z;UZKj=6ioC6seHV>{U&>A7&MMtLw!@{zYU647L*Gw#!h2FLg-hwP*|>f}2+kUW9SuuPIv22yRcCN1pXewy zw(M5wW>uM_)$8*pc3567RARArz%X}T`9^vE> zOu>h@v&>P_uhj6vh>!||{SR(B9#!WclV#wp7pYbRo2ZXxO6 zV4~z9r)=b5WyEVtE+qIuz>N>8z{bSMfXvOt+SZZJ4M6@&FCX;t&tztDvR@)jRseE! zSp_l?po0k+2NMSq3!|8ug$oZF|Mn`vBCj&P|TStnYDt_w_HE}d@u&{Ho0NRrM)M;P{ban!e zlSA{#{jO zj!p))MkYU1L6tLEK=tq%7#gthaYqD zc?S#Vs5G$t&sP0ZWeiniz{AbLVPMR{$i>dd%E)1C!pX?P#lp(S!(z(GV`{)+!okY( zt2M?(eBwX{8v|%}TG$wvnK0Ygn*GZ72{_+d1t|bI8xzYvDGJsGPNvWT0CE`%TW7a_ zGL$WBOq84qexk|B#m&vd%ge#R&c)5c#>M%MkgAD;BXlJG)XB=i#Ln?6=V!O@L5+a| zYw&ZNLKXZqq7{4a(AJCA{}Avd=% zqp`6mbUYYBXSV?xn=vD|sR=JPhcTxyn<@9dwL1b$om>qZOx~J7afD(89iG3iBBS}$ zQMCV-b~QKo*$Ys>7+IjNzX4fRvx!-9}=LPkJ z%zrz>zcBmhcK$E^{M8r#7i)m3{%a)ln2X15R*pa(5DLum<7n8%;L87=uS&=f>FNexFB7$nS}e@|eN zQ}LjQ@J>>)V({RnPcUEO%JxrD!N8EgNQu5xcAGy~a`wWS#c4lTIWVR80@Hzrv$&L7 z?`cn?N`s0WqC`_|PFr1##jILfkNHtaTi6cG4_y+W+uPM_nH2HWCp>h9W3Lr6q4e3| zz80`t=hrINBbvkcnn`qFgjb(_yy!cG)<9JBSw}|)Ubr~N;iO)uvLY5T+|%0HT6GPLmYLQR+p`A|NAEl97FAZ+}BhPX1ojuWMi+#A$aT>XiuG!NEavjh_$fN}e5E{*K`{?C8<#2>2`VVjrKQYGA7x{)Ow8)VFL-m6rsb{Zh?v9{g6fo8)%4t{ix+>Fqq=_iS~Bt!K)6S}UuNnY63@ z%Z0_J98p(skxAv{S%%C_If=?4oyaFEkW7_}tj+T2murv@T1>a<`?EX_?R3?6hB`HqJ|R&dMDT)y)I&AfO`n$PiR z<|^S^=BM-DRV*6UP8%SfcHB22-NxVd>IF3m_G_Op0u{&KOd>$sB#oCYIYye(FXRo6 z^C?_IQI$_1tBR^)ZnwbOn#4)V=Ca_Y{gzdkl>WFQ+o2iGGS?n2)b1I}UHha@Fg(8A zq{NJ?TD{{ckdoUHcdFscgml)Q9+zoH`HK8CQOqfYd|}T@a?eT1;asQRB!}_RXy)Pr z;D(H>Vy?ZWJ)e}mqMiAcaF*{n>DZ6B!7<`xg|CNlodBPVjqebb)w6nhy#@6Am@K<$ zH`Zir=XGr)Q2b)TWJI`E4P|XLBu*yLk$Ch?Ts3}Wbd{aYRFFKYZ&ua$$^#BIRxDxD z+_pv(YRPFv=skOAR`vduNTmg|HB#p>T%k#m9K++nVt%_+`dMurPjk*H_!&yi0}Ju^ z2oBPw&n;%!^)A;mO*qJTjV%Oc(wP>jSk{~&ZLXwi>owBaO>VI*`&8ppkw-1^66D_Y z1Dl8&t9N>HPU|nYOB);<9=RUARwYTVjozjP547Z09IYajY6H4Xh#m3ADbeDMGuEmP z?C4fe}TdO3}*hp6P)uqmFh8k9Dzfh2`a7-IkwAZ7z#K-D(35S~6&>2oR z%j3JpvY^If0v_&5JY+oOHd?%!=DBx`E`s(%`dvv3+$U5!T_Ogihvjls{_o?j+96(sEbfhiGfUTU>#LB-E#(QD?u+>`K0eo+s-{s9Q(>eLG#8Yu>s!-whK+FQBh*;b0N6LpuBgfufnrYsatfz3H6f8MB(;5m-($l0rp&z2MP~b68RtLLj*<#bq9!}`?|s)bG0V| zE-0GsDTYI9HPmc`mz;3t8oN-kH$RmLwf0JeC5QCKp4U??r_T=2rh_9Z7P6Ro!8W0* zO(v*F%y86m>5@Xsr96*4<)y;~+6}ZKE*g^tdv*l*v$~sED`DBK3wr0a6Cd)Pa=p}a zMV59Q%GCK`8CYv6c|X$TSS9J}ga01UKY7UN76ruk90&ioSpHgr(Nj=mb&e5sQQ~tO z0w-J1?=u%fNRye}f_W;O9xlwC(`~&cpw@kXbD!>#Y-xPXGoebSBiTurj5+0e#|0wo zRH3K<(D6w(C`Ov!*JL{)pLsr?TVLS~3R>sN3L0}==)sEH2dy}DZ% z?O!SOuf5%<1qXql7?WVS^PQu<{uSTN^eQy>ZAfA$p^s5du6DJh4wna)c~W6` z?uVmo0;*v^yGkUrK&$hoHXKr_W(BEt^P)XX(zlD+cl+?$B(1|n%&yv8@&a9bjk(ka?HRArP&84_2EV~JDXgroF9iT6=FD;r^D53m{ z{@Z-ksN$E87A?Gj z8Efi@^99fir>smQ#)_NOLfH_U4I-B3b3@Q^Yjs6$t1?V z^nWYTul)g*YdN)H6PrGMO|ioI*2n12_UWAOItGV5W^F5-j*qy&H0)bQG&}FLzxa>C z6t`sE%_nPW+6|>H6hIQI>MCY1>N~bmmb0XL>+eE=ye0e5_P11C6RO!7#7{xK%l-*c z;buC}1z(Tp(}&h8bQfLx&v`o))$beX&bU3CG&*(_u)Y1dL2;rt*}mDSY$Kd`ejfw- ztv$x`KNm3^OebH!A%#G+`k2MA6;zZ}I!fKF0+Em2EWK}#0wR86LP*C4W5f{Pzsh+f!1q|@Zz9!G5P z)rJ(z=akcZt>Wc9`$?7UX1nY=#Qg2hL14mYVOAkQa$ZJ8X0j15+khikS9wfXXk=(# zeu*=n;LP3316iYUtz!iFSRnc^^8Hb5@N9CXK5Fm%rs;O-;!tPX?dfPdtxTg`@-oLgElt}phmU9Kbn-7N82E2 z4RGd0IK)=mdS%54^!+3%IQ@>kuMi&iskO7 z-Kb{X5rllDonqF&>)JjOg+&j9ENQtJk(HFOS`xpM^)D_-tTAP4s6#W-KYO}}$dNg? zloAf#)Jq_NVw7_-?WMwL6mmgQaW+YLbHe3{kkiLVH~JLa$LA^GkxGVAf{WKU6Ft1@ zafep;t#q-0hns)OTMb$zs)bN39u7&hJfi1Q3)=SVh8|96u12aMxT9EBgaCIWQNVlJ z%XJR%Hmw7#8_Xrb3xk@Ou-1~Z-nOe@PoHi3?}wAAkV-NwR8Njbt;HApG8M2~0`BLZ zB`S0yy}J}RQB%A_VuBPg}*W%qNdncE;c*oO8I_i0}*yI?tA~6D1yeCbZn0X>5 zeN<2NC85PY4&T)xfVhcdW6YN9@Kl}B!&@`9KrXHt_uBOs8y6&#;~KYUTsC1i#oP=V z8S8Ko5&?2FlR*>V{BP>Ug+r^y3tw~~>$UlAj*5(X0LCNF(T(UZt6N#n;UV<=VyI%q zve95e93-wnxpL#2ia2RV=dM}&f>6}X*%sJ}fZzZ5WFIea7IiYlib$9H!-y{lwd8Yz z^&gQfc($qCb<(NV9aPA5M)XN+F0Xlg`q2SR9r4jifvlDuwg)9c&jgzXI-dJqxu*b~ zV3bM;QZ~7wUd<=E zM(79^t;#MnFfXaT#*ocdHMKJ_jXDvpJjmdp)j(?zvM2A=KrWv8LSv|}yEOT^;~iMg zt4!ALd*#zReaz05jYl@{akesH&TD+>q>SI2g1>q&H0%BfBC5wx6;=L*bg?YeV6^^{ zVa_SaY#Y1~NF604x-@WN-lfO0MbH`2wPnZh%;+) z#;D57C?4q4dIcn;wQ+s%rO#8*UTUP~-Za+baK5DyvH^;;x3`zN@clH9z$_m@#EY>+ z#R($!2IXp}u68ACN!ZhzE`k2A4Lkmu$RPk7)il%dF6KgxJ)S69Z?DkkFv0RTQ_qF> zzI(63qA>>o9uUJ8v;@Vr9!2)T0$vA5V+p%EATm!D(eohvMpJM-^{qQA1$id5X|qbL zLNmVXnYP(R;Gn2vCE4`}H93@^$fDVC;u|t`{B~HcTPC3xoAYR!6QZuJkTc1WTu5yv z=!jH?{X|pRgaCkT<89~+4TTv#AMaE~-XTghpz?R3ZV0lRt9cbBQ z#;UK%H9Zwg&lwMXP@la z4HLN*q|TczP*G9UwX~=kZ7d^u zZo~dfBt;r6p7(^}rsJ*1;o#up$fn?Ak4uV*ihipHOP0+V0fF+F!U28JuaPV!^JG=6Lea4Q?1Cx! z@hPdXNPQL=F!(>$)YL4xkX8I9aD*jC$TsLVZpSC35dI>Egb_{(V_<{+%13x5=>C57 z?}u(``4>e0Fm{`?Jt5dJTYA6GNLSCoNB{&Mb|>?tSw5kYqb7CyMJ=HVf0qA>#q&=E z{r|22=cJ`@a<{yuMyzbMZT#)9DLa%gEBHqD66a4<-Tb)6$Pv{RlUb9yV;qO!x*jbg zT3TAcrGK|r}(XBl-nzbef zN)uOF`az3)4Vqt;XN>q$p&(UoX~&6|O}NwUfv$#Y*RtSKqu#JL`I8kM7kne>Z9gVP z^&+3=29~aU@25x=aQ}o!je4@uVPmb$7q^y17uB_Q|Ew!(NPp&GA~6x%R`9ky?R(_e zH7Id;NhAB#=Q9A)%Alw_=Elr=XvjN=_j(1Rb?C7a7Z`3ix@NLu7wc^JyECCQD>+75 z$9TqD036C5Xeep2@5`(E&@?AL@kkQ6-muI1MqT5JJ!jepyfY|Iywr+$IPaPdT9snp zTa}3VI;rs>f%Y!PeKxZ^OTC27*tf79y<_+g>FRdJXz2KSk>KUmNY)wiw=FUQ^dg&M z1+TYYN!D!QkyL+^c#lFaE*Z@nHaA~J4B3jqiIJ0wdSU4J8$CWe<*=DYkN`E`oenCC zhvE6R`NHTlxrAuA8qHTHB@WZZxLfs-c{Mk?S;LV4HkX}oQSV)YlBD6-Yi}()n3{tT z%k8Y&zxbsq<(lg{eAmL*bH@jZRV8}vyrg;!U^FesT^oPGP90VyPnBXc(`U7Kdf(DJcA0R7_oVoA%&uL* zgPY0UUlxJH%(Z-qb0iGgTYSKk;LT(0G)=UI{OVJ+)cE0zV-d7q$yKy?2MC!bG2d9F zJ(EkxrJJHc1&wHEyt~~TSf-ZAWFr;ywUQW~CBud@#I%NL5zLkEQmntc$1dP5xJAi( z=&jSw1I#jt#juw#Pi-0jXG!@~{OZE>YjlZ+Wh9Lgl~=J%gFDk!ysn!_9Ir&_qIZxZ z+S|!~qStimO+6 zqSKMn0WcqL=HEcm95+08vzM!8xW{$PFJr7c4JuChf$|h&yB&YMC3G~m7p{NFjY@Q9 zz#+{fxc(#dC~SdPvb&;?qgXY?(z$mXi>Qv1OBqif?F5CxCGuEy!R2XUGdD_FqgsRt zEt^6;h{LqHru!P)T5T~Mqev)R2-#4+iIX#ajn?De!f&gPA%^(^M8~fY2YidU@c7+_ zjzNxd;7ye7WYQ8Nk{Q@b0_?Jc$8$zwF)w2}^2H%X%EePp&0!>E3NKfgD2Bl7mkW5= zRmsDcybTh5;@#R?O><(3l)hV8y&l=+nW(Jxb& zYD~19)6&_5*!ylAIQZ|oHY0}GMhAZMt|?0bG$Md~33?W*o=`T`aA8|ab0Ez=T!&AC zNl%AnQwZU4D(upM2O-ah=fRyO{nlK1=bb`hLHkYdG2CTGxpVc>dbDaJXAiA`Np{Lk?jK?2=s;@n`$H3$HdDZ&DTDf8O?>P5;+OFN^hBF^ZiVm7{&Zd^-|=+!LufNDYV_AH1|>Cg&9Po+`o>llZRz zaVuiSXIWb;CjQY*v-QPNc?C!5QrPJB*IwRL&em6zeeVuEOv-P@?yuG7UwyjbctK=u zW&ri}tk&Np_h!pLt={nOt(U@cG!)Ctb$>W*Wg^B7p=wFCnEa<+r63SP=&2dbFsah-3GKjd zcjZO|;pq(d;Dmh^m1D_KIg~=}DL-VKHGvl2fXCWW_zW-9GOUyL?^^cojye>5LoPHbzi1?+W)})LYPJ{iy7CmHfQCrh)*MzWh}>~5 zsKuwN3!drvT|Mk2rn{|L42iA@U+ypoJqEpzFBRY&$3(9Qp9L(69MUGv60CF`mEE7m z_0fBNzqfl*qRE~6CPpUp>I`ADTJ88Xn^0@0mZWJ2?qJB#diG8&&3A12x4P45j3lN* zFQMc2=ZwYa!641X3eGlUY-{BJhz!xKhpoZq_+n%#ULG9)&X^OliH__HBn3C^td80$LUK;bLjCZsUTkUn! z-F~GUB1X8leA}g(um=}bun{&n1Cop3O!`xrbQ1EfOW>KfTXPHcsICsbXij42i9L*c zL=Dj=+=Y?=JE(JNpVJ?;Wl(>2zvKSa6>%UrvN_<}xPhqqJ2U*@LozkhRcz8?aQq+RGx$4R3xp<*w%*}H-$1ydLaII?; z0BGdiXXxOXnD}_uq^v0_?6u}Zn}!QkshAC>Py7YpF?~p@wmzp#OdL6&v~7GwA!T9aiZo%F;ZoFPmJS4`W6_;Yu$wNS@z)uc~1|2 zB`urxpv*5m?2?KDNA+@ZAOTHhh}1e9K~G-bK1Q%C8M@ku5*_z3m;Kx)i=GOe;AUA= zb>ba0^rQ#xCKg< zTBr6*WuGj1mE?e{j;Y3qoHV}@Kl&>Q*=%_zoU3VGn(P2<=voGxwuAgiol@)r(P69$ zZp63;a=9H{4-o=Knm)AgZu`?}gs5Z{Ps^5yCo9G*MC3R`R&(wyO~$k+wHj}exC(Q= zN&i~uZN}0h!TQ2Ay@RN;zS0R#sMKk#@MU{+YPdL?nebDGnVokj!nYc3?2Va?3>+kY zVCKObiuFsbJh{ZtCy?@j(Nmnu*dw0ke*bUd=2N01jcA7b7k%o}@m@~&kDN7?yNE4~ z5v08g+p+Rfi8Ng9790oEz11aQaj;PiVn|ULZeT#73>5>z@*OWB^sEuHdja$x!`&Go zC7i9;UCCv5*dm-y2-(~@cy&%xlf*wh**0WLcy01va_U|8afzS1+|~uY+SjbAU&Om5 zMr={PM?SP($$<>h6T9aH`;pdm#i^<;9MfF;CALVRDXuH5IzqlLX-_o;S9MzLM`Epb zwubP|UJWgG`x{AH2Ev{?q_U`%4K#)ah#psIV!tcnsZm@p)i(j&^3a%_A?OG({{ zv0u&s?Ra-9{@QlKsK;5nixKb``Wj@{hF;(u&u&s1o}IbJrg>jB(@0uw`;Fe7KM7eQU1`h0h)itDoCJ<4FEjk#9$3-Z#6 z1F#q?REY6F(9`U`g9+%aoq;Z;7=lfIIq>XBnw&&3hXo7X@K}R zgYO4+#-(eV0SIf#O zajUEP!4bcY)afj*C(Wu0l1O^oh^fH|vz+WJOOacGi);FBq0fseuoR zJBsC2Y;j$(7<0;1uhS0CERe-Z8Bzw;g@q>T47~$~i^R%OKyxkLxJ{R7Aix#zkgh`d z`Zdkh-HBxUD#fR=TZL+O@;V$TLmxga3zObQM5MYqWJ&aZ4oQ3vy+*AZnz{fn;yVFzI3*pF zGc3hYxz|-3EH8}2Dj=doii*hh&0&Y79}ZZR4CTYwL&o0u@KqJT)4kDrfrG5vDcm(KDV^|oQ&Cy* z&Q1@>KgU*>Qp`vCHxnZnz#z?)YvF^$@#rnH$CH$OvgB7f*a>bcF%;c`<*?BGq~cg+ z;8j%hO`!QWIEmbt;eD(%pPImmx$5Vn4A1=UEVoz-+;xVhi^l~T-!RkYfyNWQYorF% zDo0I#ZW&fKoW97GBf~o@J1iPeAEf7uotUo7jzg{TB)1vS2}ACVM)}$u$I~hp{mB3% zk2moI*yIL$hppzH{B1?etI(aeH>Ri{58ZCF{OKS`Qb|=tH-aApt@6)CF;t zSZmQ3fg874Dd_-H=f2EY1ZUpFcb*XU%qC#B?YLABU0Lj=t z^Y~oB0mM++0#@P9rCp%J`UK(!_8GC_C^_JbKNDTwFP<@Wg!wlWHA*6n#r7( z?i=o{#n&TT@gyFBd7~n?>AvelmZ<7If(6U*i`dCy*|PYCGk7WoGz(dbH6g|xTTwNt z5#bh^BN#mg{tGSb1q<(@$0H!a2d!zjp6MByI2(h(A_Zyes@YDvG&2Sq*;=MzxVt0U zaquVMoYEAXCh!YOVPEpd??KL)PJ!yV$VJvuL_8mvu1!u`(50eQF#d9P*u)xpoY{?x zqPi}IkTgnL%g=Tuw_k&*50Zii7%iiA$C$oS)$@ow1J@q5mvrD_72lu?&T?-gd$I%~ zXCB-dz!C}!Wn4S#Bp@;j@C3AO(T|(WST&6vBmm%C%><;6MPqk*mc~Yc-Ofu@Nz3fH`QV)9#z)reW@hV zDYgrY5khNZnf=v}J?b$){LK(w$^(%WYx?3tO@TU_(6#hdR>6pjTLv0r^w40b6vdin zQ@s4fPUQVHARv*dJekXnft6oh&Ol3 zWaHtI=N`Vk$*74DG)BD$sPlX7``9J8;<33)1r3bE$W|^^SNzIF`DWD$>kfoKcwa-^!~V20;1%+*S98CR?_=3 zj1@IC;qLOkb{#+8*1ap-7M?C&oDS5;Y#-zjd zzSbJw%ln?k>+18+@^5bet*F}*q8b^@sMCn0=QWP6qLRex$kp2FLkg6SbK*#9jH?lO zN9{Ww!i0%JU1EH_%BD``rGG$h-*+^;qS*73hs~c^mcJvw_^Zt)x|DqWT_YnAHy8U3 zJ7Wg0u&{bJ?oi${44)k~O%5dVSiE+3jRc$yTffKs;Rz4#^dYY>vCH4>bQKPada$~Z z#^ff^8l)e4FxVMk>&3nX(*a}2a+Hte$kL89$xrmLpw*0er(M}YPUx*&suW9zx3`a` zV3?(o`@CHG$;_f2NLQJL1!0uAnF|K7%8nyIJgIt6{)`UNTv0Q-gB9Nod9s9SJBbN;K1l@pk-Li z%CYX-%A{~%R?Vfdm9WB{4ecW?)2&{7zU5{}oIVm5_YOEpiy6wpDE@V(9hkv>w8yd#*#R$?wa8%1`Hf3DUZip;Ca>l8v%sqjHI0^P)cWg_BT)jFD_3!i%8m3GolrkExxq1FPek3?Js0uFOM zdR4RQ>-kh9qmLi2tk)d^#Ygf6V>doM`L1j-B_yN(URyaQsSNTJAQ1A4Q~MZ&ZT{EA%X*ij6UzS<^9!kbup{HOn$F1(+ygj{DcO?hb%m`x z-T7W26cedFZmdpfnX8lqnb`PlioBwFZrBw&?!|8b?>fxv%5Qa8mc+*bFX8*OZ(uNz z_tHwpXC7B{vrwbjVk``9*vHUUa&hLnLhZ4r_{_N7SGGjkr8c8&KaT1{?N_R~eQ!ES zg-(^ErBNT)VoTHDSbVJTYQ!J=5?+^J9OP%YIkfL`T@!HU=oeSUs3i4rNP2z*fIHWf zvyriG3^A1WO}0BfvYsC;M;>nzf%2}=hIZ5lQjF^$vfZRBIiQ}8h5)$tU5<=G=ZCUw z!6Tp44xvQohB-!KOuv6y)-2@Fj@g%5+=ZFA0LuSq;8_->2YCrqssR9e^)X+4q{|I=E0)BbjrkGu+v?;-Qnm>;9|V zeji&r$!VxgREF$+YJF+yU0Uk#Mob+9 z#e;&GVz@Q<%XN%Qk>D186<$H$VP6`jqq_4c$?~XIy{|1&Rea-@3JWdEIr@GpAS}ZR zXS0e)xzx05y(aJvvE}ZKl=J!z>$NEXTH5YE&hgQ7xGFBRDQi1G8r7#*w#%bGS}P)l zCDOl?zInp7A01+5@KHy{US2JP&HdtoU-a}q_SdDb7>QTkRNqB@{HRknfrZf6*U+@R z*>k9)EAq$jEe9KWM?E(8u|h8_p!XIIYai*0R7<>p;Zyq;8Q%&BJ_yglI`2;8zKlaM z0gb?X8-ermCQ~ztvl1?ahWFB2xtP;5XtJ~#s7#PNL_N><(zB99)QMl*toG`STd0`L zVzGZLh2QoVUoajH%1%SM4q`Hx%)q@RC}tdkpU(~|(9&6_r@Kz?-ZcgWa+eIfPk2P= z*9_gpCfN`;=b}pa#$5N>O7}D;cmXFoBW3nvtS&>J32+M=Z3`5ZL38+<` zc}H3l+|BL2fw6Z2lb+aBAo>Ykkrc2QbuC8P*+t&2@AnG-L|H(`{^aQ0+(gnCpzrO) zJ2<8h{_esLrqw07U!6Q7l$y0!!%7|NUm`XxN`Y+39-Ep1|C;B*&0tuGMGZXY>W56o z6%Ad$Du63~v0APaQmUKGv4kXgos-D#^7z8;OlaoNhK)~GCj}6=vIie{s}i_8X%WMr zUuytsITI&^n5>1JX;gCVi$p%C#NcB-SDs6rlD5s%r;oEPZ1t9@Qs?ouq0_V=!S*V#`+r4hFoLk7axtxbVCX+UIRL{`G`*U)yn1 zXc9V7Fmf5|8}DAMeTW{aZyB_b{A(K_IxyKX();9zpY?KcTt-F)WaiC&XZqw8JK-WK zNzW28&hx?d1KT)qk6+i{+9^kgrSq~U#cmSGc0tKl-AT6v?Eug}G)k`N?x7eIoe z>l`-AUvN;^9ZYBKy*B2gE?R>;C8)o4*^Tl^ z^KXQd1pR}RY?Dqm;t%(w0;(9{-1VEkuAKSBRV9DmJj%26_FFhQ+%W03wMUK36ZE?S zRvv5ct@~pE^m9ZGQEkT9$rl8Ji+cP~!xunP8;=k8EJ7!}K}1-LB+gAiuaqnr&x;9b zbVVHA5kZO1waP2CnQJw0({W0)5}JPR?FlB0a`^l;o4#4PU-b(`k!3;-|7_Y@5UJ!3?}e3Yi94d$?oN6xon=_feEn zmpyr%US@}&+X_%ltg4{}<(+hwoIT4*S^{L(;Bek}rZwYVf*)?I1vR4_5*Y$6D#63qY9d){>I%>RV1Yf9;G<7Iwx4b8`2ut$qJv9IeEjH& zgNV%yjqqSeT0VF`zJCE!UM=sNjnGrA6;qNGeRg}YbEFb6O572DU-u(&L|z4QICRaA zriYe1+S_69>1wIR{aVj@bn5;>4#z%}lU-H8L5@`1wPpB+ukD#W2xA6^R5-nX9uLnc zRqvKLtV)Rnye+R^YeBX|#gvim%!8)mQn?wKW_&%vtzapK#S4DFu{Ka*3!s~L$? zU~D*)&0Lv|B}j0)Nm3T5$&gNMi;18M7^%8jJo~6XnINy*nPtz|eA%>K*ebpl%iR`8 zz6pe$C7=-@bbLevHCcu`n8N$jStB3W4^CoItX5hOY;E=0hp~9$v3o34UNt#@8SA79 z9laTBmU#9{KOX%VR#2mV<%D z7@)MD?@q?lWs51o6{m{Kr71OhH)JrW^X+eUj>OQTT%tomfk)1D-C*PS{%O_T1zuVQ zi7iut%t%WP{3$u%t2EB!)h2o)b7RjLp%>%6T=%s^fK<58<`>qVxBS_=4Wx5vXGpEq z>ZwC~LPsPzT|*6}n}@ugbWx;Z*k$q1Ms3&XEa-Xni^OF|eV6WGiLMMg-t#TtO9bJY z>3OXg>%`d;QY-0ulirjzk4kfsOQh#t2awK1W%KKCGKiac!yK8pu=vghrJl1&0IT8y zCE&}ySHgFBdIcKosJ8C%rH3X1O!!gt-z->S4O`0AkJ`u`s&_O`o4FZ!_}rnKLkL6` z^hP8A#&6Q2PSfDV+Mw6}#xs~#?b!JyXIHmlT?JhG>`IxFYUj2ZafYZCEbs!o&eKGf zSOoo_wt34v9$ssA+f&uxg)03BZJi=%`XY5=gBdT%ztK&(wm~`LLVwD6G&l>`Q{;1n zL+*tU0H*72#lHS@nMnG05^a5p%-}QshV;0BfpsQRqAPl{Jb1M~oTB*3nTh}CQyalX zq7@jv)@&ccJ5BV*X0M{^-Hq9lAWS@OA^)n@4JR!$^nnF!#5cZ0M7+o615p zmfl}Acg8ZKl9Q8ta_);y!kRXN8G-&nU&$Xag=%uO!>bWlll0!g1{N-^h)(H`I?YBG*ZZ7o^bTfvCoUhnyxlSLtr7iN{I+4ed7sVuhBNDQvFZ-@wE5V%(7-co>lVO5J=VJ3 z(njI73q(HN7FQ2^??l4+v5;N)=2EMl*m3GtY(y-?{s9^rLE`6ApMrw53rL%@5m5&m z@>4A%bmqO$fs6P$M0Bfoetj>jTzQM}G{95FNc;@MaMi$^eU#L%*d!$M$b)@f)q3YH zmF<>}KV(VRyS+lqvhP8rnsgSu457=PaitcX8;U)5v|u%4?kJ-KJ|Rk)NV6}qHp-Ap zpEWll2hrY{blRkYX9HP{zMbjM+wrBjg&3yCkiTYM-#rAixJ4#}InwP~_zCw?=@d43 zabLXbgeX!*@%akU7;;&?^Z>7n$i!P zr}uXE%Bw#p3ArqDMwbY~x8l#HdJQ~*Id8z+8I9pli^2&T+$F^Gh|5o0LjZ^CtCUj3 zg~|V**ABN#;;uXi4q@`t7~pktyex*_{?47%EW0v%TF3b6A4c#?v>%_1m67*+u!y|u zqN~GtvWStUTD4p;xo}aQM*Za|uvqZ%i&^XPB2W3}fLpJXoF_DOb!T7?+(mCMH#-+b zLU{>&T&LIu=?F*AK5b;#;r5FEat^(6B10qhlKj@L0kxc#a;`;<)3_^Pex_l|H+8*5 z&?{!nwacaX-s7Ut)$RLO5H}htq%KW>_Iud&YX-xK*Pcb8cY;U2R2^Hp`v`7>cx)(j zCRC?7WIR{VR!R$*m=)la4q(| z&F+28qOO*84|cC_@S<}}V_I`rN!tfU+U+fCEyCDbz#WD`c!KMeIy*l+86y`)Kz`8; z?r@Gc6dY;kt84u*zO>X!S~Oxe3Q9La8#x(mRQkk^llxhg{I=d^s}38~aDRm_2$8FD z7?vU@3VlCCMMqVV0$!aD(bKRWASNZ1FXg%KNtcs1nwE@SAjQ^8aIDXzmk#jqXpd$~ zR}ltWGxINoDr8&2VPQ?gF-okpBc-<5_<+1||3n9p$4!8(W%dWeBaZkY+Tgy;zH&*E z7HQ#1k5aHL8jTNH49wSV+r`}H!l8iJuK4-~5OE5V)SroZN1+D>;j?-QuJ-uvb98of zc1*zfpKjk;Jb>n$hh(;TIkRr!Yhf>nH0+jlY2>Fx=UrDAXWZM}_6c04WHtvZFm-6g z5n9~h2MsC=V*#Ys69QZF(yS8*jXrU=jdxfhXM1~RHwVxA(nkX6R?18xCs9*VT~@0- zS?@#e%Lij$4jx4C%tOmuPzSFFp7U)yLXm!iBCTV;GIA`vNXx%Q%Ed$?-?p9GpG+Rf zIgViOx?x}3_<;cD-7`33xUHH?#K>id^re>)`%nw7IYurHNgTaX^s~NNpckj`!|!)oj>#@Dt?&O z!a}}qm*3*_&PFU~xXD*V;|!`^%UHI;RJ!PYAiGKz?Fk=|9B z)BvGK3aBV3ReC2XO=_ffB7*b~dI%)}0t5&UAP@)%`}_gdFFBti z`<%1)+H0@2*ZwXXpw(1WBKYxGYNB+a%F1^5z&#$>d@{e7qdK#Qf9Xa-6OJgGRMo_N zQMu|`fSel1wo=9{xr*f{^|HhKl<9oA%9rJ5*s-dLtOiVRA3q(=$Cb$hUWe?R%(|<{=LJC zJ@O0OJ}i>2`Ig!E>%J)2i$?=YnZI5BD?S;2kXQ7Lj1ly)QOn2y9t@HgC0Kp#UoPhc z^xfME8F34le^q$r(%bX+F5N$N7J3sC)2K`Z(awjb!ji98Z=}Hj$z`f@*tA;{tM!zZCgp3*yST(KQ{m3*O-mfT@W0 z^p2*g8jd3${}yQ7u)f&U zPj`|+AKv(PE`YQx@)}W5e55RUtu>{)j(m}9Rd>XFsxj_k5SMo4gzF8XxAf?ZpScMX zl{FC~34wnS9_A+;)kUY0#2x5TP8hp)y#{JS^@Hq*ChGI1-V;<38lndkz47Y9vEwJM zRlhuWT`NiM-Oi^lt~Lu}U;om6isb`D@{n=YiC>j(oqK#@_`b1e#;swSdnfY#zQ^(Y zvNNas=uB;Ieleeuzp5|MtSC~v4a~Ksy{bLmvO^%2PS@pib*&jXG@9Djj7H#HTbtX1E}gBTS7P$G}PDL#B6{G#v`o zfOfb@4p4cCNCm;kGt_~0U?67b3|G%#-E_8=S;Dw)!{I>e8npiv9^MOVfDKG~t3$c= z>}EOG47xk9qCuGN0_EKzOU;|tYk`U74RU)s(@N~^+T$Meh^M|s2OwmhlJ`n_ZmaLp z@}||-@bzx%@_qVPdhI-ane?9B<}T~Sa4lm^w!DW6=a4}IRRnA(@Ac{zp>tp}@#3K{ zO|fW#Bsk<~e0uYn9`IvTZz<6>X-YA~=!nUiN{)npGVT~TWQ4N!^Z+Yb)K6}GBoKQK zzFKJHOddg}_MsZo*-Q~;;o*Vo5(!kV6ouP2LwJa>79fm{22$0QFFT$l+azUSyI;nd z`eai81L|1&5%I{rP+Juc+y9_$n^wWQC{9$DW`YT#HU3VkX0Bw{IPV-D_6yJQmQ6=| zE_fYGKLn?`h4M``6zo>FEZ16?Q9U6G;@dOG7z~$^eL9lWm6HzZN!?y2VvTky>kg?3 z*`IFiWd%HF4bh_t(lAJJBSog0tyXLk0-z6E#?HRjI<#uHs-A6~W-Pz-93{6+Z{yx( z_eo(6>t<35?w+LW*af*-cTHmPIBz13r^YU5rbh0Cq8u%eQC$}{iTY?U; zH$(6oJt$W@Y{>HMsPl^mGpjCWqH2uZI(F=vz{K)UD&HHK=Y%D%;3=_0zjm+)W!?e7 zfFW$`^)#i6R$KLCW=ngn*u*{b8tC9{w)L-U_~ub1ZOLLmWQ;Zl`5; zs=?d6i&8B;GaUMeD8GxBZt^zk8wtYbMx5L^J)9=Z9TG|}B4$pBWMsG^!2^RzXi22% zvv&Q)&2fF-uSds^h#W5bIN>q=ROLX8yjX2qmL5o6WDEy+4?s}3j`y80+XJYq%#ol{ zIod|lb`8a)Bk!eBf`qg*u&VF2>q3Wtl0LE^Rw_q9CCHBTmNh%y^K2CK(t6vOR&eat zESD!kS|X?I_}4&>thS*RnBIdJC}+<~>S1O1VQz|6)PeJSM|P#Zn~^Ao^v75;Wn0*e zSpMB7(7haFadBaBaR7X5q-pPUH2?00p;MeD_Ln>^2+D$&I*V6-rZNBfx8UyOO8iyS z{j#MYVNR=W9=UD5ajwVmZ79h1hIlVPrkiB?d0 zUPGTvBfOC9r_rO^2KdDfn97xsT3!_WfYa_J?z@1;X>HnUGZPU;+iVCOaIR9s^mNf} zn5V~~3xgs#X&y$zKU=sbK}P+|hf5f^Yb-T2ufl?^fnZPj5Q~DLOJ}yby{P@Eamz?y zorD5U*tC7aysWtE0ibX(eMOu+Kx_tzdyYZYDX=ywVsC<-=z#DX$=X>Lg%C%1oR_Ni z7I;9D*VK6U(ntg9Qb8I=ZR)fgCZsFBX>ncCU6MF80f2@0vsp_7lkJ^S9+&My?M0T6 zSISdNNh!MPrQqSNGbK%dw7_EUf(NF~m~GwvgEKPArUIqm&Yh5{Koz%2hjY?U|Z(H!v1C2<}8$`M!Y0a-)q4=DKlcC*K` zWDGY#PeyrfRvyEjDT^UoBeDta)Q?a&7n}fZq2ia&&b4t#lfpWu@BFwrF)sXiG(wKb zo=G6>Wn~^HPAxWp@M;##h>%XZoz$#Ksz}w7?ssrTAsB;4B2=5;P-sfQHAfA^!_J(^ z3=udh1HRdsztS?(4c~&DSuDPbb47=SN<7zejg7Zc73@ipSoSh!Gd z&XW47Cx_);*4P)Jet2kv<&Fa)vMbG?N51#UgBx?SWi!)YiXzF0#M)S{0`ew&lYODv z%axG8ZgG6N)Nhx>lsWPkXhJ!}ss!fBD=8J&NEgYv^-Txs(RN7Kg>bzBm+!*?lM|!D z!l|;X=TPD73gtK=++l#}2s^-LX36HLDDW8BWQkLQZ;)+U{yG^Xm}J18urn<$asU!o zYNJjkfsb4a3oy1xXG5DoBFxol&mBTGq+|9*O9UAqzS9F_L-blhDXe~|i}40}H5R_x zOU^FhOyZ{Bi7t;a-za01U}7xmVE%Tx$<1B2I5KaYBKcinUH`{P)l%2}aK4ti5ITFj z3E;L}l&;Pg)em9S!;cJN_`VvWrUsHFIDIW0vo6$bPC&n@+~BXG!5+Bcgj=?kqCg)=sccADO8EFX zWbIv--$0U#w~?ka*X|EK=1|FY49q;#s3z~Gw3+tCJ^@8rLVA5T89Sv>7_vGzz1!2H za`+CklseMX`opVi7k$_T9j>TV8g(zaX$2`Ms)W~j=&Z_G2Ebr5=|54HWm8TPezO^J zeL)C)##Z+m-wmb^Qk|)bs6+2wGg|lpf-0P}q+k)|eCK1zsA}KcuIh&L`Y#rN69p>R ze#<1jlIEO{a>pO-s#D|v3>W$6c311}fvsgszjoXTvQwdyIyQXA%TU;APz?+iSdLO3i3zRG!A;ZTip&~uaWM|Y-t2E; zwb=qG;FF#8NR2cbW_17U2)bpvR!H)DUX}_m*e49WwMWJ5+w2X1_Nd*5#6}J6P%m>u z;5rgQnxXa}>V#7O#FnSfz#C>`(rVewM&j#OzvoD0S6Wp^c8lK!mtJ5dar0gc!e?58Z zo1{E+$5|%)G=+n@8Sm)GU5p5RbIBk3NgZnbmM>ia5?>dd(&A#&JBNW{ld+; ze0=eL{jUGq_WvUPe@OBllKlTK(*JPDf4Jm7T=E}{^S^2C{|JTu2!;O$1&&b2xWF-Q zA2lv5E%{RkKm!AVqA~Z=3pY7OSLk$lb!}}yP0g4kO1q0HlK#^W{-aDgPT0-cd*Jn{ z^OYQ!R}MPEBl~-0=6ZV@Gb1Pmw7jBXL4JOIpW{rGE0&W(#`(Y0K|3ZUW@W4drz(UF z{l~a|RO&s`nBUN#!f9mKVz2b4ovE3bwYBxnCteU%SH@s4ZXO=6o{9}l&b6wl>V;W> zW#vFl|JK*nWrJDcXMS4E|2)K*&>Sq&=ui+F8{3Zrfex8_)W@@y8jvuh*y7@0PVVk2 z(SO=*pE58pNs+R8M(>o|WYMs$v)^9X@q1ibi7B*46;@OX{e6FyV~?LRFB!A-N8d9u z3s`yjsm2S=5y8K;ZI}!qmNyaR=!G8 zmamtLO2CUNwoI_f>Xb%|9wZyjeK zrrg2AsVe7@tZCn#T)x`%SNNq7r!U3~wZhfIhlaDAoS{DRyaVm*o>{a{=(4*HUYTofG2+Sxm?bs?*t?e|S3A=rq@q zs$(EfSvZ(R)OVR|go%_TXVzhc zy1e7n%3BmO;j8r|9F(|6$#&)s%(E?kPc!D*doT2Xe~tb zifv$t7iBxGBK%v$D%M?KVM2tMmP=*T*=n#f8T(JwoF9^=^$$iC8C15qk`mT46w%qJ zTZ~VXt6<0CLrl@pO|FFA!{=9%)*tY6SHF)dpvq`k#Oc)7^VRm*RFY#eT^|w)s%1kI zjiQ^>LK|V$SIh*Q>PT^zSO5x(^$wAcq?b-z3GV7SlSj=?UF>D@<`wVuZ`#qjh1FuZ zKM=UAnYdoRDVRZ2|7l&>gWKa&H0RGZEuv(_Tdq+YR4w zsJ+W>XiU9^nrof0<_H=FyMoO~Py;stNjYZb<62?YIBcV9j>0w3i;G9)Zdy1TJ$fJn zaeAOs&a$eTiS3C;gd+98R#^q6Mdr<@+f#^zEZfbtnC(JTcGgJ(^ieFEMIENSp_0F2 zH&SxE68$i;RP48tJUtsTspvvoB7z$;)pV%fh{{Z%Lroqa0|N&T7In7FqTEWH4R_C( z(8gP55X?3Bw!zH!*RnoWH&c)4I0>C%vmvXX@p`B{4w-|GZSid)2)AShM41JBRl|> zX}JYz+22clM3iG5(G2<0%43v%3bsw2kA1u|W{*9-Y>x&w(hA!m7VBr4g6wDM*yG}Y zX(=VLSfn+-xkl8d;>Q6*Q7R1kC0dO_V;sfAIUSVp1@DZX-);cUq_l}=B?Ng}Vs{+u zl0kcuvf9lxM&yIjPH0iOZUD`UvAzZ^-e@AXQ2FA~ENtpFt*O9uV7id@ZGyV={Y(UT zQ#8WcLk`b zF#`2U6bcU_s6V3Qlm$-9d0pxx_tZ|V^5z+|SJ4Je0+{8QG+Ym)(r4>?fAIV())8HN z#Dke#6@YS@bG!s73%*}F(`-XNfI4-2F-R=B1Srba%?NkyPdlC-*d-n8)A_wx$|}i< z#Li&4?qa?e3*`IG3|lQ}sL8IV{dp+RSc`}%H0aC9$#mKeuelL}cj_}R37tQRMylPg z@#JitjSf3be7#`3ZJJ}bc%^7ED*{5ZGm(R}(X95T6B}p*@nJ=uho_a8@bjFY&Npao z2|zgo8@c>qDX3v5$RL>P3pPR*=XkOPvi8Tt0rc%NBaD6#x*ln~>2d0CF*$vd3lI^1Gj1lnQx6yIsDJQFMmZa7^>k334y`=YZm)KGfJ(*p* zFvp}lMEEEYOS>ehjP2X`1ha7)yX3-!xgIXQhQp^I3z+2Gz47gj}pOsGLKkd?fT<4I{!w0IlCi)@+aYYtcRM zOyh#dyV4NEY@X~ucSE)H(JcBWQe{^s*9xJ;!rcQuUTodhI_BQz(&rz*Tr$8Wo@cu5 zi?t+%$5f78iAmQ8ekKO2xRvSIVAWW7M^kA=d3VLg$%WZV966ZI>PMkiRH%o?(XY#U;rceNUJ6DXClu3d{CV!Zojx`b2LsSUT-sztytZpoZkyh`Hd z6zj_Q?=N=8?-#Qra{I9W06;6n|#U@@;+%uT7<5? zfs>EM9PiaQd0UTfX2sCyv3V7DVyyViC)}v%lvfyg40Z?{vJ2R-DEmZzO%9OCe4kLf z+RQayBScw56}QTph`{CbVkc#6_HwfLro2+y0K&Z}^0*0?6$azZ_3vDOis6A>bIID^ z8ls8w;+>*8r{4OW6=f%iC!REillsDQPrZ+kM z*SMtp*!fOX13q=9FsGwvb)0d4#*f9DwI~u^ealI&^MXi5PSt5wTEPlqBvEf$zweS8 z-i_ut=@=5u?a^;ihkZziBw6TbYj?SKff)yl=@sXu)&U8v~apgYU zVab9Ph<(a#cI4LZBZP|LDE=+#C9mOlt``yew%35`N1vMZKZiWz=1M1Y( z=7#!q5}!&q=r2kiZq%uretJKA4=!e;@4%10_tOBg zG)M&->(p$|WqHLdZR>6HDT*+j8Gf-tMzNJ;g|d0}^((h)V}H~wHjBhzm*e1~B1CY> z1^knC&x)=d1`4~Dh)>NzQe1d+I_ZGo@97hi^S=?2(yS z@j+HJ?78{r4f^=nBbXGAfz>cyfca_8Zt~+AU^G2<)gd08lzX0d5&sk$<~KjoR6wl;E!OSzTh$)Pcso#b#A*H` zOMHMvZ~^GZDP^?8bR8hacrTF|!*Z=>J2@ zQFVJ%A$6$$yPhE}gO(4fHIw%G2Cy;>n?$eM`M ztB^xNFuY%c04R)jtbN7^i1~>%r6TUS|8l8XW6w#rkx>}TM(3o0G%Oq{9CUzy&YWa{ zaAeMTFyUsVJQnMygiJL7h1v_dmDnRIHm0kKX^PROE_FHg+G|#|)%&IsYW+$rT7+)7 zVF95nGHBaIZ8~(y7+(`wH_D407>y{$PN)?+_e54~beC>j0tDgeoCZzmmg32)CD8#k zE=P+QEWaLMykqJ~$b3l4@aRxNa8*t>Fw}CyQg$jO{4TinPT@uZCwl;14~!P*MS7~>i|*JRosq{=Pff3=Z#ylOtJ>Tu>G(L- z*>Es5_4I{t4aemZkmktqZ+xy<@i$ax{Q#~9gD88#|?3^_b#S=h1>No)J*w0hZjSL{ca*r<@4@D7#? zrd;OBf4OnD%sGPV`SXVyk$0eaq6E}8?_Bz+qM|}??%5bwYqQh1WK1y8@L%30#~R|rgqXa@uK|w)vO^wPH;UVv?fdR#L7S?9@b4Tf; zsa~oKW^=9`5PHO_uB$_Zo_vJiKsLv8YoTW6U+izrSyejeq;@vCVskidFTin@>$7=D z{!d5S&F#+Bi$A@l*Bp?^{Gy`UQ(!crz#uavn6|`FY&y>`AOLvs?WJce-bJ#rqQr5g z4os}BuKraBm1|v=!g1<%R>vgE%F0~E{D1$)`Kqa|&g1yqHr@@+oO?}5#xC^gad07p zgoHSK=ux#3tm8CjX#oPEI}fRZmY7`%oRt%L6zuDG`5!7Y*VZcXLOU~dZU6BLj_LmI z8)E<0VKto7ug8u(IUJsz&f1u1D&&le<7~fd4lgd&g~4FfHZ~)ZldJW7VEcSh@~<4* z>Z|<huP2Zm5Mh8P<|3OKR37iN%H_F_8`ZZv>hrDt;ahZ zo4FO(Y@hM3XLDv-%Br%XZW#O<7Q}|1<`!K3+26J;>-7mfMUJ2cjk+rkb^xiG4{Rr{xc885a+ z1 z2Z7Sw^El-2fX!f+=|#Ga^NSm6StYP(bA0Uah=qtC0@4QEXaQ}GoI0BCD-9*JtdgC+ zzd5tLiKtuZtRP}}iOiG>ULm_6o|0DnAclWAh&i|uj3@0K2%*Yl(B;#gIZJ^OUyhKp zzG*hRwfvSb*wy7?jaWo;G^wNzs(0s5Vyu#Dm35l=Bc&%wiuRak?A%km;x@CF*6cV) zg)jgm<@wh#g`u8-NF`}NxD?Y1wKM)^thNqX7B?lq44A0EJ8tz$scudP?ypbzjb*Kx z;pw~%Ekl+127{sH471cLk*waI6wA$07Kn>*OnSD z#66+b?taOB$(6JVvJd5P;RKGqL9Zw`DmrTvS(9BR^K^n)TZ^ckV0U|);tSn(K&_Iz z$n6xtBM~w=98JjFA{}LA${b<^)rz|!1LVDeeKbM`el*P0qMfGqVGuGnGz%*t-wZi- zR9QbXS+5m)G>L_b0QX#3VM^r2Do_GK(cLze=VlTtI|jUqLpVk}jhdW}ac5MCu;dQes11=*>vS%~!j_coh3>Y3%cUrn+!8@K!w@uhfzZ`O-tq5a~)6qB160nxV5Ae;xD8Iov?0`6N)T@kX+?QmFU}*9M^vCnf)Ni}a1geQ? z(+?3cEzZ6fkIVc#{x+#)cB?eZh6yYhIjE_eY2N)P$Yv1p$!Ce!x5yc3AZ2%y0<2`Z zVteYi%gB*&Lh&toDvHR4MhnBEL4vF$&LcERH?B@FH8)}=wFGsD2nn89SbH%&gfI6U z`-jGmpES1bBRlov^+7gJOD-L1#CR`(#upobyS7AUU+?`jHCntR*?C1oBlV6=bnlg^ z5M^|h!vL8?4Ty^ThpO=b!GAs(p^NnGl7xHToXtjUSu8kC$(QcC)I6P+-9~~~nw->v<+3njBil`G4X`r3E!pzU|cS_OhTZ9>VNJvxd+SQOI{;N!M|IwsP=Ink-p%cE>Nj&P`qHSp*lQEHtcm zNeW-7U#>VCTvx1I=qJAq0P_@?=a7dR1ax+T-bx=)M*Q+lHDMs6wId zz_-y=NnF)xNM7;qRzKBmg{qOr*4S8Qplm|3PiYd(rw8zzCb@y&jJg@ruCE@ywqVXB z;$#L9v9K3ho13HGq~Qo=-*5GqKh9Xf;G7JT<-J+MQ}$E)i)+LKC4$))!!uHWb!4rn z`I!39rX^?cYpO;NZr2v2igd1k)3qJ%BI6E9g1`Qw>OcXz*K+%iZCeYZIC4cSFRR`> zUpIL?->U>T>+>V1*DU#hiaPP=2d;9Z2H79M!_X9V7|aPbFzUG+s_s^ECTiSW_QjWH zW;R=!L(tvVXJp6uhaVx6OF#klS|RLJ-iKoe4Q`Cg2i zji1{mRqE~CWfan=7-LCP^~fOfUhKrqB#U}!SH_#!hez*vHCJs8P}}%DwmcyQ)Az4UgBUIp!ohWd%XHE2j8-HlCgnD_c_v1 z?K~B{t9=#SEdyF=cJNRqMYsCfCaR*kv@h#`V!FO}My0aZm}^Zytr4dxhKz91kj-vC z039N=ZZ{oQ3_+((0~9&^W0ee1Q2{zDcuY;TtBJho=ji4B1K-F*= z(O0LQWOOVVm9c7DnFEY{qt=APSfUK?5l+)Bb8C2tph4m=1yqsHhh-?h4WY3r3WXxm zk|gwAiT-oS_XZnEMJyRwH%b|+yf>k;aTB4zx_p_Fv&;#9wI3Fn76piWn6vSWm#(ut z;*bA?5Ks-xF%y1Q&l+POd|Gm9-A3d@Go8tHiu#%@IR$Ot0c}u#lcKk!3Kv>8*?p&p z_Je0FUiu7A_iQ|qY~rAPr(L)~K6R#U2ONr>|NnN113>1@==CAnkRAknNz|eLYz+mn zB|^?8otkP{+nY|Ig{W)fg0^GeaCDiRQy5p(4`FdKTBFLy#lbJ6961wz@ZH9*XAHZ9 zDGXI9Tx)eZ?fuob7SG;+)6?eqS{p(zB2X*m!Moy1vpIE3&5)xs-#Gp5pDBY5=_0o3 zDw!e>IAJ)KP~#Pv&a69%-jhHkdLPO;O^ET;;@^2TQ9@L0yXz-h`TK|Wf0rwLtYF>bPQ8E6se2#3 zLFKvo1$zjG-xJsk(GX^YWx_U8sE~~Ya@O?4nwYdSCg@YY7gA(~Bzxaf1f8gPlh*O9 zQ-2D}E_g#@ewyhhU3+_Q_#!1oK^^|;V}|}j{oI>_3(n3v!t`*sZdm=zoedSF<4P#{ z(9VqNx8JuH3_iGaZZAi3FgAOQ&=`GNNRRZPX!rMSCR3nZa9=b&Xs&-!1fP0fNf22} z>>O$UuBemM6~vCZ2Z(_eS)U-MNd4iSsfdPek5a^Ygx2SEv5%bF+<2Yw8UG9-AnKu&RQc zL9hMlJdzN%Vtg}3nlv@Cf0!=_d*nj%+FO}~iWR0HzAI_N`I>^2&~FVsf6|-OgM~~N zO4`(xO3XC>3sk9rs4ar{<>xW%vPoild)Ub{+z9D)6$lXrwEsSmUa;bEAe#+C+-g$6U zHdq`f%E-F0*Gg2U#ic95B}wl{P)j8&PU1gs}N5@b0l|PczSQr}g}e zX5D)Exb{2*SL*698W>28{1Xm2rU&{Gy)NoiB!NI%>pXn%*VDTW!$ogS1iJ&^c=r>U z_udgM{}}l4M3LQ`R3v@=bG@QtV8;dPdY6_P4;|haoWq0V{9}6+)Tq@~;Qotc>nldjjnJ~@87evHl_wP$?!%NSk%>Qq zd+D28zw`^;2ZDhgB8m^uPwjmEx;XXv?AbIOw$ir7@Yyt7KwYvOXP#eJ86Y*Xe-^v= zlY?KT14ycL?kiprqtIFScT&){OK;z&CojTQ+c|w7 zHwyR_42T_E;Qexx{LJ5B^NwK^Y-`4sA(iy6)pu{f#f3Wi$n0dg40zjZ7Ar+@ILc?N zMM84VdQLY--@VM`bQS4M0B+sl5jH*&F=>8_Er=K0sO`u75`OT}_(=GA=u|3Y57 z9)txt|1V{EWmOrG|A0XJgVtdG;@|6>^YtBXj@18xGWV9V0OpC#Hpx$qQ}ER*;Ntvml3OZ2j(UeZmbN3vZ#%!$}XxlhOE#fai0?OmN-ZcqZ@@ zaF)ua zQD(`mJp3YQ;d=4ek|8kH-E8sOa$;DTOqJ3QLt3_pka>lnbAR$-9kCGL)j5@6cl3guMtF0Omx~n_6a{Va$A?l1?ZaXz5 zVgtGLRzU9Ak#((B#KlT?2|EcZ#iP+l?4*|ppWbaP)lI~X*byq$q?X+bT8esRq5oHA zicv1oK_jVDKGjpuFuBypGZNiCZ2(zW^)Q;-YrQGPRQ5HJhtcm}RLPeKbm>BhZM(U! z(me(+wlD4_cFX56HNyj;K1xZFTGUXkICwZSVYmLhB~y*yfqxVDvw82U)J0N zkH#MFGjUT1{8ia2u-m4S7Ayl-l)Ncql- zcCoxXf$q|Whz-a(?0!Zx6}`EcHOtfxT9JPyfZ_rWK;oN%q16MYECq3x>BuSfubU@~A^MOyxeIaBvj|Izth+B@ssfS8Pt5z@*#Z85Mh6S8UUn-Ds+3~W#`6Y7t7qd>0;w2AF}4BZ|peox8&MPjsUfBE}M=-Zbe zF-0T$!0=Ofn@ju0M2&2U#R=i$ml-J0Zj*$+=>5fUdws$52^H-5!lzMkA>UIF3lOhw#N!zvVQCQ@+B&d ze|DIDs?@VKP0UPaf-fCPy}nL(S0oAjU9Cb5!tI=4U;+~;7#VGtoy+Ksp}1YW5Es~( zvH-w6BxrXe^H&#FWVNeZ2&ML#wG;a+8n5lH*KOe=!)*JG1xh`2rkPcjCZ>Met+)zN z6rJ^+K3falI%`iO4;QuRa-SyN>v4#(7iA7_D}a+a(7z&;vca_u*G6jikg|v{^mWe{ zE$+vsvjNJr2M`xeR#a0)*U;R;)az@aY9?J>eC;uyiJkRSDS}XxLCsw0T0r{KlQ^HA zU!NFZDkPT!+-@9NP@XFx_-7;*eqgkW`rlm9f7h%OJk+2_qG_RMT`o562bbI0``%~H zLDJ+yMYbLYd7}bczNZBROM_J{_E;VExTOhcN?U4#d`eMtMTeBb)+{nqi`~m;Ma`AH zTp5!V3ZCkJV!nQVSl7IrY5J?Nu2)8i4yR;q8_VZLyAz&kxyPpJ3@M`e^XJrf3Od~P zF;CD24iW$0f)FwDLH=}w4*Ey>M|^Y|8hIn;e5=>kE2*5(=p(r@ekOj8;R+OkT94fRdN}|K7^S4i_n7H=rm3<)n{{Ga|!f#wzDj@_l#fF6;AG zr6i`G)5_#_-eCsZHu)y(_#OEMgKW$Cs^?^BsU3ena`l@=U>XMtyIr)(8Vma>#2K zb?$%NHY&M%Jf6520EHZk-F~;tRBw!n`qt2sNCBESqBR71w+xIf9pWR;^9)pp*Vvf^ zB$+QFV{6)+GI5Qk^U@nMEQ88F`g&WBpyA!8iACnSxCa=I*dn>*_e~e$Z0v(}1{U|7 zG##F42fE~xoC*Ft)@SN@W9X2(E7b_M8dO#k1o6EygLZPy(YC=z%`b0y4rY$m;l5sT z{#Aj>M!71mA~zg=ybJ4ioKok}@EfW`t6i74NwFU}Y#9$>vMaqa$G7@p@=X!;gfGdd z2vBL^?8`~hO7>esT-h|=2$+VeeHeRTMfVzZt?vnU67^1aU1ADgwve_L{%7&Uu;YUQ z#>U2GBw}Z@Q8xC`2+0V73o$`>G;)9Fr8=X3 z1%xur>yX55m^-)Mq_3lUI1K+J_m4u-=p9oKD0MHhZejgS(-xmKvcUZZ_TQfdfjh3*~DmHyF45|Z%e(EuP`hO>Yqiq09$NG0gMfz`u}QsydbFXcwn7 zc1n5Kq};Ri07F@kze%SJ+&0Hs-hVI$T6U&bWR8nE+Wb;j6&L0yF*Fz?dr7tWd+3v2 zxvAVA?R#QN@h@@4$~dGv38wMt_6 z4@S1C5>5GsPlCktD9dLVqn^mgt|}Q9^QDoi*jGT;qQPBF>*T$T@!`ONbY986LVT#( z-X+zD-ci4)?g%fNVRy@4>=B*AKP>LHTx#FhBYfKJSYH3sR_KU`+6dGt- z%xTu7{Mf+8KwgLVlW&cIS5TZFi#e_-;4lggckeMt7oM@j*ZU|1?eI|TADu(rJCbx& zN&WPwbmka;Rt}bT%H&1AaS zrUWNK6VIKEV30EA+9qVN9zeb+)@dSUA}c@|8ZO*dSo3prl;F=sy{A2d>F1ekr9|_+ z`Av50L`2UKb?0g9W%q|u6800To?GSPZ@Co&IdF13NIiYE&ehvhXB##zz=-1cZpzrz zYeH_g0tEvW3_yEw=d=M;&lz@}mTq(FTxa`T&pl5=+UMTfI>O4iL2vr$dzxXta&cXE zTK}yWzv|=f_w7U0_jYk^vkkGrr__c03w?zN{^EQvZEa6o`Hya&TBNM1k@XiuZxE_lQMQ}Z_8ILPg`ux82SzF8P&JoWkXt?~}2I@byN^<__Ms*co;*ve%~?@kFJz*wzf;J(EUHszC*4Mfsju7+_gaG5)&1bgqLNh4^2iI{-})?I@@_B;P)Vg>c(Y?aa9_iv08b+t~D`N z;GP1d?Q_7(?Pe?Sc(04R!Y!os7x9}4YV|4JHx~8fn`B>@)PE%OZ|QgSkJ}22_C_gq z9%&z{(mfq2Gl=Dd2{~7=4qdWNht5_vSN3HBvvqE0Vs0EgxxxIMW<00~wh{tfLr$)n zP+p98_BX;`zp6S;x>`e*i2>PnW;AHJnT?Nm+8FmQ{K(i|Tt4BdJka^_aJt`A2iWqS z>&&)EOQ$2kd{}tA6sQ$hoRE_8v_M#%UX=%eJu59!BO;ATF-dk5{wIDvIi2*A(}!&E z4itrQ6>I0erlUp2#=^ck>-njv^Vj%WZ8xbU{+9aE-Aip`IjEwM0GSZa>@DofYtS#$ zXy&H@@218q@8ZP=VwDT>6E=3mPEO^2b#Id?7b|>5UjC@EF*IoeUR2CZcDya5I$ew_ z*q;Nkasm?_=+k)_4l5Do&o$ojxBeP4GxUnJIUuwYYu_=)JPB-)^A{M;_}-J+0Pouu zJe`ZDxw*64+G>ait-D2?-qw!SJ(zbnl7B57ee-XF=%)kXKlp(AYKl-NzhJ5n3wk5@ zvG3WdM{iHKh|OHT_|lRt>!71gxjVXPtn8y6v^c_g!W3+4yWQ_@7Cwe&dMTtxn}k>F zA3@?)?S2QC3Yu(uUhw_SU3o>tUwNT|?CrM-fFBq~lw<;Q-s{Y+ypQm(6Dvg>{TTvP z6C%eoP7BNy1?lK0XrxVui|5a;t(LYNEG-%4ya7zZ9})cL=M#KrKAK&Z2f#DBtW<-d zopv|5Rnxf#3(w1~{S1`E{FsL0vc(Ug9|%E(Zh3Dgi`y@aA0FcaUrc?{@#Fh}71J1e zFKhaieU+$YD__{8E5GsC;-x3oZZ3L{OXuFs%lq=>!60Qf!`ULs&ZjwGZf+eD{>!{~ zQN4?SjBI&k_i~=hPI_ySJ#THL&xYT1tPR_#u>ty5*W7OBXk%^6t2kK=K2n1(`nTd& z`kXRK9JYO|@T>rTnwpIhqiz4;b=3Avn;O}qr|vo*ZM0@hLl31Nw_Ba16lj~RZ}z8l zOJomxR)s#+sF^Ap-ztquS70=U#tIQx3L@d3KH^V}g6n*IldqGTj$B z)o>vN*4erXp}E%u4o$!M{%PbXt&36n?%NGL)5V0R60)_Bi=w3Q_EXmo!aU`0Kn5m8 zeCugp*#*l393W)$x{ZcGEJ(V6o=LS~2eynU$L(bNWY{)aRo>t3vDD5BK3Q05&5vHG z-64R4O76J)(Bc-ta8YX>-6>9Ya#^CoHB`!#uD@0Rpih~VU#+xL=gK3*iYn5FhF;CF zth}#CYUxb<#qD}bRUEFjNS-I0chaaDH;J}Zm{zG-mEt?Qbz}P1r-6kim7R&pW@Kyb z9pQyt(?5HTP0Y`wN8~w9hPug5N0MV*HJW#Rb3OL8YijE4l(u*7iB6CBH?~=NBdd!# zU-M5wl-zKKfAr|hTS=;}%ku~7>>p<+6NYwvvFtA4pWd-^JG*`YQlu5<^yTHz z*4@f<=-`rmOkJ^`jOWm$jpxQlOCFav*Xu41Vicci$QDqv$MB!0dEU>*Ml@{@K7ZQc zey`*GqJc8Ya`YnPUTg1a?Tk0pOoz&y4Kq+Vjk@~esSr>FX}vzOwpALFewhK^seU;7 zaQ4}W%n&0jhsCxNU5S(F=Jx)=2Bi`XvCF_7N)eeL5hlGM99$1suh8gw4$s5hvEdP; zw|j;bT=W2tnV`!aRn8m}`?*&Y$AZbq9H+~U({aU}3fd*!F+*UVaDj5+3-$Z?X#ak`r|xoV$B z*hz-2&LWm;IzqRVc*$H$Z5z*rmH%-^MlA9AsEvHPx`3h}&oq9wbqst3J!;_l6;P`z$R% zOLi=?bGebnf*((!dS#|;Q?5p8I{$?xC2H;*&FG0bP+AP!LRP)sDyEAne4vRSU%Br# zSzj5Txs4pV35b)$xO!8!64wwj)Jq33z>LHV+Zj*i(F%9@@YHOXD~4NWcI291r9B-y z_fsQ{hfY{Bt(iVhktxU^HHdrOv`llh>hj8b(lE6~8X+fc5@hGKD8+m(<*z??} zefHVfgnS`4Om1hm5D*$b1Qhesg<$d203;PZss{rNhoJj`H7(48+#6t-hhUwVjPq7D(p9crY7wg}e$s4H+PmWKx zh=NhzKyRx&?-S?vXoR7YJEIb{ns!f7jcrzWYx39pTJbBYId?}>9YEQd{v4LnrE(Nd z%-jiiPEP_Yx}^5uq#gng>S)uU+8m`iM7SUcUs&ci%(dxMhpTGuSW7%({kdDI-t6wR!yIMm}#7U?kTA2{bHP~cdV^AesDGquJ3Jr zRWD{xncIR*2Zb6z;bZPBfEDgCF6T}q)*O1Aq}#{uZc}c!ZeK(f%LR$^@BYjnH^F1A z!DX4S@2Z^==?P^B4X{9nL-7Lu7n#GeSJW4n;zqr~pYk0;`UdMh&Tbrf8aN*ZGtF@M zjqdfm+{M2|uL%cB)?F4mP&)ooP17}b{ zjJ}v# zuKZC&8Qs~L^W!m~f3Zp1=_&1Tw}6_reHh-DgdqkEGbbV#Y$w-GASjlt+u$9#tz%gi zrhDg{H87kxp3Gs^eoxP(JWxf{CH#1Wkct9}K0;_PmwRJ#ZWpQXDDx zwYaXykiuh&@SOlDDDl?h0+?9N=G5|NSI#yAE2;HTgSBQ=#}I##;}5pcEIuF&V-J#a zAUoZFw=9H3k4Pb3%7;0H7YaR{uC5#+m;}Yp5Kt`4%~hlfr=m&^JNpg;fNOwLfft`4 zdQAfmCt2@x2lc9iRIC}tosUEH!U%9EoD*t{n{w&QxIf? zQoifi&pkqKkJX3LMz8`ATriNt7BN^X24_J}f*d0NMQZ7ji(cZZPHJ?J^` zbD(hk;v948r8?PD!=O`bqf4|uo45W-ge^)j{OLvalvOMqu2xxmPIJB5-eo{4j{{`hve=5qUax^YKq{d3I%dFCMZu zPN92GNRPE_I4}X4dyDmXtfIe3d3>q@KE9u2*c{OBKNC=@znyA)X>k9=YYIU+1?tY# z${Cq9_O7PaT-R1}AvY-`F*sq@N8WCSWE$=jMx`0Itok$><1L{(ri^<-xw$Y*NF$cO z^R~zwfDg1OALE1J4sqH2r@706AfwR{!*d3^WA;OjM1bWgS55b~N{3;Io0+o_vCe@r zUJfnHMkjxaOGvGR>y@GgZHi{Drol>V@c}QW$CrKKKP1KC02AowfIn@AoI4P_Um@8f z;V#*rzyXdQrHSr@MzTQqWNzUUa%{H@+=}{R23`{_gg#yhv@ZLox?3AxHFo%XQxaDQ zXGG-flD0w#oKU&-C|H4OmyK5s;;ecy!`lEVcWOG)#S>7qPk|E5LAzf%!asBJT47 ze-0!NFxz~|2MQ-1o=Vk!-DFwS;_lB6ZGn7@O|ES()S%26!Rv2?yD8hLYWMMAz^FEs zMM*3SzKQUJY@3AVZe=w6$>Vkn_6`Uu{FviyhFhX6GSDu%>082O%Qltad>^VPi!I8! zxcMtV!XuyZlprCx3m<7l1HlyQ^^w*-BuUA;B?%>RVe@EU)Z6eP`?gyj_vWc3k7|&* z5TgOj#tQr)QMoMztGZC6C(%}N*MG8hrKbZ6u0GZ|36VRt>@0%$Nmx0qbfNG6F0?rQ z;Zn<%6ck6~(c(CSm?YAC8)5RHeSajpm?J_eWD@zW?AoVjup>uQi zkGS}+QBe(so6xFG9I7}#_5K1BaBPHW_Jyv_s?nT;Bi$kE4o75OhKmf^%(;(ELXqgD zJkAp2!uB=J(PkedYf0}M2Zk)D{UJs8Qwg!djm+CR{+j{TCBr}Ats)!FUaGO|w#6^` zMr=<>?`d{pJy76M$6uNlLLpR|9!1#FvRG@j>aC#_}qtvofnod$WNTWw>Pi)0HbxMG1qD0ax z@%qYWt6dK%aXD__^6eI&Rt7UPz<2g&E_q8ZMwnP}3@dHv0Qs9gEE*o(m%jP;3YAl< zI~(5bIsF2ptMV2lr8<}UnSh(}V&hIzj+Y1F@0_Jxme6Z`w!ecUykCeemfGA9c~PT9 zA}P5oH!I{whC??C7Ma+M(hzHDudWLr;EZh>{`Mt#DY=|MIig_~tq7smQ9`!jvu#p$z78#dU3ZKE8qyFZ{jIy%R)hMPS~=-VZ9HgI(XSx?`dcjA}wz_`mWATU#Sks>SF!T)LFt z4aQP+s<@NT%=|ZUndt~c)zwP7jZ%boITupG181hymBD;|ua|7xx*th%C1Q9j{Tr{= zrr$DEqLp7()}o-GprX8psH|*Q+RXlVV|^6mDiS+!7h_6Y9oZt!qD&DVk&eW>hxTyX zMKO_{xeL(QfAj zzj+8ItejOZ8vgB$K8ZgBSpS4~$SpcKlArG+pU}mCLfja>LnS^Zw5Ql{&z`A!2}Lu6 zK4b+KR0`JfyD2Z8TF*pAS-In2V2C<8_A0_DKeHdH+bXx7BU`YbpF4JD`WtaZzpwxF zJayw@vQv>%C!|rB=t!f{w?OR;_QhPxQv4g~j<7dIPB)1HxY9_}z5iKjp~^*`z(x2S z1zP5#n+_PRf}bK#+>Xzx?u{9zJheMx#pjGAFIqxaUW3=T925$aq0A4mXr|g=tp;1KFxGKSd2JdNqEc2X!`=d=)qrIT}l7shu;q5gOsDJLm!^@&3Ux5I(&d8x% zO(JCm)J0Q|_5n8b*Y^f2gVK2$R@_mWz5|)9c7B~`gSebtaPV_RQ~!79A@4RZw3!73 z&T?ICVPuQ=QG|cwhiC{6t0VLNO?{Ln^cla`4X3(Z80VnrG&jdhfzX7d_ETDNS7OBx zf2u5E6EU30;V!~_lMR^ZMP>~eZ5F0}Vu9DS#4@7`gih>~(P#K`JRGsU;yq_x3m!Op zUT+*A4Xb;5-z0~pV6MW~$JQd^-_#}5%Rjl?H}pRD!G5AN^=igVJI`& zPfD;M*?nquN&dMDs(A2uUiRSJLxc8>yc)RN6LC7NY1rE_s^T*L%(zQX{D+X%)Awif z$EWB{T{D#7vVH4r7qq5`*{7^!dR!+>?twj4^T2^5pc0{c_gu$GYIz8cvc2;EZ9iM% z|EOGJt~yNBESL52AD*^&XoX-e@}nwE9^N%uls@g_O)nw_fM$^LANY2I&FI)g`9FBp zT$-v#m2E9qzJ?{mG38}^4b5%ZUHuDs|A$Zgu~h!TvVDi==r8`vjZYYP=LsYK0Y6V^ zw11i}e?eHiF^Bh7X-l9;&lVCgg*q+7Lr`Njsx4V{-=WZieFS-f>i=e#Wx{a+OU z!8xE=Jgkv8x}|YZ`9DoRn_&JAy=0OV5;-PDNuq2`eC~wYS6?bA_bG1*m7Mp6w!zTh zAQ(T`a$Y0EsQr1qC3mpGkOX#eq5yU|--q~k&4r-(ig53_@U~;Sb_e04% z-w~I9WsvY`Y!lXr$;BK_(%z*wN=)y6cmX=Y<3)!P>h6!4ALYVu6d+Iu1d!O^YxX-} zaasY^*L%02s;txhHJYyc|AMCXuwL9WkIs1QSW~Hmd5*nV&Q}`b9 zV4;cmQXKw>QsPIjVHuByBHtzL@3?jDSA_90(i7|h()uyJWY)cb=q!N+% z=kd(Jp?8o6?dU0mA3vTh|J>c0k@bSQ*ORvmbXs6&k~n9?Ea^d%oYH&>D*t+N@^N|y z{}ZCNa0n#9jKjcI2mD;Dwjdx*Faf-((Zr#(Ep?{cbALu|cQa~+Np(6<>C*JsDgwCM zQ&m*5-8C+b!-(#+iv6O;DO***i_6pThnRrt&utjo+tzHe!kW-o>lE-H+*sWwdAugp zCo|JCH|mR&4mffYrb=NmJodwpI%3b&iq(hECUj(vE9P_onmaiEpC?Dp=CRndU!({a zn$}xvbk@+Bu+qLL<TQ#)vY7X$2Z1sMW^J18gD=?*nNwPCT!$f$w>wx)ZdYsgyuTNNFm8USk zK1(xcn&tF(c*wUKhozYz=8$d(Xqo;22NkY-sgiRzggSyRJdNgK;~f4kj{yry;P+~Q zJfp(S@&4xmwg{*oW7+zoWdG(U{gHuOnlHJp7G|4Czf}Vk(qN=ElQgNk#U@TQ54fU4 z{1Zj*+C?tOw=&ZL5-5xy{ChLyz}p{FcaFR<=T*)RT%KREZVg$q$lQlfO0w}qIWdNh z5SKCOjXzbg5&-qvJS~k1vQn43{9i)zwKCtKZ0^aj^ZF?2KDh#r7au(6%uwU9(zQJg zSZg+SzA!~%SBj(mogKk}no=0O>;m1ylq;@-t%9`OxCJ-H_#^nUn`(tIzYVUCoRpPb zCvnSU$sPp*3D%!@Juy#p@G;dxvg4x~*=b)XA193(bn{x9AOYz+?pMz72ZT zRpA8{(^Esu@Q>*Dx?WEM+M?{=evKlft5nEbG{P6Bs6WSSh!Y}hu&LH8uIC@DKcE@S zBQ^VJnj`=vfamgw(G1VwzdvgAUB4P?WzD{`vN!q|$93JXE33%O10k;v0~+lWIdw0Q zrvhv!D0X_BExUjg^2i2w_B(W{7K{{cyuA3C>teo#Ru%$W*`3bFl2^BUJF_vc#WiSTnOoN1q z_WdXPnT1^rugO=u^;JuVf9P^+b=z|x%OmxHcuVo)dvH0PzLLhq*!ZM|j0RR_qNtIP z<>+?Qx+^J3JOH9Dvl;w;fyD3Yxe`>++-B>tD7ah5a|*?p_0LW2Hyf( z#m-00Tb<&lHn7j!nwmB1JT_{!*c1zql(qyF6%|tG2JSa7op=@(Dw5q_pim2ypTLxmK@1JC|#%(hoc1y2qNSHN?m{^T%eh zwp0r%0V&jD4rkVh&1XpbHP9@ULvgvrheFIt>#pT&M%Km_;S_GSS%+mERX#ghRvXl> zw;uWG{qBp*(n4qnhetptudts)GYn4ysK)=da zm~)+6G@5y=3ol;LEG)h|j~b`tPyf++C=rWF9q}-Ng(6G~+_)noGe}b0KR}eJ5hJI+ z&+-k4%`%ysH5wBEqpc{B;sRP2iE5}cxG+bOZ3a1FJ*_NyHTWJmFDD6@BO=2P*A7<$ zZFHZ}MU)uc#a~1$O@8lLZL`oYTbR4!#+ZnxG?*X}3QHMk@I1=W=4%@Td5*-YtlLq2 zJ#~c{terMe`1qz%zSQo6^-I~@GeeM;9iQ?0eHv85hb^MnMc(cosKV&hY%$G{hltHi zeuKS4KIfgNdQW#kVpQJV;pB~NF&JIZ!-U#X{U!(Hx$+Z$QYzcC0I260f`52ozrQYh z9(x1^&%OM#He;=1ArdSNq~gC&gors|Ns;;C#r*694qK-8`ttR4c69G=#n)lJ=dzJ* zqh?rYv1@3il;zha{#>>pP`g9JcrjXLdWSuWgx|6DlY}E`;38@v{_zw|iu6h5|1(w1 z+8YkWP9@JhRYfo!7rvm~;Rx_eU{JUJt!93;0&w=6x!q{T$E&LH{R3u`)H1ngHx}Ua z*}FtZzw|OveLNdo&nB$@@o(iOJe!iJzt#DF{}f!npL&(o@4r~){%@B+{pZ4wKn?MK z)_z3k@X-CUMkI3QA2=2EciEH*4wjU&@jDt=CFkpY`P(~%{cE(4&o#`o%}zDn?CTVq zNeP9VZWKxgUn7PdNF=aN&L6jO! z4^bh;lo%>G{9>Uh+;-q}BKz@lx2J*-jrK?h>D4pg#F5uqkQ}MB=yb2PCv7Uo^SaUw z_kM8kR?-c$6{=nrZo#x_t_g^GsBWs9D64Rv3$JyXVIvWUg-ngzV3t=G<-4@aTW zpy^7!lyU{c=xu@PDzPh`&I{f-SV`rB(962r8Q($n?UeW~5T4)T$qpXVsUfR$P3@85 zPP3H03Xw#BB08(v42})*@XiD$Bsk;IKS}0j>Z67t41Z{)Gu5n0t@c*Cq1gcg=&q!nW+rjY^!SrD_W*OWgZbo>5ew% z$`xx9R+*0A8YreIB9MmFSW%xdzzOG}1ttxT5r^pBD`^5b->NliREc0i4fYoiNGi=R zOlg8Cj_8gD^W@_$a<(P~`cfe=BxCc|eps~skhxMSp77!l2jhXv`~hj}o2E~}OVy`0 z3i2eE%eRtdR3eygCrr3fN1AvGz$bd0ykEkL$^0&lr|hNPp2cLw`N})hM^XqPu+4SN zL22teOiRJQ)n+m5fcoDS5hw)nF@fT2EC9Z$)fl2Qs>sLk@4rx0o2XI5gz3XUIwS3W z>FYCnEmiKr_~fSy9ov%INr%dewcm8)v(N;3uX1FmoXjVzY$%M`NVkqDMY|0W)tDAk zKar*;bUOoYSQ-@jLXtvKMQ7 z;eFK0BovtjMcyXb0;Ydz_)82C|8ksfp`kVNre@=VD;iQUgc)?(MDF~wU*oLA7$M*9 zUeAeABkz6%Vf>vtTWNX?_V4W0o45O{Y%#_=jdAdVYODN=Fab_*Vyop3T+0NZs<``E`z7Kydup1j^TuRT#mn3M$Q9PChB_JoM?>-E}AZb9QlG1;!UDI{TU3c z^H(VHNV%wXMKS{ON~ax4z!0Zudi%W9K&?`*YaD@jCMD?v$-Y;eVB)srI4NJes4~Zw ztw2yK?|aD* zmHV>_cO|mePlcAbm=}3uge%=8fFOgsgf8Mx18qG%1lM>IJ+{Y8PeQjE2GwD!8F^9Z zknPfSY@%gcohFg%L_!U5_8+zsiA&f`R2nkel1UZS`3Z0P=MQH@lmT@u`n4=K_+f#i(OUk0 z@iF@%d{D|wc==zr3p+kQ-tf<28@Sl9rAnDc(ZjeMwH8WrM~KhMT;Fs>>B;*;EWZv0 ziF3r_Q(}11=fWfiZq}? z4WZLSLfo@qoJl@#pje-uJbNdnkFR|EQ~^?9B|}GExp@L}Xg6g=M*1>2lR!7qCcPLD z<-t3%NPz$WBDW`}`Ifm}f!6!9m0S0Fx2=^#xd(PGK$T{Rm3P@HL8 zJ3=2#;J}~}|9A2-T$Jz+hpn&R5A`l`92=%2>6qx~#=N{dQv@n1>H@T~rI4i zA!p~y)!PvI!U)`|O&h=xs1ZbXnP0me80KN$-fsOs%N`Pwk!67s0C<*y(UN^;XLIbg zeHM(C^0ak!ghv=@ra$wA0Xl4wEs_?~&+%%skB`?r0srz>Vkj%5-tcDjbp7^!`qDge zrPN5afAab*95T%vM>A1$MOl-6n@efg8UwST>XZD}!~>8{1QNtlsgp!;v8$J(=nsb7 z{qk4EZ`Wh3)AlJ->OOHVKS272C$^!n1<`ck)^eFsel zkJA?->Il*7!R8T+>KnCDB4(Lzi09LI0hN~tXy+gBuZc8y9bKu9v@eJxxPK-lcfQ-5AGG#7=96BHB-&x_9Z zMOGM)`@{A0wYSTt=DR)qmkD-#QZoDSL%X;>P*Vc8nc?^5^G3}47rGUgX&RddU7q?r z;1T^1X^&WC5S>(-xD#K{!INafM z{ob{xhUymvCyj#e*0n#J_1Fu0LV>3VkKOMH-WFV?im7z^z}&d$-0~#9y(PurG9Ev+ zG!-#Cp14RhTN;PrBAdWL&r)!aaF~7kmhMHkD?_Y$n??%)?E3-_@3^*#O8UJ|HqXU5 zM^m#yf}osUTW_CJ#(9lohhkhq7 z+QAwxh7rueaSK@JzG9H0hm;KwxaXH*f^rEmf?GLxSw`E6!bn z%m*Iq;Oi-XoEq9C`k31>yQHVdlYY{u1=PwTUw$1MrG2};&m0?LkdA>kmFMb4SFc@g z0vvgMG~prX_Xp3NBUS(Kj9};8@K0VEy&-z)lK3WFF?jXx#m~>n)Wyc}kJy(fa_J?> zL4i+4pLPtGo8ybEcKHK`Melj;A^+L>?v~HR1YfLVLSHB`y{^1tG~ll=f{M%e3jFm8 zzI?Iet(1HB`;tiNQ}77>oc`F)Z-4)fms9Zw&ia_4EV>g7rz{XA!+Y`6DcrgbK2Cei zx~{V`6^>*Vi$rT=LX4V5e&E^+#5$#Pt80>2x?BG#CNpT+lVeL;dkbo7_WP>8SlZ72 zWDKrahs0MGg{ciJvJXC9QcBZ(Qn28U$Bit6Ui-C4e3fvHvl`4(U8&%s__0|jd{Qa{ zI=}*|_EgCHLlc==XAV)%k!TBA-W=walJ<9pz zThzpca#+9kwF<{cFg2&*j-2Ifn$K1Rqg87j7h4Wd163K4_dJZsjGt z9GPQUCr#>ixuIp9h*jouL8vv^yy%aLJkj?UzsIQ(kKoyK2o26`6BdKV83yjNjdZ11 zm}Lac(Z@sShRU+@BC%1lVbPR@q%Gw6lb&QuwNU4%#O0BBG!#9&uiq5TbdP~AO=g-1 zJ>Je4-(S)w*ej_dA)YOMPmNxN5*;R08}@CzuqPakp+53n!s3rWq{W{LGYZp6o2eah zm4W+g=}1~qOeh&bk#emg>>7@K+ndZ;Gyz@Bq;DECVlhKUJyor}wBKp{u0NerzP5H^ zycqhu(F5HwV!eE{#!UVCGNhc9Q&&Bx#v%6T40y!mvvGmv_fLc)r zECp*LPUBS1;_vVnU60JVP`&*6-I$Ymy6^@EM58Rd@LNqu;a1GQ$TDCwVjQaWu)I>p zRw_RnI&4jH9yoPJwnCR+&C}LJ;I8SeG#Oc3V2ZF@Z110;K{x)$#)a-xvT;}x;uah$ zJ*<0dv`;K9(uh@fF#>9g&X?94bX`trOtRZ;+hRzVvFD>p9T_|mJe@siWRu$7>!&MP z@JR9WgXnEB`{7^d{w%DfQg8F2JH?)sz#rXUEtlDpG-?5VBYDuOo8vds6z0zI?9Efk z43c`aC_Zt%Vs&ckOZj0VVPNhtX^`8OZ!IWcA+MCGP>QLT^;2DoOeVwr*NlnG?-1R zQ5Z_14m42)8uKNF znRv?vTUH9Kv+k1fab1S`nlVMyx&r+#Ek%v_@VA|cR?BcpjJCt@6@2M+OTig>?}R2~ zr=<&i#0TIgeX}H=;%o0t&2N}knmj;*7l?1cpA=>B% zb0JV}%ZQka*%%cm?OThxB|SX95OhqZWQ<+d?uDjL&7|OKolb|~w~$u5Re$32Vtg6v znYW&eR0KvfK|l|7^oD%)=u%oY!v6&AZKti?Tro*6Qla;58&@_ zr-lgTi9@jP;d4FTx$lUxp!c2o&rgf6dzuHU`!cJ;qbqi}DhNHvV-|?K# zWc-|*;`m`bC*kG3Y_H*Sk4A@e?b6|&wqT+vc(%4o1m0y6sk5OOlj*vbFEc`9b1TTz z#o6kWGyIRymra*^Vm)ReOBdd7#n`3}7nO8xC`M^@rji>jx0+1n5&G6(=BQb-Ire_| zbUW^w=Xj>CWijuw4QKJbZuD6y&B1l|s&h;7k@oaD!S>KN2lhbip^)E8eV24u47}>2 zZnr^{DDzS1yx?EU4RW}f5pPA@OTRsEgDsziWC_gE?gNdvJd8=ht-L+oUAfQM1<*;~LiD#9F6o&hkBFSJa=ynN<1U-qr9i>0+vc_imPAMFqpKeTw?|_u z9Pdeb6d(Plh>J;=FJB-1(A8OJOh$e#X`(h%2PuCc|9oCTO+sPPHX|bz19v>BpoD^l zXQ`Y*d%G5lf3PSb)xEZB;}xKoh%l~u<62`LPNvskha)Zwc%tCmDQ0l3+qe3nSqlJ> zS;FDVtUJter!#arm*fU%_1Tln7Z1no2qyXP1ia3{3(YhOCexnH33euICBzGTvGQLr z>EpxViVt7!D-)Aql~LXgwO2mJe9gQrS3)r*pBXf39nRv$waw1(uC|{$yli-Xe%> zvEl7ReR6zgT*S;$!$+sL3#u9+bA$L1gSmBt6N!D@gO2XW8vdN`o_T@hk<=9qFWuS> zjMg?ht%eNl`RxAvR>y!50{|y#lnr4?f^qVN(7SccFCdBG(|`U22+C|sZfRkgdG=?l zcng?~k4!EPr0ZMDgx*{PgyuE^VRSpEZQ1Z+LNA7>)`<4I_xZ$k9v|SDB|M{PciR(E zg4jpX`rdKAPu=+DJi?&WcJmhF8iT@nkIG!xq7Fw3Fvi;1Se4vJ{4 zT-D)qNfz%Ghs5vWUFN)`kQR$USv#+hN8JoWi}LcgLC)`MdG3MZagC=GN4dj#6~^sG z>J7bmRe4A)I^*8I4fKg}I?UU+OBTCbx*-zPxato9!LL0VN_BQKf>e6hJ#%?EWIYXA zx3&!mDBLd5t6eBh(qOXmW4b%J0z}4!nTcMWb#6~r$2XnyuivSe2f>qR1scc&JXUNI zX%&QT4ema062=LRShJ19cD2WV#u!YWZ*n(x*2xlb+|Sd7Z(IK}SY?Pb*zsYFBvf7q z81vR)D|+1}DwZd3p{H)Tn0(<+#2nr-c_ZYxhn)Hml=fknuraaH2$hFtvAiyku&0XB zlGI2WFNpt$eB?l|yg_qE#uhv2DqH`IS};s)^TWY$Q1_a6|9o+tI%8~RF7eW+_o1k| zYC|PvH4Vq8de16O1{i;=Uh_4S=S-$xS;t`OVE5pvLPGez-6iX>;-7bT>O+sT&($iD@~T zoY*pg{oT_UztMf~^mFmYrzsWFbnHjN*O6Y;2nz#EJvuEDlP0*p6?s}a6jg?j;trf0jw9}-V-zVMo?fp@XzrrQG z=4kyiVG4--yW@9;m4tTh_oOPbOA%Fo#%sr++agzyfY8~IQvH#=M5Nc+QmxUD1J#E3 zcn}0gKgd~sXZKcdud@m(#isrgpxuHcbbX3hBk2*xdB3c8KC^VfPHFe>^Rv(;XFItt z##X*)tMtQ@35);EotNozY*~gfPh%@VM_?FA54n}$ULP|OA!AC6t|R^7N?-c=Tw`wc z+uCr}87-Z?ACWMLBNb4{RlHLdf;n*H{ADQ74OU@&$7z^$xxHH%SXi>I#xFuRPS<%> zf+Qfz`lj(UiBoJlWb39Q=OW8dOWWO@$0q;Db)?Bq0o>B@r{nFpODFz0QOgcce%l^` zIYO*8S`~TAv6kntsnbGmydv7WUNMj`d|;f6_?5MRX{TA|Zt4AD$Me|xD|r<4=eBi@ zzbVEbAcjVHL|0Mge@0{PPyV8@E84-b$;vqqU3PNVe7t!W1iv|?EluuK=fa^9a}!8+ zXu>LCiF8AkVE$C(%#<;doW=+Ym=wF=T|WPgZ|lgvKR6Zo`I{1ze=VXnqDx^_hVFy* ztdrO<@?XCua&c&vkbuOCs%O255Dcn$?2E`vLYx-ARbewE0eIGZlbrkOeQ5pFlRtk; zB508sqeYm%V)p3}vCM@H_17_3ek@pD@l}>Q!|oDSPR;*d zu7e-|Jz$3V99g@o!f>UG%ZQG+=A`qybvfWM1};+i_Z?HW`#+_xs((*kaqR!k=&RD; z|10{+nSkm&2{zZRPPCwuug?XW*>Taci?8$t5Nf3v4obO~(H*w;JaBKx{}lia(-jvs z`4?*lur;8eqfmAJeu3gLc>AkBqx}c60mBEw(Smb=@#INqS1=DnYLkN-K({L?+0~F| zMbkmSlrzu-23@W{pc#a0u(k|y2ihI&s@2w&zU@Dda+++R>a!DYm2=%To(~|*mC1#d zAR3S9B;?Z(PNeZ9!d79c!1s+FJA!+B$uh)L6B z89&lQ0WeYMPC3qyf5r%9)j40B2_$|%h`KW12n%A3jftXIdSLUM5<+R23(U}>aHO2C z3^hT>6D!tRNLcVT->tlLVKRH4o)1RqEwx$9MJO2{)6~Mcd8wjht~P<_|wyB)7q9yG^v(m=TAbV&jcN z0kX;1y#M~QUE~7K0 zZf88YoM*g(Hw}F)DJCb<4w&2g8ga@ z&TZ6D_yztfg=1*^X@hD?yNx(IOe$5(bzP0j3o=AF`z%3lT)cbSg6YuLi@fy;@0KU+ zs&vN4y50nJ9tK~kr(UMTf}NTGXAm(gaV*b)VOiZVenVaTKFD!-q6FP%%uaHEtZU6I{lqg@_zR5Kls=H=o|+eZLv z*2%umXG6~w;p?xdc(vWayqeL%cQ@bL(;gZ~T~Y2b)nE{b!;LkZ+S$840;y45lD0$e zvW~O_tE6Az#Wmq}p20{fw|um>r=3nd@)gE%Z2~86B$R@SY~WzOU}s7hinFmT`=LXn zm|Gyi1O;M+I)~XJ5t0e@8o05+eT_+Ie&M|Ce?u zdHzpmC#BlIXs6V&L(74ES9qD{1(V!!w~0{#9{i7A-vJr;xv@u+dl{-wrb66w#hUwq z0Lor&jNOij)!J5D8i`^1y=)bm3h(?76Pd)OGi}l>+&T3rvB4G^{1u_y{4p6uSlSwc zikWh@EUSh3qSd)*jYjZE7%5f_A-*=@TY%>(sAz;9{`PIICW!l&FlP$YJ}CH-CDN1W zj`Lhwr9tFVmYV|+XkQLILT{>l7=d>~1+0#EJhvS5-e&b`3iV}?VKy}c$G+nAi3-wT z6ndSU69>D=D@}o{y_HYfp4lAs>FPx>SRbvKToQ_G$myYLwP>k4jn44uzlNuA$^w-u z?f(M(q>C-048&QC5s}swQ?Qagrt&Rw!WWlVBUKSo{Wr$xQ1rjSIJMKAXP_1GaFkEP zaTu|7p=?efKE+Y3C|Qdm`RfeVxWyuEL$ccNHu32D8-u;!cz6wwIxMi5|J1iRq%H4f zcOIqybJ^;JQnbpXps3qso71zN^)o6<f{b0AwU-LQ9isJPNjqpv&si#N^L8px*sxq)s)*rm6$>5sRReT-1qiYz#w zBgHb0FNyCH_xype#@Xupd|zGSC&*AE5V|VsF4f)R=Rm{BR*W07UX+lCsU%mTLB{tm zVuxNt8DW?2Y(iwG*|}`rwjr87@hN%PagQ$or~cM`&MbVxU`~`k0}6ki@m8h<%pHn9 z^%Yz?ytY&9?G$7pF7fX4Gg%bZT^w)gg4oP}D482{Ro{o>Srj&(i^N=AXi+a`;^+nh zCjP!3U#E?g=4Y_iN1_r!YjgUFVCVzk+6%Oy?ybrxxV5U!LiMeKCur>46ajq z9i+GxL}ZP+T`+sx(z4Iza@ttUS4p{?I@dHq38oWFIHtF)f;IaQ<%$C$6T!$3LdUm3-ths6#q|HD8fK$RZKRwxNM^jq{)yV{5@Cm^OOa@ z@1vxser7FV0hB0ZTaDi|>Ky!!_wvB@7!7ZG)~ThdU^;%XUGn|aYx1=ieSy% zjKZF6xliu&|I+h-!2)tHY$oDRhswF|duoQd4eGwq?8uhqW^Z+1I6QljbimXqV2T&9~B zrXqRPet3SQgf4wxG!=hJ%Li%Y_}2OPVp$&EOy&DWFI00-09elpMgWd(ZG*5iZeTfk zpL3=m6Jp0QfpWIV4ZvYEfI)1$jdV1^UI}*rDWdLwkK|$9z3Okx$Jhw~l~M}zp(~vS z1Fzx)aLSp0>&u#{Uv$DOkz5&YyqrWDt-TE&@1r|MTxF*9hlp>U(|fo9>S5o^X_%; z-%E~RebR>)`J|tTxh-|{-oC}0zmiy{zxlqC|77$`t4%!4>^|wKGv<2#HZOYdqxPTW z!VvfDf-U@d@OX;`cN!nE|8kpH=JR(2!{yulY30Z6>ss$KnQt*&%X@zMk!5e}w@u0} zns)f>N!ukimU9a4ubj2@{9fOXPt1JtmW#bVmv^c!&$+Ya!t-MiM=Mi*dDy6zKD^xg zN(Y?3PAoqEa_>)KXGT@neSlR+>x1WUOaAn0-~6I5OWIA*X|>4V+pGs#_k>1WH2AOo z#(L?qHn7=GCT%)>6WGfvlF#1|U0$%IZuW|7ezj#E`_K8v-=Fp8qx8=gZ~Jc)ykj&` z>^Yukz05xM|3rCzbMZCPzirGp<&yTNfb;8qaK}d_{7O8clbM$u#s%pRyQpjrMw(zX iwSWmqA#PFVmcT-UE1d8-u5-pUXO@geCyn1PZPI From 0539b6b4566c48fe10329ea3cb78e149842d1dcc Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 12 Jul 2024 13:34:23 +0200 Subject: [PATCH 219/335] Strip block whitespace in templates Ref: #168 --- crates/dtmm/src/controller/deploy.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index f0cfe96..957db5d 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -268,6 +268,8 @@ fn build_mod_data_lua(state: Arc) -> Result { } let mut env = Environment::new(); + env.set_trim_blocks(true); + env.set_lstrip_blocks(true); env.add_template("mod_data.lua", include_str!("../../assets/mod_data.lua.j2")) .wrap_err("Failed to compile template for `mod_data.lua`")?; let tmpl = env @@ -495,14 +497,17 @@ async fn patch_boot_bundle(state: Arc) -> Result> { let _enter = span.enter(); let mut env = Environment::new(); + env.set_trim_blocks(true); + env.set_lstrip_blocks(true); env.add_template("mod_main.lua", include_str!("../../assets/mod_main.lua.j2")) .wrap_err("Failed to compile template for `mod_main.lua`")?; let tmpl = env .get_template("mod_main.lua") .wrap_err("Failed to get template `mod_main.lua`")?; + let is_io_enabled = if state.is_io_enabled { "true" } else { "false" }; let lua = tmpl - .render(minijinja::context!(is_io_enabled => if state.is_io_enabled { "true" } else {"false"})) + .render(minijinja::context!(is_io_enabled => is_io_enabled)) .wrap_err("Failed to render template `mod_main.lua`")?; tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); From ba31d5109817b2908ac40402baad79841a87a336 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 12 Jul 2024 13:39:03 +0200 Subject: [PATCH 220/335] Align Crashify property with Fatshark They recently submitted a PR to DML with their preferred property names, so we should match that. Ref: #168 --- crates/dtmm/assets/mod_loader.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/dtmm/assets/mod_loader.lua b/crates/dtmm/assets/mod_loader.lua index 4da08a4..e000292 100644 --- a/crates/dtmm/assets/mod_loader.lua +++ b/crates/dtmm/assets/mod_loader.lua @@ -289,7 +289,7 @@ ModLoader._load_mod = function(self, index) mod.state = "loading" - Crashify.print_property(string.format("Mod:%s:%s", mod.id, mod.name), true) + Crashify.print_property(string.format("Mod:%s", mod.name), true) self._mod_load_index = index From 1020efe53d314d282b8b3de416e132d072bc1379 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 12 Jul 2024 13:40:34 +0200 Subject: [PATCH 221/335] Add version field to mod loader logging Ref: #168 --- crates/dtmm/assets/mod_data.lua.j2 | 1 + crates/dtmm/assets/mod_loader.lua | 11 +++++++++-- crates/dtmm/src/controller/deploy.rs | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/dtmm/assets/mod_data.lua.j2 b/crates/dtmm/assets/mod_data.lua.j2 index 9f87ad1..b5e7f17 100644 --- a/crates/dtmm/assets/mod_data.lua.j2 +++ b/crates/dtmm/assets/mod_data.lua.j2 @@ -4,6 +4,7 @@ return { id = "{{ mod.id }}", name = "{{ mod.name }}", bundled = {{ mod.bundled }}, + version = {{ mod.version }}, packages = { {% for pkg in mod.packages %} "{{ pkg }}", diff --git a/crates/dtmm/assets/mod_loader.lua b/crates/dtmm/assets/mod_loader.lua index e000292..126c0eb 100644 --- a/crates/dtmm/assets/mod_loader.lua +++ b/crates/dtmm/assets/mod_loader.lua @@ -254,8 +254,15 @@ ModLoader._build_mod_table = function(self) fassert(table.is_empty(self._mods), "Trying to add mods to non-empty mod table") for i, mod_data in ipairs(self._mod_data) do - Log.info("ModLoader", "mods[%d] = id=%q | name=%q | bundled=%s", i, mod_data.id, mod_data.name, - tostring(mod_data.bundled)) + Log.info( + "ModLoader", + "mods[%d] = id=%q | name=%q | version=%q | bundled=%s", + i, + mod_data.id, + mod_data.name, + mod_data.version, + tostring(mod_data.bundled) + ) self._mods[i] = { id = mod_data.id, diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index 957db5d..dcedd5b 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -261,6 +261,7 @@ fn build_mod_data_lua(state: Arc) -> Result { id: String, name: String, bundled: bool, + version: String, init: String, data: Option, localization: Option, @@ -288,6 +289,7 @@ fn build_mod_data_lua(state: Arc) -> Result { id: m.id.clone(), name: m.name.clone(), bundled: m.bundled, + version: m.version.clone(), init: m.resources.init.to_string_lossy().to_string(), data: m .resources From a4e78f1c6b1d83d942ce31ddce0dd1217d863157 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 12 Jul 2024 14:35:43 +0200 Subject: [PATCH 222/335] Log deployment data Closes #168 --- crates/dtmm/assets/mod_main.lua.j2 | 1 + crates/dtmm/src/controller/deploy.rs | 49 +++++++++++++++------------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/crates/dtmm/assets/mod_main.lua.j2 b/crates/dtmm/assets/mod_main.lua.j2 index 4dd2787..29caa79 100644 --- a/crates/dtmm/assets/mod_main.lua.j2 +++ b/crates/dtmm/assets/mod_main.lua.j2 @@ -12,6 +12,7 @@ local log = function(category, format, ...) end log("mod_main", "Initializing mods...") +log("mod_main", "[DTMM] Deployment data:\n{{ deployment_info }}") local require_store = {} diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index dcedd5b..e2d9c0e 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -453,7 +453,10 @@ async fn build_bundles(state: Arc) -> Result> { } #[tracing::instrument(skip_all)] -async fn patch_boot_bundle(state: Arc) -> Result> { +async fn patch_boot_bundle( + state: Arc, + deployment_info: &String, +) -> Result> { let bundle_dir = Arc::new(state.game_dir.join("bundle")); let bundle_path = bundle_dir.join(format!("{:x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes()))); @@ -508,8 +511,9 @@ async fn patch_boot_bundle(state: Arc) -> Result> { .wrap_err("Failed to get template `mod_main.lua`")?; let is_io_enabled = if state.is_io_enabled { "true" } else { "false" }; + let deployment_info = deployment_info.replace("\"", "\\\"").replace("\n", "\\n"); let lua = tmpl - .render(minijinja::context!(is_io_enabled => is_io_enabled)) + .render(minijinja::context!(is_io_enabled => is_io_enabled, deployment_info => deployment_info)) .wrap_err("Failed to render template `mod_main.lua`")?; tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); @@ -574,14 +578,10 @@ where } #[tracing::instrument(skip_all, fields(bundles = bundles.as_ref().len()))] -async fn write_deployment_data( - state: Arc, - bundles: B, - mod_folders: Vec, -) -> Result<()> -where - B: AsRef<[Bundle]>, -{ +fn build_deployment_data( + bundles: impl AsRef<[Bundle]>, + mod_folders: impl AsRef<[String]>, +) -> Result { let info = DeploymentData { timestamp: OffsetDateTime::now_utc(), bundles: bundles @@ -590,16 +590,13 @@ where .map(|bundle| format!("{:x}", bundle.name().to_murmur64())) .collect(), // TODO: - mod_folders, + mod_folders: mod_folders + .as_ref() + .iter() + .map(|folder| folder.clone()) + .collect(), }; - let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); - let data = serde_sjson::to_string(&info).wrap_err("Failed to serizalie deployment data")?; - - fs::write(&path, &data) - .await - .wrap_err_with(|| format!("Failed to write deployment data to '{}'", path.display()))?; - - Ok(()) + serde_sjson::to_string(&info).wrap_err("Failed to serizalize deployment data") } #[tracing::instrument(skip_all, fields( @@ -728,8 +725,11 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { .await .wrap_err("Failed to build mod bundles")?; + let new_deployment_info = build_deployment_data(&bundles, &mod_folders) + .wrap_err("Failed to build new deployment data")?; + tracing::info!("Patch boot bundle"); - let mut boot_bundles = patch_boot_bundle(state.clone()) + let mut boot_bundles = patch_boot_bundle(state.clone(), &new_deployment_info) .await .wrap_err("Failed to patch boot bundle")?; bundles.append(&mut boot_bundles); @@ -804,9 +804,12 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { .wrap_err("Failed to patch bundle database")?; tracing::info!("Writing deployment data"); - write_deployment_data(state.clone(), &bundles, mod_folders) - .await - .wrap_err("Failed to write deployment data")?; + { + let path = state.game_dir.join(DEPLOYMENT_DATA_PATH); + fs::write(&path, &new_deployment_info) + .await + .wrap_err_with(|| format!("Failed to write deployment data to '{}'", path.display()))?; + } tracing::info!("Finished deploying mods"); Ok(()) From a47167b7356de45e9539f720c03827df18616508 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 12 Jul 2024 15:58:39 +0200 Subject: [PATCH 223/335] Fix printing hashes with leading zeroes Closes #179 --- lib/sdk/src/murmur/types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/sdk/src/murmur/types.rs b/lib/sdk/src/murmur/types.rs index 1146494..e96b992 100644 --- a/lib/sdk/src/murmur/types.rs +++ b/lib/sdk/src/murmur/types.rs @@ -50,7 +50,7 @@ impl fmt::LowerHex for Murmur64 { impl fmt::Display for Murmur64 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) + write!(f, "{:016X}", self) } } @@ -152,7 +152,7 @@ impl fmt::UpperHex for Murmur32 { impl fmt::Display for Murmur32 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::UpperHex::fmt(&self.0, f) + write!(f, "{:08X}", self) } } From b7e26eee5754ac1272a297e5752d21f861e9e410 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 3 Mar 2023 15:32:41 +0100 Subject: [PATCH 224/335] refactor(sdk): Split BundleFileType into its own file --- lib/sdk/src/bundle/database.rs | 2 +- lib/sdk/src/bundle/file.rs | 397 +------------------------------ lib/sdk/src/bundle/filetype.rs | 400 ++++++++++++++++++++++++++++++++ lib/sdk/src/bundle/mod.rs | 4 +- lib/sdk/src/filetype/package.rs | 11 +- 5 files changed, 408 insertions(+), 406 deletions(-) create mode 100644 lib/sdk/src/bundle/filetype.rs diff --git a/lib/sdk/src/bundle/database.rs b/lib/sdk/src/bundle/database.rs index 6152ede..fce26ee 100644 --- a/lib/sdk/src/bundle/database.rs +++ b/lib/sdk/src/bundle/database.rs @@ -13,7 +13,7 @@ use crate::binary::ToBinary; use crate::murmur::Murmur64; use crate::Bundle; -use super::file::BundleFileType; +use super::filetype::BundleFileType; const DATABASE_VERSION: u32 = 0x6; const FILE_VERSION: u32 = 0x4; diff --git a/lib/sdk/src/bundle/file.rs b/lib/sdk/src/bundle/file.rs index 4d6c56e..1780a5d 100644 --- a/lib/sdk/src/bundle/file.rs +++ b/lib/sdk/src/bundle/file.rs @@ -5,407 +5,12 @@ use bitflags::bitflags; use color_eyre::eyre::Context; use color_eyre::{eyre, Result}; use futures::future::join_all; -use serde::Serialize; use crate::binary::sync::*; use crate::filetype::*; use crate::murmur::{HashGroup, IdString64, Murmur64}; -#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] -pub enum BundleFileType { - Animation, - AnimationCurves, - Apb, - BakedLighting, - Bik, - BlendSet, - Bones, - Chroma, - CommonPackage, - Config, - Crypto, - Data, - Entity, - Flow, - Font, - Ies, - Ini, - Input, - Ivf, - Keys, - Level, - Lua, - Material, - Mod, - MouseCursor, - NavData, - NetworkConfig, - OddleNet, - Package, - Particles, - PhysicsProperties, - RenderConfig, - RtPipeline, - Scene, - Shader, - ShaderLibrary, - ShaderLibraryGroup, - ShadingEnvionmentMapping, - ShadingEnvironment, - Slug, - SlugAlbum, - SoundEnvironment, - SpuJob, - StateMachine, - StaticPVS, - Strings, - SurfaceProperties, - Texture, - TimpaniBank, - TimpaniMaster, - Tome, - Ugg, - Unit, - Upb, - VectorField, - Wav, - WwiseBank, - WwiseDep, - WwiseEvent, - WwiseMetadata, - WwiseStream, - Xml, - - Unknown(Murmur64), -} - -impl BundleFileType { - pub fn ext_name(&self) -> String { - match self { - BundleFileType::AnimationCurves => String::from("animation_curves"), - BundleFileType::Animation => String::from("animation"), - BundleFileType::Apb => String::from("apb"), - BundleFileType::BakedLighting => String::from("baked_lighting"), - BundleFileType::Bik => String::from("bik"), - BundleFileType::BlendSet => String::from("blend_set"), - BundleFileType::Bones => String::from("bones"), - BundleFileType::Chroma => String::from("chroma"), - BundleFileType::CommonPackage => String::from("common_package"), - BundleFileType::Config => String::from("config"), - BundleFileType::Crypto => String::from("crypto"), - BundleFileType::Data => String::from("data"), - BundleFileType::Entity => String::from("entity"), - BundleFileType::Flow => String::from("flow"), - BundleFileType::Font => String::from("font"), - BundleFileType::Ies => String::from("ies"), - BundleFileType::Ini => String::from("ini"), - BundleFileType::Input => String::from("input"), - BundleFileType::Ivf => String::from("ivf"), - BundleFileType::Keys => String::from("keys"), - BundleFileType::Level => String::from("level"), - BundleFileType::Lua => String::from("lua"), - BundleFileType::Material => String::from("material"), - BundleFileType::Mod => String::from("mod"), - BundleFileType::MouseCursor => String::from("mouse_cursor"), - BundleFileType::NavData => String::from("nav_data"), - BundleFileType::NetworkConfig => String::from("network_config"), - BundleFileType::OddleNet => String::from("oodle_net"), - BundleFileType::Package => String::from("package"), - BundleFileType::Particles => String::from("particles"), - BundleFileType::PhysicsProperties => String::from("physics_properties"), - BundleFileType::RenderConfig => String::from("render_config"), - BundleFileType::RtPipeline => String::from("rt_pipeline"), - BundleFileType::Scene => String::from("scene"), - BundleFileType::ShaderLibraryGroup => String::from("shader_library_group"), - BundleFileType::ShaderLibrary => String::from("shader_library"), - BundleFileType::Shader => String::from("shader"), - BundleFileType::ShadingEnvionmentMapping => String::from("shading_environment_mapping"), - BundleFileType::ShadingEnvironment => String::from("shading_environment"), - BundleFileType::SlugAlbum => String::from("slug_album"), - BundleFileType::Slug => String::from("slug"), - BundleFileType::SoundEnvironment => String::from("sound_environment"), - BundleFileType::SpuJob => String::from("spu_job"), - BundleFileType::StateMachine => String::from("state_machine"), - BundleFileType::StaticPVS => String::from("static_pvs"), - BundleFileType::Strings => String::from("strings"), - BundleFileType::SurfaceProperties => String::from("surface_properties"), - BundleFileType::Texture => String::from("texture"), - BundleFileType::TimpaniBank => String::from("timpani_bank"), - BundleFileType::TimpaniMaster => String::from("timpani_master"), - BundleFileType::Tome => String::from("tome"), - BundleFileType::Ugg => String::from("ugg"), - BundleFileType::Unit => String::from("unit"), - BundleFileType::Upb => String::from("upb"), - BundleFileType::VectorField => String::from("vector_field"), - BundleFileType::Wav => String::from("wav"), - BundleFileType::WwiseBank => String::from("wwise_bank"), - BundleFileType::WwiseDep => String::from("wwise_dep"), - BundleFileType::WwiseEvent => String::from("wwise_event"), - BundleFileType::WwiseMetadata => String::from("wwise_metadata"), - BundleFileType::WwiseStream => String::from("wwise_stream"), - BundleFileType::Xml => String::from("xml"), - - BundleFileType::Unknown(s) => format!("{s:016X}"), - } - } - - pub fn decompiled_ext_name(&self) -> String { - match self { - BundleFileType::Texture => String::from("dds"), - BundleFileType::WwiseBank => String::from("bnk"), - BundleFileType::WwiseStream => String::from("ogg"), - _ => self.ext_name(), - } - } - - pub fn hash(&self) -> Murmur64 { - Murmur64::from(*self) - } -} - -impl std::str::FromStr for BundleFileType { - type Err = color_eyre::Report; - - fn from_str(s: &str) -> Result { - let val = match s { - "animation_curves" => BundleFileType::AnimationCurves, - "animation" => BundleFileType::Animation, - "apb" => BundleFileType::Apb, - "baked_lighting" => BundleFileType::BakedLighting, - "bik" => BundleFileType::Bik, - "blend_set" => BundleFileType::BlendSet, - "bones" => BundleFileType::Bones, - "chroma" => BundleFileType::Chroma, - "common_package" => BundleFileType::CommonPackage, - "config" => BundleFileType::Config, - "crypto" => BundleFileType::Crypto, - "data" => BundleFileType::Data, - "entity" => BundleFileType::Entity, - "flow" => BundleFileType::Flow, - "font" => BundleFileType::Font, - "ies" => BundleFileType::Ies, - "ini" => BundleFileType::Ini, - "input" => BundleFileType::Input, - "ivf" => BundleFileType::Ivf, - "keys" => BundleFileType::Keys, - "level" => BundleFileType::Level, - "lua" => BundleFileType::Lua, - "material" => BundleFileType::Material, - "mod" => BundleFileType::Mod, - "mouse_cursor" => BundleFileType::MouseCursor, - "nav_data" => BundleFileType::NavData, - "network_config" => BundleFileType::NetworkConfig, - "oodle_net" => BundleFileType::OddleNet, - "package" => BundleFileType::Package, - "particles" => BundleFileType::Particles, - "physics_properties" => BundleFileType::PhysicsProperties, - "render_config" => BundleFileType::RenderConfig, - "rt_pipeline" => BundleFileType::RtPipeline, - "scene" => BundleFileType::Scene, - "shader_library_group" => BundleFileType::ShaderLibraryGroup, - "shader_library" => BundleFileType::ShaderLibrary, - "shader" => BundleFileType::Shader, - "shading_environment_mapping" => BundleFileType::ShadingEnvionmentMapping, - "shading_environment" => BundleFileType::ShadingEnvironment, - "slug_album" => BundleFileType::SlugAlbum, - "slug" => BundleFileType::Slug, - "sound_environment" => BundleFileType::SoundEnvironment, - "spu_job" => BundleFileType::SpuJob, - "state_machine" => BundleFileType::StateMachine, - "static_pvs" => BundleFileType::StaticPVS, - "strings" => BundleFileType::Strings, - "surface_properties" => BundleFileType::SurfaceProperties, - "texture" => BundleFileType::Texture, - "timpani_bank" => BundleFileType::TimpaniBank, - "timpani_master" => BundleFileType::TimpaniMaster, - "tome" => BundleFileType::Tome, - "ugg" => BundleFileType::Ugg, - "unit" => BundleFileType::Unit, - "upb" => BundleFileType::Upb, - "vector_field" => BundleFileType::VectorField, - "wav" => BundleFileType::Wav, - "wwise_bank" => BundleFileType::WwiseBank, - "wwise_dep" => BundleFileType::WwiseDep, - "wwise_event" => BundleFileType::WwiseEvent, - "wwise_metadata" => BundleFileType::WwiseMetadata, - "wwise_stream" => BundleFileType::WwiseStream, - "xml" => BundleFileType::Xml, - s => eyre::bail!("Unknown type string '{}'", s), - }; - - Ok(val) - } -} - -impl Serialize for BundleFileType { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let value = self.ext_name(); - value.serialize(serializer) - } -} - -impl From for BundleFileType { - fn from(value: Murmur64) -> Self { - Self::from(Into::::into(value)) - } -} - -impl From for BundleFileType { - fn from(hash: u64) -> BundleFileType { - match hash { - 0x931e336d7646cc26 => BundleFileType::Animation, - 0xdcfb9e18fff13984 => BundleFileType::AnimationCurves, - 0x3eed05ba83af5090 => BundleFileType::Apb, - 0x7ffdb779b04e4ed1 => BundleFileType::BakedLighting, - 0xaa5965f03029fa18 => BundleFileType::Bik, - 0xe301e8af94e3b5a3 => BundleFileType::BlendSet, - 0x18dead01056b72e9 => BundleFileType::Bones, - 0xb7893adf7567506a => BundleFileType::Chroma, - 0xfe9754bd19814a47 => BundleFileType::CommonPackage, - 0x82645835e6b73232 => BundleFileType::Config, - 0x69108ded1e3e634b => BundleFileType::Crypto, - 0x8fd0d44d20650b68 => BundleFileType::Data, - 0x9831ca893b0d087d => BundleFileType::Entity, - 0x92d3ee038eeb610d => BundleFileType::Flow, - 0x9efe0a916aae7880 => BundleFileType::Font, - 0x8f7d5a2c0f967655 => BundleFileType::Ies, - 0xd526a27da14f1dc5 => BundleFileType::Ini, - 0x2bbcabe5074ade9e => BundleFileType::Input, - 0xfa4a8e091a91201e => BundleFileType::Ivf, - 0xa62f9297dc969e85 => BundleFileType::Keys, - 0x2a690fd348fe9ac5 => BundleFileType::Level, - 0xa14e8dfa2cd117e2 => BundleFileType::Lua, - 0xeac0b497876adedf => BundleFileType::Material, - 0x3fcdd69156a46417 => BundleFileType::Mod, - 0xb277b11fe4a61d37 => BundleFileType::MouseCursor, - 0x169de9566953d264 => BundleFileType::NavData, - 0x3b1fa9e8f6bac374 => BundleFileType::NetworkConfig, - 0xb0f2c12eb107f4d8 => BundleFileType::OddleNet, - 0xad9c6d9ed1e5e77a => BundleFileType::Package, - 0xa8193123526fad64 => BundleFileType::Particles, - 0xbf21403a3ab0bbb1 => BundleFileType::PhysicsProperties, - 0x27862fe24795319c => BundleFileType::RenderConfig, - 0x9ca183c2d0e76dee => BundleFileType::RtPipeline, - 0x9d0a795bfe818d19 => BundleFileType::Scene, - 0xcce8d5b5f5ae333f => BundleFileType::Shader, - 0xe5ee32a477239a93 => BundleFileType::ShaderLibrary, - 0x9e5c3cc74575aeb5 => BundleFileType::ShaderLibraryGroup, - 0x250e0a11ac8e26f8 => BundleFileType::ShadingEnvionmentMapping, - 0xfe73c7dcff8a7ca5 => BundleFileType::ShadingEnvironment, - 0xa27b4d04a9ba6f9e => BundleFileType::Slug, - 0xe9fc9ea7042e5ec0 => BundleFileType::SlugAlbum, - 0xd8b27864a97ffdd7 => BundleFileType::SoundEnvironment, - 0xf97af9983c05b950 => BundleFileType::SpuJob, - 0xa486d4045106165c => BundleFileType::StateMachine, - 0xe3f0baa17d620321 => BundleFileType::StaticPVS, - 0x0d972bab10b40fd3 => BundleFileType::Strings, - 0xad2d3fa30d9ab394 => BundleFileType::SurfaceProperties, - 0xcd4238c6a0c69e32 => BundleFileType::Texture, - 0x99736be1fff739a4 => BundleFileType::TimpaniBank, - 0x00a3e6c59a2b9c6c => BundleFileType::TimpaniMaster, - 0x19c792357c99f49b => BundleFileType::Tome, - 0x712d6e3dd1024c9c => BundleFileType::Ugg, - 0xe0a48d0be9a7453f => BundleFileType::Unit, - 0xa99510c6e86dd3c2 => BundleFileType::Upb, - 0xf7505933166d6755 => BundleFileType::VectorField, - 0x786f65c00a816b19 => BundleFileType::Wav, - 0x535a7bd3e650d799 => BundleFileType::WwiseBank, - 0xaf32095c82f2b070 => BundleFileType::WwiseDep, - 0xaabdd317b58dfc8a => BundleFileType::WwiseEvent, - 0xd50a8b7e1c82b110 => BundleFileType::WwiseMetadata, - 0x504b55235d21440e => BundleFileType::WwiseStream, - 0x76015845a6003765 => BundleFileType::Xml, - - _ => BundleFileType::Unknown(Murmur64::from(hash)), - } - } -} - -impl From for u64 { - fn from(t: BundleFileType) -> u64 { - match t { - BundleFileType::Animation => 0x931e336d7646cc26, - BundleFileType::AnimationCurves => 0xdcfb9e18fff13984, - BundleFileType::Apb => 0x3eed05ba83af5090, - BundleFileType::BakedLighting => 0x7ffdb779b04e4ed1, - BundleFileType::Bik => 0xaa5965f03029fa18, - BundleFileType::BlendSet => 0xe301e8af94e3b5a3, - BundleFileType::Bones => 0x18dead01056b72e9, - BundleFileType::Chroma => 0xb7893adf7567506a, - BundleFileType::CommonPackage => 0xfe9754bd19814a47, - BundleFileType::Config => 0x82645835e6b73232, - BundleFileType::Crypto => 0x69108ded1e3e634b, - BundleFileType::Data => 0x8fd0d44d20650b68, - BundleFileType::Entity => 0x9831ca893b0d087d, - BundleFileType::Flow => 0x92d3ee038eeb610d, - BundleFileType::Font => 0x9efe0a916aae7880, - BundleFileType::Ies => 0x8f7d5a2c0f967655, - BundleFileType::Ini => 0xd526a27da14f1dc5, - BundleFileType::Input => 0x2bbcabe5074ade9e, - BundleFileType::Ivf => 0xfa4a8e091a91201e, - BundleFileType::Keys => 0xa62f9297dc969e85, - BundleFileType::Level => 0x2a690fd348fe9ac5, - BundleFileType::Lua => 0xa14e8dfa2cd117e2, - BundleFileType::Material => 0xeac0b497876adedf, - BundleFileType::Mod => 0x3fcdd69156a46417, - BundleFileType::MouseCursor => 0xb277b11fe4a61d37, - BundleFileType::NavData => 0x169de9566953d264, - BundleFileType::NetworkConfig => 0x3b1fa9e8f6bac374, - BundleFileType::OddleNet => 0xb0f2c12eb107f4d8, - BundleFileType::Package => 0xad9c6d9ed1e5e77a, - BundleFileType::Particles => 0xa8193123526fad64, - BundleFileType::PhysicsProperties => 0xbf21403a3ab0bbb1, - BundleFileType::RenderConfig => 0x27862fe24795319c, - BundleFileType::RtPipeline => 0x9ca183c2d0e76dee, - BundleFileType::Scene => 0x9d0a795bfe818d19, - BundleFileType::Shader => 0xcce8d5b5f5ae333f, - BundleFileType::ShaderLibrary => 0xe5ee32a477239a93, - BundleFileType::ShaderLibraryGroup => 0x9e5c3cc74575aeb5, - BundleFileType::ShadingEnvionmentMapping => 0x250e0a11ac8e26f8, - BundleFileType::ShadingEnvironment => 0xfe73c7dcff8a7ca5, - BundleFileType::Slug => 0xa27b4d04a9ba6f9e, - BundleFileType::SlugAlbum => 0xe9fc9ea7042e5ec0, - BundleFileType::SoundEnvironment => 0xd8b27864a97ffdd7, - BundleFileType::SpuJob => 0xf97af9983c05b950, - BundleFileType::StateMachine => 0xa486d4045106165c, - BundleFileType::StaticPVS => 0xe3f0baa17d620321, - BundleFileType::Strings => 0x0d972bab10b40fd3, - BundleFileType::SurfaceProperties => 0xad2d3fa30d9ab394, - BundleFileType::Texture => 0xcd4238c6a0c69e32, - BundleFileType::TimpaniBank => 0x99736be1fff739a4, - BundleFileType::TimpaniMaster => 0x00a3e6c59a2b9c6c, - BundleFileType::Tome => 0x19c792357c99f49b, - BundleFileType::Ugg => 0x712d6e3dd1024c9c, - BundleFileType::Unit => 0xe0a48d0be9a7453f, - BundleFileType::Upb => 0xa99510c6e86dd3c2, - BundleFileType::VectorField => 0xf7505933166d6755, - BundleFileType::Wav => 0x786f65c00a816b19, - BundleFileType::WwiseBank => 0x535a7bd3e650d799, - BundleFileType::WwiseDep => 0xaf32095c82f2b070, - BundleFileType::WwiseEvent => 0xaabdd317b58dfc8a, - BundleFileType::WwiseMetadata => 0xd50a8b7e1c82b110, - BundleFileType::WwiseStream => 0x504b55235d21440e, - BundleFileType::Xml => 0x76015845a6003765, - - BundleFileType::Unknown(hash) => hash.into(), - } - } -} -impl From for Murmur64 { - fn from(t: BundleFileType) -> Murmur64 { - let hash: u64 = t.into(); - Murmur64::from(hash) - } -} - -impl std::fmt::Display for BundleFileType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.ext_name()) - } -} +use super::filetype::BundleFileType; #[derive(Debug)] struct BundleFileHeader { diff --git a/lib/sdk/src/bundle/filetype.rs b/lib/sdk/src/bundle/filetype.rs new file mode 100644 index 0000000..0b4f292 --- /dev/null +++ b/lib/sdk/src/bundle/filetype.rs @@ -0,0 +1,400 @@ +use color_eyre::{eyre, Result}; +use serde::Serialize; + +use crate::murmur::Murmur64; + +#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] +pub enum BundleFileType { + Animation, + AnimationCurves, + Apb, + BakedLighting, + Bik, + BlendSet, + Bones, + Chroma, + CommonPackage, + Config, + Crypto, + Data, + Entity, + Flow, + Font, + Ies, + Ini, + Input, + Ivf, + Keys, + Level, + Lua, + Material, + Mod, + MouseCursor, + NavData, + NetworkConfig, + OddleNet, + Package, + Particles, + PhysicsProperties, + RenderConfig, + RtPipeline, + Scene, + Shader, + ShaderLibrary, + ShaderLibraryGroup, + ShadingEnvionmentMapping, + ShadingEnvironment, + Slug, + SlugAlbum, + SoundEnvironment, + SpuJob, + StateMachine, + StaticPVS, + Strings, + SurfaceProperties, + Texture, + TimpaniBank, + TimpaniMaster, + Tome, + Ugg, + Unit, + Upb, + VectorField, + Wav, + WwiseBank, + WwiseDep, + WwiseEvent, + WwiseMetadata, + WwiseStream, + Xml, + + Unknown(Murmur64), +} + +impl BundleFileType { + pub fn ext_name(&self) -> String { + match self { + BundleFileType::AnimationCurves => String::from("animation_curves"), + BundleFileType::Animation => String::from("animation"), + BundleFileType::Apb => String::from("apb"), + BundleFileType::BakedLighting => String::from("baked_lighting"), + BundleFileType::Bik => String::from("bik"), + BundleFileType::BlendSet => String::from("blend_set"), + BundleFileType::Bones => String::from("bones"), + BundleFileType::Chroma => String::from("chroma"), + BundleFileType::CommonPackage => String::from("common_package"), + BundleFileType::Config => String::from("config"), + BundleFileType::Crypto => String::from("crypto"), + BundleFileType::Data => String::from("data"), + BundleFileType::Entity => String::from("entity"), + BundleFileType::Flow => String::from("flow"), + BundleFileType::Font => String::from("font"), + BundleFileType::Ies => String::from("ies"), + BundleFileType::Ini => String::from("ini"), + BundleFileType::Input => String::from("input"), + BundleFileType::Ivf => String::from("ivf"), + BundleFileType::Keys => String::from("keys"), + BundleFileType::Level => String::from("level"), + BundleFileType::Lua => String::from("lua"), + BundleFileType::Material => String::from("material"), + BundleFileType::Mod => String::from("mod"), + BundleFileType::MouseCursor => String::from("mouse_cursor"), + BundleFileType::NavData => String::from("nav_data"), + BundleFileType::NetworkConfig => String::from("network_config"), + BundleFileType::OddleNet => String::from("oodle_net"), + BundleFileType::Package => String::from("package"), + BundleFileType::Particles => String::from("particles"), + BundleFileType::PhysicsProperties => String::from("physics_properties"), + BundleFileType::RenderConfig => String::from("render_config"), + BundleFileType::RtPipeline => String::from("rt_pipeline"), + BundleFileType::Scene => String::from("scene"), + BundleFileType::ShaderLibraryGroup => String::from("shader_library_group"), + BundleFileType::ShaderLibrary => String::from("shader_library"), + BundleFileType::Shader => String::from("shader"), + BundleFileType::ShadingEnvionmentMapping => String::from("shading_environment_mapping"), + BundleFileType::ShadingEnvironment => String::from("shading_environment"), + BundleFileType::SlugAlbum => String::from("slug_album"), + BundleFileType::Slug => String::from("slug"), + BundleFileType::SoundEnvironment => String::from("sound_environment"), + BundleFileType::SpuJob => String::from("spu_job"), + BundleFileType::StateMachine => String::from("state_machine"), + BundleFileType::StaticPVS => String::from("static_pvs"), + BundleFileType::Strings => String::from("strings"), + BundleFileType::SurfaceProperties => String::from("surface_properties"), + BundleFileType::Texture => String::from("texture"), + BundleFileType::TimpaniBank => String::from("timpani_bank"), + BundleFileType::TimpaniMaster => String::from("timpani_master"), + BundleFileType::Tome => String::from("tome"), + BundleFileType::Ugg => String::from("ugg"), + BundleFileType::Unit => String::from("unit"), + BundleFileType::Upb => String::from("upb"), + BundleFileType::VectorField => String::from("vector_field"), + BundleFileType::Wav => String::from("wav"), + BundleFileType::WwiseBank => String::from("wwise_bank"), + BundleFileType::WwiseDep => String::from("wwise_dep"), + BundleFileType::WwiseEvent => String::from("wwise_event"), + BundleFileType::WwiseMetadata => String::from("wwise_metadata"), + BundleFileType::WwiseStream => String::from("wwise_stream"), + BundleFileType::Xml => String::from("xml"), + + BundleFileType::Unknown(s) => format!("{s:016X}"), + } + } + + pub fn decompiled_ext_name(&self) -> String { + match self { + BundleFileType::Texture => String::from("dds"), + BundleFileType::WwiseBank => String::from("bnk"), + BundleFileType::WwiseStream => String::from("ogg"), + _ => self.ext_name(), + } + } + + pub fn hash(&self) -> Murmur64 { + Murmur64::from(*self) + } +} + +impl std::str::FromStr for BundleFileType { + type Err = color_eyre::Report; + + fn from_str(s: &str) -> Result { + let val = match s { + "animation_curves" => BundleFileType::AnimationCurves, + "animation" => BundleFileType::Animation, + "apb" => BundleFileType::Apb, + "baked_lighting" => BundleFileType::BakedLighting, + "bik" => BundleFileType::Bik, + "blend_set" => BundleFileType::BlendSet, + "bones" => BundleFileType::Bones, + "chroma" => BundleFileType::Chroma, + "common_package" => BundleFileType::CommonPackage, + "config" => BundleFileType::Config, + "crypto" => BundleFileType::Crypto, + "data" => BundleFileType::Data, + "entity" => BundleFileType::Entity, + "flow" => BundleFileType::Flow, + "font" => BundleFileType::Font, + "ies" => BundleFileType::Ies, + "ini" => BundleFileType::Ini, + "input" => BundleFileType::Input, + "ivf" => BundleFileType::Ivf, + "keys" => BundleFileType::Keys, + "level" => BundleFileType::Level, + "lua" => BundleFileType::Lua, + "material" => BundleFileType::Material, + "mod" => BundleFileType::Mod, + "mouse_cursor" => BundleFileType::MouseCursor, + "nav_data" => BundleFileType::NavData, + "network_config" => BundleFileType::NetworkConfig, + "oodle_net" => BundleFileType::OddleNet, + "package" => BundleFileType::Package, + "particles" => BundleFileType::Particles, + "physics_properties" => BundleFileType::PhysicsProperties, + "render_config" => BundleFileType::RenderConfig, + "rt_pipeline" => BundleFileType::RtPipeline, + "scene" => BundleFileType::Scene, + "shader_library_group" => BundleFileType::ShaderLibraryGroup, + "shader_library" => BundleFileType::ShaderLibrary, + "shader" => BundleFileType::Shader, + "shading_environment_mapping" => BundleFileType::ShadingEnvionmentMapping, + "shading_environment" => BundleFileType::ShadingEnvironment, + "slug_album" => BundleFileType::SlugAlbum, + "slug" => BundleFileType::Slug, + "sound_environment" => BundleFileType::SoundEnvironment, + "spu_job" => BundleFileType::SpuJob, + "state_machine" => BundleFileType::StateMachine, + "static_pvs" => BundleFileType::StaticPVS, + "strings" => BundleFileType::Strings, + "surface_properties" => BundleFileType::SurfaceProperties, + "texture" => BundleFileType::Texture, + "timpani_bank" => BundleFileType::TimpaniBank, + "timpani_master" => BundleFileType::TimpaniMaster, + "tome" => BundleFileType::Tome, + "ugg" => BundleFileType::Ugg, + "unit" => BundleFileType::Unit, + "upb" => BundleFileType::Upb, + "vector_field" => BundleFileType::VectorField, + "wav" => BundleFileType::Wav, + "wwise_bank" => BundleFileType::WwiseBank, + "wwise_dep" => BundleFileType::WwiseDep, + "wwise_event" => BundleFileType::WwiseEvent, + "wwise_metadata" => BundleFileType::WwiseMetadata, + "wwise_stream" => BundleFileType::WwiseStream, + "xml" => BundleFileType::Xml, + s => eyre::bail!("Unknown type string '{}'", s), + }; + + Ok(val) + } +} + +impl Serialize for BundleFileType { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let value = self.ext_name(); + value.serialize(serializer) + } +} + +impl From for BundleFileType { + fn from(value: Murmur64) -> Self { + Self::from(Into::::into(value)) + } +} + +impl From for BundleFileType { + fn from(hash: u64) -> BundleFileType { + match hash { + 0x931e336d7646cc26 => BundleFileType::Animation, + 0xdcfb9e18fff13984 => BundleFileType::AnimationCurves, + 0x3eed05ba83af5090 => BundleFileType::Apb, + 0x7ffdb779b04e4ed1 => BundleFileType::BakedLighting, + 0xaa5965f03029fa18 => BundleFileType::Bik, + 0xe301e8af94e3b5a3 => BundleFileType::BlendSet, + 0x18dead01056b72e9 => BundleFileType::Bones, + 0xb7893adf7567506a => BundleFileType::Chroma, + 0xfe9754bd19814a47 => BundleFileType::CommonPackage, + 0x82645835e6b73232 => BundleFileType::Config, + 0x69108ded1e3e634b => BundleFileType::Crypto, + 0x8fd0d44d20650b68 => BundleFileType::Data, + 0x9831ca893b0d087d => BundleFileType::Entity, + 0x92d3ee038eeb610d => BundleFileType::Flow, + 0x9efe0a916aae7880 => BundleFileType::Font, + 0x8f7d5a2c0f967655 => BundleFileType::Ies, + 0xd526a27da14f1dc5 => BundleFileType::Ini, + 0x2bbcabe5074ade9e => BundleFileType::Input, + 0xfa4a8e091a91201e => BundleFileType::Ivf, + 0xa62f9297dc969e85 => BundleFileType::Keys, + 0x2a690fd348fe9ac5 => BundleFileType::Level, + 0xa14e8dfa2cd117e2 => BundleFileType::Lua, + 0xeac0b497876adedf => BundleFileType::Material, + 0x3fcdd69156a46417 => BundleFileType::Mod, + 0xb277b11fe4a61d37 => BundleFileType::MouseCursor, + 0x169de9566953d264 => BundleFileType::NavData, + 0x3b1fa9e8f6bac374 => BundleFileType::NetworkConfig, + 0xb0f2c12eb107f4d8 => BundleFileType::OddleNet, + 0xad9c6d9ed1e5e77a => BundleFileType::Package, + 0xa8193123526fad64 => BundleFileType::Particles, + 0xbf21403a3ab0bbb1 => BundleFileType::PhysicsProperties, + 0x27862fe24795319c => BundleFileType::RenderConfig, + 0x9ca183c2d0e76dee => BundleFileType::RtPipeline, + 0x9d0a795bfe818d19 => BundleFileType::Scene, + 0xcce8d5b5f5ae333f => BundleFileType::Shader, + 0xe5ee32a477239a93 => BundleFileType::ShaderLibrary, + 0x9e5c3cc74575aeb5 => BundleFileType::ShaderLibraryGroup, + 0x250e0a11ac8e26f8 => BundleFileType::ShadingEnvionmentMapping, + 0xfe73c7dcff8a7ca5 => BundleFileType::ShadingEnvironment, + 0xa27b4d04a9ba6f9e => BundleFileType::Slug, + 0xe9fc9ea7042e5ec0 => BundleFileType::SlugAlbum, + 0xd8b27864a97ffdd7 => BundleFileType::SoundEnvironment, + 0xf97af9983c05b950 => BundleFileType::SpuJob, + 0xa486d4045106165c => BundleFileType::StateMachine, + 0xe3f0baa17d620321 => BundleFileType::StaticPVS, + 0x0d972bab10b40fd3 => BundleFileType::Strings, + 0xad2d3fa30d9ab394 => BundleFileType::SurfaceProperties, + 0xcd4238c6a0c69e32 => BundleFileType::Texture, + 0x99736be1fff739a4 => BundleFileType::TimpaniBank, + 0x00a3e6c59a2b9c6c => BundleFileType::TimpaniMaster, + 0x19c792357c99f49b => BundleFileType::Tome, + 0x712d6e3dd1024c9c => BundleFileType::Ugg, + 0xe0a48d0be9a7453f => BundleFileType::Unit, + 0xa99510c6e86dd3c2 => BundleFileType::Upb, + 0xf7505933166d6755 => BundleFileType::VectorField, + 0x786f65c00a816b19 => BundleFileType::Wav, + 0x535a7bd3e650d799 => BundleFileType::WwiseBank, + 0xaf32095c82f2b070 => BundleFileType::WwiseDep, + 0xaabdd317b58dfc8a => BundleFileType::WwiseEvent, + 0xd50a8b7e1c82b110 => BundleFileType::WwiseMetadata, + 0x504b55235d21440e => BundleFileType::WwiseStream, + 0x76015845a6003765 => BundleFileType::Xml, + + _ => BundleFileType::Unknown(Murmur64::from(hash)), + } + } +} + +impl From for u64 { + fn from(t: BundleFileType) -> u64 { + match t { + BundleFileType::Animation => 0x931e336d7646cc26, + BundleFileType::AnimationCurves => 0xdcfb9e18fff13984, + BundleFileType::Apb => 0x3eed05ba83af5090, + BundleFileType::BakedLighting => 0x7ffdb779b04e4ed1, + BundleFileType::Bik => 0xaa5965f03029fa18, + BundleFileType::BlendSet => 0xe301e8af94e3b5a3, + BundleFileType::Bones => 0x18dead01056b72e9, + BundleFileType::Chroma => 0xb7893adf7567506a, + BundleFileType::CommonPackage => 0xfe9754bd19814a47, + BundleFileType::Config => 0x82645835e6b73232, + BundleFileType::Crypto => 0x69108ded1e3e634b, + BundleFileType::Data => 0x8fd0d44d20650b68, + BundleFileType::Entity => 0x9831ca893b0d087d, + BundleFileType::Flow => 0x92d3ee038eeb610d, + BundleFileType::Font => 0x9efe0a916aae7880, + BundleFileType::Ies => 0x8f7d5a2c0f967655, + BundleFileType::Ini => 0xd526a27da14f1dc5, + BundleFileType::Input => 0x2bbcabe5074ade9e, + BundleFileType::Ivf => 0xfa4a8e091a91201e, + BundleFileType::Keys => 0xa62f9297dc969e85, + BundleFileType::Level => 0x2a690fd348fe9ac5, + BundleFileType::Lua => 0xa14e8dfa2cd117e2, + BundleFileType::Material => 0xeac0b497876adedf, + BundleFileType::Mod => 0x3fcdd69156a46417, + BundleFileType::MouseCursor => 0xb277b11fe4a61d37, + BundleFileType::NavData => 0x169de9566953d264, + BundleFileType::NetworkConfig => 0x3b1fa9e8f6bac374, + BundleFileType::OddleNet => 0xb0f2c12eb107f4d8, + BundleFileType::Package => 0xad9c6d9ed1e5e77a, + BundleFileType::Particles => 0xa8193123526fad64, + BundleFileType::PhysicsProperties => 0xbf21403a3ab0bbb1, + BundleFileType::RenderConfig => 0x27862fe24795319c, + BundleFileType::RtPipeline => 0x9ca183c2d0e76dee, + BundleFileType::Scene => 0x9d0a795bfe818d19, + BundleFileType::Shader => 0xcce8d5b5f5ae333f, + BundleFileType::ShaderLibrary => 0xe5ee32a477239a93, + BundleFileType::ShaderLibraryGroup => 0x9e5c3cc74575aeb5, + BundleFileType::ShadingEnvionmentMapping => 0x250e0a11ac8e26f8, + BundleFileType::ShadingEnvironment => 0xfe73c7dcff8a7ca5, + BundleFileType::Slug => 0xa27b4d04a9ba6f9e, + BundleFileType::SlugAlbum => 0xe9fc9ea7042e5ec0, + BundleFileType::SoundEnvironment => 0xd8b27864a97ffdd7, + BundleFileType::SpuJob => 0xf97af9983c05b950, + BundleFileType::StateMachine => 0xa486d4045106165c, + BundleFileType::StaticPVS => 0xe3f0baa17d620321, + BundleFileType::Strings => 0x0d972bab10b40fd3, + BundleFileType::SurfaceProperties => 0xad2d3fa30d9ab394, + BundleFileType::Texture => 0xcd4238c6a0c69e32, + BundleFileType::TimpaniBank => 0x99736be1fff739a4, + BundleFileType::TimpaniMaster => 0x00a3e6c59a2b9c6c, + BundleFileType::Tome => 0x19c792357c99f49b, + BundleFileType::Ugg => 0x712d6e3dd1024c9c, + BundleFileType::Unit => 0xe0a48d0be9a7453f, + BundleFileType::Upb => 0xa99510c6e86dd3c2, + BundleFileType::VectorField => 0xf7505933166d6755, + BundleFileType::Wav => 0x786f65c00a816b19, + BundleFileType::WwiseBank => 0x535a7bd3e650d799, + BundleFileType::WwiseDep => 0xaf32095c82f2b070, + BundleFileType::WwiseEvent => 0xaabdd317b58dfc8a, + BundleFileType::WwiseMetadata => 0xd50a8b7e1c82b110, + BundleFileType::WwiseStream => 0x504b55235d21440e, + BundleFileType::Xml => 0x76015845a6003765, + + BundleFileType::Unknown(hash) => hash.into(), + } + } +} +impl From for Murmur64 { + fn from(t: BundleFileType) -> Murmur64 { + let hash: u64 = t.into(); + Murmur64::from(hash) + } +} + +impl std::fmt::Display for BundleFileType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.ext_name()) + } +} diff --git a/lib/sdk/src/bundle/mod.rs b/lib/sdk/src/bundle/mod.rs index 813df06..ca36393 100644 --- a/lib/sdk/src/bundle/mod.rs +++ b/lib/sdk/src/bundle/mod.rs @@ -12,8 +12,10 @@ use crate::murmur::{HashGroup, IdString64, Murmur64}; pub(crate) mod database; pub(crate) mod file; +pub(crate) mod filetype; -pub use file::{BundleFile, BundleFileType, BundleFileVariant}; +pub use file::{BundleFile, BundleFileVariant}; +pub use filetype::BundleFileType; #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] enum BundleFormat { diff --git a/lib/sdk/src/filetype/package.rs b/lib/sdk/src/filetype/package.rs index 1f50f81..a36719e 100644 --- a/lib/sdk/src/filetype/package.rs +++ b/lib/sdk/src/filetype/package.rs @@ -11,7 +11,8 @@ use path_slash::PathBufExt; use tokio::fs; use crate::binary::sync::{ReadExt, WriteExt}; -use crate::bundle::file::{BundleFileType, UserFile}; +use crate::bundle::file::UserFile; +use crate::bundle::filetype::BundleFileType; use crate::murmur::{HashGroup, Murmur64}; #[tracing::instrument] @@ -280,17 +281,11 @@ where Ok(vec![UserFile::new(s.into_bytes())]) } -// #[tracing::instrument(skip_all)] -// pub fn compile(_ctx: &crate::Context, data: String) -> Result> { -// let pkg = Package::from_sjson(data)?; -// pkg.to_binary() -// } - #[cfg(test)] mod test { use std::path::PathBuf; - use crate::BundleFileType; + use crate::bundle::filetype::BundleFileType; use super::resolve_wildcard; use super::Package; From 95fc6c160b2176770ab54c4380f208bb664b76f4 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 4 Oct 2023 09:47:25 +0200 Subject: [PATCH 225/335] dtmt: Implement name overrides For most of the game files, we don't know the actual name, only the hash of that name. To still allow building bundles that contain files with that name (e.g. to override a game file with a custom one), there needs to be a way to tell DTMT to name a file such that its hash is the same as the one in the game. The initial idea was to just expect the file name on disk to be the hash, but that wouldn't allow for arbitrary folder structures anymore. So instead, there is now a new, optional setting in `dtmt.cfg`, where the modder can map a file path to an override name. --- crates/dtmm/src/controller/deploy.rs | 8 ++-- crates/dtmm/src/controller/import.rs | 1 + crates/dtmt/src/cmd/build.rs | 68 ++++++++++++++-------------- crates/dtmt/src/cmd/migrate.rs | 1 + crates/dtmt/src/cmd/watch.rs | 17 +++---- lib/dtmt-shared/src/lib.rs | 3 ++ lib/sdk/src/bundle/file.rs | 26 ++++------- lib/sdk/src/filetype/lua.rs | 13 ++---- lib/sdk/src/filetype/package.rs | 48 +++++++++++--------- lib/sdk/src/murmur/types.rs | 28 ++++++++++-- 10 files changed, 116 insertions(+), 97 deletions(-) diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index e2d9c0e..02b6860 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -324,11 +324,11 @@ async fn build_bundles(state: Arc) -> Result> { let mut bundles = Vec::new(); - let mut add_lua_asset = |name, data: &str| { + let mut add_lua_asset = |name: &str, data: &str| { let span = tracing::info_span!("Compiling Lua", name, data_len = data.len()); let _enter = span.enter(); - let file = lua::compile(name, data).wrap_err("Failed to compile Lua")?; + let file = lua::compile(name.to_string(), data).wrap_err("Failed to compile Lua")?; mod_bundle.add_file(file); @@ -517,8 +517,8 @@ async fn patch_boot_bundle( .wrap_err("Failed to render template `mod_main.lua`")?; tracing::trace!("Main script rendered:\n===========\n{}\n=============", lua); - let file = - lua::compile(MOD_BOOT_SCRIPT, lua).wrap_err("Failed to compile mod main Lua file")?; + let file = lua::compile(MOD_BOOT_SCRIPT.to_string(), lua) + .wrap_err("Failed to compile mod main Lua file")?; boot_bundle.add_file(file); } diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 8e47d23..2f5f90b 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -297,6 +297,7 @@ fn extract_mod_config(archive: &mut ZipArchive) -> Result<(Mo packages: Vec::new(), resources, depends: Vec::new(), + name_overrides: Default::default(), }; Ok((cfg, root)) diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index 77ab629..fab072b 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -103,38 +103,41 @@ async fn find_project_config(dir: Option) -> Result { } #[tracing::instrument(skip_all)] -async fn compile_package_files

(pkg: &Package, root: P) -> Result> -where - P: AsRef + std::fmt::Debug, -{ - let root = Arc::new(root.as_ref()); +async fn compile_package_files(pkg: &Package, cfg: &ModConfig) -> Result> { + let root = Arc::new(&cfg.dir); + let name_overrides = &cfg.name_overrides; let tasks = pkg .iter() - .flat_map(|(file_type, paths)| { - paths.iter().map(|path| { + .flat_map(|(file_type, names)| { + names.iter().map(|name| { ( *file_type, - path, + name, // Cloning the `Arc` here solves the issue that in the next `.map`, I need to // `move` the closure parameters, but can't `move` `root` before it was cloned. root.clone(), ) }) }) - .map(|(file_type, path, root)| async move { - let sjson = fs::read_to_string(&path).await?; + .map(|(file_type, name, root)| async move { + let path = PathBuf::from(name); + let sjson = fs::read_to_string(&path) + .await + .wrap_err_with(|| format!("Failed to read file '{}'", path.display()))?; - let mut path = path.clone(); - path.set_extension(""); - - BundleFile::from_sjson( - path.to_slash_lossy().to_string(), - file_type, - sjson, - root.as_ref(), - ) - .await + let name = path.with_extension("").to_slash_lossy().to_string(); + let name = if let Some(new_name) = name_overrides.get(&name) { + let new_name = match u64::from_str_radix(new_name, 16) { + Ok(hash) => IdString64::from(hash), + Err(_) => IdString64::from(new_name.clone()), + }; + tracing::info!("Overriding '{}' -> '{}'", name, new_name.display()); + new_name + } else { + IdString64::from(name.clone()) + }; + BundleFile::from_sjson(name, file_type, sjson, root.as_ref()).await }); let results = futures::stream::iter(tasks) @@ -146,12 +149,11 @@ where } #[tracing::instrument] -async fn build_package(package: P1, root: P2) -> Result -where - P1: AsRef + std::fmt::Debug, - P2: AsRef + std::fmt::Debug, -{ - let root = root.as_ref(); +async fn build_package( + cfg: &ModConfig, + package: impl AsRef + std::fmt::Debug, +) -> Result { + let root = &cfg.dir; let package = package.as_ref(); let mut path = root.join(package); @@ -165,7 +167,7 @@ where .await .wrap_err_with(|| format!("Invalid package file {}", &pkg_name))?; - let files = compile_package_files(&pkg, root).await?; + let files = compile_package_files(&pkg, cfg).await?; let mut bundle = Bundle::new(pkg_name); for file in files { bundle.add_file(file); @@ -254,14 +256,14 @@ pub(crate) async fn read_project_config(dir: Option) -> Result( +#[tracing::instrument] +pub(crate) async fn build

( cfg: &ModConfig, - out_path: P1, - game_dir: Arc>, + out_path: impl AsRef + std::fmt::Debug, + game_dir: Arc>, ) -> Result<()> where - P1: AsRef, - P2: AsRef, + P: AsRef + std::fmt::Debug, { let out_path = out_path.as_ref(); @@ -286,7 +288,7 @@ where ); } - let bundle = build_package(path, &cfg.dir).await.wrap_err_with(|| { + let bundle = build_package(&cfg, path).await.wrap_err_with(|| { format!( "Failed to build package '{}' at '{}'", path.display(), diff --git a/crates/dtmt/src/cmd/migrate.rs b/crates/dtmt/src/cmd/migrate.rs index 2eacded..d7bfa19 100644 --- a/crates/dtmt/src/cmd/migrate.rs +++ b/crates/dtmt/src/cmd/migrate.rs @@ -351,6 +351,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> }, depends: vec![ModDependency::ID(String::from("DMF"))], bundled: true, + name_overrides: HashMap::new(), }; tracing::debug!(?dtmt_cfg); diff --git a/crates/dtmt/src/cmd/watch.rs b/crates/dtmt/src/cmd/watch.rs index 79fab67..2abd0f7 100644 --- a/crates/dtmt/src/cmd/watch.rs +++ b/crates/dtmt/src/cmd/watch.rs @@ -77,17 +77,14 @@ pub(crate) fn command_definition() -> Command { ) } -async fn compile( +#[tracing::instrument] +async fn compile( cfg: &ModConfig, - out_path: P1, - archive_path: P2, - game_dir: Arc>, -) -> Result<()> -where - P1: AsRef + std::marker::Copy, - P2: AsRef, - P3: AsRef, -{ + out_path: impl AsRef + std::fmt::Debug, + archive_path: impl AsRef + std::fmt::Debug, + game_dir: Arc + std::fmt::Debug>>, +) -> Result<()> { + let out_path = out_path.as_ref(); build(cfg, out_path, game_dir) .await .wrap_err("Failed to build bundles")?; diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index 3c9530e..d1f69c7 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::path::PathBuf; use color_eyre::eyre::{OptionExt as _, WrapErr as _}; @@ -67,6 +68,8 @@ pub struct ModConfig { pub depends: Vec, #[serde(default = "default_true", skip_serializing_if = "is_true")] pub bundled: bool, + #[serde(default)] + pub name_overrides: HashMap, } pub const STEAMAPP_ID: u32 = 1361210; diff --git a/lib/sdk/src/bundle/file.rs b/lib/sdk/src/bundle/file.rs index 1780a5d..f387409 100644 --- a/lib/sdk/src/bundle/file.rs +++ b/lib/sdk/src/bundle/file.rs @@ -120,7 +120,7 @@ pub struct BundleFile { } impl BundleFile { - pub fn new(name: String, file_type: BundleFileType) -> Self { + pub fn new(name: impl Into, file_type: BundleFileType) -> Self { Self { file_type, name: name.into(), @@ -252,20 +252,15 @@ impl BundleFile { Ok(w.into_inner()) } - #[tracing::instrument(name = "File::from_sjson", skip(sjson))] - pub async fn from_sjson( - name: String, + #[tracing::instrument("File::from_sjson", skip(sjson, name), fields(name = %name.display()))] + pub async fn from_sjson( + name: IdString64, file_type: BundleFileType, - sjson: S, - root: P, - ) -> Result - where - P: AsRef + std::fmt::Debug, - S: AsRef, - { + sjson: impl AsRef, + root: impl AsRef + std::fmt::Debug, + ) -> Result { match file_type { - BundleFileType::Lua => lua::compile(name.clone(), sjson) - .wrap_err_with(|| format!("Failed to compile Lua file '{}'", name)), + BundleFileType::Lua => lua::compile(name, sjson).wrap_err("Failed to compile Lua file"), BundleFileType::Unknown(_) => { eyre::bail!("Unknown file type. Cannot compile from SJSON"); } @@ -304,10 +299,7 @@ impl BundleFile { s } - pub fn matches_name(&self, name: S) -> bool - where - S: Into, - { + pub fn matches_name(&self, name: impl Into) -> bool { let name = name.into(); if self.name == name { return true; diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index bfe6de7..14f0d6b 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -15,6 +15,7 @@ use tokio::fs; use crate::binary::sync::ReadExt; use crate::binary::sync::WriteExt; use crate::bundle::file::{BundleFileVariant, UserFile}; +use crate::murmur::IdString64; use crate::{BundleFile, BundleFileType}; const BITSQUID_LUAJIT_HEADER: u32 = 0x8253461B; @@ -117,17 +118,13 @@ where } #[tracing::instrument(skip_all)] -pub fn compile(name: S, code: C) -> Result -where - S: Into, - C: AsRef, -{ +pub fn compile(name: impl Into, code: impl AsRef) -> Result { let name = name.into(); let code = code.as_ref(); tracing::trace!( "Compiling '{}', {} bytes of code", - name, + name.display(), code.as_bytes().len() ); @@ -135,8 +132,8 @@ where let state = lua::luaL_newstate(); lua::luaL_openlibs(state); - let name = CString::new(format!("@{name}").into_bytes()) - .wrap_err_with(|| format!("Cannot convert name into CString: {}", name))?; + let name = CString::new(format!("@{}", name.display()).into_bytes()) + .wrap_err_with(|| format!("Cannot convert name into CString: {}", name.display()))?; match lua::luaL_loadbuffer( state, code.as_ptr() as _, diff --git a/lib/sdk/src/filetype/package.rs b/lib/sdk/src/filetype/package.rs index a36719e..6394e42 100644 --- a/lib/sdk/src/filetype/package.rs +++ b/lib/sdk/src/filetype/package.rs @@ -7,13 +7,12 @@ use std::str::FromStr; use async_recursion::async_recursion; use color_eyre::eyre::{self, Context}; use color_eyre::Result; -use path_slash::PathBufExt; use tokio::fs; use crate::binary::sync::{ReadExt, WriteExt}; use crate::bundle::file::UserFile; use crate::bundle::filetype::BundleFileType; -use crate::murmur::{HashGroup, Murmur64}; +use crate::murmur::{HashGroup, IdString64, Murmur64}; #[tracing::instrument] #[async_recursion] @@ -91,12 +90,12 @@ where Ok(paths) } -type PackageType = HashMap>; +type PackageType = HashMap>; type PackageDefinition = HashMap>; #[derive(Default)] pub struct Package { - _name: String, + _name: IdString64, _root: PathBuf, inner: PackageType, flags: u8, @@ -117,9 +116,9 @@ impl DerefMut for Package { } impl Package { - pub fn new(name: String, root: PathBuf) -> Self { + pub fn new(name: impl Into, root: PathBuf) -> Self { Self { - _name: name, + _name: name.into(), _root: root, inner: Default::default(), flags: 1, @@ -130,17 +129,22 @@ impl Package { self.values().fold(0, |total, files| total + files.len()) } - pub fn add_file>(&mut self, file_type: BundleFileType, name: P) { + pub fn add_file(&mut self, file_type: BundleFileType, name: impl Into) { self.inner.entry(file_type).or_default().insert(name.into()); } #[tracing::instrument("Package::from_sjson", skip(sjson), fields(sjson_len = sjson.as_ref().len()))] - pub async fn from_sjson(sjson: S, name: String, root: P) -> Result + pub async fn from_sjson( + sjson: S, + name: impl Into + std::fmt::Debug, + root: P, + ) -> Result where P: AsRef + std::fmt::Debug, S: AsRef, { let root = root.as_ref(); + let name = name.into(); let definition: PackageDefinition = serde_sjson::from_str(sjson.as_ref())?; let mut inner: PackageType = Default::default(); @@ -174,7 +178,11 @@ impl Package { continue; }; - inner.entry(t).or_default().insert(path); + tracing::debug!("Adding file {}", path.display()); + inner + .entry(t) + .or_default() + .insert(path.display().to_string()); } } } @@ -193,11 +201,9 @@ impl Package { pub fn to_sjson(&self) -> Result { let mut map: PackageDefinition = Default::default(); - for (t, paths) in self.iter() { - for path in paths.iter() { - map.entry(t.ext_name()) - .or_default() - .insert(path.display().to_string()); + for (t, names) in self.iter() { + for name in names.iter() { + map.entry(t.ext_name()).or_default().insert(name.clone()); } } @@ -223,11 +229,11 @@ impl Package { for _ in 0..file_count { let t = BundleFileType::from(r.read_u64()?); let hash = Murmur64::from(r.read_u64()?); - let path = ctx.lookup_hash(hash, HashGroup::Filename); + let name = ctx.lookup_hash(hash, HashGroup::Filename); inner .entry(t) .or_default() - .insert(PathBuf::from(path.display().to_string())); + .insert(name.display().to_string()); } let flags = r.read_u8()?; @@ -240,7 +246,7 @@ impl Package { let pkg = Self { inner, - _name: name, + _name: name.into(), _root: PathBuf::new(), flags, }; @@ -256,12 +262,10 @@ impl Package { w.write_u32(0x2b)?; w.write_u32(self.values().flatten().count() as u32)?; - for (t, paths) in self.iter() { - for path in paths.iter() { + for (t, names) in self.iter() { + for name in names.iter() { w.write_u64(t.hash().into())?; - - let hash = Murmur64::hash(path.to_slash_lossy().as_bytes()); - w.write_u64(hash.into())?; + w.write_u64(Murmur64::hash(name.as_bytes()).into())?; } } diff --git a/lib/sdk/src/murmur/types.rs b/lib/sdk/src/murmur/types.rs index e96b992..20bf46a 100644 --- a/lib/sdk/src/murmur/types.rs +++ b/lib/sdk/src/murmur/types.rs @@ -1,3 +1,7 @@ +use std::path::Path; + +use path_slash::PathExt; + use self::util::{parse_hex32, parse_hex64}; use super::*; @@ -263,11 +267,23 @@ impl IdString64 { IdString64::String(_) => false, } } + + // Would love to have this as a proper `impl From`, but + // rustc will complain that it overlaps with the `impl From>`. + pub fn from_path(p: impl AsRef) -> Self { + Self::String(p.as_ref().to_slash_lossy().to_string()) + } } -impl> From for IdString64 { - fn from(value: S) -> Self { - Self::String(value.into()) +impl From for IdString64 { + fn from(value: String) -> Self { + Self::String(value) + } +} + +impl From for IdString64 { + fn from(value: u64) -> Self { + Self::Hash(value.into()) } } @@ -283,6 +299,12 @@ impl From for Murmur64 { } } +impl Default for IdString64 { + fn default() -> Self { + Self::Hash(0.into()) + } +} + impl PartialEq for IdString64 { fn eq(&self, other: &Self) -> bool { self.to_murmur64() == other.to_murmur64() From 74a7aaa6e5dacc758a7846ab720999756203bbc6 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sat, 16 Sep 2023 18:50:40 +0200 Subject: [PATCH 226/335] dtmt-shared: Write log lines to stderr Ideally, I would prefer the usual split per logging level, but that seems to be somewhat complex with `tracing_subscriber`, so this simply switches everything over to stderr, so that some of the experiment commands can write results to stdout. --- lib/dtmt-shared/src/log.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/dtmt-shared/src/log.rs b/lib/dtmt-shared/src/log.rs index ab0b7b5..9c95c63 100644 --- a/lib/dtmt-shared/src/log.rs +++ b/lib/dtmt-shared/src/log.rs @@ -84,7 +84,7 @@ pub fn create_tracing_subscriber() { EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::try_new("info").unwrap()); let (dev_stdout_layer, prod_stdout_layer, filter_layer) = if cfg!(debug_assertions) { - let fmt_layer = fmt::layer().pretty(); + let fmt_layer = fmt::layer().pretty().with_writer(std::io::stderr); (Some(fmt_layer), None, None) } else { // Creates a layer that @@ -93,6 +93,7 @@ pub fn create_tracing_subscriber() { // - does not print spans/targets // - only prints time, not date let fmt_layer = fmt::layer() + .with_writer(std::io::stderr) .event_format(Formatter) .fmt_fields(debug_fn(format_fields)); From edad0d44930d306c5e6493db920ff97c7160e66c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 30 Aug 2023 18:44:24 +0200 Subject: [PATCH 227/335] Improve file listing output Adds pretty printing for file size and always shows the bundle hash name --- crates/dtmt/src/cmd/bundle/list.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/crates/dtmt/src/cmd/bundle/list.rs b/crates/dtmt/src/cmd/bundle/list.rs index dd72ad2..558126b 100644 --- a/crates/dtmt/src/cmd/bundle/list.rs +++ b/crates/dtmt/src/cmd/bundle/list.rs @@ -36,6 +36,18 @@ enum OutputFormat { Text, } +fn format_byte_size(size: usize) -> String { + if size < 1024 { + format!("{} Bytes", size) + } else if size < 1024 * 1024 { + format!("{} kB", size / 1024) + } else if size < 1024 * 1024 * 1024 { + format!("{} MB", size / (1024 * 1024)) + } else { + format!("{} GB", size / (1024 * 1024 * 1024)) + } +} + #[tracing::instrument(skip(ctx))] async fn print_bundle_contents

(ctx: &sdk::Context, path: P, fmt: OutputFormat) -> Result<()> where @@ -50,7 +62,11 @@ where match fmt { OutputFormat::Text => { - println!("Bundle: {}", bundle.name().display()); + println!( + "Bundle: {} ({:016x})", + bundle.name().display(), + bundle.name() + ); for f in bundle.files().iter() { if f.variants().len() != 1 { @@ -63,9 +79,10 @@ where let v = &f.variants()[0]; println!( - "\t{}.{}: {} bytes", + "\t{}.{}: {} ({})", f.base_name().display(), f.file_type().ext_name(), + format_byte_size(v.size()), v.size() ); } From 08219f05ba81b16bd34afa82ef37f99d32d65e3b Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Thu, 31 Aug 2023 17:14:54 +0200 Subject: [PATCH 228/335] sdk: Fix reading strings Fatshark has a few weird string fields, where they provide a length field, but then sometimes write a shorter, NUL-terminated string into that same field and adding padding up to the "advertised" length. To properly read those strings, we can't rely on just the length field anymore, but need to check for a NUL, too. --- lib/sdk/src/binary.rs | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/lib/sdk/src/binary.rs b/lib/sdk/src/binary.rs index 1fcc90e..9348e1b 100644 --- a/lib/sdk/src/binary.rs +++ b/lib/sdk/src/binary.rs @@ -43,6 +43,7 @@ impl FromBinary for Vec { } pub mod sync { + use std::ffi::CStr; use std::io::{self, Read, Seek, SeekFrom}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; @@ -165,25 +166,13 @@ pub mod sync { } fn read_string_len(&mut self, len: usize) -> Result { - let mut buf = vec![0; len]; - let res = self - .read_exact(&mut buf) - .map_err(Report::new) - .and_then(|_| { - String::from_utf8(buf).map_err(|err| { - let ascii = String::from_utf8_lossy(err.as_bytes()).to_string(); - let bytes = format!("{:?}", err.as_bytes()); - Report::new(err) - .with_section(move || bytes.header("Bytes:")) - .with_section(move || ascii.header("ASCII:")) - }) - }); + let pos = self.stream_position(); + let res = read_string_len(self, len); if res.is_ok() { return res; } - let pos = self.stream_position(); if pos.is_ok() { res.with_section(|| { format!("{pos:#X} ({pos})", pos = pos.unwrap()).header("Position: ") @@ -243,4 +232,22 @@ pub mod sync { Err(err).with_section(|| format!("{pos:#X} ({pos})").header("Position: ")) } + + fn read_string_len(mut r: impl Read, len: usize) -> Result { + let mut buf = vec![0; len]; + r.read_exact(&mut buf) + .wrap_err_with(|| format!("Failed to read {} bytes", len))?; + + let res = match CStr::from_bytes_until_nul(&buf) { + Ok(s) => { + let s = s.to_str()?; + Ok(s.to_string()) + } + Err(_) => String::from_utf8(buf.clone()).map_err(Report::new), + }; + + res.wrap_err("Invalid binary for UTF8 string") + .with_section(|| format!("{}", String::from_utf8_lossy(&buf)).header("ASCI:")) + .with_section(|| format!("{:x?}", buf).header("Bytes:")) + } } From c997489e18de825300a9660f48973ff932663ddc Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 22 Sep 2023 15:35:39 +0200 Subject: [PATCH 229/335] Add some doc comments --- crates/dtmt/src/cmd/build.rs | 7 +++++++ lib/sdk/src/filetype/package.rs | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/crates/dtmt/src/cmd/build.rs b/crates/dtmt/src/cmd/build.rs index fab072b..74a627d 100644 --- a/crates/dtmt/src/cmd/build.rs +++ b/crates/dtmt/src/cmd/build.rs @@ -55,6 +55,7 @@ pub(crate) fn command_definition() -> Command { ) } +/// Try to find a `dtmt.cfg` in the given directory or traverse up the parents. #[tracing::instrument] async fn find_project_config(dir: Option) -> Result { let (path, mut file) = if let Some(path) = dir { @@ -102,6 +103,8 @@ async fn find_project_config(dir: Option) -> Result { Ok(cfg) } +/// Iterate over the paths in the given `Package` and +/// compile each file by its file type. #[tracing::instrument(skip_all)] async fn compile_package_files(pkg: &Package, cfg: &ModConfig) -> Result> { let root = Arc::new(&cfg.dir); @@ -148,6 +151,8 @@ async fn compile_package_files(pkg: &Package, cfg: &ModConfig) -> Result>(path: P) -> Result { let path = path.as_ref(); diff --git a/lib/sdk/src/filetype/package.rs b/lib/sdk/src/filetype/package.rs index 6394e42..758f79f 100644 --- a/lib/sdk/src/filetype/package.rs +++ b/lib/sdk/src/filetype/package.rs @@ -14,6 +14,15 @@ use crate::bundle::file::UserFile; use crate::bundle::filetype::BundleFileType; use crate::murmur::{HashGroup, IdString64, Murmur64}; +/// Resolves a relative path that might contain wildcards into a list of +/// paths that exist on disk and match that wildcard. +/// This is similar to globbing in Unix shells, but with much less features. +/// +/// The only wilcard character allowed is `*`, and only at the end of the string, +/// where it matches all files recursively in that directory. +/// +/// `t` is an optional extension name, that may be used to force a wildcard +/// path to only match that file type `t`. #[tracing::instrument] #[async_recursion] async fn resolve_wildcard( From f1f9a818cc4dc006f2d8e8512564b4322d8ef1da Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 22 Sep 2023 15:37:37 +0200 Subject: [PATCH 230/335] sdk: Allow any byte stream for hashing dictionary entries --- lib/sdk/src/murmur/dictionary.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/sdk/src/murmur/dictionary.rs b/lib/sdk/src/murmur/dictionary.rs index 2d51af1..267f0a4 100644 --- a/lib/sdk/src/murmur/dictionary.rs +++ b/lib/sdk/src/murmur/dictionary.rs @@ -147,14 +147,14 @@ impl Dictionary { Ok(()) } - pub fn add(&mut self, value: String, group: HashGroup) { - let long = Murmur64::from(murmurhash64::hash(value.as_bytes(), SEED as u64)); - let short = Murmur32::from(murmurhash64::hash32(value.as_bytes(), SEED)); + pub fn add(&mut self, value: impl AsRef<[u8]>, group: HashGroup) { + let long = Murmur64::from(murmurhash64::hash(value.as_ref(), SEED as u64)); + let short = Murmur32::from(murmurhash64::hash32(value.as_ref(), SEED)); let entry = Entry { long, short, - value, + value: String::from_utf8_lossy(value.as_ref()).to_string(), group, }; From 3a6e954f9a45ac14df3ccb797ac86a797fb22ba4 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 19 Jul 2024 11:30:09 +0200 Subject: [PATCH 231/335] sdk: Refactor murmur modules and add IdString32 --- lib/sdk/src/murmur/idstring32.rs | 162 ++++++++++++++++++++++++++++ lib/sdk/src/murmur/idstring64.rs | 175 +++++++++++++++++++++++++++++++ lib/sdk/src/murmur/mod.rs | 4 + lib/sdk/src/murmur/types.rs | 173 ++---------------------------- 4 files changed, 347 insertions(+), 167 deletions(-) create mode 100644 lib/sdk/src/murmur/idstring32.rs create mode 100644 lib/sdk/src/murmur/idstring64.rs diff --git a/lib/sdk/src/murmur/idstring32.rs b/lib/sdk/src/murmur/idstring32.rs new file mode 100644 index 0000000..99ea7aa --- /dev/null +++ b/lib/sdk/src/murmur/idstring32.rs @@ -0,0 +1,162 @@ +use std::fmt; + +use serde::{Deserializer, Serializer}; + +use super::Murmur32; + +// This type encodes the fact that when reading in a bundle, we don't always have a dictionary +// entry for every hash in there. So we do want to have the real string available when needed, +// but at the same time retain the original hash information for when we don't. +// This is especially important when wanting to write back the read bundle, as the hashes need to +// stay the same. +// The previous system of always turning hashes into strings worked well for the purpose of +// displaying hashes, but would have made it very hard to turn a stringyfied hash back into +// an actual hash. +#[derive(Clone, Debug, Eq)] +pub enum IdString32 { + Hash(Murmur32), + String(String), +} + +impl IdString32 { + pub fn to_murmur32(&self) -> Murmur32 { + match self { + Self::Hash(hash) => *hash, + Self::String(s) => Murmur32::hash(s.as_bytes()), + } + } + + pub fn display(&self) -> IdString32Display { + let s = match self { + IdString32::Hash(hash) => hash.to_string(), + IdString32::String(s) => s.clone(), + }; + + IdString32Display(s) + } + + pub fn is_string(&self) -> bool { + match self { + IdString32::Hash(_) => false, + IdString32::String(_) => true, + } + } + + pub fn is_hash(&self) -> bool { + match self { + IdString32::Hash(_) => true, + IdString32::String(_) => false, + } + } +} + +impl From for IdString32 { + fn from(value: String) -> Self { + Self::String(value) + } +} + +impl From for IdString32 { + fn from(value: u32) -> Self { + Self::Hash(value.into()) + } +} + +impl From for u32 { + fn from(value: IdString32) -> Self { + value.to_murmur32().into() + } +} + +impl From for IdString32 { + fn from(value: Murmur32) -> Self { + Self::Hash(value) + } +} + +impl From for Murmur32 { + fn from(value: IdString32) -> Self { + value.to_murmur32() + } +} + +impl PartialEq for IdString32 { + fn eq(&self, other: &Self) -> bool { + self.to_murmur32() == other.to_murmur32() + } +} + +impl std::hash::Hash for IdString32 { + fn hash(&self, state: &mut H) { + state.write_u32(self.to_murmur32().into()); + } +} + +impl serde::Serialize for IdString32 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u32(self.to_murmur32().into()) + } +} + +struct IdString32Visitor; + +impl<'de> serde::de::Visitor<'de> for IdString32Visitor { + type Value = IdString32; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an u32 or a string") + } + + fn visit_u32(self, value: u32) -> Result + where + E: serde::de::Error, + { + Ok(IdString32::Hash(value.into())) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + Ok(IdString32::String(v.to_string())) + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + Ok(IdString32::String(v)) + } +} + +impl<'de> serde::Deserialize<'de> for IdString32 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_u32(IdString32Visitor) + } +} + +pub struct IdString32Display(String); + +impl std::fmt::Display for IdString32Display { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::fmt::UpperHex for IdString32 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::UpperHex::fmt(&self.to_murmur32(), f) + } +} + +impl std::fmt::LowerHex for IdString32 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::LowerHex::fmt(&self.to_murmur32(), f) + } +} diff --git a/lib/sdk/src/murmur/idstring64.rs b/lib/sdk/src/murmur/idstring64.rs new file mode 100644 index 0000000..781a0cd --- /dev/null +++ b/lib/sdk/src/murmur/idstring64.rs @@ -0,0 +1,175 @@ +use std::{fmt, path::Path}; + +use path_slash::PathExt as _; +use serde::{Deserializer, Serializer}; + +use super::Murmur64; + +// This type encodes the fact that when reading in a bundle, we don't always have a dictionary +// entry for every hash in there. So we do want to have the real string available when needed, +// but at the same time retain the original hash information for when we don't. +// This is especially important when wanting to write back the read bundle, as the hashes need to +// stay the same. +// The previous system of always turning hashes into strings worked well for the purpose of +// displaying hashes, but would have made it very hard to turn a stringyfied hash back into +// an actual hash. +#[derive(Clone, Debug, Eq)] +pub enum IdString64 { + Hash(Murmur64), + String(String), +} + +impl IdString64 { + pub fn to_murmur64(&self) -> Murmur64 { + match self { + Self::Hash(hash) => *hash, + Self::String(s) => Murmur64::hash(s.as_bytes()), + } + } + + pub fn display(&self) -> IdString64Display { + let s = match self { + IdString64::Hash(hash) => hash.to_string(), + IdString64::String(s) => s.clone(), + }; + + IdString64Display(s) + } + + pub fn is_string(&self) -> bool { + match self { + IdString64::Hash(_) => false, + IdString64::String(_) => true, + } + } + + pub fn is_hash(&self) -> bool { + match self { + IdString64::Hash(_) => true, + IdString64::String(_) => false, + } + } + + // Would love to have this as a proper `impl From`, but + // rustc will complain that it overlaps with the `impl From>`. + pub fn from_path(p: impl AsRef) -> Self { + Self::String(p.as_ref().to_slash_lossy().to_string()) + } +} + +impl From for IdString64 { + fn from(value: String) -> Self { + Self::String(value) + } +} + +impl From for IdString64 { + fn from(value: u64) -> Self { + Self::Hash(value.into()) + } +} + +impl From for IdString64 { + fn from(value: Murmur64) -> Self { + Self::Hash(value) + } +} + +impl From for Murmur64 { + fn from(value: IdString64) -> Self { + value.to_murmur64() + } +} + +impl From for u64 { + fn from(value: IdString64) -> Self { + value.to_murmur64().into() + } +} + +impl Default for IdString64 { + fn default() -> Self { + Self::Hash(0.into()) + } +} + +impl PartialEq for IdString64 { + fn eq(&self, other: &Self) -> bool { + self.to_murmur64() == other.to_murmur64() + } +} + +impl std::hash::Hash for IdString64 { + fn hash(&self, state: &mut H) { + state.write_u64(self.to_murmur64().into()); + } +} + +impl serde::Serialize for IdString64 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_u64(self.to_murmur64().into()) + } +} + +struct IdString64Visitor; + +impl<'de> serde::de::Visitor<'de> for IdString64Visitor { + type Value = IdString64; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an u64 or a string") + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + Ok(IdString64::Hash(value.into())) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + Ok(IdString64::String(v.to_string())) + } + + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, + { + Ok(IdString64::String(v)) + } +} + +impl<'de> serde::Deserialize<'de> for IdString64 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_u64(IdString64Visitor) + } +} + +pub struct IdString64Display(String); + +impl std::fmt::Display for IdString64Display { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::fmt::UpperHex for IdString64 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::UpperHex::fmt(&self.to_murmur64(), f) + } +} + +impl std::fmt::LowerHex for IdString64 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + std::fmt::LowerHex::fmt(&self.to_murmur64(), f) + } +} diff --git a/lib/sdk/src/murmur/mod.rs b/lib/sdk/src/murmur/mod.rs index 87a8473..6449d38 100644 --- a/lib/sdk/src/murmur/mod.rs +++ b/lib/sdk/src/murmur/mod.rs @@ -8,6 +8,8 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; mod dictionary; // Currently unused // mod murmurhash32; +mod idstring32; +mod idstring64; mod murmurhash64; mod types; mod util; @@ -15,6 +17,8 @@ mod util; pub const SEED: u32 = 0; pub use dictionary::{Dictionary, Entry, HashGroup}; +pub use idstring32::*; +pub use idstring64::*; pub use murmurhash64::hash; pub use murmurhash64::hash32; pub use murmurhash64::hash_inverse as inverse; diff --git a/lib/sdk/src/murmur/types.rs b/lib/sdk/src/murmur/types.rs index 20bf46a..c66e2cf 100644 --- a/lib/sdk/src/murmur/types.rs +++ b/lib/sdk/src/murmur/types.rs @@ -1,7 +1,3 @@ -use std::path::Path; - -use path_slash::PathExt; - use self::util::{parse_hex32, parse_hex64}; use super::*; @@ -154,6 +150,12 @@ impl fmt::UpperHex for Murmur32 { } } +impl fmt::LowerHex for Murmur32 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::LowerHex::fmt(&self.0, f) + } +} + impl fmt::Display for Murmur32 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:08X}", self) @@ -222,166 +224,3 @@ impl<'de> Deserialize<'de> for Murmur32 { deserializer.deserialize_any(Self(0)) } } - -// This type encodes the fact that when reading in a bundle, we don't always have a dictionary -// entry for every hash in there. So we do want to have the real string available when needed, -// but at the same time retain the original hash information for when we don't. -// This is especially important when wanting to write back the read bundle, as the hashes need to -// stay the same. -// The previous system of always turning hashes into strings worked well for the purpose of -// displaying hashes, but would have made it very hard to turn a stringyfied hash back into -// an actual hash. -#[derive(Clone, Debug, Eq)] -pub enum IdString64 { - Hash(Murmur64), - String(String), -} - -impl IdString64 { - pub fn to_murmur64(&self) -> Murmur64 { - match self { - Self::Hash(hash) => *hash, - Self::String(s) => Murmur64::hash(s.as_bytes()), - } - } - - pub fn display(&self) -> IdString64Display { - let s = match self { - IdString64::Hash(hash) => hash.to_string(), - IdString64::String(s) => s.clone(), - }; - - IdString64Display(s) - } - - pub fn is_string(&self) -> bool { - match self { - IdString64::Hash(_) => false, - IdString64::String(_) => true, - } - } - - pub fn is_hash(&self) -> bool { - match self { - IdString64::Hash(_) => true, - IdString64::String(_) => false, - } - } - - // Would love to have this as a proper `impl From`, but - // rustc will complain that it overlaps with the `impl From>`. - pub fn from_path(p: impl AsRef) -> Self { - Self::String(p.as_ref().to_slash_lossy().to_string()) - } -} - -impl From for IdString64 { - fn from(value: String) -> Self { - Self::String(value) - } -} - -impl From for IdString64 { - fn from(value: u64) -> Self { - Self::Hash(value.into()) - } -} - -impl From for IdString64 { - fn from(value: Murmur64) -> Self { - Self::Hash(value) - } -} - -impl From for Murmur64 { - fn from(value: IdString64) -> Self { - value.to_murmur64() - } -} - -impl Default for IdString64 { - fn default() -> Self { - Self::Hash(0.into()) - } -} - -impl PartialEq for IdString64 { - fn eq(&self, other: &Self) -> bool { - self.to_murmur64() == other.to_murmur64() - } -} - -impl std::hash::Hash for IdString64 { - fn hash(&self, state: &mut H) { - state.write_u64(self.to_murmur64().into()); - } -} - -impl serde::Serialize for IdString64 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_u64(self.to_murmur64().into()) - } -} - -struct IdString64Visitor; - -impl<'de> serde::de::Visitor<'de> for IdString64Visitor { - type Value = IdString64; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an u64 or a string") - } - - fn visit_u64(self, value: u64) -> Result - where - E: serde::de::Error, - { - Ok(IdString64::Hash(value.into())) - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - Ok(IdString64::String(v.to_string())) - } - - fn visit_string(self, v: String) -> Result - where - E: serde::de::Error, - { - Ok(IdString64::String(v)) - } -} - -impl<'de> serde::Deserialize<'de> for IdString64 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_u64(IdString64Visitor) - } -} - -pub struct IdString64Display(String); - -impl std::fmt::Display for IdString64Display { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl std::fmt::UpperHex for IdString64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - std::fmt::UpperHex::fmt(&self.to_murmur64(), f) - } -} - -impl std::fmt::LowerHex for IdString64 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - std::fmt::LowerHex::fmt(&self.to_murmur64(), f) - } -} From dbf060032b015a9211f475339b8fbfcae234c3d4 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 28 Jul 2024 14:46:10 +0200 Subject: [PATCH 232/335] sdk: Implement bundle database resource hashes Algorithm reverse engineered by WhiteGoat. --- lib/sdk/src/bundle/database.rs | 41 +++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/lib/sdk/src/bundle/database.rs b/lib/sdk/src/bundle/database.rs index fce26ee..d9e0f03 100644 --- a/lib/sdk/src/bundle/database.rs +++ b/lib/sdk/src/bundle/database.rs @@ -36,6 +36,25 @@ pub struct BundleDatabase { bundle_contents: HashMap>, } +// Implements the partial Murmur that's used by the engine to compute bundle resource hashes, +// but in a way that the loop can be done outside the function. +#[inline(always)] +fn add_to_resource_hash(mut k: u64, name: impl Into) -> u64 { + const M: u64 = 0xc6a4a7935bd1e995; + const R: u64 = 47; + + let mut h: u64 = name.into(); + + k = k.wrapping_mul(M); + k ^= k >> R; + k = k.wrapping_mul(M); + + h ^= k; + k = M.wrapping_mul(h); + + k +} + impl BundleDatabase { pub fn add_bundle(&mut self, bundle: &Bundle) { let hash = bundle.name().to_murmur64(); @@ -69,20 +88,26 @@ impl BundleDatabase { } } + let mut resource_hash = 0; + for f in bundle.files() { + let name = f.base_name().to_murmur64(); let file_name = FileName { extension: f.file_type(), - name: f.base_name().to_murmur64(), + name, }; - // TODO: Compute actual resource hash - self.resource_hashes.insert(hash, 0); + resource_hash = add_to_resource_hash(resource_hash, name); + // TODO: Make sure each file name only exists once. Probably best to turn + // the `Vec` into a sorted `HashSet`. self.bundle_contents .entry(hash) .or_default() .push(file_name); } + + self.resource_hashes.insert(hash, resource_hash); } } @@ -103,7 +128,7 @@ impl FromBinary for BundleDatabase { let mut stored_files = HashMap::with_capacity(num_entries); for _ in 0..num_entries { - let hash = Murmur64::from(r.read_u64()?); + let hash = r.read_u64().map(Murmur64::from)?; let num_files = r.read_u32()? as usize; let mut files = Vec::with_capacity(num_files); @@ -161,7 +186,7 @@ impl FromBinary for BundleDatabase { let mut resource_hashes = HashMap::with_capacity(num_hashes); for _ in 0..num_hashes { - let name = Murmur64::from(r.read_u64()?); + let name = r.read_u64().map(Murmur64::from)?; let hash = r.read_u64()?; resource_hashes.insert(name, hash); @@ -171,14 +196,14 @@ impl FromBinary for BundleDatabase { let mut bundle_contents = HashMap::with_capacity(num_contents); for _ in 0..num_contents { - let hash = Murmur64::from(r.read_u64()?); + let hash = r.read_u64().map(Murmur64::from)?; let num_files = r.read_u32()? as usize; let mut files = Vec::with_capacity(num_files); for _ in 0..num_files { - let extension = BundleFileType::from(r.read_u64()?); - let name = Murmur64::from(r.read_u64()?); + let extension = r.read_u64().map(BundleFileType::from)?; + let name = r.read_u64().map(Murmur64::from)?; files.push(FileName { extension, name }); } From 7fa08c2efd10ae87f9bcf588d508bf1631eea845 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 28 Jul 2024 17:55:10 +0200 Subject: [PATCH 233/335] dtmt: Implement listing bundle database contents --- CHANGELOG.adoc | 1 + crates/dtmt/src/cmd/bundle/db.rs | 129 ++++++++++++++++++++++++++++++ crates/dtmt/src/cmd/bundle/mod.rs | 3 + lib/sdk/src/bundle/database.rs | 20 +++-- 4 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 crates/dtmt/src/cmd/bundle/db.rs diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index db30865..c5ba065 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -20,6 +20,7 @@ - dtmm: fetch file version for Nexus mods - dtmm: handle `nxm://` URIs via IPC and import the corresponding mod - dtmm: Add button to open mod on nexusmods.com +- dtmt: Implement commands to list bundles and contents === Fixed diff --git a/crates/dtmt/src/cmd/bundle/db.rs b/crates/dtmt/src/cmd/bundle/db.rs new file mode 100644 index 0000000..6d4da59 --- /dev/null +++ b/crates/dtmt/src/cmd/bundle/db.rs @@ -0,0 +1,129 @@ +use std::{io::Cursor, path::PathBuf}; + +use clap::{value_parser, Arg, ArgMatches, Command}; +use color_eyre::{eyre::Context as _, Result}; +use sdk::murmur::{HashGroup, IdString64, Murmur64}; +use sdk::{BundleDatabase, FromBinary as _}; +use tokio::fs; + +pub(crate) fn command_definition() -> Command { + Command::new("db") + .about("Various operations regarding `bundle_database.data`.") + .subcommand_required(true) + .subcommand( + Command::new("list-files") + .about("List bundle contents") + .arg( + Arg::new("database") + .required(true) + .help("Path to the bundle database") + .value_parser(value_parser!(PathBuf)), + ) + .arg( + Arg::new("bundle") + .help("The bundle name. If omitted, all bundles will be listed.") + .required(false), + ), + ) + .subcommand( + Command::new("list-bundles").about("List bundles").arg( + Arg::new("database") + .required(true) + .help("Path to the bundle database") + .value_parser(value_parser!(PathBuf)), + ), + ) +} + +#[tracing::instrument(skip_all)] +pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { + let Some((op, sub_matches)) = matches.subcommand() else { + unreachable!("clap is configured to require a subcommand"); + }; + + let database = { + let path = sub_matches + .get_one::("database") + .expect("argument is required"); + + let binary = fs::read(&path) + .await + .wrap_err_with(|| format!("Failed to read file '{}'", path.display()))?; + + let mut r = Cursor::new(binary); + + BundleDatabase::from_binary(&mut r).wrap_err("Failed to parse bundle database")? + }; + + match op { + "list-files" => { + let index = database.files(); + + if let Some(bundle) = sub_matches.get_one::("bundle") { + let hash = u64::from_str_radix(bundle, 16) + .map(Murmur64::from) + .wrap_err("Invalid hex sequence")?; + + if let Some(files) = index.get(&hash) { + for file in files { + let name = ctx.lookup_hash(file.name, HashGroup::Filename); + let extension = file.extension.ext_name(); + println!("{}.{}", name.display(), extension); + } + } else { + tracing::info!("Bundle {} not found in the database", bundle); + } + } else { + for (bundle_hash, files) in index.iter() { + let bundle_name = ctx.lookup_hash(*bundle_hash, HashGroup::Filename); + + match bundle_name { + IdString64::String(name) => { + println!("{:016X} {}", bundle_hash, name); + } + IdString64::Hash(hash) => { + println!("{:016X}", hash); + } + } + + for file in files { + let name = ctx.lookup_hash(file.name, HashGroup::Filename); + let extension = file.extension.ext_name(); + + match name { + IdString64::String(name) => { + println!("\t{:016X}.{:<12} {}", file.name, extension, name); + } + IdString64::Hash(hash) => { + println!("\t{:016X}.{}", hash, extension); + } + } + } + + println!(); + } + } + + Ok(()) + } + "list-bundles" => { + for bundle_hash in database.bundles().keys() { + let bundle_name = ctx.lookup_hash(*bundle_hash, HashGroup::Filename); + + match bundle_name { + IdString64::String(name) => { + println!("{:016X} {}", bundle_hash, name); + } + IdString64::Hash(hash) => { + println!("{:016X}", hash); + } + } + } + + Ok(()) + } + _ => unreachable!( + "clap is configured to require a subcommand, and they're all handled above" + ), + } +} diff --git a/crates/dtmt/src/cmd/bundle/mod.rs b/crates/dtmt/src/cmd/bundle/mod.rs index 0e7c9f7..c5145e4 100644 --- a/crates/dtmt/src/cmd/bundle/mod.rs +++ b/crates/dtmt/src/cmd/bundle/mod.rs @@ -1,6 +1,7 @@ use clap::{ArgMatches, Command}; use color_eyre::eyre::Result; +mod db; mod decompress; mod extract; mod inject; @@ -14,6 +15,7 @@ pub(crate) fn command_definition() -> Command { .subcommand(extract::command_definition()) .subcommand(inject::command_definition()) .subcommand(list::command_definition()) + .subcommand(db::command_definition()) } #[tracing::instrument(skip_all)] @@ -23,6 +25,7 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { Some(("extract", sub_matches)) => extract::run(ctx, sub_matches).await, Some(("inject", sub_matches)) => inject::run(ctx, sub_matches).await, Some(("list", sub_matches)) => list::run(ctx, sub_matches).await, + Some(("db", sub_matches)) => db::run(ctx, sub_matches).await, _ => unreachable!( "clap is configured to require a subcommand, and they're all handled above" ), diff --git a/lib/sdk/src/bundle/database.rs b/lib/sdk/src/bundle/database.rs index d9e0f03..185c62f 100644 --- a/lib/sdk/src/bundle/database.rs +++ b/lib/sdk/src/bundle/database.rs @@ -19,15 +19,15 @@ const DATABASE_VERSION: u32 = 0x6; const FILE_VERSION: u32 = 0x4; pub struct BundleFile { - name: String, - stream: String, - platform_specific: bool, - file_time: u64, + pub name: String, + pub stream: String, + pub platform_specific: bool, + pub file_time: u64, } pub struct FileName { - extension: BundleFileType, - name: Murmur64, + pub extension: BundleFileType, + pub name: Murmur64, } pub struct BundleDatabase { @@ -56,6 +56,14 @@ fn add_to_resource_hash(mut k: u64, name: impl Into) -> u64 { } impl BundleDatabase { + pub fn bundles(&self) -> &HashMap> { + &self.stored_files + } + + pub fn files(&self) -> &HashMap> { + &self.bundle_contents + } + pub fn add_bundle(&mut self, bundle: &Bundle) { let hash = bundle.name().to_murmur64(); let name = hash.to_string(); From d931e6b9cada6fdce92595a27a96b7e7428f7356 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Sun, 28 Jul 2024 22:02:01 +0200 Subject: [PATCH 234/335] dtmt: Add command to search for files Not really of much use at the moment, but inspired by the HD2 community. --- CHANGELOG.adoc | 1 + crates/dtmt/src/cmd/bundle/db.rs | 57 ++++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c5ba065..1bfb0dd 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -21,6 +21,7 @@ - dtmm: handle `nxm://` URIs via IPC and import the corresponding mod - dtmm: Add button to open mod on nexusmods.com - dtmt: Implement commands to list bundles and contents +- dtmt: Implement command to search for files === Fixed diff --git a/crates/dtmt/src/cmd/bundle/db.rs b/crates/dtmt/src/cmd/bundle/db.rs index 6d4da59..b537991 100644 --- a/crates/dtmt/src/cmd/bundle/db.rs +++ b/crates/dtmt/src/cmd/bundle/db.rs @@ -33,6 +33,21 @@ pub(crate) fn command_definition() -> Command { .value_parser(value_parser!(PathBuf)), ), ) + .subcommand( + Command::new("find-file") + .about("Find the bundle a file belongs to") + .arg( + Arg::new("database") + .required(true) + .help("Path to the bundle database") + .value_parser(value_parser!(PathBuf)), + ) + .arg( + Arg::new("file-name") + .required(true) + .help("Name of the file. May be a hash in hex representation or a string"), + ), + ) } #[tracing::instrument(skip_all)] @@ -79,10 +94,10 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { match bundle_name { IdString64::String(name) => { - println!("{:016X} {}", bundle_hash, name); + println!("{:016x} {}", bundle_hash, name); } IdString64::Hash(hash) => { - println!("{:016X}", hash); + println!("{:016x}", hash); } } @@ -92,10 +107,10 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { match name { IdString64::String(name) => { - println!("\t{:016X}.{:<12} {}", file.name, extension, name); + println!("\t{:016x}.{:<12} {}", file.name, extension, name); } IdString64::Hash(hash) => { - println!("\t{:016X}.{}", hash, extension); + println!("\t{:016x}.{}", hash, extension); } } } @@ -112,16 +127,46 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { match bundle_name { IdString64::String(name) => { - println!("{:016X} {}", bundle_hash, name); + println!("{:016x} {}", bundle_hash, name); } IdString64::Hash(hash) => { - println!("{:016X}", hash); + println!("{:016x}", hash); } } } Ok(()) } + "find-file" => { + let name = sub_matches + .get_one::("file-name") + .expect("required argument"); + let name = match u64::from_str_radix(name, 16).map(Murmur64::from) { + Ok(hash) => hash, + Err(_) => Murmur64::hash(name), + }; + + let bundles = database.files().iter().filter_map(|(bundle_hash, files)| { + if files.iter().any(|file| file.name == name) { + Some(bundle_hash) + } else { + None + } + }); + + let mut found = false; + + for bundle in bundles { + found = true; + println!("{:016x}", bundle); + } + + if !found { + std::process::exit(1); + } + + Ok(()) + } _ => unreachable!( "clap is configured to require a subcommand, and they're all handled above" ), From 2a1d8d815f3af3cde183183636f55698b8cc5ee2 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 14 Aug 2024 09:22:24 +0200 Subject: [PATCH 235/335] Add tests for hash inversion Just a quick round trip test, and an additional assert to demonstrate that byte order does matter. --- lib/sdk/src/murmur/murmurhash64.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/sdk/src/murmur/murmurhash64.rs b/lib/sdk/src/murmur/murmurhash64.rs index f15248c..ca69852 100644 --- a/lib/sdk/src/murmur/murmurhash64.rs +++ b/lib/sdk/src/murmur/murmurhash64.rs @@ -119,4 +119,9 @@ fn test_hash() { } #[test] -fn test_inverse() {} +fn test_inverse() { + let h = hash("lua".as_bytes(), crate::murmur::SEED as u64); + let inv = hash_inverse(h, crate::murmur::SEED as u64); + assert_eq!(h, hash(&inv.to_le_bytes(), crate::murmur::SEED as u64)); + assert_ne!(h, hash(&inv.to_be_bytes(), crate::murmur::SEED as u64)); +} From e3362400943a318db323cf46a8fe9981e8501b99 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 20 Aug 2024 16:28:08 +0200 Subject: [PATCH 236/335] Consilidate template libraries Remove last uses of `string_template` in favor of `minijinja`. Closes #124. --- Cargo.lock | 11 +------- Cargo.toml | 1 + crates/dtmm/Cargo.toml | 2 +- crates/dtmt/Cargo.toml | 2 +- crates/dtmt/src/cmd/new.rs | 54 ++++++++++++++++++++++---------------- 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a251de9..ce13303 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,6 +937,7 @@ dependencies = [ "futures-util", "glob", "luajit2-sys", + "minijinja", "nanorand", "notify", "oodle", @@ -948,7 +949,6 @@ dependencies = [ "serde", "serde_sjson", "shlex", - "string_template", "tempfile", "tokio", "tokio-stream", @@ -3307,15 +3307,6 @@ dependencies = [ "float-cmp", ] -[[package]] -name = "string_template" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f2c6b2c3fa950895c9aeb0c3cb9271d7eb580662af9af2b711b593f446320" -dependencies = [ - "regex", -] - [[package]] name = "strip-ansi-escapes" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index f38ac22..62d1fd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ exclude = ["lib/color-eyre"] [workspace.dependencies] zip = { version = "2.1.3", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } +minijinja = { version = "2.0.1", default-features = false } [patch.crates-io] color-eyre = { path = "lib/color-eyre" } diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index f947fc6..b7bee49 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -27,7 +27,7 @@ futures = "0.3.25" interprocess = "2.1.0" lazy_static = "1.4.0" luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } -minijinja = { version = "2.0.1", default-features = false } +minijinja = { workspace = true } nexusmods = { path = "../../lib/nexusmods", version = "*" } oodle = { path = "../../lib/oodle", version = "*" } open = "5.0.1" diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index d836a50..e2e3fb9 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -20,7 +20,7 @@ promptly = "0.3.1" sdk = { path = "../../lib/sdk", version = "*" } serde_sjson = { path = "../../lib/serde_sjson", version = "*" } serde = { version = "1.0.147", features = ["derive"] } -string_template = "0.2.1" +minijinja = { workspace = true } tokio-stream = { version = "0.1.11", features = ["fs", "io-util"] } tokio = { version = "1.21.2", features = ["rt-multi-thread", "fs", "process", "macros", "tracing", "io-util", "io-std"] } tracing-error = "0.2.0" diff --git a/crates/dtmt/src/cmd/new.rs b/crates/dtmt/src/cmd/new.rs index eb6a4f9..1993ea2 100644 --- a/crates/dtmt/src/cmd/new.rs +++ b/crates/dtmt/src/cmd/new.rs @@ -1,11 +1,10 @@ -use std::collections::HashMap; use std::path::PathBuf; use clap::{Arg, ArgMatches, Command}; use color_eyre::eyre::{self, Context, Result}; use color_eyre::Help; use futures::{StreamExt, TryStreamExt}; -use string_template::Template; +use minijinja::Environment; use tokio::fs::{self, DirBuilder}; const TEMPLATES: [(&str, &str); 5] = [ @@ -137,34 +136,45 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> tracing::debug!(root = %root.display(), name, id); - let mut data = HashMap::new(); - data.insert("name", name.as_str()); - data.insert("id", id.as_str()); + let render_ctx = minijinja::context!(name => name.as_str(), id => id.as_str()); + let env = Environment::new(); let templates = TEMPLATES .iter() .map(|(path_tmpl, content_tmpl)| { - let path = Template::new(path_tmpl).render(&data); - let content = Template::new(content_tmpl).render(&data); - - (root.join(path), content) + env.render_str(path_tmpl, &render_ctx) + .wrap_err_with(|| format!("Failed to render template: {}", path_tmpl)) + .and_then(|path| { + env.render_named_str(&path, content_tmpl, &render_ctx) + .wrap_err_with(|| format!("Failed to render template '{}'", &path)) + .map(|content| (root.join(path), content)) + }) }) - .map(|(path, content)| async move { - let dir = path - .parent() - .ok_or_else(|| eyre::eyre!("invalid root path"))?; + .map(|res| async move { + match res { + Ok((path, content)) => { + let dir = path + .parent() + .ok_or_else(|| eyre::eyre!("invalid root path"))?; - DirBuilder::new() - .recursive(true) - .create(&dir) - .await - .wrap_err_with(|| format!("Failed to create directory {}", dir.display()))?; + DirBuilder::new() + .recursive(true) + .create(&dir) + .await + .wrap_err_with(|| { + format!("Failed to create directory {}", dir.display()) + })?; - tracing::trace!("Writing file {}", path.display()); + tracing::trace!("Writing file {}", path.display()); - fs::write(&path, content.as_bytes()) - .await - .wrap_err_with(|| format!("Failed to write content to path {}", path.display())) + fs::write(&path, content.as_bytes()) + .await + .wrap_err_with(|| { + format!("Failed to write content to path {}", path.display()) + }) + } + Err(e) => Err(e), + } }); futures::stream::iter(templates) From df2992a4768e7c6f582049e45089141cd69ec7bd Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 20 Aug 2024 16:28:56 +0200 Subject: [PATCH 237/335] Improve mod template comments --- crates/dtmt/src/cmd/new.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/crates/dtmt/src/cmd/new.rs b/crates/dtmt/src/cmd/new.rs index 1993ea2..571b0cb 100644 --- a/crates/dtmt/src/cmd/new.rs +++ b/crates/dtmt/src/cmd/new.rs @@ -10,8 +10,21 @@ use tokio::fs::{self, DirBuilder}; const TEMPLATES: [(&str, &str); 5] = [ ( "dtmt.cfg", - r#"id = "{{id}}" + r#"// +// This is your mod's main configuration file. It tells DTMT how to build the mod, +// and DTMM what to display to your users. +// Certain files have been pre-filled by the template, the ones commented out (`//`) +// are optional. +// +// A unique identifier (preferably lower case, alphanumeric) +id = "{{id}}" +// The display name that your users will see. +// This doesn't have to be unique, but you still want to avoid being confused with other +// mods. name = "{{name}}" +// It's good practice to increase this number whenever you publish changes. +// It's up to you if you use SemVer or something simpler like `1970-12-24`. It should sort and +// compare well, though. version = "0.1.0" // author = "" @@ -31,16 +44,25 @@ categories = [ // A list of mod IDs that this mod depends on. You can find // those IDs by downloading the mod and extracting their `dtmt.cfg`. +// To make your fellow modders' lives easier, publish your own mods' IDs +// somewhere visible, such as the Nexusmods page. depends = [ DMF ] +// The primary resources that serve as the entry point to your +// mod's code. Unless for very specific use cases, the generated +// values shouldn't be changed. resources = { init = "scripts/mods/{{id}}/init" data = "scripts/mods/{{id}}/data" localization = "scripts/mods/{{id}}/localization" } +// The list of packages, or bundles, to build. +// Each one corresponds to a package definition in the named folder. +// For mods that contain only code and/or a few small assets, a single +// package will suffice. packages = [ "packages/mods/{{id}}" ] @@ -58,7 +80,6 @@ packages = [ r#"local mod = get_mod("{{id}}") -- Your mod code goes here. --- https://vmf-docs.verminti.de "#, ), ( From 6d94a4dd2c296bf1f044ee4c70fb10dca4c1c241 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 21 Aug 2024 14:33:15 +0200 Subject: [PATCH 238/335] Update bindgen --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1f48fa3..69e935b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,6 @@ links = "luajit" libc = "0.2" [build-dependencies] -bindgen = "0.69.4" +bindgen = "0.70.1" cc = "1" fs_extra = "1.1.0" From a2bbab1398e6f2caecd14a10efc564d0ef95d7c4 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 21 Aug 2024 14:25:41 +0200 Subject: [PATCH 239/335] Update dependencies --- Cargo.lock | 870 +++++++++++++++++++++---------------- Cargo.toml | 49 ++- crates/dtmm/Cargo.toml | 60 +-- crates/dtmt/Cargo.toml | 54 +-- lib/color-eyre | 2 +- lib/dtmt-shared/Cargo.toml | 16 +- lib/luajit2-sys | 2 +- lib/oodle/Cargo.toml | 6 +- lib/oodle/src/lib.rs | 1 + lib/sdk/Cargo.toml | 40 +- 10 files changed, 639 insertions(+), 461 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ce13303..26ccff7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -17,6 +17,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aho-corasick" version = "1.1.3" @@ -29,7 +35,8 @@ dependencies = [ [[package]] name = "ansi-parser" version = "0.9.1" -source = "git+https://gitlab.com/lschwiderski/ansi-parser.git?branch=issue/outdated-heapless#bfcd2689677fa93ce72c55833e891af36c65b2aa" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43e7fd8284f025d0bd143c2855618ecdf697db55bde39211e5c9faec7669173" dependencies = [ "heapless", "nom", @@ -46,9 +53,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.14" +version = "0.6.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", @@ -61,33 +68,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -95,9 +102,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arbitrary" @@ -110,15 +117,15 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "associative-cache" @@ -134,7 +141,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -161,6 +168,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.3.0" @@ -169,15 +182,15 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.7.4", "object", "rustc-demangle", ] @@ -205,16 +218,14 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.4" +version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cexpr", "clang-sys", "itertools", - "lazy_static", - "lazycell", "log", "prettyplease", "proc-macro2", @@ -222,8 +233,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.63", - "which", + "syn 2.0.75", ] [[package]] @@ -234,9 +244,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitmaps" @@ -276,9 +286,9 @@ checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytemuck" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" +checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31" [[package]] name = "byteorder" @@ -288,9 +298,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "bzip2" @@ -340,13 +350,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.97" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" dependencies = [ "jobserver", "libc", - "once_cell", + "shlex", ] [[package]] @@ -376,9 +386,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clang-sys" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -387,9 +397,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" dependencies = [ "clap_builder", "clap_derive", @@ -397,9 +407,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" dependencies = [ "anstream", "anstyle", @@ -411,27 +421,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "cli-table" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adfbb116d9e2c4be7011360d0c0bee565712c11e969c9609b25b619366dc379d" +checksum = "b53f9241f288a7b12c56565f04aaeaeeab6b8923d42d99255d4ca428b4d97f89" dependencies = [ "cli-table-derive", "termcolor", @@ -440,9 +450,9 @@ dependencies = [ [[package]] name = "cli-table-derive" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af3bfb9da627b0a6c467624fb7963921433774ed435493b5c08a3053e829ad4" +checksum = "3e83a93253aaae7c74eb7428ce4faa6e219ba94886908048888701819f82fb94" dependencies = [ "proc-macro2", "quote", @@ -530,9 +540,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "colors-transform" @@ -549,7 +559,7 @@ dependencies = [ "directories", "serde", "thiserror", - "toml 0.8.13", + "toml 0.8.19", ] [[package]] @@ -574,9 +584,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core-graphics" @@ -616,9 +626,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" dependencies = [ "libc", ] @@ -649,9 +659,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] @@ -721,7 +731,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -793,15 +803,21 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] +[[package]] +name = "doctest-file" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" + [[package]] name = "druid" version = "0.8.3" @@ -890,7 +906,7 @@ dependencies = [ "ansi-parser", "async-recursion", "bincode", - "bitflags 2.5.0", + "bitflags 2.6.0", "clap", "color-eyre", "colors-transform", @@ -986,9 +1002,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encoding_rs" @@ -1079,24 +1095,24 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" +checksum = "bf401df4a4e3872c4fe8151134cf483738e74b67fc934d6532c882b3d24a4550" dependencies = [ "cfg-if", "libc", - "redox_syscall", - "windows-sys 0.52.0", + "libredox", + "windows-sys 0.59.0", ] [[package]] name = "flate2" -version = "1.0.30" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "9c0596c1eac1f9e04ed902702e9878208b336edc9d6fddc8a48387349bab3666" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.0", ] [[package]] @@ -1147,11 +1163,11 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fontconfig-parser" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a595cb550439a117696039dfc69830492058211b771a2a165379f2a1a53d84d" +checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7" dependencies = [ - "roxmltree 0.19.0", + "roxmltree 0.20.0", ] [[package]] @@ -1261,7 +1277,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -1386,9 +1402,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "gio" @@ -1544,15 +1560,15 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http", "indexmap", "slab", @@ -1604,15 +1620,6 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" -[[package]] -name = "home" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] - [[package]] name = "http" version = "1.1.0" @@ -1626,9 +1633,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", @@ -1636,12 +1643,12 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "futures-core", + "futures-util", "http", "http-body", "pin-project-lite", @@ -1649,15 +1656,15 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "hyper" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", @@ -1673,6 +1680,23 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -1691,9 +1715,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.3" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" dependencies = [ "bytes", "futures-channel", @@ -1756,9 +1780,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.2.6" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" dependencies = [ "equivalent", "hashbrown", @@ -1786,9 +1810,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", "js-sys", @@ -1798,10 +1822,11 @@ dependencies = [ [[package]] name = "interprocess" -version = "2.1.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4d0250d41da118226e55b3d50ca3f0d9e0a0f6829b92f543ac0054aeea1572" +checksum = "d2f4e4a06d42fab3e85ab1b419ad32b09eab58b901d40c57935ff92db3287a13" dependencies = [ + "doctest-file", "libc", "recvmsg", "widestring", @@ -1854,15 +1879,15 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" dependencies = [ "either", ] @@ -1875,9 +1900,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] @@ -1890,9 +1915,9 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -1969,30 +1994,24 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2001,15 +2020,16 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", + "redox_syscall", ] [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lockfree-object-pool" @@ -2019,9 +2039,9 @@ checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "luajit2-sys" @@ -2059,9 +2079,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -2097,10 +2117,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "minijinja" -version = "2.0.1" +name = "minicov" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7165d0e94806d52ad5295e4b54a95176d831814840bc067298ca647e1c956338" +checksum = "5c71e683cd655513b99affab7d317deb690528255a0d5f717f1024093c12b169" +dependencies = [ + "cc", + "walkdir", +] + +[[package]] +name = "minijinja" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf369fce3289017a63e514dfca10a0a6f1a02216b21b588b79f6a1081eb999f5" dependencies = [ "serde", ] @@ -2113,14 +2143,23 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", "simd-adler32", ] +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + [[package]] name = "mio" version = "0.8.11" @@ -2133,6 +2172,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] + [[package]] name = "nanorand" version = "0.7.0" @@ -2141,11 +2192,10 @@ checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -2223,7 +2273,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -2231,7 +2281,7 @@ dependencies = [ "kqueue", "libc", "log", - "mio", + "mio 0.8.11", "walkdir", "windows-sys 0.48.0", ] @@ -2261,16 +2311,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "num_threads" version = "0.1.7" @@ -2291,9 +2331,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "27b64972346851a39438c60b341ebc01bba47464ae329e55cf343eb93964efd9" dependencies = [ "memchr", ] @@ -2315,9 +2355,9 @@ dependencies = [ [[package]] name = "open" -version = "5.1.2" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449f0ff855d85ddbf1edd5b646d65249ead3f5e422aaa86b7d2d0b049b103e32" +checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3" dependencies = [ "is-wsl", "libc", @@ -2326,11 +2366,11 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -2347,7 +2387,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -2358,9 +2398,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -2465,9 +2505,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.10" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "560131c633294438da9f7c4b08189194b20946c8274c6b9e38881a7874dc8ee8" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" dependencies = [ "memchr", "thiserror", @@ -2476,9 +2516,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.10" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26293c9193fbca7b1a3bf9b79dc1e388e927e6cacaa78b4a3ab705a1d3d41459" +checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" dependencies = [ "pest", "pest_generator", @@ -2486,22 +2526,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.10" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ec22af7d3fb470a85dd2ca96b7c577a1eb4ef6f1683a9fe9a8c16e136c04687" +checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] name = "pest_meta" -version = "2.7.10" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a240022f37c361ec1878d646fc5b7d7c4d28d5946e1a80ad5a7a4f4ca0bdcd" +checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" dependencies = [ "once_cell", "pest", @@ -2618,7 +2658,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -2649,7 +2689,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide", + "miniz_oxide 0.7.4", ] [[package]] @@ -2675,7 +2715,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -2714,9 +2754,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -2778,18 +2818,18 @@ checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", @@ -2798,14 +2838,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -2819,13 +2859,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax 0.8.4", ] [[package]] @@ -2836,15 +2876,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" dependencies = [ "base64 0.22.1", "bytes", @@ -2856,6 +2896,7 @@ dependencies = [ "http-body", "http-body-util", "hyper", + "hyper-rustls", "hyper-tls", "hyper-util", "ipnet", @@ -2879,7 +2920,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg 0.52.0", + "windows-registry", ] [[package]] @@ -2902,13 +2943,28 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.37" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8" +checksum = "0f86ae463694029097b846d8f99fd5536740602ae00022c0c50c5600720b2f71" dependencies = [ "bytemuck", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "roxmltree" version = "0.15.1" @@ -2920,9 +2976,9 @@ dependencies = [ [[package]] name = "roxmltree" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" [[package]] name = "rustc-demangle" @@ -2951,7 +3007,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -2959,10 +3015,23 @@ dependencies = [ ] [[package]] -name = "rustls-pemfile" -version = "2.1.2" +name = "rustls" +version = "0.23.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ "base64 0.22.1", "rustls-pki-types", @@ -2970,9 +3039,20 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + +[[package]] +name = "rustls-webpki" +version = "0.102.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] [[package]] name = "rustybuzz" @@ -3055,7 +3135,7 @@ name = "sdk" version = "0.3.0" dependencies = [ "async-recursion", - "bitflags 2.5.0", + "bitflags 2.6.0", "byteorder", "color-eyre", "csv-async", @@ -3078,11 +3158,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -3091,9 +3171,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -3122,31 +3202,32 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -3162,9 +3243,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" dependencies = [ "serde", ] @@ -3272,6 +3353,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -3289,7 +3376,7 @@ dependencies = [ "keyvalues-parser", "keyvalues-serde", "serde", - "winreg 0.51.0", + "winreg", ] [[package]] @@ -3322,6 +3409,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "svgfilters" version = "0.4.0" @@ -3354,9 +3447,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.63" +version = "2.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" dependencies = [ "proc-macro2", "quote", @@ -3365,26 +3458,29 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -3399,26 +3495,27 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.13", + "toml 0.8.19", "version-compare", ] [[package]] name = "target-lexicon" -version = "0.12.14" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3432,22 +3529,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -3520,18 +3617,18 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c02bf3c538ab32ba913408224323915f4ef9a6d61c0e85d493f355921c0ece" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", ] [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -3544,32 +3641,31 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" dependencies = [ "backtrace", "bytes", "libc", - "mio", - "num_cpus", + "mio 1.0.2", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", "tracing", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -3582,6 +3678,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.15" @@ -3617,21 +3724,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.13", + "toml_edit 0.22.20", ] [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -3649,15 +3756,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.8", + "winnow 0.6.18", ] [[package]] @@ -3673,20 +3780,19 @@ dependencies = [ "tokio", "tower-layer", "tower-service", - "tracing", ] [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -3694,7 +3800,6 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3708,7 +3813,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] @@ -3941,15 +4046,21 @@ checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -3991,9 +4102,9 @@ checksum = "14706d2a800ee8ff38c1d3edb873cd616971ea59eb7c0d046bb44ef59b06a1ae" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "valuable" @@ -4015,9 +4126,9 @@ checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vte" @@ -4031,9 +4142,9 @@ dependencies = [ [[package]] name = "vte_generate_state_changes" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" dependencies = [ "proc-macro2", "quote", @@ -4066,34 +4177,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -4103,9 +4215,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4113,31 +4225,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-bindgen-test" -version = "0.3.42" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" +checksum = "68497a05fb21143a08a7d24fc81763384a3072ee43c44e86aad1744d6adef9d9" dependencies = [ "console_error_panic_hook", "js-sys", + "minicov", "scoped-tls", "wasm-bindgen", "wasm-bindgen-futures", @@ -4146,20 +4259,20 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.42" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" +checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.75", ] [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -4171,18 +4284,6 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix", -] - [[package]] name = "widestring" version = "1.1.0" @@ -4207,11 +4308,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4220,6 +4321,36 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -4235,7 +4366,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -4255,18 +4395,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -4277,9 +4417,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -4289,9 +4429,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -4301,15 +4441,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -4319,9 +4459,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -4331,9 +4471,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -4343,9 +4483,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -4355,9 +4495,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -4370,9 +4510,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -4387,16 +4527,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "winres" version = "0.1.12" @@ -4440,10 +4570,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] -name = "zip" -version = "2.1.3" +name = "zeroize" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775a2b471036342aa69bc5a602bc889cb0a06cda00477d0c69566757d5553d39" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zip" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" dependencies = [ "arbitrary", "bzip2", @@ -4475,27 +4611,27 @@ dependencies = [ [[package]] name = "zstd" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.1.0" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.10+zstd.1.5.6" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", diff --git a/Cargo.toml b/Cargo.toml index 62d1fd8..9e08de5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,12 +13,51 @@ members = [ exclude = ["lib/color-eyre"] [workspace.dependencies] -zip = { version = "2.1.3", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } -minijinja = { version = "2.0.1", default-features = false } - -[patch.crates-io] +ansi-parser = "0.9.1" +ansi_term = "0.12.1" +async-recursion = "1.0.5" +bincode = "1.3.3" +bitflags = "2.5.0" +byteorder = "1.4.3" +clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } +cli-table = { version = "0.4.7", default-features = false, features = ["derive"] } color-eyre = { path = "lib/color-eyre" } -ansi-parser = { git = "https://gitlab.com/lschwiderski/ansi-parser.git", branch = "issue/outdated-heapless", version = "0.9.1" } +colors-transform = "0.2.11" +confy = "0.6.1" +csv-async = { version = "1.2.4", features = ["tokio", "serde"] } +druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] } +druid-widget-nursery = "0.1" +dtmt-shared = { path = "lib/dtmt-shared" } +fastrand = "2.1.0" +futures = "0.3.25" +futures-util = "0.3.24" +glob = "0.3.0" +interprocess = "2.1.0" +lazy_static = "1.4.0" +luajit2-sys = { path = "lib/luajit2-sys" } +minijinja = { version = "2.0.1", default-features = false } +nanorand = "0.7.0" +nexusmods = { path = "lib/nexusmods" } +notify = "6.1.1" +oodle = { path = "lib/oodle" } +open = "5.0.1" +path-clean = "1.0.1" +path-slash = "0.2.1" +pin-project-lite = "0.2.9" +promptly = "0.3.1" +sdk = { path = "lib/sdk" } +serde = { version = "1.0.152", features = ["derive", "rc"] } +serde_sjson = { path = "lib/serde_sjson" } +steamlocate = "2.0.0-beta.2" +strip-ansi-escapes = "0.2.0" +time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset", "formatting", "macros"] } +tokio = { version = "1.23.0", features = ["rt-multi-thread", "fs", "process", "macros", "tracing", "io-util", "io-std"] } +tokio-stream = { version = "0.1.12", features = ["fs", "io-util"] } +tracing = { version = "0.1.37", features = ["async-await"] } +tracing-error = "0.2.0" +tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } +usvg = "0.25.0" +zip = { version = "2.1.3", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } [profile.dev.package.backtrace] opt-level = 3 diff --git a/crates/dtmm/Cargo.toml b/crates/dtmm/Cargo.toml index b7bee49..52c0522 100644 --- a/crates/dtmm/Cargo.toml +++ b/crates/dtmm/Cargo.toml @@ -12,37 +12,37 @@ license-file = "LICENSE" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ansi-parser = "0.9.0" -async-recursion = "1.0.5" -bincode = "1.3.3" -bitflags = "2.5.0" -clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } -color-eyre = "0.6.2" -colors-transform = "0.2.11" -confy = "0.6.1" -druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] } -druid-widget-nursery = "0.1" -dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } -futures = "0.3.25" -interprocess = "2.1.0" -lazy_static = "1.4.0" -luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } +ansi-parser = { workspace = true } +async-recursion = { workspace = true } +bincode = { workspace = true } +bitflags = { workspace = true } +clap = { workspace = true } +color-eyre = { workspace = true } +colors-transform = { workspace = true } +confy = { workspace = true } +druid = { workspace = true } +druid-widget-nursery = { workspace = true } +dtmt-shared = { workspace = true } +futures = { workspace = true } +interprocess = { workspace = true } +lazy_static = { workspace = true } +luajit2-sys = { workspace = true } minijinja = { workspace = true } -nexusmods = { path = "../../lib/nexusmods", version = "*" } -oodle = { path = "../../lib/oodle", version = "*" } -open = "5.0.1" -path-slash = "0.2.1" -sdk = { path = "../../lib/sdk", version = "*" } -serde = { version = "1.0.152", features = ["derive", "rc"] } -serde_sjson = { path = "../../lib/serde_sjson", version = "*" } -strip-ansi-escapes = "0.2.0" -time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset"] } -tokio = { version = "1.23.0", features = ["rt", "fs", "tracing", "sync"] } -tokio-stream = { version = "0.1.12", features = ["fs"] } -tracing = "0.1.37" -tracing-error = "0.2.0" -tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } -usvg = "0.25.0" +nexusmods = { workspace = true } +oodle = { workspace = true } +open = { workspace = true } +path-slash = { workspace = true } +sdk = { workspace = true } +serde = { workspace = true } +serde_sjson = { workspace = true } +strip-ansi-escapes = { workspace = true } +time = { workspace = true } +tokio = { workspace = true } +tokio-stream = { workspace = true } +tracing = { workspace = true } +tracing-error = { workspace = true } +tracing-subscriber = { workspace = true } +usvg = { workspace = true } zip = { workspace = true } [build-dependencies] diff --git a/crates/dtmt/Cargo.toml b/crates/dtmt/Cargo.toml index e2e3fb9..183d6a5 100644 --- a/crates/dtmt/Cargo.toml +++ b/crates/dtmt/Cargo.toml @@ -4,34 +4,36 @@ version = "0.3.0" edition = "2021" [dependencies] -clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "unicode"] } -cli-table = { version = "0.4.7", default-features = false, features = ["derive"] } -color-eyre = "0.6.2" -confy = "0.6.1" -csv-async = { version = "1.2.4", features = ["tokio", "serde"] } -dtmt-shared = { path = "../../lib/dtmt-shared", version = "*" } -futures = "0.3.25" -futures-util = "0.3.24" -glob = "0.3.0" -nanorand = "0.7.0" -oodle = { path = "../../lib/oodle", version = "*" } -pin-project-lite = "0.2.9" -promptly = "0.3.1" -sdk = { path = "../../lib/sdk", version = "*" } -serde_sjson = { path = "../../lib/serde_sjson", version = "*" } -serde = { version = "1.0.147", features = ["derive"] } +async-recursion = { workspace = true } +clap = { workspace = true } +cli-table = { workspace = true } +color-eyre = { workspace = true } +confy = { workspace = true } +csv-async = { workspace = true } +dtmt-shared = { workspace = true } +futures = { workspace = true } +futures-util = { workspace = true } +glob = { workspace = true } +luajit2-sys = { workspace = true } minijinja = { workspace = true } -tokio-stream = { version = "0.1.11", features = ["fs", "io-util"] } -tokio = { version = "1.21.2", features = ["rt-multi-thread", "fs", "process", "macros", "tracing", "io-util", "io-std"] } -tracing-error = "0.2.0" -tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } -tracing = { version = "0.1.37", features = ["async-await"] } +nanorand = { workspace = true } +notify = { workspace = true } +oodle = { workspace = true } +path-clean = { workspace = true } +path-slash = { workspace = true } +pin-project-lite = { workspace = true } +promptly = { workspace = true } +sdk = { workspace = true } +serde = { workspace = true } +serde_sjson = { workspace = true } +tokio = { workspace = true } +tokio-stream = { workspace = true } +tracing = { workspace = true } +tracing-error = { workspace = true } +tracing-subscriber = { workspace = true } zip = { workspace = true } -path-clean = "1.0.1" -path-slash = "0.2.1" -async-recursion = "1.0.2" -notify = "6.1.1" -luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } + +# Cannot be a workspace dependencies when it's optional shlex = { version = "1.2.0", optional = true } [dev-dependencies] diff --git a/lib/color-eyre b/lib/color-eyre index b40962a..228b8ca 160000 --- a/lib/color-eyre +++ b/lib/color-eyre @@ -1 +1 @@ -Subproject commit b40962a61c748756d7da293d9fff26aca019603e +Subproject commit 228b8ca37ee79ab9afa45c40da415e4dcb029751 diff --git a/lib/dtmt-shared/Cargo.toml b/lib/dtmt-shared/Cargo.toml index b547dbe..26e1b6a 100644 --- a/lib/dtmt-shared/Cargo.toml +++ b/lib/dtmt-shared/Cargo.toml @@ -6,11 +6,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ansi_term = "0.12.1" -color-eyre = "0.6.2" -serde = "1.0.152" -steamlocate = "2.0.0-beta.2" -time = { version = "0.3.19", features = ["formatting", "local-offset", "macros"] } -tracing = "0.1.37" -tracing-error = "0.2.0" -tracing-subscriber = "0.3.16" +ansi_term = { workspace = true } +color-eyre = { workspace = true } +serde = { workspace = true } +steamlocate = { workspace = true } +time = { workspace = true } +tracing = { workspace = true } +tracing-error = { workspace = true } +tracing-subscriber = { workspace = true } diff --git a/lib/luajit2-sys b/lib/luajit2-sys index 5d1a075..6d94a4d 160000 --- a/lib/luajit2-sys +++ b/lib/luajit2-sys @@ -1 +1 @@ -Subproject commit 5d1a075742395f767c79d9c0d7466c6fb442f106 +Subproject commit 6d94a4dd2c296bf1f044ee4c70fb10dca4c1c241 diff --git a/lib/oodle/Cargo.toml b/lib/oodle/Cargo.toml index 6fc5039..feb9951 100644 --- a/lib/oodle/Cargo.toml +++ b/lib/oodle/Cargo.toml @@ -6,8 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -color-eyre = "0.6.2" -tracing = "0.1.37" +color-eyre = { workspace = true } +tracing = { workspace = true } [build-dependencies] -bindgen = "0.69.4" +bindgen = "0.70.1" diff --git a/lib/oodle/src/lib.rs b/lib/oodle/src/lib.rs index 76b1d16..871daab 100644 --- a/lib/oodle/src/lib.rs +++ b/lib/oodle/src/lib.rs @@ -7,6 +7,7 @@ use std::ptr; use color_eyre::{eyre, Result}; #[allow(dead_code)] +#[allow(clippy::identity_op)] mod bindings { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } diff --git a/lib/sdk/Cargo.toml b/lib/sdk/Cargo.toml index b164d47..4667a1c 100644 --- a/lib/sdk/Cargo.toml +++ b/lib/sdk/Cargo.toml @@ -4,23 +4,23 @@ version = "0.3.0" edition = "2021" [dependencies] -bitflags = "2.5.0" -byteorder = "1.4.3" -color-eyre = "0.6.2" -csv-async = { version = "1.2.4", features = ["tokio", "serde"] } -fastrand = "2.1.0" -futures = "0.3.25" -futures-util = "0.3.24" -glob = "0.3.0" -nanorand = "0.7.0" -pin-project-lite = "0.2.9" -serde = { version = "1.0.147", features = ["derive"] } -serde_sjson = { path = "../../lib/serde_sjson", version = "*" } -oodle = { path = "../../lib/oodle", version = "*" } -tokio = { version = "1.21.2", features = ["rt-multi-thread", "fs", "process", "macros", "tracing", "io-util", "io-std"] } -tokio-stream = { version = "0.1.11", features = ["fs", "io-util"] } -tracing = { version = "0.1.37", features = ["async-await"] } -tracing-error = "0.2.0" -luajit2-sys = { path = "../../lib/luajit2-sys", version = "*" } -async-recursion = "1.0.2" -path-slash = "0.2.1" +async-recursion = { workspace = true } +bitflags = { workspace = true } +byteorder = { workspace = true } +color-eyre = { workspace = true } +csv-async = { workspace = true } +fastrand = { workspace = true } +futures = { workspace = true } +futures-util = { workspace = true } +glob = { workspace = true } +luajit2-sys = { workspace = true } +nanorand = { workspace = true } +oodle = { workspace = true } +path-slash = { workspace = true } +pin-project-lite = { workspace = true } +serde = { workspace = true } +serde_sjson = { workspace = true } +tokio = { workspace = true } +tokio-stream = { workspace = true } +tracing = { workspace = true } +tracing-error = { workspace = true } From 7cb44532b23582e3e37969f1d8337c25ce45b1ae Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 21 Aug 2024 12:31:14 +0000 Subject: [PATCH 240/335] Add .renovaterc --- .renovaterc | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .renovaterc diff --git a/.renovaterc b/.renovaterc new file mode 100644 index 0000000..b36f3b4 --- /dev/null +++ b/.renovaterc @@ -0,0 +1,11 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + ":combinePatchMinorReleases", + ":enableVulnerabilityAlerts", + ":rebaseStalePrs" + ], + "prConcurrentLimit": 10, + "branchPrefix": "renovate/" +} From 4d665200fa36905fe453c3e312f5abf50633f903 Mon Sep 17 00:00:00 2001 From: Renovate Date: Fri, 23 Aug 2024 21:30:32 +0000 Subject: [PATCH 241/335] fix(deps): update rust crate serde_json to v1.0.127 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26ccff7..5b85e79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3222,9 +3222,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.125" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ "itoa", "memchr", From ffd4927d27140365948cddc00cbda2ba398bb022 Mon Sep 17 00:00:00 2001 From: Renovate Date: Sat, 24 Aug 2024 10:02:43 +0000 Subject: [PATCH 242/335] chore(deps): update rust crate fastrand to v2.1.1 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b85e79..4789666 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1059,9 +1059,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fd-lock" From 67c64bb3579b31073f38a57d4cd4208d665bb378 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 26 Aug 2024 20:45:40 +0000 Subject: [PATCH 243/335] chore(deps): update rust crate minijinja to v2.2.0 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4789666..4add151 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2128,9 +2128,9 @@ dependencies = [ [[package]] name = "minijinja" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf369fce3289017a63e514dfca10a0a6f1a02216b21b588b79f6a1081eb999f5" +checksum = "6d7d3e3a3eece1fa4618237ad41e1de855ced47eab705cec1c9a920e1d1c5aad" dependencies = [ "serde", ] From 659b63bfe998da90fcab4e88d4a94c7b8653bd1b Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 27 Aug 2024 06:30:39 +0000 Subject: [PATCH 244/335] fix(deps): update rust crate serde to v1.0.209 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4add151..e187021 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3202,18 +3202,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.208" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.208" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", From 72ce06b0e5dbe695842340604941bc69b00c8437 Mon Sep 17 00:00:00 2001 From: Renovate Date: Fri, 25 Oct 2024 17:32:26 +0000 Subject: [PATCH 245/335] chore(deps): update rust crate notify to v7 --- Cargo.lock | 47 ++++++++++++++++++----------------------------- Cargo.toml | 2 +- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e187021..7e460c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -657,15 +657,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.20" @@ -1790,9 +1781,9 @@ dependencies = [ [[package]] name = "inotify" -version = "0.9.6" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" dependencies = [ "bitflags 1.3.2", "inotify-sys", @@ -2160,18 +2151,6 @@ dependencies = [ "adler2", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "mio" version = "1.0.2" @@ -2180,6 +2159,7 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi", "libc", + "log", "wasi", "windows-sys 0.52.0", ] @@ -2269,21 +2249,30 @@ dependencies = [ [[package]] name = "notify" -version = "6.1.1" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009" dependencies = [ "bitflags 2.6.0", - "crossbeam-channel", "filetime", "fsevent-sys", "inotify", "kqueue", "libc", "log", - "mio 0.8.11", + "mio", + "notify-types", "walkdir", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "notify-types" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7393c226621f817964ffb3dc5704f9509e107a8b024b489cc2c1b217378785df" +dependencies = [ + "instant", ] [[package]] @@ -3648,7 +3637,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.2", + "mio", "pin-project-lite", "signal-hook-registry", "socket2", diff --git a/Cargo.toml b/Cargo.toml index 9e08de5..0ff601e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ luajit2-sys = { path = "lib/luajit2-sys" } minijinja = { version = "2.0.1", default-features = false } nanorand = "0.7.0" nexusmods = { path = "lib/nexusmods" } -notify = "6.1.1" +notify = "7.0.0" oodle = { path = "lib/oodle" } open = "5.0.1" path-clean = "1.0.1" From b219e20f3adb62f0dc8f4717484af6bdbd723416 Mon Sep 17 00:00:00 2001 From: Renovate Date: Fri, 6 Dec 2024 20:32:37 +0000 Subject: [PATCH 246/335] chore(deps): update rust crate bindgen to 0.71.0 --- Cargo.lock | 38 ++++++++++++++++++++++++++++++++------ lib/oodle/Cargo.toml | 2 +- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7e460c7..6f6bc4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -231,7 +231,27 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.75", +] + +[[package]] +name = "bindgen" +version = "0.71.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360897d4f2fdeea5d32f6dac9287952ae5babdc2aad42ad787c9470a4a6e3fee" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.0", "shlex", "syn 2.0.75", ] @@ -1122,7 +1142,7 @@ dependencies = [ "fluent-syntax", "intl-memoizer", "intl_pluralrules", - "rustc-hash", + "rustc-hash 1.1.0", "self_cell 0.10.3", "smallvec", "unic-langid", @@ -2038,7 +2058,7 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" name = "luajit2-sys" version = "0.0.2" dependencies = [ - "bindgen", + "bindgen 0.70.1", "cc", "fs_extra", "libc", @@ -2337,7 +2357,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" name = "oodle" version = "0.1.0" dependencies = [ - "bindgen", + "bindgen 0.71.0", "color-eyre", "tracing", ] @@ -2981,6 +3001,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + [[package]] name = "rustc_version" version = "0.4.0" @@ -3883,7 +3909,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f" dependencies = [ - "rustc-hash", + "rustc-hash 1.1.0", ] [[package]] diff --git a/lib/oodle/Cargo.toml b/lib/oodle/Cargo.toml index feb9951..e679b5f 100644 --- a/lib/oodle/Cargo.toml +++ b/lib/oodle/Cargo.toml @@ -10,4 +10,4 @@ color-eyre = { workspace = true } tracing = { workspace = true } [build-dependencies] -bindgen = "0.70.1" +bindgen = "0.71.0" From adf9610eccb6d277fd962f4850de78d273502d00 Mon Sep 17 00:00:00 2001 From: Renovate Date: Fri, 10 Jan 2025 14:48:31 +0000 Subject: [PATCH 247/335] chore(deps): update rust crate notify to v8 --- Cargo.lock | 45 +++++++++++++++++++++------------------------ Cargo.toml | 2 +- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f6bc4f..67e6323 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,7 +222,7 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "cexpr", "clang-sys", "itertools", @@ -242,7 +242,7 @@ version = "0.71.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "360897d4f2fdeea5d32f6dac9287952ae5babdc2aad42ad787c9470a4a6e3fee" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "cexpr", "clang-sys", "itertools", @@ -264,9 +264,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" [[package]] name = "bitmaps" @@ -917,7 +917,7 @@ dependencies = [ "ansi-parser", "async-recursion", "bincode", - "bitflags 2.6.0", + "bitflags 2.7.0", "clap", "color-eyre", "colors-transform", @@ -1801,11 +1801,11 @@ dependencies = [ [[package]] name = "inotify" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" +checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.7.0", "inotify-sys", "libc", ] @@ -2031,7 +2031,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "libc", "redox_syscall", ] @@ -2269,11 +2269,11 @@ dependencies = [ [[package]] name = "notify" -version = "7.0.0" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c533b4c39709f9ba5005d8002048266593c1cfaf3c5f0739d5b8ab0c6c504009" +checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "filetime", "fsevent-sys", "inotify", @@ -2283,17 +2283,14 @@ dependencies = [ "mio", "notify-types", "walkdir", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "notify-types" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7393c226621f817964ffb3dc5704f9509e107a8b024b489cc2c1b217378785df" -dependencies = [ - "instant", -] +checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" [[package]] name = "nu-ansi-term" @@ -2379,7 +2376,7 @@ version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "cfg-if", "foreign-types", "libc", @@ -2831,7 +2828,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", ] [[package]] @@ -3022,7 +3019,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "errno", "libc", "linux-raw-sys", @@ -3150,7 +3147,7 @@ name = "sdk" version = "0.3.0" dependencies = [ "async-recursion", - "bitflags 2.6.0", + "bitflags 2.7.0", "byteorder", "color-eyre", "csv-async", @@ -3177,7 +3174,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "core-foundation", "core-foundation-sys", "libc", @@ -3486,7 +3483,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.7.0", "core-foundation", "system-configuration-sys", ] diff --git a/Cargo.toml b/Cargo.toml index 0ff601e..3f00fe6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ luajit2-sys = { path = "lib/luajit2-sys" } minijinja = { version = "2.0.1", default-features = false } nanorand = "0.7.0" nexusmods = { path = "lib/nexusmods" } -notify = "7.0.0" +notify = "8.0.0" oodle = { path = "lib/oodle" } open = "5.0.1" path-clean = "1.0.1" From a3583b4485196e3446ccd51da401b0ebe5860811 Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 10 Dec 2024 10:18:35 +0000 Subject: [PATCH 248/335] fix(deps): update rust crate thiserror to v2 --- Cargo.lock | 90 ++++++++++++++++++++++++---------------- lib/nexusmods/Cargo.toml | 2 +- lib/nexusmods/src/lib.rs | 4 +- 3 files changed, 58 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f6bc4f..ccb479f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,7 +141,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -233,7 +233,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -253,7 +253,7 @@ dependencies = [ "regex", "rustc-hash 2.1.0", "shlex", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -354,7 +354,7 @@ dependencies = [ "glib", "libc", "once_cell", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -448,7 +448,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -532,7 +532,7 @@ dependencies = [ "once_cell", "owo-colors", "pretty_assertions", - "thiserror", + "thiserror 1.0.63", "tracing", "tracing-error", "tracing-subscriber", @@ -578,7 +578,7 @@ checksum = "45b1f4c00870f07dc34adcac82bb6a72cc5aabca8536ba1797e01df51d2ce9a0" dependencies = [ "directories", "serde", - "thiserror", + "thiserror 1.0.63", "toml 0.8.19", ] @@ -742,7 +742,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -820,7 +820,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -1163,7 +1163,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" dependencies = [ - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1288,7 +1288,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -1434,7 +1434,7 @@ dependencies = [ "once_cell", "pin-project-lite", "smallvec", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1469,7 +1469,7 @@ dependencies = [ "libc", "once_cell", "smallvec", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1950,7 +1950,7 @@ checksum = "7e4c8354918309196302015ac9cae43362f1a13d0d5c5539a33b4c2fd2cd6d25" dependencies = [ "pest", "pest_derive", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -1961,7 +1961,7 @@ checksum = "0447866c47c00f8bd1949618e8f63017cf93e985b4684dc28d784527e2882390" dependencies = [ "keyvalues-parser", "serde", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -2217,7 +2217,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror", + "thiserror 2.0.6", "time", "tokio", "tracing", @@ -2396,7 +2396,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -2519,7 +2519,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" dependencies = [ "memchr", - "thiserror", + "thiserror 1.0.63", "ucd-trie", ] @@ -2543,7 +2543,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -2667,7 +2667,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -2724,7 +2724,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -2763,9 +2763,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -2842,7 +2842,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", - "thiserror", + "thiserror 1.0.63", ] [[package]] @@ -3232,7 +3232,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -3462,9 +3462,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.75" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -3548,7 +3548,16 @@ version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.63", +] + +[[package]] +name = "thiserror" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +dependencies = [ + "thiserror-impl 2.0.6", ] [[package]] @@ -3559,7 +3568,18 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] @@ -3680,7 +3700,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -3828,7 +3848,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -4212,7 +4232,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", "wasm-bindgen-shared", ] @@ -4246,7 +4266,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4280,7 +4300,7 @@ checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" dependencies = [ "proc-macro2", "quote", - "syn 2.0.75", + "syn 2.0.90", ] [[package]] @@ -4604,7 +4624,7 @@ dependencies = [ "flate2", "indexmap", "memchr", - "thiserror", + "thiserror 1.0.63", "time", "zopfli", "zstd", diff --git a/lib/nexusmods/Cargo.toml b/lib/nexusmods/Cargo.toml index 2f90a46..b9cc879 100644 --- a/lib/nexusmods/Cargo.toml +++ b/lib/nexusmods/Cargo.toml @@ -12,7 +12,7 @@ regex = "1.7.1" reqwest = { version = "0.12.4" } serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.94" -thiserror = "1.0.39" +thiserror = "2.0.0" time = { version = "0.3.20", features = ["serde"] } tracing = "0.1.37" url = { version = "2.3.1", features = ["serde"] } diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index 314acb1..cddf6a0 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -28,7 +28,7 @@ pub enum Error { HTTP(#[from] reqwest::Error), #[error("invalid URL: {0:?}")] URLParseError(#[from] url::ParseError), - #[error("failed to deserialize '{error}': {json}")] + #[error("failed to deserialize due to {error}: {json}")] Deserialize { json: String, error: serde_json::Error, @@ -37,7 +37,7 @@ pub enum Error { InvalidHeaderValue(#[from] InvalidHeaderValue), #[error("this error cannot happen")] Infallible(#[from] Infallible), - #[error("invalid NXM URL '{}': {0}", .1.as_str())] + #[error("invalid NXM URL '{url}': {0}", url = .1.as_str())] InvalidNXM(&'static str, Url), #[error("{0}")] Custom(String), From 5612e271fbde6b8808cc8d4c318099bec124f7b1 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 12 Mar 2025 11:26:24 +0100 Subject: [PATCH 249/335] Improve version name for CI artifacts built off master The name from `git describe --tags` is rather confusing to people that aren't familiar with it. Especially in the current situation, where there are no proper versioned releases. A name like `master-123456` should be much clearer. Closes #205. --- .ci/tasks/build.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index bb96775..11b0300 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -25,7 +25,8 @@ if [ -n "$PR" ]; then title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$(cat .git/ref || echo "HEAD")" 2>/dev/null || echo 'manual')" else - ref=$(git describe --tags) + ref=$(cat .git/ref || echo "HEAD") + ref=$(git rev-parse --abbrev-ref $ref)-$(git rev-parse --short $ref) fi title "Version: '$ref'" From beba47f340ea5f1990458029752ca0b138f226a6 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 12 Mar 2025 11:33:48 +0100 Subject: [PATCH 250/335] Push a packaged with a fixed version for master To provide something that can easily be linked to, also push packages built from `master` to a version that doesn't contain the SHA. --- .ci/pipelines/base.yml | 38 +++++++++++++++++++++++++++++++------- .ci/tasks/build.yml | 1 - 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/.ci/pipelines/base.yml b/.ci/pipelines/base.yml index 474c090..8d3cc77 100644 --- a/.ci/pipelines/base.yml +++ b/.ci/pipelines/base.yml @@ -125,8 +125,6 @@ jobs: vars: pr: "" target: msvc - gitea_url: http://forgejo:3000 - gitea_api_key: ((gitea_api_key)) - load_var: version_number reveal: true @@ -142,10 +140,21 @@ jobs: fail_fast: true override: true globs: - - artifact/dtmt - - artifact/dtmm - artifact/*.exe - - artifact/*.sha256 + - artifact/*.exe.sha256 + + - put: package + resource: gitea-package + no_get: true + inputs: + - artifact + params: + version: master + fail_fast: true + override: true + globs: + - artifact/*.exe + - artifact/*.exe.sha256 - name: build-linux on_success: @@ -202,5 +211,20 @@ jobs: globs: - artifact/dtmt - artifact/dtmm - - artifact/*.exe - - artifact/*.sha256 + - artifact/dtmm.sha256 + - artifact/dtmt.sha256 + + - put: package + resource: gitea-package + no_get: true + inputs: + - artifact + params: + version: master + fail_fast: true + override: true + globs: + - artifact/dtmt + - artifact/dtmm + - artifact/dtmm.sha256 + - artifact/dtmt.sha256 diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml index dce44d0..a7f47b4 100644 --- a/.ci/tasks/build.yml +++ b/.ci/tasks/build.yml @@ -22,7 +22,6 @@ caches: params: CI: "true" TARGET: ((target)) - GITEA_API_KEY: ((gitea_api_key)) PR: ((pr)) OUTPUT: artifact From d15f533e19edbde4062eeb2161418b90fdb859b0 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 12 Mar 2025 11:52:53 +0100 Subject: [PATCH 251/335] Fix branch name in package version --- .ci/tasks/build.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 11b0300..3cb9776 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -26,7 +26,11 @@ if [ -n "$PR" ]; then ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$(cat .git/ref || echo "HEAD")" 2>/dev/null || echo 'manual')" else ref=$(cat .git/ref || echo "HEAD") - ref=$(git rev-parse --abbrev-ref $ref)-$(git rev-parse --short $ref) + branch=$(git rev-parse --abbrev-ref $ref) + if [ -z "$branch" ]; then + branch=$(cat .git/ref) + fi + ref=${branch}-$(git rev-parse --short $ref) fi title "Version: '$ref'" From 71f945a96c95742c4655c5b05ff4b7696905bf3c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 12 Mar 2025 13:11:01 +0100 Subject: [PATCH 252/335] Explicitly define base branches Currently, the dependency dashboard lists a bunch of pending updates under a section called "Other branches". I'm not sure, but this sounds like one of the configs I extend from enables base branches other than only the default. To test, and make it explicit, set only the branches I really want checked. I'm adding the `release/.*` regex for now, even though I don't have any release process yet. --- .renovaterc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.renovaterc b/.renovaterc index b36f3b4..4a2fbf4 100644 --- a/.renovaterc +++ b/.renovaterc @@ -7,5 +7,9 @@ ":rebaseStalePrs" ], "prConcurrentLimit": 10, - "branchPrefix": "renovate/" + "branchPrefix": "renovate/", + "baseBranches": [ + "$default", + "/^release\\/.*/" + ] } From 6ba13ac1ec06c0334cccfda1fd022d7cfd291b7c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 12 Mar 2025 13:24:03 +0100 Subject: [PATCH 253/335] Fix using branch for version number --- .ci/tasks/build.sh | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 3cb9776..0b4f2aa 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -24,13 +24,10 @@ PR=${PR:-} if [ -n "$PR" ]; then title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$(cat .git/ref || echo "HEAD")" 2>/dev/null || echo 'manual')" +elif [ -f ".git/branch"]; then + ref=$(cat .git/branch)-$(git rev-parse --short $ref) else - ref=$(cat .git/ref || echo "HEAD") - branch=$(git rev-parse --abbrev-ref $ref) - if [ -z "$branch" ]; then - branch=$(cat .git/ref) - fi - ref=${branch}-$(git rev-parse --short $ref) + ref=$(git rev-parse --short "$(cat .git/ref || echo "HEAD")") fi title "Version: '$ref'" From e61a252ee643d958d4e7eac93aa098e99fce7118 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 15:18:19 +0200 Subject: [PATCH 254/335] Remove internal URLs from CI Due to using internal URLs, the pipelines demanded a very specific network setup to work. By changing everything to their public-facing URLs, they now become agnostic to internal topology. --- .ci/pipelines/base.yml | 24 ++++++++++++++++-------- .ci/pipelines/pr.yml | 22 +++++++++++++--------- Justfile | 4 +++- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/.ci/pipelines/base.yml b/.ci/pipelines/base.yml index 8d3cc77..79c1e51 100644 --- a/.ci/pipelines/base.yml +++ b/.ci/pipelines/base.yml @@ -6,24 +6,30 @@ resource_types: - name: gitea-package type: registry-image source: - repository: registry.local:5000/gitea-package + repository: registry.sclu1034.dev/gitea-package + username: ((registry_user)) + password: ((registry_password)) - name: gitea-status type: registry-image source: - repository: registry.local:5000/gitea-status + repository: registry.sclu1034.dev/gitea-status + username: ((registry_user)) + password: ((registry_password)) - name: gitea-pr type: registry-image source: - repository: registry.local:5000/gitea-pr + repository: registry.sclu1034.dev/gitea-pr + username: ((registry_user)) + password: ((registry_password)) resources: - name: repo type: git source: - uri: http://forgejo:3000/bitsquid_dt/dtmt + uri: http://git.sclu1034.dev/bitsquid_dt/dtmt branch: master - name: repo-pr @@ -38,7 +44,7 @@ resources: type: gitea-package source: access_token: ((gitea_api_key)) - url: http://forgejo:3000 + url: http://git.sclu1034.dev owner: bitsquid_dt type: generic name: dtmt @@ -48,7 +54,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://forgejo:3000 + url: http://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/msvc @@ -58,7 +64,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://forgejo:3000 + url: http://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/linux @@ -85,6 +91,8 @@ jobs: vars: pr: ((.:pr)) gitea_api_key: ((gitea_api_key)) + registry_user: ((registry_user)) + registry_password: ((registry_password)) instance_vars: number: ((.:pr.number)) @@ -192,7 +200,7 @@ jobs: vars: pr: "" target: linux - gitea_url: http://forgejo:3000 + gitea_url: http://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) - load_var: version_number diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index bca0ebb..b2f514a 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -6,26 +6,30 @@ resource_types: - name: gitea-package type: registry-image source: - repository: registry.local:5000/gitea-package + repository: registry.sclu1034.dev/gitea-package + username: ((registry_user)) + password: ((registry_password)) - name: gitea-status type: registry-image source: - repository: registry.local:5000/gitea-status + repository: registry.sclu1034.dev/gitea-status + username: ((registry_user)) + password: ((registry_password)) resources: - name: repo type: git source: - uri: http://forgejo:3000/bitsquid_dt/dtmt + uri: http://git.sclu1034.dev/bitsquid_dt/dtmt branch: ((pr.head.ref)) - name: gitea-package type: gitea-package source: access_token: ((gitea_api_key)) - url: http://forgejo:3000 + url: http://git.sclu1034.dev owner: bitsquid_dt type: generic name: dtmt @@ -34,7 +38,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://forgejo:3000 + url: http://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: lint/clippy @@ -44,7 +48,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://forgejo:3000 + url: http://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/msvc @@ -54,7 +58,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://forgejo:3000 + url: http://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/linux @@ -135,7 +139,7 @@ jobs: vars: target: msvc pr: ((pr)) - gitea_url: http://forgejo:3000 + gitea_url: http://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) - load_var: version_number @@ -193,7 +197,7 @@ jobs: vars: target: linux pr: ((pr)) - gitea_url: http://forgejo:3000 + gitea_url: http://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) - load_var: version_number diff --git a/Justfile b/Justfile index dbecc22..697bdcc 100644 --- a/Justfile +++ b/Justfile @@ -40,6 +40,8 @@ set-base-pipeline: --pipeline dtmt \ --config .ci/pipelines/base.yml \ -v gitea_api_key=${GITEA_API_KEY} \ + -v registry_user=${REGISTRY_USER} \ + -v registry_password=${REGISTRY_PASSWORD} \ -v owner=bitsquid_dt \ -v repo=dtmt @@ -48,7 +50,7 @@ set-pr-pipeline pr: -H "Authorization: ${GITEA_API_KEY}" \ -H 'Accept: application/json' \ 'https://git.sclu1034.dev/api/v1/repos/bitsquid_dt/dtmt/pulls/{{pr}}' \ - | yq -y '.' - > 'pr-{{pr}}.yaml' + | yq -y '.' - > 'pr-{{pr}}.yaml' fly -t main set-pipeline \ --pipeline dtmt-pr \ --config .ci/pipelines/pr.yml \ From 5aa8421f7dcc702f4fdfaef814056ce51c34a201 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 15:28:27 +0200 Subject: [PATCH 255/335] Fix incorrect URLs --- .ci/pipelines/base.yml | 10 +++++----- .ci/pipelines/pr.yml | 14 +++++++------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.ci/pipelines/base.yml b/.ci/pipelines/base.yml index 79c1e51..ce2682c 100644 --- a/.ci/pipelines/base.yml +++ b/.ci/pipelines/base.yml @@ -29,7 +29,7 @@ resources: - name: repo type: git source: - uri: http://git.sclu1034.dev/bitsquid_dt/dtmt + uri: https://git.sclu1034.dev/bitsquid_dt/dtmt branch: master - name: repo-pr @@ -44,7 +44,7 @@ resources: type: gitea-package source: access_token: ((gitea_api_key)) - url: http://git.sclu1034.dev + url: https://git.sclu1034.dev owner: bitsquid_dt type: generic name: dtmt @@ -54,7 +54,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://git.sclu1034.dev + url: https://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/msvc @@ -64,7 +64,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://git.sclu1034.dev + url: https://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/linux @@ -200,7 +200,7 @@ jobs: vars: pr: "" target: linux - gitea_url: http://git.sclu1034.dev + gitea_url: https://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) - load_var: version_number diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index b2f514a..3198410 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -22,14 +22,14 @@ resources: - name: repo type: git source: - uri: http://git.sclu1034.dev/bitsquid_dt/dtmt + uri: https://git.sclu1034.dev/bitsquid_dt/dtmt branch: ((pr.head.ref)) - name: gitea-package type: gitea-package source: access_token: ((gitea_api_key)) - url: http://git.sclu1034.dev + url: https://git.sclu1034.dev owner: bitsquid_dt type: generic name: dtmt @@ -38,7 +38,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://git.sclu1034.dev + url: https://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: lint/clippy @@ -48,7 +48,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://git.sclu1034.dev + url: https://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/msvc @@ -58,7 +58,7 @@ resources: type: gitea-status source: access_token: ((gitea_api_key)) - url: http://git.sclu1034.dev + url: https://git.sclu1034.dev owner: bitsquid_dt repo: dtmt context: build/linux @@ -139,7 +139,7 @@ jobs: vars: target: msvc pr: ((pr)) - gitea_url: http://git.sclu1034.dev + gitea_url: https://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) - load_var: version_number @@ -197,7 +197,7 @@ jobs: vars: target: linux pr: ((pr)) - gitea_url: http://git.sclu1034.dev + gitea_url: https://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) - load_var: version_number From c2207c22ef3c6ea384ab617b4f516e79009cc1b5 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 15:33:33 +0200 Subject: [PATCH 256/335] Fix more URLs in CI pipelines --- .ci/pipelines/base.yml | 4 ++++ .ci/pipelines/check.yml | 4 ++++ .ci/pipelines/pr.yml | 6 ++++++ .ci/tasks/build.yml | 4 +++- .ci/tasks/clippy.yml | 4 +++- 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.ci/pipelines/base.yml b/.ci/pipelines/base.yml index ce2682c..a5b0b6e 100644 --- a/.ci/pipelines/base.yml +++ b/.ci/pipelines/base.yml @@ -133,6 +133,8 @@ jobs: vars: pr: "" target: msvc + registry_user: ((registry_user)) + registry_password: ((registry_password)) - load_var: version_number reveal: true @@ -202,6 +204,8 @@ jobs: target: linux gitea_url: https://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) + registry_user: ((registry_user)) + registry_password: ((registry_password)) - load_var: version_number reveal: true diff --git a/.ci/pipelines/check.yml b/.ci/pipelines/check.yml index 4d350ac..c8d5d47 100644 --- a/.ci/pipelines/check.yml +++ b/.ci/pipelines/check.yml @@ -18,6 +18,8 @@ jobs: file: repo/.ci/tasks/build.yml vars: target: msvc + registry_user: ((registry_user)) + registry_password: ((registry_password)) - name: build-linux plan: - get: repo @@ -26,3 +28,5 @@ jobs: file: repo/.ci/tasks/build.yml vars: target: linux + registry_user: ((registry_user)) + registry_password: ((registry_password)) diff --git a/.ci/pipelines/pr.yml b/.ci/pipelines/pr.yml index 3198410..5c8d7cd 100644 --- a/.ci/pipelines/pr.yml +++ b/.ci/pipelines/pr.yml @@ -101,6 +101,8 @@ jobs: file: repo/.ci/tasks/clippy.yml vars: gitea_api_key: ((gitea_api_key)) + registry_user: ((registry_user)) + registry_password: ((registry_password)) - name: build-msvc @@ -141,6 +143,8 @@ jobs: pr: ((pr)) gitea_url: https://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) + registry_user: ((registry_user)) + registry_password: ((registry_password)) - load_var: version_number reveal: true @@ -199,6 +203,8 @@ jobs: pr: ((pr)) gitea_url: https://git.sclu1034.dev gitea_api_key: ((gitea_api_key)) + registry_user: ((registry_user)) + registry_password: ((registry_password)) - load_var: version_number reveal: true diff --git a/.ci/tasks/build.yml b/.ci/tasks/build.yml index a7f47b4..9b9c094 100644 --- a/.ci/tasks/build.yml +++ b/.ci/tasks/build.yml @@ -6,7 +6,9 @@ image_resource: name: ctmt-bi-base-((target)) type: registry-image source: - repository: registry.local:5000/dtmt-ci-base-((target)) + repository: registry.sclu1034.dev/dtmt-ci-base-((target)) + username: ((registry_user)) + password: ((registry_password)) tag: latest inputs: diff --git a/.ci/tasks/clippy.yml b/.ci/tasks/clippy.yml index 483cb30..806dfd4 100644 --- a/.ci/tasks/clippy.yml +++ b/.ci/tasks/clippy.yml @@ -6,7 +6,9 @@ image_resource: name: dtmt-ci-base-linux type: registry-image source: - repository: registry.local:5000/dtmt-ci-base-linux + repository: registry.sclu1034.dev/dtmt-ci-base-linux + username: ((registry_user)) + password: ((registry_password)) tag: latest inputs: From 1b56acfd6328dc96e74365cd16c838bf95e5f45b Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 13:42:19 +0000 Subject: [PATCH 257/335] chore(deps): update rust crate bindgen to v0.71.1 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a9af3f..6fe2e80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -238,9 +238,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.71.0" +version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360897d4f2fdeea5d32f6dac9287952ae5babdc2aad42ad787c9470a4a6e3fee" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ "bitflags 2.7.0", "cexpr", @@ -2022,7 +2022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -2354,7 +2354,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" name = "oodle" version = "0.1.0" dependencies = [ - "bindgen 0.71.0", + "bindgen 0.71.1", "color-eyre", "tracing", ] From 96c0953cf9e1a6610859806e680c3993526e0a5a Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 13:42:24 +0000 Subject: [PATCH 258/335] chore(deps): update rust crate clap to v4.5.37 --- Cargo.lock | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a9af3f..516a0e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -417,9 +417,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" dependencies = [ "clap_builder", "clap_derive", @@ -427,23 +427,23 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", "unicase", - "unicode-width", + "unicode-width 0.2.0", ] [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -453,9 +453,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "cli-table" @@ -465,7 +465,7 @@ checksum = "b53f9241f288a7b12c56565f04aaeaeeab6b8923d42d99255d4ca428b4d97f89" dependencies = [ "cli-table-derive", "termcolor", - "unicode-width", + "unicode-width 0.1.13", ] [[package]] @@ -2022,7 +2022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -3101,7 +3101,7 @@ dependencies = [ "scopeguard", "smallvec", "unicode-segmentation", - "unicode-width", + "unicode-width 0.1.13", "utf8parse", "winapi", ] @@ -4082,6 +4082,12 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "untrusted" version = "0.9.0" From 5fa6ecfd0d0faea187fed641b8da2a8225f5170b Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 13:42:33 +0000 Subject: [PATCH 259/335] chore(deps): update rust crate interprocess to v2.2.3 --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a9af3f..0def154 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1833,9 +1833,9 @@ dependencies = [ [[package]] name = "interprocess" -version = "2.2.1" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f4e4a06d42fab3e85ab1b419ad32b09eab58b901d40c57935ff92db3287a13" +checksum = "d941b405bd2322993887859a8ee6ac9134945a24ec5ec763a8a962fc64dfec2d" dependencies = [ "doctest-file", "libc", @@ -2022,7 +2022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] From 9e209add0c46e6f1cc4d33d96a05ae6b8bd23ac0 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 13:42:36 +0000 Subject: [PATCH 260/335] chore(deps): update rust crate open to v5.3.2 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a9af3f..2d95ba6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2361,9 +2361,9 @@ dependencies = [ [[package]] name = "open" -version = "5.3.0" +version = "5.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" dependencies = [ "is-wsl", "libc", From f1291bdf33cb609ac72f6dec7e88a00dcea87442 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 13:42:40 +0000 Subject: [PATCH 261/335] chore(deps): update rust crate pin-project-lite to v0.2.16 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a9af3f..a2c61f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2669,9 +2669,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" From b19225c75a4b647b9a58f8282c3197a5a4109fbd Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 13:42:48 +0000 Subject: [PATCH 262/335] chore(deps): update rust crate strip-ansi-escapes to v0.2.1 --- Cargo.lock | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a9af3f..695fa27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3408,9 +3408,9 @@ dependencies = [ [[package]] name = "strip-ansi-escapes" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" +checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025" dependencies = [ "vte", ] @@ -4164,22 +4164,11 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vte" -version = "0.11.1" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" +checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077" dependencies = [ - "utf8parse", - "vte_generate_state_changes", -] - -[[package]] -name = "vte_generate_state_changes" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" -dependencies = [ - "proc-macro2", - "quote", + "memchr", ] [[package]] From 86b8f9e40ff19b9510023c36774c2a24262b09fc Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:16:06 +0000 Subject: [PATCH 263/335] chore(deps): update rust crate glob to v0.3.2 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b29c8f..ffd7666 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1499,9 +1499,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "gobject-sys" From 64d9aa12aac1bb5594cc93ebd03174875dfe1486 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:16:16 +0000 Subject: [PATCH 264/335] chore(deps): update rust crate tokio-stream to v0.1.17 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b29c8f..93832be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3723,9 +3723,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", From f2bf1493e528e887df4545cb0f66f29087b3ad67 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:16:28 +0000 Subject: [PATCH 265/335] fix(deps): update rust crate reqwest to v0.12.15 --- Cargo.lock | 180 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 120 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b29c8f..6f34a57 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1673,9 +1673,9 @@ checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "hyper" -version = "1.4.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", @@ -1726,9 +1726,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-channel", @@ -1736,10 +1736,10 @@ dependencies = [ "http", "http-body", "hyper", + "libc", "pin-project-lite", "socket2", "tokio", - "tower", "tower-service", "tracing", ] @@ -1926,10 +1926,11 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2011,9 +2012,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libloading" @@ -2647,26 +2648,6 @@ dependencies = [ "xi-unicode", ] -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "pin-project-lite" version = "0.2.16" @@ -2888,9 +2869,9 @@ checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "reqwest" -version = "0.12.7" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ "base64 0.22.1", "bytes", @@ -2921,6 +2902,7 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", + "tower", "tower-service", "url", "wasm-bindgen", @@ -3066,6 +3048,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + [[package]] name = "rustybuzz" version = "0.6.0" @@ -3357,9 +3345,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -3801,14 +3789,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.13" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -4204,24 +4192,24 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn 2.0.90", @@ -4242,9 +4230,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4252,9 +4240,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -4265,9 +4253,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-bindgen-test" @@ -4339,7 +4330,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -4349,33 +4340,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-registry" -version = "0.2.0" +name = "windows-link" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result", "windows-strings", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] name = "windows-result" -version = "0.2.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" dependencies = [ - "windows-result", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -4429,13 +4425,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -4448,6 +4460,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -4460,6 +4478,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -4472,12 +4496,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -4490,6 +4526,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -4502,6 +4544,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -4514,6 +4562,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -4526,6 +4580,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.5.40" From e6744404a948f211a0dbba2eb8528cfcfc6c6cd8 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 17:35:58 +0200 Subject: [PATCH 266/335] Make PR pipelines public --- .ci/pipelines/base.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/pipelines/base.yml b/.ci/pipelines/base.yml index a5b0b6e..4e7ed14 100644 --- a/.ci/pipelines/base.yml +++ b/.ci/pipelines/base.yml @@ -88,6 +88,7 @@ jobs: values: ((.:prs)) set_pipeline: dtmt-pr file: repo/.ci/pipelines/pr.yml + public: true vars: pr: ((.:pr)) gitea_api_key: ((gitea_api_key)) From bbb701176ffe1d2a54978fb77570dba7dfb354c8 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:46:05 +0000 Subject: [PATCH 267/335] fix(deps): update rust crate serde to v1.0.219 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..4273331 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3202,18 +3202,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.209" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", From b119b0d38da4048561f6f3c84a6f1ac22a1f5888 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:46:14 +0000 Subject: [PATCH 268/335] fix(deps): update rust crate serde_json to v1.0.140 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..c81ed9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3222,9 +3222,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", From ccac8e3859f1e0d634fd5f27928882fb2fdd6e8d Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:46:21 +0000 Subject: [PATCH 269/335] fix(deps): update rust crate thiserror to v2.0.12 --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..03da545 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2218,7 +2218,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror 2.0.6", + "thiserror 2.0.12", "time", "tokio", "tracing", @@ -3538,11 +3538,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.6" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.6", + "thiserror-impl 2.0.12", ] [[package]] @@ -3558,9 +3558,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.6" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", From 36fba192c0a2bae82509d4ee454265b6ec806db0 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:46:46 +0000 Subject: [PATCH 270/335] fix(deps): update rust-futures monorepo to v0.3.31 --- Cargo.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..1eb170d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1234,9 +1234,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1249,9 +1249,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1259,15 +1259,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1276,15 +1276,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -1293,21 +1293,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", From bcab6e697f4c3955f96875e7270dbee5bf0b98bd Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:46:56 +0000 Subject: [PATCH 271/335] fix(deps): update tokio-tracing monorepo --- Cargo.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..65cb15a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3816,9 +3816,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -3827,9 +3827,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", @@ -3838,9 +3838,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -3848,9 +3848,9 @@ dependencies = [ [[package]] name = "tracing-error" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" dependencies = [ "tracing", "tracing-subscriber", @@ -3869,9 +3869,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "matchers", "nu-ansi-term", From be28b7045cdeefe7f35e17f5935d332cef52acef Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:47:03 +0000 Subject: [PATCH 272/335] chore(deps): update rust crate bitflags to v2.9.0 --- Cargo.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..fa94879 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -222,7 +222,7 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools", @@ -242,7 +242,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "cexpr", "clang-sys", "itertools", @@ -264,9 +264,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.7.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "bitmaps" @@ -917,7 +917,7 @@ dependencies = [ "ansi-parser", "async-recursion", "bincode", - "bitflags 2.7.0", + "bitflags 2.9.0", "clap", "color-eyre", "colors-transform", @@ -1805,7 +1805,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "inotify-sys", "libc", ] @@ -2032,7 +2032,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "libc", "redox_syscall", ] @@ -2274,7 +2274,7 @@ version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "filetime", "fsevent-sys", "inotify", @@ -2377,7 +2377,7 @@ version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "cfg-if", "foreign-types", "libc", @@ -2809,7 +2809,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", ] [[package]] @@ -3001,7 +3001,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys", @@ -3135,7 +3135,7 @@ name = "sdk" version = "0.3.0" dependencies = [ "async-recursion", - "bitflags 2.7.0", + "bitflags 2.9.0", "byteorder", "color-eyre", "csv-async", @@ -3162,7 +3162,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "core-foundation", "core-foundation-sys", "libc", @@ -3471,7 +3471,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "core-foundation", "system-configuration-sys", ] From c2f02e7ce67fad873db7f09cf86816d4228a0fe7 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:47:13 +0000 Subject: [PATCH 273/335] chore(deps): update rust crate fastrand to v2.3.0 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..b475485 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1070,9 +1070,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fd-lock" From 3f3c4aa3210120448115bdae046e434e10d45a73 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:47:20 +0000 Subject: [PATCH 274/335] chore(deps): update rust crate minijinja to v2.9.0 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..198c53c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2140,9 +2140,9 @@ dependencies = [ [[package]] name = "minijinja" -version = "2.2.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7d3e3a3eece1fa4618237ad41e1de855ced47eab705cec1c9a920e1d1c5aad" +checksum = "98642a6dfca91122779a307b77cd07a4aa951fbe32232aaf5bad9febc66be754" dependencies = [ "serde", ] From 221a6b8485508ee758bbb26b63bb0008e3ec898e Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:47:28 +0000 Subject: [PATCH 275/335] chore(deps): update rust crate tempfile to v3.19.1 --- Cargo.lock | 81 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..5d27dfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1040,12 +1040,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1081,7 +1081,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5" dependencies = [ "cfg-if", - "rustix", + "rustix 0.38.34", "windows-sys 0.48.0", ] @@ -1398,7 +1398,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -2043,6 +2055,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "lockfree-object-pool" version = "0.1.6" @@ -2181,7 +2199,7 @@ dependencies = [ "hermit-abi", "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -2766,6 +2784,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "radix_trie" version = "0.2.1" @@ -2818,7 +2842,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror 1.0.63", ] @@ -2946,7 +2970,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", "spin", "untrusted", @@ -3004,10 +3028,23 @@ dependencies = [ "bitflags 2.7.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.14", "windows-sys 0.52.0", ] +[[package]] +name = "rustix" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +dependencies = [ + "bitflags 2.7.0", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.59.0", +] + [[package]] name = "rustls" version = "0.23.12" @@ -3507,14 +3544,14 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.12.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ - "cfg-if", "fastrand", + "getrandom 0.3.2", "once_cell", - "rustix", + "rustix 1.0.5", "windows-sys 0.59.0", ] @@ -4190,6 +4227,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.100" @@ -4632,6 +4678,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.7.0", +] + [[package]] name = "xi-unicode" version = "0.3.0" From 42e2eb7cc12fbf5192c95e5c846b241580644d54 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:47:37 +0000 Subject: [PATCH 276/335] chore(deps): update rust crate tokio to v1.44.2 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..e613ae1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3661,9 +3661,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.3" +version = "1.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" dependencies = [ "backtrace", "bytes", @@ -3679,9 +3679,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", From bdce3e7787ecef128c3ba329baaf9967673de976 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:47:48 +0000 Subject: [PATCH 277/335] chore(deps): update rust crate zip to v2.6.1 --- Cargo.lock | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..9470462 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,9 +108,9 @@ checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" dependencies = [ "derive_arbitrary", ] @@ -324,22 +324,20 @@ checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "bzip2" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" dependencies = [ "bzip2-sys", - "libc", ] [[package]] name = "bzip2-sys" -version = "0.1.11+1.0.8" +version = "0.1.13+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" dependencies = [ "cc", - "libc", "pkg-config", ] @@ -679,9 +677,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" @@ -726,9 +724,9 @@ checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", "serde", @@ -736,9 +734,9 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", @@ -3579,9 +3577,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -3596,15 +3594,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -4664,19 +4662,17 @@ checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zip" -version = "2.2.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5e4288ea4057ae23afc69a4472434a87a2495cafce6632fd1c4ec9f5cf3494" +checksum = "1dcb24d0152526ae49b9b96c1dcf71850ca1e0b882e4e28ed898a93c41334744" dependencies = [ "arbitrary", "bzip2", "crc32fast", "crossbeam-utils", - "displaydoc", "flate2", "indexmap", "memchr", - "thiserror 1.0.63", "time", "zopfli", "zstd", From be7b16c5ef2a88199b6a644313f2a01ada08402d Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 15:47:56 +0000 Subject: [PATCH 278/335] fix(deps): update rust crate regex to v1.11.1 --- Cargo.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d693ce1..e3400e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2825,14 +2825,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] @@ -2846,13 +2846,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -2863,9 +2863,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" From 1a000371faad19de32863a2e7bca270a0db6104f Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 18:41:59 +0200 Subject: [PATCH 279/335] Treat lint warnings as errors in CI Warnings will not show up if they don't fail CI. --- .ci/tasks/clippy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/tasks/clippy.sh b/.ci/tasks/clippy.sh index 33901a9..7c27b5f 100755 --- a/.ci/tasks/clippy.sh +++ b/.ci/tasks/clippy.sh @@ -10,6 +10,6 @@ title "Install clippy" rustup component add clippy title "Run clippy" -cargo clippy --color always --no-deps +cargo clippy --color always --no-deps -- -D warnings title "Done" From 9ac13834a1b1adceffdef3d1a9bcdda26b8fc121 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 18:45:18 +0200 Subject: [PATCH 280/335] Commit lock file changes Seems like Renovate missed this. --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbc1c09..7417133 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3036,7 +3036,7 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", "errno", "libc", "linux-raw-sys 0.9.4", @@ -4682,7 +4682,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.9.0", ] [[package]] From 38a023bea638c4d2d555d68f9145db7139dcc0ed Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 22:52:54 +0200 Subject: [PATCH 281/335] Move serde_sjson to its own project --- .gitmodules | 3 --- Cargo.lock | 31 +++++++++++++++++++++---------- Cargo.toml | 3 +-- lib/serde_sjson | 1 - 4 files changed, 22 insertions(+), 16 deletions(-) delete mode 160000 lib/serde_sjson diff --git a/.gitmodules b/.gitmodules index a03eebf..e1964a8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "lib/serde_sjson"] - path = lib/serde_sjson - url = https://git.sclu1034.dev/lucas/serde_sjson.git [submodule "lib/luajit2-sys"] path = lib/luajit2-sys url = https://github.com/sclu1034/luajit2-sys.git diff --git a/Cargo.lock b/Cargo.lock index 7417133..874331a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,7 +39,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43e7fd8284f025d0bd143c2855618ecdf697db55bde39211e5c9faec7669173" dependencies = [ "heapless", - "nom", + "nom 7.1.3", ] [[package]] @@ -383,7 +383,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] @@ -2033,7 +2033,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -2274,14 +2274,23 @@ dependencies = [ ] [[package]] -name = "nom_locate" -version = "4.2.0" +name = "nom" +version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "nom_locate" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b577e2d69827c4740cba2b52efaad1c4cc7c73042860b199710b3575c68438d" dependencies = [ "bytecount", "memchr", - "nom", + "nom 8.0.0", ] [[package]] @@ -3269,9 +3278,11 @@ dependencies = [ [[package]] name = "serde_sjson" -version = "1.0.0" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c5b0d7af37492e99b8559436ee76b9ec8935c75449b1c2ef08c205a9e92ae6f" dependencies = [ - "nom", + "nom 8.0.0", "nom_locate", "serde", ] @@ -4374,7 +4385,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3f00fe6..31130f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ members = [ "lib/dtmt-shared", "lib/oodle", "lib/sdk", - "lib/serde_sjson", "lib/luajit2-sys", "lib/color-eyre", ] @@ -47,7 +46,7 @@ pin-project-lite = "0.2.9" promptly = "0.3.1" sdk = { path = "lib/sdk" } serde = { version = "1.0.152", features = ["derive", "rc"] } -serde_sjson = { path = "lib/serde_sjson" } +serde_sjson = "1.2.1" steamlocate = "2.0.0-beta.2" strip-ansi-escapes = "0.2.0" time = { version = "0.3.20", features = ["serde", "serde-well-known", "local-offset", "formatting", "macros"] } diff --git a/lib/serde_sjson b/lib/serde_sjson deleted file mode 160000 index 73d2b23..0000000 --- a/lib/serde_sjson +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 73d2b23ce50e75b184f5092ad515e97a0adbe6da From 3d05a2395e005de0443afb82a8c90c434f597a83 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 23:13:36 +0200 Subject: [PATCH 282/335] Fix clippy warnings --- Cargo.toml | 2 +- crates/dtmm/src/controller/deploy.rs | 15 ++++----------- crates/dtmm/src/controller/game.rs | 6 +++--- crates/dtmm/src/controller/import.rs | 2 +- crates/dtmt/src/cmd/bundle/extract.rs | 2 +- crates/dtmt/src/shell_parse.rs | 14 ++++---------- lib/sdk/src/bundle/mod.rs | 2 +- lib/sdk/src/filetype/lua.rs | 8 ++++---- lib/sdk/src/filetype/strings.rs | 8 ++++++-- 9 files changed, 25 insertions(+), 34 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 31130f4..23e92ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ glob = "0.3.0" interprocess = "2.1.0" lazy_static = "1.4.0" luajit2-sys = { path = "lib/luajit2-sys" } -minijinja = { version = "2.0.1", default-features = false } +minijinja = { version = "2.0.1", default-features = false, features = ["serde"] } nanorand = "0.7.0" nexusmods = { path = "lib/nexusmods" } notify = "8.0.0" diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index 02b6860..481b07c 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -116,14 +116,14 @@ async fn patch_game_settings(state: Arc) -> Result<()> { eyre::bail!("couldn't find 'boot_script' field"); }; - f.write_all(settings[0..i].as_bytes()).await?; + f.write_all(&settings.as_bytes()[0..i]).await?; f.write_all(b"boot_script = \"scripts/mod_main\"").await?; let Some(j) = settings[i..].find('\n') else { eyre::bail!("couldn't find end of 'boot_script' field"); }; - f.write_all(settings[(i + j)..].as_bytes()).await?; + f.write_all(&settings.as_bytes()[(i + j)..]).await?; Ok(()) } @@ -453,10 +453,7 @@ async fn build_bundles(state: Arc) -> Result> { } #[tracing::instrument(skip_all)] -async fn patch_boot_bundle( - state: Arc, - deployment_info: &String, -) -> Result> { +async fn patch_boot_bundle(state: Arc, deployment_info: &str) -> Result> { let bundle_dir = Arc::new(state.game_dir.join("bundle")); let bundle_path = bundle_dir.join(format!("{:x}", Murmur64::hash(BOOT_BUNDLE_NAME.as_bytes()))); @@ -590,11 +587,7 @@ fn build_deployment_data( .map(|bundle| format!("{:x}", bundle.name().to_murmur64())) .collect(), // TODO: - mod_folders: mod_folders - .as_ref() - .iter() - .map(|folder| folder.clone()) - .collect(), + mod_folders: mod_folders.as_ref().to_vec(), }; serde_sjson::to_string(&info).wrap_err("Failed to serizalize deployment data") } diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 6b91169..b93d985 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -91,14 +91,14 @@ async fn patch_game_settings(state: Arc) -> Result<()> { eyre::bail!("couldn't find 'boot_script' field"); }; - f.write_all(settings[0..i].as_bytes()).await?; + f.write_all(&settings.as_bytes()[0..i]).await?; f.write_all(b"boot_script = \"scripts/mod_main\"").await?; let Some(j) = settings[i..].find('\n') else { eyre::bail!("couldn't find end of 'boot_script' field"); }; - f.write_all(settings[(i + j)..].as_bytes()).await?; + f.write_all(&settings.as_bytes()[(i + j)..]).await?; Ok(()) } @@ -208,7 +208,7 @@ pub(crate) async fn reset_mod_deployment(state: ActionState) -> Result<()> { for p in paths { let path = bundle_dir.join(p); - let backup = bundle_dir.join(&format!("{}.bak", p)); + let backup = bundle_dir.join(format!("{}.bak", p)); let res = async { tracing::debug!( diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 2f5f90b..6fc9693 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -397,7 +397,7 @@ fn extract_legacy_mod( tracing::trace!("Writing file '{}'", name.display()); let mut out = std::fs::OpenOptions::new() .write(true) - .create(true) + .truncate(true) .open(&name) .wrap_err_with(|| format!("Failed to open file '{}'", name.display()))?; diff --git a/crates/dtmt/src/cmd/bundle/extract.rs b/crates/dtmt/src/cmd/bundle/extract.rs index 9a0f1dd..75f1360 100644 --- a/crates/dtmt/src/cmd/bundle/extract.rs +++ b/crates/dtmt/src/cmd/bundle/extract.rs @@ -150,7 +150,7 @@ async fn parse_command_line_template(tmpl: &String) -> Result { String::from_utf8_unchecked(bytes.to_vec()) }); - while let Some(arg) = parsed.next() { + for arg in parsed.by_ref() { // Safety: See above. cmd.arg(unsafe { String::from_utf8_unchecked(arg.to_vec()) }); } diff --git a/crates/dtmt/src/shell_parse.rs b/crates/dtmt/src/shell_parse.rs index 6f35a5f..13b3c4d 100644 --- a/crates/dtmt/src/shell_parse.rs +++ b/crates/dtmt/src/shell_parse.rs @@ -54,17 +54,11 @@ impl<'a> ShellParser<'a> { } _ => {} }, - ParserState::SingleQuote => match c { - b'\'' => { - return Some(&self.bytes[start..(self.offset - 1)]); - } - _ => {} + ParserState::SingleQuote => if c == b'\'' { + return Some(&self.bytes[start..(self.offset - 1)]); }, - ParserState::DoubleQuote => match c { - b'"' => { - return Some(&self.bytes[start..(self.offset - 1)]); - } - _ => {} + ParserState::DoubleQuote => if c == b'"' { + return Some(&self.bytes[start..(self.offset - 1)]); }, } } diff --git a/lib/sdk/src/bundle/mod.rs b/lib/sdk/src/bundle/mod.rs index ca36393..075f4d2 100644 --- a/lib/sdk/src/bundle/mod.rs +++ b/lib/sdk/src/bundle/mod.rs @@ -237,7 +237,7 @@ impl Bundle { // Ceiling division (or division toward infinity) to calculate // the number of chunks required to fit the unpacked data. - let num_chunks = (unpacked_data.len() + CHUNK_SIZE - 1) / CHUNK_SIZE; + let num_chunks = unpacked_data.len().div_ceil(CHUNK_SIZE); tracing::trace!(num_chunks); w.write_u32(num_chunks as u32)?; diff --git a/lib/sdk/src/filetype/lua.rs b/lib/sdk/src/filetype/lua.rs index 14f0d6b..dd0494e 100644 --- a/lib/sdk/src/filetype/lua.rs +++ b/lib/sdk/src/filetype/lua.rs @@ -125,7 +125,7 @@ pub fn compile(name: impl Into, code: impl AsRef) -> Result, code: impl AsRef) -> Result unreachable!(), } - lua::lua_setglobal(state, b"fn\0".as_ptr() as _); + lua::lua_setglobal(state, c"fn".as_ptr()); - let run = b"return string.dump(fn, false)\0"; - match lua::luaL_loadstring(state, run.as_ptr() as _) as u32 { + let run = c"return string.dump(fn, false)"; + match lua::luaL_loadstring(state, run.as_ptr()) as u32 { lua::LUA_OK => {} lua::LUA_ERRSYNTAX => { let err = lua::lua_tostring(state, -1); diff --git a/lib/sdk/src/filetype/strings.rs b/lib/sdk/src/filetype/strings.rs index ca2ed8c..8643266 100644 --- a/lib/sdk/src/filetype/strings.rs +++ b/lib/sdk/src/filetype/strings.rs @@ -28,10 +28,14 @@ impl Language { #[derive(serde::Serialize)] pub struct Strings(HashMap>); +#[inline(always)] fn read_string(r: R) -> Result where R: Read, { + // We can safely ignore the warning here, as all data is already in memory, and no additional + // `BufReader` should be needed. + #[allow(clippy::unbuffered_bytes)] r.bytes() .take_while(|b| b.as_ref().map(|b| *b != 0).unwrap_or(false)) .map(|b| b.map_err(Report::new)) @@ -41,7 +45,7 @@ where impl Strings { #[tracing::instrument(skip_all, fields(languages = variants.len()))] - pub fn from_variants(ctx: &crate::Context, variants: &Vec) -> Result { + pub fn from_variants(ctx: &crate::Context, variants: &[BundleFileVariant]) -> Result { let mut map: HashMap> = HashMap::new(); for (i, variant) in variants.iter().enumerate() { @@ -76,7 +80,7 @@ impl Strings { } #[tracing::instrument(skip_all)] -pub fn decompile(ctx: &crate::Context, variants: &Vec) -> Result> { +pub fn decompile(ctx: &crate::Context, variants: &[BundleFileVariant]) -> Result> { let strings = Strings::from_variants(ctx, variants)?; let content = strings.to_sjson()?; From bb6396c9321291a70f90a15116edc3d229bdf5a7 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 23:15:08 +0200 Subject: [PATCH 283/335] Fix build script --- .ci/tasks/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 0b4f2aa..68c61a0 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -24,7 +24,7 @@ PR=${PR:-} if [ -n "$PR" ]; then title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$(cat .git/ref || echo "HEAD")" 2>/dev/null || echo 'manual')" -elif [ -f ".git/branch"]; then +elif [ -f ".git/branch" ]; then ref=$(cat .git/branch)-$(git rev-parse --short $ref) else ref=$(git rev-parse --short "$(cat .git/ref || echo "HEAD")") From bf5c21dd03fb9e9ed74b1609399feb41ff05da12 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 21:46:09 +0000 Subject: [PATCH 284/335] chore(deps): update rust crate steamlocate to v2.0.1 --- Cargo.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 874331a..f4ff703 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -768,15 +768,6 @@ dependencies = [ "dirs-sys", ] -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - [[package]] name = "dirs-next" version = "2.0.0" @@ -1641,6 +1632,15 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "http" version = "1.1.0" @@ -3413,12 +3413,12 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "steamlocate" -version = "2.0.0-beta.2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3b6a4810c4e7fecb0123a9a8ba99b335c17d92e636c265ef99108ee4734c812" +checksum = "a13160bc6ea5cd80cde195ad4a4c629701db2bf397b62c139aa9e739016d2499" dependencies = [ "crc", - "dirs", + "home", "keyvalues-parser", "keyvalues-serde", "serde", @@ -4661,12 +4661,12 @@ dependencies = [ [[package]] name = "winreg" -version = "0.51.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "937f3df7948156640f46aacef17a70db0de5917bda9c92b0f751f3a955b588fc" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] From 6e58449dac11edf43770971e3f5907e1d4623310 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 21:46:13 +0000 Subject: [PATCH 285/335] fix(deps): update rust crate url to v2.5.4 --- Cargo.lock | 276 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 245 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 874331a..a788e09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1755,13 +1755,142 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.5.0" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -2059,6 +2188,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + [[package]] name = "lockfree-object-pool" version = "0.1.6" @@ -3511,6 +3646,17 @@ dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "system-configuration" version = "0.6.0" @@ -3688,23 +3834,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" version = "1.44.2" @@ -4083,15 +4215,6 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-script" version = "0.5.6" @@ -4130,9 +4253,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -4166,12 +4289,24 @@ dependencies = [ "xmlwriter", ] +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + [[package]] name = "utf16_lit" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14706d2a800ee8ff38c1d3edb873cd616971ea59eb7c0d046bb44ef59b06a1ae" +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -4696,6 +4831,18 @@ dependencies = [ "bitflags 2.9.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "xi-unicode" version = "0.3.0" @@ -4720,12 +4867,79 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "zip" version = "2.6.1" From 83de50409b9a31822508c4fe2b947f8d5443dcd9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Mon, 21 Apr 2025 23:55:55 +0200 Subject: [PATCH 286/335] Fix locating Steam game path --- lib/dtmt-shared/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/dtmt-shared/src/lib.rs b/lib/dtmt-shared/src/lib.rs index d1f69c7..db11579 100644 --- a/lib/dtmt-shared/src/lib.rs +++ b/lib/dtmt-shared/src/lib.rs @@ -87,7 +87,7 @@ pub fn collect_game_info() -> Result> { .find_app(STEAMAPP_ID) .wrap_err("Failed to look up game by Steam app ID")?; - let Some((app, _)) = found else { + let Some((app, library)) = found else { return Ok(None); }; @@ -96,7 +96,7 @@ pub fn collect_game_info() -> Result> { .ok_or_eyre("Missing field 'last_updated'")?; Ok(Some(GameInfo { - path: app.install_dir.into(), + path: library.path().join(app.install_dir), last_updated: last_updated.into(), })) } From ddc69112bc4f1985f06e8768eb04cd1ec2e22bf3 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 22:01:08 +0000 Subject: [PATCH 287/335] chore(deps): update rust crate cli-table to 0.5.0 --- Cargo.lock | 70 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a788e09..c69417f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,7 +141,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -233,7 +233,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -253,7 +253,7 @@ dependencies = [ "regex", "rustc-hash 2.1.0", "shlex", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -446,7 +446,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -457,24 +457,24 @@ checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "cli-table" -version = "0.4.9" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53f9241f288a7b12c56565f04aaeaeeab6b8923d42d99255d4ca428b4d97f89" +checksum = "14da8d951cef7cc4f13ccc9b744d736963d57863c7e6fc33c070ea274546082c" dependencies = [ "cli-table-derive", "termcolor", - "unicode-width 0.1.13", + "unicode-width 0.2.0", ] [[package]] name = "cli-table-derive" -version = "0.4.6" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e83a93253aaae7c74eb7428ce4faa6e219ba94886908048888701819f82fb94" +checksum = "9f7c1b60bae2c3d45228dfb096046aa51ef6c300de70b658d7a13fcb0c4f832e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.100", ] [[package]] @@ -740,7 +740,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -818,7 +818,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -1286,7 +1286,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -1869,7 +1869,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -2554,7 +2554,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -2701,7 +2701,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -2862,7 +2862,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -2901,9 +2901,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -2919,9 +2919,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.36" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -3396,7 +3396,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -3628,9 +3628,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.90" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -3654,7 +3654,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -3745,7 +3745,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -3756,7 +3756,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -3863,7 +3863,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -4011,7 +4011,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -4402,7 +4402,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", "wasm-bindgen-shared", ] @@ -4436,7 +4436,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4473,7 +4473,7 @@ checksum = "4b8220be1fa9e4c889b30fd207d4906657e7e90b12e0e6b0c8b8d8709f5de021" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] @@ -4887,7 +4887,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", "synstructure", ] @@ -4908,7 +4908,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", "synstructure", ] @@ -4937,7 +4937,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.90", + "syn 2.0.100", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 23e92ef..b5fd830 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ bincode = "1.3.3" bitflags = "2.5.0" byteorder = "1.4.3" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } -cli-table = { version = "0.4.7", default-features = false, features = ["derive"] } +cli-table = { version = "0.5.0", default-features = false, features = ["derive"] } color-eyre = { path = "lib/color-eyre" } colors-transform = "0.2.11" confy = "0.6.1" From afb5bc07954386e1c3bd0a8f2777cffd387220bb Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 21 Apr 2025 22:01:13 +0000 Subject: [PATCH 288/335] chore(deps): update rust crate bincode to v2 --- Cargo.lock | 27 +++++++++++++++++++++++++-- Cargo.toml | 2 +- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a788e09..358c5a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -209,11 +209,22 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bincode" -version = "1.3.3" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" dependencies = [ + "bincode_derive", "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +dependencies = [ + "virtue", ] [[package]] @@ -4251,6 +4262,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + [[package]] name = "url" version = "2.5.4" @@ -4337,6 +4354,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + [[package]] name = "vte" version = "0.14.1" diff --git a/Cargo.toml b/Cargo.toml index 23e92ef..a4667fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ exclude = ["lib/color-eyre"] ansi-parser = "0.9.1" ansi_term = "0.12.1" async-recursion = "1.0.5" -bincode = "1.3.3" +bincode = "2.0.0" bitflags = "2.5.0" byteorder = "1.4.3" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } From 72ab8811c34502fbcfd34db76dd253fd798ef119 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 22 Apr 2025 00:05:29 +0200 Subject: [PATCH 289/335] Ignore broken pipe error when printing dictionary stdout closing prematurely is normal behaviour, e.g. piping into `head`. --- crates/dtmt/src/cmd/dictionary.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/dtmt/src/cmd/dictionary.rs b/crates/dtmt/src/cmd/dictionary.rs index 62a5295..4c54c34 100644 --- a/crates/dtmt/src/cmd/dictionary.rs +++ b/crates/dtmt/src/cmd/dictionary.rs @@ -227,9 +227,12 @@ pub(crate) async fn run(mut ctx: sdk::Context, matches: &ArgMatches) -> Result<( let lookup = &ctx.lookup; let rows: Vec<_> = lookup.entries().iter().map(TableRow::from).collect(); - print_stdout(rows.with_title())?; - - Ok(()) + match print_stdout(rows.with_title()) { + Ok(_) => Ok(()), + // Closing stdout prematurely is normal behavior with things like piping into `head` + Err(err) if err.kind() == std::io::ErrorKind::BrokenPipe => Ok(()), + Err(err) => Err(err.into()), + } } _ => unreachable!( "clap is configured to require a subcommand, and they're all handled above" From cd4a953a63785cc9bd22fdc549bff11ea02b804c Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 22 Apr 2025 00:14:43 +0200 Subject: [PATCH 290/335] Update bincode --- crates/dtmm/src/main.rs | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 6c0bb48..54e101a 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -52,10 +52,14 @@ fn notify_nxm_download( tracing::debug!("Connected to main process at '{}'", IPC_ADDRESS); - bincode::serialize_into(&mut stream, uri.as_ref()).wrap_err("Failed to send URI")?; + let bincode_config = bincode::config::standard(); + + bincode::encode_into_std_write(uri.as_ref(), &mut stream, bincode_config) + .wrap_err("Failed to send URI")?; // We don't really care what the message is, we just need an acknowledgement. - let _: String = bincode::deserialize_from(&mut stream).wrap_err("Failed to receive reply")?; + let _: String = bincode::decode_from_std_read(&mut stream, bincode_config) + .wrap_err("Failed to receive reply")?; tracing::info!( "Notified DTMM with uri '{}'. Check the main window.", @@ -160,22 +164,33 @@ fn main() -> Result<()> { match res { Ok(mut stream) => { - let res = bincode::deserialize_from(&mut stream) - .wrap_err("Failed to read message") - .and_then(|uri: String| { - tracing::trace!(uri, "Received NXM uri"); + let res = bincode::decode_from_std_read( + &mut stream, + bincode::config::standard(), + ) + .wrap_err("Failed to read message") + .and_then(|uri: String| { + tracing::trace!(uri, "Received NXM uri"); - event_sink - .submit_command(ACTION_HANDLE_NXM, uri, druid::Target::Auto) - .wrap_err("Failed to start NXM download") - }); + event_sink + .submit_command(ACTION_HANDLE_NXM, uri, druid::Target::Auto) + .wrap_err("Failed to start NXM download") + }); match res { Ok(()) => { - let _ = bincode::serialize_into(&mut stream, "Ok"); + let _ = bincode::encode_into_std_write( + "Ok", + &mut stream, + bincode::config::standard(), + ); } Err(err) => { tracing::error!("{:?}", err); - let _ = bincode::serialize_into(&mut stream, "Error"); + let _ = bincode::encode_into_std_write( + "Error", + &mut stream, + bincode::config::standard(), + ); } } } From 5bb0f2acf561edca6e358d934c79c9d0a8c9cb44 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 22 Apr 2025 14:50:37 +0200 Subject: [PATCH 291/335] Fix build script --- .ci/tasks/build.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.ci/tasks/build.sh b/.ci/tasks/build.sh index 68c61a0..b362266 100755 --- a/.ci/tasks/build.sh +++ b/.ci/tasks/build.sh @@ -20,14 +20,15 @@ install_artifact() { cd "repo" PR=${PR:-} +ref=$(cat .git/ref || echo "HEAD") if [ -n "$PR" ]; then title "PR: $(echo "$PR" | jq '.number') - $(echo "$PR" | jq '.title')" - ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$(cat .git/ref || echo "HEAD")" 2>/dev/null || echo 'manual')" + ref="pr-$(echo "$PR" | jq '.number')-$(git rev-parse --short "$ref" 2>/dev/null || echo 'manual')" elif [ -f ".git/branch" ]; then - ref=$(cat .git/branch)-$(git rev-parse --short $ref) + ref=$(cat .git/branch)-$(git rev-parse --short "$ref") else - ref=$(git rev-parse --short "$(cat .git/ref || echo "HEAD")") + ref=$(git rev-parse --short "$ref") fi title "Version: '$ref'" From 43e3bf7b60de613258f4da7fc116bebaefc9c1f1 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 26 Jul 2024 16:03:04 +0200 Subject: [PATCH 292/335] Add cmdline to tracing output Can come in handy when other people report problems and show the error message or full log, but not the command line. Setting that span to `level = "error"` ensures that it won't be disabled by level filters. --- crates/dtmt/src/main.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/dtmt/src/main.rs b/crates/dtmt/src/main.rs index 2e10b17..e41e802 100644 --- a/crates/dtmt/src/main.rs +++ b/crates/dtmt/src/main.rs @@ -36,10 +36,21 @@ struct GlobalConfig { } #[tokio::main] -#[tracing::instrument] +#[tracing::instrument(level = "error", fields(cmd_line = tracing::field::Empty))] async fn main() -> Result<()> { color_eyre::install()?; + { + let span = tracing::Span::current(); + if !span.is_disabled() { + let cmdline: String = std::env::args_os().fold(String::new(), |mut s, arg| { + s.push_str(&arg.to_string_lossy()); + s + }); + span.record("cmd_line", cmdline); + } + } + let matches = command!() .subcommand_required(true) .arg( From 636279edfe376bd9d748347c7195d4e82af90015 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 21 Feb 2025 14:51:42 +0100 Subject: [PATCH 293/335] Use macro to generate file type enum and impls Due to the large amount of variants, and the different kind of values connected to each variant (hash, extension name) being scattered across the various `impl` blocks, the file became rather convoluted. While I don't generally like the indirection of macros or meta programming, it's not that bad with Rust, thanks to Rust Analyzer being able to attach diagnostics to the source inside the macro definition, and the ability to generate the macro's output for validation. Therefore, the new macro allows putting all data used for this enum definition into a single block. --- lib/sdk/src/bundle/filetype.rs | 490 +++++++++------------------------ 1 file changed, 132 insertions(+), 358 deletions(-) diff --git a/lib/sdk/src/bundle/filetype.rs b/lib/sdk/src/bundle/filetype.rs index 0b4f292..68ff6b5 100644 --- a/lib/sdk/src/bundle/filetype.rs +++ b/lib/sdk/src/bundle/filetype.rs @@ -3,232 +3,147 @@ use serde::Serialize; use crate::murmur::Murmur64; -#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] -pub enum BundleFileType { - Animation, - AnimationCurves, - Apb, - BakedLighting, - Bik, - BlendSet, - Bones, - Chroma, - CommonPackage, - Config, - Crypto, - Data, - Entity, - Flow, - Font, - Ies, - Ini, - Input, - Ivf, - Keys, - Level, - Lua, - Material, - Mod, - MouseCursor, - NavData, - NetworkConfig, - OddleNet, - Package, - Particles, - PhysicsProperties, - RenderConfig, - RtPipeline, - Scene, - Shader, - ShaderLibrary, - ShaderLibraryGroup, - ShadingEnvionmentMapping, - ShadingEnvironment, - Slug, - SlugAlbum, - SoundEnvironment, - SpuJob, - StateMachine, - StaticPVS, - Strings, - SurfaceProperties, - Texture, - TimpaniBank, - TimpaniMaster, - Tome, - Ugg, - Unit, - Upb, - VectorField, - Wav, - WwiseBank, - WwiseDep, - WwiseEvent, - WwiseMetadata, - WwiseStream, - Xml, +macro_rules! make_enum { + ( + $( $variant:ident, $hash:expr, $ext:expr $(, $decompiled:expr)? ; )+ + ) => { + #[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] + pub enum BundleFileType { + $( + $variant, + )+ + Unknown(Murmur64), + } - Unknown(Murmur64), + impl BundleFileType { + pub fn ext_name(&self) -> String { + match self { + $( + Self::$variant => String::from($ext), + )+ + Self::Unknown(s) => format!("{s:016X}"), + } + } + + pub fn decompiled_ext_name(&self) -> String { + match self { + $( + $( Self::$variant => String::from($decompiled), )? + )+ + _ => self.ext_name(), + } + } + } + + impl std::str::FromStr for BundleFileType { + type Err = color_eyre::Report; + fn from_str(s: &str) -> Result { + match s { + $( + $ext => Ok(Self::$variant), + )+ + s => eyre::bail!("Unknown type string '{}'", s), + } + } + } + + impl From for BundleFileType { + fn from(h: u64) -> Self { + match h { + $( + $hash => Self::$variant, + )+ + hash => Self::Unknown(hash.into()), + } + } + } + + impl From for u64 { + fn from(t: BundleFileType) -> u64 { + match t { + $( + BundleFileType::$variant => $hash, + )+ + BundleFileType::Unknown(hash) => hash.into(), + } + } + } + } +} + +make_enum! { + AnimationCurves, 0xdcfb9e18fff13984, "animation_curves"; + Animation, 0x931e336d7646cc26, "animation"; + Apb, 0x3eed05ba83af5090, "apb"; + BakedLighting, 0x7ffdb779b04e4ed1, "baked_lighting"; + Bik, 0xaa5965f03029fa18, "bik"; + BlendSet, 0xe301e8af94e3b5a3, "blend_set"; + Bones, 0x18dead01056b72e9, "bones"; + Chroma, 0xb7893adf7567506a, "chroma"; + CommonPackage, 0xfe9754bd19814a47, "common_package"; + Config, 0x82645835e6b73232, "config"; + Crypto, 0x69108ded1e3e634b, "crypto"; + Data, 0x8fd0d44d20650b68, "data"; + Entity, 0x9831ca893b0d087d, "entity"; + Flow, 0x92d3ee038eeb610d, "flow"; + Font, 0x9efe0a916aae7880, "font"; + Ies, 0x8f7d5a2c0f967655, "ies"; + Ini, 0xd526a27da14f1dc5, "ini"; + Input, 0x2bbcabe5074ade9e, "input"; + Ivf, 0xfa4a8e091a91201e, "ivf"; + Keys, 0xa62f9297dc969e85, "keys"; + Level, 0x2a690fd348fe9ac5, "level"; + Lua, 0xa14e8dfa2cd117e2, "lua"; + Material, 0xeac0b497876adedf, "material"; + Mod, 0x3fcdd69156a46417, "mod"; + MouseCursor, 0xb277b11fe4a61d37, "mouse_cursor"; + NavData, 0x169de9566953d264, "nav_data"; + NetworkConfig, 0x3b1fa9e8f6bac374, "network_config"; + OddleNet, 0xb0f2c12eb107f4d8, "oodle_net"; + Package, 0xad9c6d9ed1e5e77a, "package"; + Particles, 0xa8193123526fad64, "particles"; + PhysicsProperties, 0xbf21403a3ab0bbb1, "physics_properties"; + RenderConfig, 0x27862fe24795319c, "render_config"; + RtPipeline, 0x9ca183c2d0e76dee, "rt_pipeline"; + Scene, 0x9d0a795bfe818d19, "scene"; + Shader, 0xcce8d5b5f5ae333f, "shader"; + ShaderLibrary, 0xe5ee32a477239a93, "shader_library"; + ShaderLibraryGroup, 0x9e5c3cc74575aeb5, "shader_library_group"; + ShadingEnvionmentMapping, 0x250e0a11ac8e26f8, "shading_envionment_mapping"; + ShadingEnvironment, 0xfe73c7dcff8a7ca5, "shading_environment"; + Slug, 0xa27b4d04a9ba6f9e, "slug"; + SlugAlbum, 0xe9fc9ea7042e5ec0, "slug_album"; + SoundEnvironment, 0xd8b27864a97ffdd7, "sound_environment"; + SpuJob, 0xf97af9983c05b950, "spu_job"; + StateMachine, 0xa486d4045106165c, "state_machine"; + StaticPVS, 0xe3f0baa17d620321, "static_pvs"; + Strings, 0x0d972bab10b40fd3, "strings"; + SurfaceProperties, 0xad2d3fa30d9ab394, "surface_properties"; + Texture, 0xcd4238c6a0c69e32, "texture", "dds"; + TimpaniBank, 0x99736be1fff739a4, "timpani_bank"; + TimpaniMaster, 0x00a3e6c59a2b9c6c, "timpani_master"; + Tome, 0x19c792357c99f49b, "tome"; + Ugg, 0x712d6e3dd1024c9c, "ugg"; + Unit, 0xe0a48d0be9a7453f, "unit"; + Upb, 0xa99510c6e86dd3c2, "upb"; + VectorField, 0xf7505933166d6755, "vector_field"; + Wav, 0x786f65c00a816b19, "wav"; + WwiseBank, 0x535a7bd3e650d799, "wwise_bank", "bnk"; + WwiseDep, 0xaf32095c82f2b070, "wwise_dep"; + WwiseEvent, 0xaabdd317b58dfc8a, "wwise_event"; + WwiseMetadata, 0xd50a8b7e1c82b110, "wwise_metadata"; + WwiseStream, 0x504b55235d21440e, "wwise_stream", "ogg"; + Xml, 0x76015845a6003765, "xml"; + Theme, 0x38BB9442048A7FBD, "theme"; + MissionThemes, 0x80F2DE893657F83A, "mission_themes"; } impl BundleFileType { - pub fn ext_name(&self) -> String { - match self { - BundleFileType::AnimationCurves => String::from("animation_curves"), - BundleFileType::Animation => String::from("animation"), - BundleFileType::Apb => String::from("apb"), - BundleFileType::BakedLighting => String::from("baked_lighting"), - BundleFileType::Bik => String::from("bik"), - BundleFileType::BlendSet => String::from("blend_set"), - BundleFileType::Bones => String::from("bones"), - BundleFileType::Chroma => String::from("chroma"), - BundleFileType::CommonPackage => String::from("common_package"), - BundleFileType::Config => String::from("config"), - BundleFileType::Crypto => String::from("crypto"), - BundleFileType::Data => String::from("data"), - BundleFileType::Entity => String::from("entity"), - BundleFileType::Flow => String::from("flow"), - BundleFileType::Font => String::from("font"), - BundleFileType::Ies => String::from("ies"), - BundleFileType::Ini => String::from("ini"), - BundleFileType::Input => String::from("input"), - BundleFileType::Ivf => String::from("ivf"), - BundleFileType::Keys => String::from("keys"), - BundleFileType::Level => String::from("level"), - BundleFileType::Lua => String::from("lua"), - BundleFileType::Material => String::from("material"), - BundleFileType::Mod => String::from("mod"), - BundleFileType::MouseCursor => String::from("mouse_cursor"), - BundleFileType::NavData => String::from("nav_data"), - BundleFileType::NetworkConfig => String::from("network_config"), - BundleFileType::OddleNet => String::from("oodle_net"), - BundleFileType::Package => String::from("package"), - BundleFileType::Particles => String::from("particles"), - BundleFileType::PhysicsProperties => String::from("physics_properties"), - BundleFileType::RenderConfig => String::from("render_config"), - BundleFileType::RtPipeline => String::from("rt_pipeline"), - BundleFileType::Scene => String::from("scene"), - BundleFileType::ShaderLibraryGroup => String::from("shader_library_group"), - BundleFileType::ShaderLibrary => String::from("shader_library"), - BundleFileType::Shader => String::from("shader"), - BundleFileType::ShadingEnvionmentMapping => String::from("shading_environment_mapping"), - BundleFileType::ShadingEnvironment => String::from("shading_environment"), - BundleFileType::SlugAlbum => String::from("slug_album"), - BundleFileType::Slug => String::from("slug"), - BundleFileType::SoundEnvironment => String::from("sound_environment"), - BundleFileType::SpuJob => String::from("spu_job"), - BundleFileType::StateMachine => String::from("state_machine"), - BundleFileType::StaticPVS => String::from("static_pvs"), - BundleFileType::Strings => String::from("strings"), - BundleFileType::SurfaceProperties => String::from("surface_properties"), - BundleFileType::Texture => String::from("texture"), - BundleFileType::TimpaniBank => String::from("timpani_bank"), - BundleFileType::TimpaniMaster => String::from("timpani_master"), - BundleFileType::Tome => String::from("tome"), - BundleFileType::Ugg => String::from("ugg"), - BundleFileType::Unit => String::from("unit"), - BundleFileType::Upb => String::from("upb"), - BundleFileType::VectorField => String::from("vector_field"), - BundleFileType::Wav => String::from("wav"), - BundleFileType::WwiseBank => String::from("wwise_bank"), - BundleFileType::WwiseDep => String::from("wwise_dep"), - BundleFileType::WwiseEvent => String::from("wwise_event"), - BundleFileType::WwiseMetadata => String::from("wwise_metadata"), - BundleFileType::WwiseStream => String::from("wwise_stream"), - BundleFileType::Xml => String::from("xml"), - - BundleFileType::Unknown(s) => format!("{s:016X}"), - } - } - - pub fn decompiled_ext_name(&self) -> String { - match self { - BundleFileType::Texture => String::from("dds"), - BundleFileType::WwiseBank => String::from("bnk"), - BundleFileType::WwiseStream => String::from("ogg"), - _ => self.ext_name(), - } - } - pub fn hash(&self) -> Murmur64 { Murmur64::from(*self) } } -impl std::str::FromStr for BundleFileType { - type Err = color_eyre::Report; - - fn from_str(s: &str) -> Result { - let val = match s { - "animation_curves" => BundleFileType::AnimationCurves, - "animation" => BundleFileType::Animation, - "apb" => BundleFileType::Apb, - "baked_lighting" => BundleFileType::BakedLighting, - "bik" => BundleFileType::Bik, - "blend_set" => BundleFileType::BlendSet, - "bones" => BundleFileType::Bones, - "chroma" => BundleFileType::Chroma, - "common_package" => BundleFileType::CommonPackage, - "config" => BundleFileType::Config, - "crypto" => BundleFileType::Crypto, - "data" => BundleFileType::Data, - "entity" => BundleFileType::Entity, - "flow" => BundleFileType::Flow, - "font" => BundleFileType::Font, - "ies" => BundleFileType::Ies, - "ini" => BundleFileType::Ini, - "input" => BundleFileType::Input, - "ivf" => BundleFileType::Ivf, - "keys" => BundleFileType::Keys, - "level" => BundleFileType::Level, - "lua" => BundleFileType::Lua, - "material" => BundleFileType::Material, - "mod" => BundleFileType::Mod, - "mouse_cursor" => BundleFileType::MouseCursor, - "nav_data" => BundleFileType::NavData, - "network_config" => BundleFileType::NetworkConfig, - "oodle_net" => BundleFileType::OddleNet, - "package" => BundleFileType::Package, - "particles" => BundleFileType::Particles, - "physics_properties" => BundleFileType::PhysicsProperties, - "render_config" => BundleFileType::RenderConfig, - "rt_pipeline" => BundleFileType::RtPipeline, - "scene" => BundleFileType::Scene, - "shader_library_group" => BundleFileType::ShaderLibraryGroup, - "shader_library" => BundleFileType::ShaderLibrary, - "shader" => BundleFileType::Shader, - "shading_environment_mapping" => BundleFileType::ShadingEnvionmentMapping, - "shading_environment" => BundleFileType::ShadingEnvironment, - "slug_album" => BundleFileType::SlugAlbum, - "slug" => BundleFileType::Slug, - "sound_environment" => BundleFileType::SoundEnvironment, - "spu_job" => BundleFileType::SpuJob, - "state_machine" => BundleFileType::StateMachine, - "static_pvs" => BundleFileType::StaticPVS, - "strings" => BundleFileType::Strings, - "surface_properties" => BundleFileType::SurfaceProperties, - "texture" => BundleFileType::Texture, - "timpani_bank" => BundleFileType::TimpaniBank, - "timpani_master" => BundleFileType::TimpaniMaster, - "tome" => BundleFileType::Tome, - "ugg" => BundleFileType::Ugg, - "unit" => BundleFileType::Unit, - "upb" => BundleFileType::Upb, - "vector_field" => BundleFileType::VectorField, - "wav" => BundleFileType::Wav, - "wwise_bank" => BundleFileType::WwiseBank, - "wwise_dep" => BundleFileType::WwiseDep, - "wwise_event" => BundleFileType::WwiseEvent, - "wwise_metadata" => BundleFileType::WwiseMetadata, - "wwise_stream" => BundleFileType::WwiseStream, - "xml" => BundleFileType::Xml, - s => eyre::bail!("Unknown type string '{}'", s), - }; - - Ok(val) - } -} - impl Serialize for BundleFileType { fn serialize(&self, serializer: S) -> Result where @@ -245,147 +160,6 @@ impl From for BundleFileType { } } -impl From for BundleFileType { - fn from(hash: u64) -> BundleFileType { - match hash { - 0x931e336d7646cc26 => BundleFileType::Animation, - 0xdcfb9e18fff13984 => BundleFileType::AnimationCurves, - 0x3eed05ba83af5090 => BundleFileType::Apb, - 0x7ffdb779b04e4ed1 => BundleFileType::BakedLighting, - 0xaa5965f03029fa18 => BundleFileType::Bik, - 0xe301e8af94e3b5a3 => BundleFileType::BlendSet, - 0x18dead01056b72e9 => BundleFileType::Bones, - 0xb7893adf7567506a => BundleFileType::Chroma, - 0xfe9754bd19814a47 => BundleFileType::CommonPackage, - 0x82645835e6b73232 => BundleFileType::Config, - 0x69108ded1e3e634b => BundleFileType::Crypto, - 0x8fd0d44d20650b68 => BundleFileType::Data, - 0x9831ca893b0d087d => BundleFileType::Entity, - 0x92d3ee038eeb610d => BundleFileType::Flow, - 0x9efe0a916aae7880 => BundleFileType::Font, - 0x8f7d5a2c0f967655 => BundleFileType::Ies, - 0xd526a27da14f1dc5 => BundleFileType::Ini, - 0x2bbcabe5074ade9e => BundleFileType::Input, - 0xfa4a8e091a91201e => BundleFileType::Ivf, - 0xa62f9297dc969e85 => BundleFileType::Keys, - 0x2a690fd348fe9ac5 => BundleFileType::Level, - 0xa14e8dfa2cd117e2 => BundleFileType::Lua, - 0xeac0b497876adedf => BundleFileType::Material, - 0x3fcdd69156a46417 => BundleFileType::Mod, - 0xb277b11fe4a61d37 => BundleFileType::MouseCursor, - 0x169de9566953d264 => BundleFileType::NavData, - 0x3b1fa9e8f6bac374 => BundleFileType::NetworkConfig, - 0xb0f2c12eb107f4d8 => BundleFileType::OddleNet, - 0xad9c6d9ed1e5e77a => BundleFileType::Package, - 0xa8193123526fad64 => BundleFileType::Particles, - 0xbf21403a3ab0bbb1 => BundleFileType::PhysicsProperties, - 0x27862fe24795319c => BundleFileType::RenderConfig, - 0x9ca183c2d0e76dee => BundleFileType::RtPipeline, - 0x9d0a795bfe818d19 => BundleFileType::Scene, - 0xcce8d5b5f5ae333f => BundleFileType::Shader, - 0xe5ee32a477239a93 => BundleFileType::ShaderLibrary, - 0x9e5c3cc74575aeb5 => BundleFileType::ShaderLibraryGroup, - 0x250e0a11ac8e26f8 => BundleFileType::ShadingEnvionmentMapping, - 0xfe73c7dcff8a7ca5 => BundleFileType::ShadingEnvironment, - 0xa27b4d04a9ba6f9e => BundleFileType::Slug, - 0xe9fc9ea7042e5ec0 => BundleFileType::SlugAlbum, - 0xd8b27864a97ffdd7 => BundleFileType::SoundEnvironment, - 0xf97af9983c05b950 => BundleFileType::SpuJob, - 0xa486d4045106165c => BundleFileType::StateMachine, - 0xe3f0baa17d620321 => BundleFileType::StaticPVS, - 0x0d972bab10b40fd3 => BundleFileType::Strings, - 0xad2d3fa30d9ab394 => BundleFileType::SurfaceProperties, - 0xcd4238c6a0c69e32 => BundleFileType::Texture, - 0x99736be1fff739a4 => BundleFileType::TimpaniBank, - 0x00a3e6c59a2b9c6c => BundleFileType::TimpaniMaster, - 0x19c792357c99f49b => BundleFileType::Tome, - 0x712d6e3dd1024c9c => BundleFileType::Ugg, - 0xe0a48d0be9a7453f => BundleFileType::Unit, - 0xa99510c6e86dd3c2 => BundleFileType::Upb, - 0xf7505933166d6755 => BundleFileType::VectorField, - 0x786f65c00a816b19 => BundleFileType::Wav, - 0x535a7bd3e650d799 => BundleFileType::WwiseBank, - 0xaf32095c82f2b070 => BundleFileType::WwiseDep, - 0xaabdd317b58dfc8a => BundleFileType::WwiseEvent, - 0xd50a8b7e1c82b110 => BundleFileType::WwiseMetadata, - 0x504b55235d21440e => BundleFileType::WwiseStream, - 0x76015845a6003765 => BundleFileType::Xml, - - _ => BundleFileType::Unknown(Murmur64::from(hash)), - } - } -} - -impl From for u64 { - fn from(t: BundleFileType) -> u64 { - match t { - BundleFileType::Animation => 0x931e336d7646cc26, - BundleFileType::AnimationCurves => 0xdcfb9e18fff13984, - BundleFileType::Apb => 0x3eed05ba83af5090, - BundleFileType::BakedLighting => 0x7ffdb779b04e4ed1, - BundleFileType::Bik => 0xaa5965f03029fa18, - BundleFileType::BlendSet => 0xe301e8af94e3b5a3, - BundleFileType::Bones => 0x18dead01056b72e9, - BundleFileType::Chroma => 0xb7893adf7567506a, - BundleFileType::CommonPackage => 0xfe9754bd19814a47, - BundleFileType::Config => 0x82645835e6b73232, - BundleFileType::Crypto => 0x69108ded1e3e634b, - BundleFileType::Data => 0x8fd0d44d20650b68, - BundleFileType::Entity => 0x9831ca893b0d087d, - BundleFileType::Flow => 0x92d3ee038eeb610d, - BundleFileType::Font => 0x9efe0a916aae7880, - BundleFileType::Ies => 0x8f7d5a2c0f967655, - BundleFileType::Ini => 0xd526a27da14f1dc5, - BundleFileType::Input => 0x2bbcabe5074ade9e, - BundleFileType::Ivf => 0xfa4a8e091a91201e, - BundleFileType::Keys => 0xa62f9297dc969e85, - BundleFileType::Level => 0x2a690fd348fe9ac5, - BundleFileType::Lua => 0xa14e8dfa2cd117e2, - BundleFileType::Material => 0xeac0b497876adedf, - BundleFileType::Mod => 0x3fcdd69156a46417, - BundleFileType::MouseCursor => 0xb277b11fe4a61d37, - BundleFileType::NavData => 0x169de9566953d264, - BundleFileType::NetworkConfig => 0x3b1fa9e8f6bac374, - BundleFileType::OddleNet => 0xb0f2c12eb107f4d8, - BundleFileType::Package => 0xad9c6d9ed1e5e77a, - BundleFileType::Particles => 0xa8193123526fad64, - BundleFileType::PhysicsProperties => 0xbf21403a3ab0bbb1, - BundleFileType::RenderConfig => 0x27862fe24795319c, - BundleFileType::RtPipeline => 0x9ca183c2d0e76dee, - BundleFileType::Scene => 0x9d0a795bfe818d19, - BundleFileType::Shader => 0xcce8d5b5f5ae333f, - BundleFileType::ShaderLibrary => 0xe5ee32a477239a93, - BundleFileType::ShaderLibraryGroup => 0x9e5c3cc74575aeb5, - BundleFileType::ShadingEnvionmentMapping => 0x250e0a11ac8e26f8, - BundleFileType::ShadingEnvironment => 0xfe73c7dcff8a7ca5, - BundleFileType::Slug => 0xa27b4d04a9ba6f9e, - BundleFileType::SlugAlbum => 0xe9fc9ea7042e5ec0, - BundleFileType::SoundEnvironment => 0xd8b27864a97ffdd7, - BundleFileType::SpuJob => 0xf97af9983c05b950, - BundleFileType::StateMachine => 0xa486d4045106165c, - BundleFileType::StaticPVS => 0xe3f0baa17d620321, - BundleFileType::Strings => 0x0d972bab10b40fd3, - BundleFileType::SurfaceProperties => 0xad2d3fa30d9ab394, - BundleFileType::Texture => 0xcd4238c6a0c69e32, - BundleFileType::TimpaniBank => 0x99736be1fff739a4, - BundleFileType::TimpaniMaster => 0x00a3e6c59a2b9c6c, - BundleFileType::Tome => 0x19c792357c99f49b, - BundleFileType::Ugg => 0x712d6e3dd1024c9c, - BundleFileType::Unit => 0xe0a48d0be9a7453f, - BundleFileType::Upb => 0xa99510c6e86dd3c2, - BundleFileType::VectorField => 0xf7505933166d6755, - BundleFileType::Wav => 0x786f65c00a816b19, - BundleFileType::WwiseBank => 0x535a7bd3e650d799, - BundleFileType::WwiseDep => 0xaf32095c82f2b070, - BundleFileType::WwiseEvent => 0xaabdd317b58dfc8a, - BundleFileType::WwiseMetadata => 0xd50a8b7e1c82b110, - BundleFileType::WwiseStream => 0x504b55235d21440e, - BundleFileType::Xml => 0x76015845a6003765, - - BundleFileType::Unknown(hash) => hash.into(), - } - } -} impl From for Murmur64 { fn from(t: BundleFileType) -> Murmur64 { let hash: u64 = t.into(); From 7b95918000430088139af81b9aea2d540e25f98b Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 22 Sep 2023 15:42:16 +0200 Subject: [PATCH 294/335] Refactor code for file injection I ended up wrapping the raw data in a `BundleFile` twice. I also made '--compile' the default, as it should be much less often that raw data needs to be inserted. Even files that are essentially raw binary blobs, like `.wwise_event`, still have some custom fields that need to be accounted for. --- crates/dtmt/src/cmd/bundle/inject.rs | 327 +++++++++++++++++++++------ lib/sdk/src/bundle/file.rs | 23 +- lib/sdk/src/bundle/mod.rs | 3 +- lib/sdk/src/lib.rs | 2 +- 4 files changed, 277 insertions(+), 78 deletions(-) diff --git a/crates/dtmt/src/cmd/bundle/inject.rs b/crates/dtmt/src/cmd/bundle/inject.rs index 2b86691..21f4a91 100644 --- a/crates/dtmt/src/cmd/bundle/inject.rs +++ b/crates/dtmt/src/cmd/bundle/inject.rs @@ -1,112 +1,297 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use std::str::FromStr as _; -use clap::{value_parser, Arg, ArgMatches, Command}; -use color_eyre::eyre::{self, Context, Result}; +use clap::{value_parser, Arg, ArgAction, ArgMatches, Command}; +use color_eyre::eyre::{self, Context, OptionExt, Result}; use color_eyre::Help; -use sdk::Bundle; -use tokio::fs::{self, File}; -use tokio::io::AsyncReadExt; +use path_slash::PathBufExt as _; +use sdk::murmur::IdString64; +use sdk::{Bundle, BundleFile, BundleFileType}; +use tokio::fs; pub(crate) fn command_definition() -> Command { Command::new("inject") - .about("Inject a file into a bundle.") - .arg( - Arg::new("replace") - .help("The name of a file in the bundle whos content should be replaced.") - .short('r') - .long("replace"), - ) + .subcommand_required(true) + .about("Inject a file into a bundle.\n\ + Raw binary data can be used to directly replace the file's variant data blob without affecting the metadata.\n\ + Alternatively, a compiler format may be specified, and a complete bundle file is created.") .arg( Arg::new("output") .help( "The path to write the changed bundle to. \ - If omitted, the input bundle will be overwritten.", + If omitted, the input bundle will be overwritten.\n\ + Remember to add a `.patch_` suffix if you also use '--patch'.", ) .short('o') .long("output") .value_parser(value_parser!(PathBuf)), ) .arg( - Arg::new("bundle") - .help("Path to the bundle to inject the file into.") - .required(true) - .value_parser(value_parser!(PathBuf)), + Arg::new("patch") + .help("Create a patch bundle. Optionally, a patch NUMBER may be specified as \ + '--patch=123'.\nThe maximum number is 999, the default is 1.\n\ + If `--output` is not specified, the `.patch_` suffix is added to \ + the given bundle name.") + .short('p') + .long("patch") + .num_args(0..=1) + .require_equals(true) + .default_missing_value("1") + .value_name("NUMBER") + .value_parser(value_parser!(u16)) ) .arg( - Arg::new("file") - .help("Path to the file to inject.") - .required(true) - .value_parser(value_parser!(PathBuf)), + Arg::new("type") + .help("Compile the new file as the given TYPE. If omitted, the file type is \ + is guessed from the file extension.") + .value_name("TYPE") ) + .subcommand( + Command::new("replace") + .about("Replace an existing file in the bundle") + .arg( + Arg::new("variant") + .help("In combination with '--raw', specify the variant index to replace.") + .long("variant") + .default_value("0") + .value_parser(value_parser!(u8)) + ) + .arg( + Arg::new("raw") + .help("Insert the given file as raw binary data.\n\ + Cannot be used with '--patch'.") + .long("raw") + .action(ArgAction::SetTrue) + ) + .arg( + Arg::new("bundle") + .help("Path to the bundle to inject the file into.") + .required(true) + .value_parser(value_parser!(PathBuf)), + ) + .arg( + Arg::new("bundle-file") + .help("The name of a file in the bundle whose content should be replaced.") + .required(true), + ) + .arg( + Arg::new("new-file") + .help("Path to the file to inject.") + .required(true) + .value_parser(value_parser!(PathBuf)), + ), + ) + // .subcommand( + // Command::new("add") + // .about("Add a new file to the bundle") + // .arg( + // Arg::new("new-file") + // .help("Path to the file to inject.") + // .required(true) + // .value_parser(value_parser!(PathBuf)), + // ) + // .arg( + // Arg::new("bundle") + // .help("Path to the bundle to inject the file into.") + // .required(true) + // .value_parser(value_parser!(PathBuf)), + // ), + // ) } -#[tracing::instrument(skip_all)] +#[tracing::instrument] +async fn compile_file( + path: impl AsRef + std::fmt::Debug, + name: impl Into + std::fmt::Debug, + file_type: BundleFileType, +) -> Result { + let path = path.as_ref(); + + let file_data = fs::read(&path) + .await + .wrap_err_with(|| format!("Failed to read file '{}'", path.display()))?; + let _sjson = String::from_utf8(file_data) + .wrap_err_with(|| format!("Invalid UTF8 data in '{}'", path.display()))?; + + let _root = path.parent().ok_or_eyre("File path has no parent")?; + + eyre::bail!( + "Compilation for type '{}' is not implemented, yet", + file_type + ) +} + +#[tracing::instrument( + skip_all, + fields( + bundle_path = tracing::field::Empty, + in_file_path = tracing::field::Empty, + output_path = tracing::field::Empty, + target_name = tracing::field::Empty, + file_type = tracing::field::Empty, + raw = tracing::field::Empty, + ) +)] pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { - let bundle_path = matches + let Some((op, sub_matches)) = matches.subcommand() else { + unreachable!("clap is configured to require a subcommand, and they're all handled above"); + }; + + let bundle_path = sub_matches .get_one::("bundle") .expect("required parameter not found"); - let file_path = matches - .get_one::("file") + let in_file_path = sub_matches + .get_one::("new-file") .expect("required parameter not found"); - tracing::trace!(bundle_path = %bundle_path.display(), file_path = %file_path.display()); + let patch_number = matches + .get_one::("patch") + .map(|num| format!("{:03}", num)); - let mut bundle = { - let binary = fs::read(bundle_path).await?; - let name = Bundle::get_name_from_path(&ctx, bundle_path); - Bundle::from_binary(&ctx, name, binary).wrap_err("Failed to open bundle file")? + let output_path = matches + .get_one::("output") + .cloned() + .unwrap_or_else(|| { + let mut output_path = bundle_path.clone(); + + if let Some(patch_number) = patch_number.as_ref() { + output_path.set_extension(format!("patch_{:03}", patch_number)); + } + + output_path + }); + + let target_name = if op == "replace" { + sub_matches + .get_one::("bundle-file") + .map(|name| match u64::from_str_radix(name, 16) { + Ok(id) => IdString64::from(id), + Err(_) => IdString64::String(name.clone()), + }) + .expect("argument is required") + } else { + let mut path = PathBuf::from(in_file_path); + path.set_extension(""); + IdString64::from(path.to_slash_lossy().to_string()) }; - if let Some(name) = matches.get_one::("replace") { - let mut file = File::open(&file_path) - .await - .wrap_err_with(|| format!("Failed to open '{}'", file_path.display()))?; + let file_type = if let Some(forced_type) = matches.get_one::("type") { + BundleFileType::from_str(forced_type.as_str()).wrap_err("Unknown file type")? + } else { + in_file_path + .extension() + .and_then(|s| s.to_str()) + .ok_or_eyre("File extension missing") + .and_then(BundleFileType::from_str) + .wrap_err("Unknown file type") + .with_suggestion(|| "Use '--type TYPE' to specify the file type")? + }; - if let Some(variant) = bundle - .files_mut() - .filter(|file| file.matches_name(name.clone())) - // TODO: Handle file variants - .find_map(|file| file.variants_mut().next()) - { - let mut data = Vec::new(); - file.read_to_end(&mut data) - .await - .wrap_err("Failed to read input file")?; - variant.set_data(data); - } else { - let err = eyre::eyre!("No file '{}' in this bundle.", name) - .with_suggestion(|| { + { + let span = tracing::Span::current(); + if !span.is_disabled() { + span.record("bundle_path", bundle_path.display().to_string()); + span.record("in_file_path", in_file_path.display().to_string()); + span.record("output_path", output_path.display().to_string()); + span.record("raw", sub_matches.get_flag("raw")); + span.record("target_name", target_name.display().to_string()); + span.record("file_type", format!("{:?}", file_type)); + } + } + + let bundle_name = Bundle::get_name_from_path(&ctx, bundle_path); + let mut bundle = { + let binary = fs::read(bundle_path).await?; + Bundle::from_binary(&ctx, bundle_name.clone(), binary) + .wrap_err_with(|| format!("Failed to open bundle '{}'", bundle_path.display()))? + }; + + if op == "copy" { + unimplemented!("Implement copying a file from one bundle to the other."); + } + + let output_bundle = match op { + "replace" => { + let Some(file) = bundle + .files_mut() + .find(|file| *file.base_name() == target_name) + else { + let err = eyre::eyre!( + "No file with name '{}' in bundle '{}'", + target_name.display(), + bundle_path.display() + ); + + return Err(err).with_suggestion(|| { format!( - "Run '{} bundle list {}' to list the files in this bundle.", + "Run '{} bundle list \"{}\"' to list the files in this bundle.", clap::crate_name!(), bundle_path.display() ) - }) - .with_suggestion(|| { - format!( - "Use '{} bundle inject --add {} {} {}' to add it as a new file", - clap::crate_name!(), - name, - bundle_path.display(), - file_path.display() - ) }); + }; - return Err(err); + if sub_matches.get_flag("raw") { + let variant_index = sub_matches + .get_one::("variant") + .expect("argument with default missing"); + + let Some(variant) = file.variants_mut().nth(*variant_index as usize) else { + let err = eyre::eyre!( + "Variant index '{}' does not exist in '{}'", + variant_index, + target_name.display() + ); + + return Err(err).with_suggestion(|| { + format!( + "See '{} bundle inject add --help' if you want to add it as a new file", + clap::crate_name!(), + ) + }); + }; + + let data = tokio::fs::read(&in_file_path).await.wrap_err_with(|| { + format!("Failed to read file '{}'", in_file_path.display()) + })?; + variant.set_data(data); + file.set_modded(true); + bundle + } else { + let mut bundle_file = compile_file(in_file_path, target_name.clone(), file_type) + .await + .wrap_err("Failed to compile")?; + + bundle_file.set_modded(true); + + if patch_number.is_some() { + let mut output_bundle = Bundle::new(bundle_name); + output_bundle.add_file(bundle_file); + output_bundle + } else { + *file = bundle_file; + + dbg!(&file); + bundle + } + } } + "add" => { + unimplemented!("Implement adding a new file to the bundle."); + } + _ => unreachable!("no other operations exist"), + }; - let out_path = matches.get_one::("output").unwrap_or(bundle_path); - let data = bundle - .to_binary() - .wrap_err("Failed to write changed bundle to output")?; + let data = output_bundle + .to_binary() + .wrap_err("Failed to write changed bundle to output")?; - fs::write(out_path, &data) - .await - .wrap_err("Failed to write data to output file")?; + fs::write(&output_path, &data) + .await + .wrap_err_with(|| format!("Failed to write data to '{}'", output_path.display()))?; - Ok(()) - } else { - eyre::bail!("Currently, only the '--replace' operation is supported."); - } + tracing::info!("Modified bundle written to '{}'", output_path.display()); + + Ok(()) } diff --git a/lib/sdk/src/bundle/file.rs b/lib/sdk/src/bundle/file.rs index f387409..6d49821 100644 --- a/lib/sdk/src/bundle/file.rs +++ b/lib/sdk/src/bundle/file.rs @@ -20,6 +20,7 @@ struct BundleFileHeader { len_data_file_name: usize, } +#[derive(Clone, Debug)] pub struct BundleFileVariant { property: u32, data: Vec, @@ -109,9 +110,12 @@ bitflags! { #[derive(Default, Clone, Copy, Debug)] pub struct Properties: u32 { const DATA = 0b100; + // A custom flag used by DTMT to signify a file altered by mods. + const MODDED = 1 << 31; } } +#[derive(Clone, Debug)] pub struct BundleFile { file_type: BundleFileType, name: IdString64, @@ -133,6 +137,18 @@ impl BundleFile { self.variants.push(variant) } + pub fn set_variants(&mut self, variants: Vec) { + self.variants = variants; + } + + pub fn set_props(&mut self, props: Properties) { + self.props = props; + } + + pub fn set_modded(&mut self, is_modded: bool) { + self.props.set(Properties::MODDED, is_modded); + } + #[tracing::instrument(name = "File::read", skip(ctx, r))] pub fn from_reader(ctx: &crate::Context, r: &mut R, props: Properties) -> Result where @@ -299,14 +315,13 @@ impl BundleFile { s } - pub fn matches_name(&self, name: impl Into) -> bool { - let name = name.into(); - if self.name == name { + pub fn matches_name(&self, name: &IdString64) -> bool { + if self.name == *name { return true; } if let IdString64::String(name) = name { - self.name(false, None) == name || self.name(true, None) == name + self.name(false, None) == *name || self.name(true, None) == *name } else { false } diff --git a/lib/sdk/src/bundle/mod.rs b/lib/sdk/src/bundle/mod.rs index 075f4d2..edb71bb 100644 --- a/lib/sdk/src/bundle/mod.rs +++ b/lib/sdk/src/bundle/mod.rs @@ -7,14 +7,13 @@ use color_eyre::{Help, Report, SectionExt}; use oodle::{OodleLZ_CheckCRC, OodleLZ_FuzzSafe, CHUNK_SIZE}; use crate::binary::sync::*; -use crate::bundle::file::Properties; use crate::murmur::{HashGroup, IdString64, Murmur64}; pub(crate) mod database; pub(crate) mod file; pub(crate) mod filetype; -pub use file::{BundleFile, BundleFileVariant}; +pub use file::{BundleFile, BundleFileVariant, Properties}; pub use filetype::BundleFileType; #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] diff --git a/lib/sdk/src/lib.rs b/lib/sdk/src/lib.rs index a24b3bd..9b1806b 100644 --- a/lib/sdk/src/lib.rs +++ b/lib/sdk/src/lib.rs @@ -9,5 +9,5 @@ pub mod murmur; pub use binary::{FromBinary, ToBinary}; pub use bundle::database::BundleDatabase; pub use bundle::decompress; -pub use bundle::{Bundle, BundleFile, BundleFileType, BundleFileVariant}; +pub use bundle::{Bundle, BundleFile, BundleFileType, BundleFileVariant, Properties}; pub use context::{CmdLine, Context}; From f521e20f2b61aef35315081c7f6c78746acda679 Mon Sep 17 00:00:00 2001 From: Renovate Date: Thu, 15 May 2025 22:16:23 +0000 Subject: [PATCH 295/335] chore(deps): update rust crate csv-async to v1.3.1 --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c4ec109..88a9bb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -704,9 +704,9 @@ dependencies = [ [[package]] name = "csv-async" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37fe5b0d07f4a8260ce1e9a81413e88f459af0f2dfc55c15e96868a2f99c0f0" +checksum = "888dbb0f640d2c4c04e50f933885c7e9c95995d93cec90aba8735b4c610f26f1" dependencies = [ "cfg-if", "csv-core", @@ -2173,7 +2173,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] From 164cb7bc131199a5089e9a2cb8dffe89fabbc9ff Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 20 May 2025 12:46:31 +0000 Subject: [PATCH 296/335] chore(deps): update rust crate tempfile to v3.20.0 --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88a9bb9..bc37db3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1045,7 +1045,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -3195,7 +3195,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -3710,15 +3710,15 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4543,7 +4543,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] From ca677606fa25fd62466963e3e65efc7db82bb5f9 Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 20 May 2025 13:16:25 +0000 Subject: [PATCH 297/335] chore(deps): update rust crate bitflags to v2.9.1 --- Cargo.lock | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc37db3..aa01c87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -233,7 +233,7 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cexpr", "clang-sys", "itertools", @@ -253,7 +253,7 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cexpr", "clang-sys", "itertools", @@ -275,9 +275,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bitmaps" @@ -917,7 +917,7 @@ dependencies = [ "ansi-parser", "async-recursion", "bincode", - "bitflags 2.9.0", + "bitflags 2.9.1", "clap", "color-eyre", "colors-transform", @@ -1955,7 +1955,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "inotify-sys", "libc", ] @@ -2182,7 +2182,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "libc", "redox_syscall", ] @@ -2445,7 +2445,7 @@ version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "filetime", "fsevent-sys", "inotify", @@ -2548,7 +2548,7 @@ version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg-if", "foreign-types", "libc", @@ -2986,7 +2986,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -3178,7 +3178,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.4.14", @@ -3191,7 +3191,7 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.9.4", @@ -3325,7 +3325,7 @@ name = "sdk" version = "0.3.0" dependencies = [ "async-recursion", - "bitflags 2.9.0", + "bitflags 2.9.1", "byteorder", "color-eyre", "csv-async", @@ -3352,7 +3352,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "core-foundation", "core-foundation-sys", "libc", @@ -3674,7 +3674,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "core-foundation", "system-configuration-sys", ] @@ -4851,7 +4851,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] From 1975435805f5aded7a9eaf3cb9c0a6eefd1c9ff0 Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 20 May 2025 13:16:29 +0000 Subject: [PATCH 298/335] chore(deps): update rust crate clap to v4.5.38 --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc37db3..ed45902 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -426,9 +426,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", "clap_derive", @@ -436,9 +436,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstream", "anstyle", @@ -1045,7 +1045,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3195,7 +3195,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3718,7 +3718,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] From 1e9738c953051cd678000ab78e2cd01b2428e30c Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 20 May 2025 13:16:37 +0000 Subject: [PATCH 299/335] chore(deps): update rust crate tokio to v1.45.0 --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc37db3..29df0e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1045,7 +1045,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3195,7 +3195,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3718,7 +3718,7 @@ dependencies = [ "getrandom 0.3.2", "once_cell", "rustix 1.0.5", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3850,9 +3850,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.2" +version = "1.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" +checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" dependencies = [ "backtrace", "bytes", From 5e1581b428f028ef637c05b2d10598d6bbb148fc Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 21 May 2025 08:31:36 +0000 Subject: [PATCH 300/335] chore(deps): update rust crate minijinja to v2.10.2 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3eb31bc..7fb7b22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2302,9 +2302,9 @@ dependencies = [ [[package]] name = "minijinja" -version = "2.9.0" +version = "2.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98642a6dfca91122779a307b77cd07a4aa951fbe32232aaf5bad9febc66be754" +checksum = "dd72e8b4e42274540edabec853f607c015c73436159b06c39c7af85a20433155" dependencies = [ "serde", ] From 14eded5b7e1d3b43bcbd6b5d04860fe69ee4ea42 Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 21 May 2025 08:31:45 +0000 Subject: [PATCH 301/335] chore(deps): update rust crate zip to v3 --- Cargo.lock | 41 +++++++++++++++++++++++++---------------- Cargo.toml | 2 +- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3eb31bc..212dc5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -686,12 +686,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - [[package]] name = "crypto-common" version = "0.1.6" @@ -1118,12 +1112,13 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.32" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c0596c1eac1f9e04ed902702e9878208b336edc9d6fddc8a48387349bab3666" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", - "miniz_oxide 0.8.0", + "libz-rs-sys", + "miniz_oxide 0.8.8", ] [[package]] @@ -2173,7 +2168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -2187,6 +2182,15 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "libz-rs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6489ca9bd760fe9642d7644e827b0c9add07df89857b0416ee15c1cc1a3b8c5a" +dependencies = [ + "zlib-rs", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -2327,9 +2331,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -4543,7 +4547,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -4965,14 +4969,13 @@ dependencies = [ [[package]] name = "zip" -version = "2.6.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dcb24d0152526ae49b9b96c1dcf71850ca1e0b882e4e28ed898a93c41334744" +checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308" dependencies = [ "arbitrary", "bzip2", "crc32fast", - "crossbeam-utils", "flate2", "indexmap", "memchr", @@ -4981,6 +4984,12 @@ dependencies = [ "zstd", ] +[[package]] +name = "zlib-rs" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "868b928d7949e09af2f6086dfc1e01936064cc7a819253bce650d4e2a2d63ba8" + [[package]] name = "zopfli" version = "0.8.1" diff --git a/Cargo.toml b/Cargo.toml index 87d9ea6..4b083a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ tracing = { version = "0.1.37", features = ["async-await"] } tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } usvg = "0.25.0" -zip = { version = "2.1.3", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } +zip = { version = "3.0.0", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } [profile.dev.package.backtrace] opt-level = 3 From 6d576be4ae62d95e4e827cda3a1123430cf31120 Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 21 May 2025 09:16:27 +0000 Subject: [PATCH 302/335] chore(deps): update rust crate confy to v1 --- Cargo.lock | 31 +++++++++++++++++++++---------- Cargo.toml | 2 +- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee85157..f4ee177 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -581,13 +581,13 @@ checksum = "9226dbc05df4fb986f48d730b001532580883c4c06c5d1c213f4b34c1c157178" [[package]] name = "confy" -version = "0.6.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45b1f4c00870f07dc34adcac82bb6a72cc5aabca8536ba1797e01df51d2ce9a0" +checksum = "f29222b549d4e3ded127989d523da9e928918d0d0d7f7c1690b439d0d538bae9" dependencies = [ "directories", "serde", - "thiserror 1.0.63", + "thiserror 2.0.12", "toml 0.8.19", ] @@ -766,9 +766,9 @@ dependencies = [ [[package]] name = "directories" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" dependencies = [ "dirs-sys", ] @@ -785,14 +785,14 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users", - "windows-sys 0.48.0", + "redox_users 0.5.0", + "windows-sys 0.59.0", ] [[package]] @@ -802,7 +802,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.6", "winapi", ] @@ -3004,6 +3004,17 @@ dependencies = [ "thiserror 1.0.63", ] +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror 2.0.12", +] + [[package]] name = "regex" version = "1.11.1" diff --git a/Cargo.toml b/Cargo.toml index 4b083a9..3619e5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "str cli-table = { version = "0.5.0", default-features = false, features = ["derive"] } color-eyre = { path = "lib/color-eyre" } colors-transform = "0.2.11" -confy = "0.6.1" +confy = "1.0.0" csv-async = { version = "1.2.4", features = ["tokio", "serde"] } druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "bmp", "webp", "svg"] } druid-widget-nursery = "0.1" From 27062d22040199a00db630623f4e76d4c0b907e9 Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 21 May 2025 22:01:36 +0000 Subject: [PATCH 303/335] chore(deps): update rust crate zip to v4 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee85157..831d598 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4969,9 +4969,9 @@ dependencies = [ [[package]] name = "zip" -version = "3.0.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308" +checksum = "153a6fff49d264c4babdcfa6b4d534747f520e56e8f0f384f3b808c4b64cc1fd" dependencies = [ "arbitrary", "bzip2", diff --git a/Cargo.toml b/Cargo.toml index 4b083a9..eb0dceb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ tracing = { version = "0.1.37", features = ["async-await"] } tracing-error = "0.2.0" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } usvg = "0.25.0" -zip = { version = "3.0.0", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } +zip = { version = "4.0.0", default-features = false, features = ["deflate", "bzip2", "zstd", "time"] } [profile.dev.package.backtrace] opt-level = 3 From 220f37c7288c16e84c7a24a0c985b0d73cf99dc1 Mon Sep 17 00:00:00 2001 From: Renovate Date: Sat, 24 May 2025 15:01:28 +0000 Subject: [PATCH 304/335] chore(deps): update rust crate tokio to v1.45.1 --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee85157..555a11a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2168,7 +2168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -3854,9 +3854,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.0" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", From 3901355f9d89104d5c9f461609852702ba4462f7 Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 27 May 2025 18:16:25 +0000 Subject: [PATCH 305/335] chore(deps): update rust crate clap to v4.5.39 --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee85157..09c09c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -426,9 +426,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.38" +version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" +checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" dependencies = [ "clap_builder", "clap_derive", @@ -436,9 +436,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.38" +version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" +checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" dependencies = [ "anstream", "anstyle", @@ -2168,7 +2168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] From d66dcb5cfd9a83f376c389add3eca4d71a575fd6 Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 28 May 2025 16:31:22 +0000 Subject: [PATCH 306/335] fix(deps): update rust crate reqwest to v0.12.18 --- Cargo.lock | 73 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee85157..e9263f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1742,22 +1742,28 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" dependencies = [ + "base64 0.22.1", "bytes", "futures-channel", + "futures-core", "futures-util", "http", "http-body", "hyper", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", "socket2", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -2014,6 +2020,16 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is-docker" version = "0.2.0" @@ -2168,7 +2184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -3050,15 +3066,14 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "e98ff6b0dbbe4d5a37318f433d4fc82babd21631f194d370409ceb2e40b2f0b5" dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", "futures-core", - "futures-util", "h2", "http", "http-body", @@ -3075,21 +3090,20 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", - "system-configuration", "tokio", "tokio-native-tls", "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-registry", ] [[package]] @@ -3215,21 +3229,14 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-pemfile" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" -dependencies = [ - "base64 0.22.1", - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] [[package]] name = "rustls-webpki" @@ -3674,9 +3681,9 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.9.1", "core-foundation", @@ -3995,6 +4002,24 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" +dependencies = [ + "bitflags 2.9.1", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -4547,7 +4572,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] From 138cb79ff6285b6ea504d287e2c4509275cdeaad Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 30 May 2025 11:52:44 +0200 Subject: [PATCH 307/335] Disable excessive rebase for Renovate --- .renovaterc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.renovaterc b/.renovaterc index 4a2fbf4..7ad59cd 100644 --- a/.renovaterc +++ b/.renovaterc @@ -3,8 +3,7 @@ "extends": [ "config:recommended", ":combinePatchMinorReleases", - ":enableVulnerabilityAlerts", - ":rebaseStalePrs" + ":enableVulnerabilityAlerts" ], "prConcurrentLimit": 10, "branchPrefix": "renovate/", From bb9223a43eaf2aba6cbfc38660bd9c5769e3dfe6 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 2 Jun 2025 12:16:19 +0000 Subject: [PATCH 308/335] fix(deps): update rust crate reqwest to v0.12.19 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f1039a..87a86de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3077,9 +3077,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.18" +version = "0.12.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e98ff6b0dbbe4d5a37318f433d4fc82babd21631f194d370409ceb2e40b2f0b5" +checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119" dependencies = [ "base64 0.22.1", "bytes", @@ -4015,9 +4015,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" +checksum = "5cc2d9e086a412a451384326f521c8123a99a466b329941a9403696bff9b0da2" dependencies = [ "bitflags 2.9.1", "bytes", From 8749d4e6be6974bf91050d702b759a32f5081516 Mon Sep 17 00:00:00 2001 From: Renovate Date: Sun, 8 Jun 2025 11:46:19 +0000 Subject: [PATCH 309/335] chore(deps): update rust crate bindgen to 0.72.0 --- Cargo.lock | 10 +++++----- lib/oodle/Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87a86de..89bb171 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -249,9 +249,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.71.1" +version = "0.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f" dependencies = [ "bitflags 2.9.1", "cexpr", @@ -2184,7 +2184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -2546,7 +2546,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" name = "oodle" version = "0.1.0" dependencies = [ - "bindgen 0.71.1", + "bindgen 0.72.0", "color-eyre", "tracing", ] @@ -4583,7 +4583,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/lib/oodle/Cargo.toml b/lib/oodle/Cargo.toml index e679b5f..4a6fe2f 100644 --- a/lib/oodle/Cargo.toml +++ b/lib/oodle/Cargo.toml @@ -10,4 +10,4 @@ color-eyre = { workspace = true } tracing = { workspace = true } [build-dependencies] -bindgen = "0.71.0" +bindgen = "0.72.0" From 4fd17a2d0d42673c626b46bd378e53de2076d2ee Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 9 Jun 2025 18:16:20 +0000 Subject: [PATCH 310/335] chore(deps): update rust crate clap to v4.5.40 --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87a86de..5e49e15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -426,9 +426,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -436,9 +436,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -450,9 +450,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck 0.5.0", "proc-macro2", From 6b7d5265ad64c76d5babf8fb33c7109a165c9b29 Mon Sep 17 00:00:00 2001 From: Renovate Date: Tue, 10 Jun 2025 19:01:29 +0000 Subject: [PATCH 311/335] fix(deps): update rust crate reqwest to v0.12.20 --- Cargo.lock | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87a86de..4136148 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3077,9 +3077,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.19" +version = "0.12.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119" +checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" dependencies = [ "base64 0.22.1", "bytes", @@ -3093,12 +3093,10 @@ dependencies = [ "hyper-rustls", "hyper-tls", "hyper-util", - "ipnet", "js-sys", "log", "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", "rustls-pki-types", From fb072e1fba9f5e21670628c0c01a21ff33826f4e Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 11 Jun 2025 09:31:38 +0000 Subject: [PATCH 312/335] chore(deps): update rust crate nanorand to 0.8.0 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe6f1cd..44197f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2369,9 +2369,9 @@ dependencies = [ [[package]] name = "nanorand" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +checksum = "6e3d189da485332e96ba8a5ef646a311871abd7915bf06ac848a9117f19cf6e4" [[package]] name = "native-tls" diff --git a/Cargo.toml b/Cargo.toml index 8d51a45..99ce493 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ interprocess = "2.1.0" lazy_static = "1.4.0" luajit2-sys = { path = "lib/luajit2-sys" } minijinja = { version = "2.0.1", default-features = false, features = ["serde"] } -nanorand = "0.7.0" +nanorand = "0.8.0" nexusmods = { path = "lib/nexusmods" } notify = "8.0.0" oodle = { path = "lib/oodle" } From 03a11269a244f0d2b088d28daed5368f7ccb2a12 Mon Sep 17 00:00:00 2001 From: Renovate Date: Sun, 15 Jun 2025 04:01:33 +0000 Subject: [PATCH 313/335] chore(deps): update rust crate zip to v4.1.0 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 44197f8..609555e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5003,9 +5003,9 @@ dependencies = [ [[package]] name = "zip" -version = "4.0.0" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "153a6fff49d264c4babdcfa6b4d534747f520e56e8f0f384f3b808c4b64cc1fd" +checksum = "af7dcdb4229c0e79c2531a24de7726a0e980417a74fb4d030a35f535665439a0" dependencies = [ "arbitrary", "bzip2", From 881434abea1065c6a3ed52a405caa577156d4dd9 Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 23 Jun 2025 04:46:23 +0000 Subject: [PATCH 314/335] chore(deps): update rust crate zip to v4.2.0 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 609555e..97d6524 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5003,9 +5003,9 @@ dependencies = [ [[package]] name = "zip" -version = "4.1.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7dcdb4229c0e79c2531a24de7726a0e980417a74fb4d030a35f535665439a0" +checksum = "95ab361742de920c5535880f89bbd611ee62002bf11341d16a5f057bb8ba6899" dependencies = [ "arbitrary", "bzip2", From 28a3875f863ed11a3827ef83b281168aed238e9f Mon Sep 17 00:00:00 2001 From: Renovate Date: Sat, 28 Jun 2025 11:01:26 +0000 Subject: [PATCH 315/335] chore(deps): update rust crate minijinja to v2.11.0 --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 609555e..5b0b6aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2322,9 +2322,9 @@ dependencies = [ [[package]] name = "minijinja" -version = "2.10.2" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd72e8b4e42274540edabec853f607c015c73436159b06c39c7af85a20433155" +checksum = "4e60ac08614cc09062820e51d5d94c2fce16b94ea4e5003bb81b99a95f84e876" dependencies = [ "serde", ] From 1fe8e6f1505c3698a78d002557aceba8d9c54a5b Mon Sep 17 00:00:00 2001 From: Renovate Date: Mon, 30 Jun 2025 19:31:32 +0000 Subject: [PATCH 316/335] fix(deps): update rust crate reqwest to v0.12.21 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b0b6aa..8eeff96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2184,7 +2184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -3077,9 +3077,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.20" +version = "0.12.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" +checksum = "4c8cea6b35bcceb099f30173754403d2eba0a5dc18cea3630fccd88251909288" dependencies = [ "base64 0.22.1", "bytes", @@ -4581,7 +4581,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] From 98e0ea70e5c44ab23979c19b8059ffc67b74a524 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Fri, 30 May 2025 15:48:47 +0200 Subject: [PATCH 317/335] Improve file injection error handling --- crates/dtmt/src/cmd/bundle/inject.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/dtmt/src/cmd/bundle/inject.rs b/crates/dtmt/src/cmd/bundle/inject.rs index 21f4a91..23e3f8b 100644 --- a/crates/dtmt/src/cmd/bundle/inject.rs +++ b/crates/dtmt/src/cmd/bundle/inject.rs @@ -202,15 +202,13 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { let bundle_name = Bundle::get_name_from_path(&ctx, bundle_path); let mut bundle = { - let binary = fs::read(bundle_path).await?; - Bundle::from_binary(&ctx, bundle_name.clone(), binary) + fs::read(bundle_path) + .await + .map_err(From::from) + .and_then(|binary| Bundle::from_binary(&ctx, bundle_name.clone(), binary)) .wrap_err_with(|| format!("Failed to open bundle '{}'", bundle_path.display()))? }; - if op == "copy" { - unimplemented!("Implement copying a file from one bundle to the other."); - } - let output_bundle = match op { "replace" => { let Some(file) = bundle @@ -280,6 +278,9 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { "add" => { unimplemented!("Implement adding a new file to the bundle."); } + "copy" => { + unimplemented!("Implement copying a file from one bundle to the other."); + } _ => unreachable!("no other operations exist"), }; From 2c51ab1fcccc6e94635e0fa61af3c9581027dc67 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 1 Jul 2025 10:49:42 +0200 Subject: [PATCH 318/335] sdk: Add name for unknown file header field --- lib/sdk/src/binary.rs | 25 ++++++++++++++++++------- lib/sdk/src/bundle/file.rs | 28 ++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/lib/sdk/src/binary.rs b/lib/sdk/src/binary.rs index 9348e1b..9ce9d23 100644 --- a/lib/sdk/src/binary.rs +++ b/lib/sdk/src/binary.rs @@ -44,10 +44,10 @@ impl FromBinary for Vec { pub mod sync { use std::ffi::CStr; - use std::io::{self, Read, Seek, SeekFrom}; + use std::io::{self, Read, Seek, SeekFrom, Write}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; - use color_eyre::eyre::WrapErr; + use color_eyre::eyre::{self, WrapErr}; use color_eyre::{Help, Report, Result, SectionExt}; macro_rules! make_read { @@ -123,7 +123,7 @@ pub mod sync { }; } - pub trait ReadExt: ReadBytesExt + Seek { + pub trait ReadExt: Read + Seek { fn read_u8(&mut self) -> io::Result { ReadBytesExt::read_u8(self) } @@ -131,7 +131,6 @@ pub mod sync { make_read!(read_u32, read_u32_le, u32); make_read!(read_u64, read_u64_le, u64); - make_skip!(skip_u8, read_u8, u8); make_skip!(skip_u32, read_u32, u32); // Implementation based on https://en.wikipedia.com/wiki/LEB128 @@ -181,9 +180,17 @@ pub mod sync { res } } + + fn read_bool(&mut self) -> Result { + match ReadExt::read_u8(self)? { + 0 => Ok(false), + 1 => Ok(true), + v => eyre::bail!("Invalid value for boolean '{}'", v), + } + } } - pub trait WriteExt: WriteBytesExt + Seek { + pub trait WriteExt: Write + Seek { fn write_u8(&mut self, val: u8) -> io::Result<()> { WriteBytesExt::write_u8(self, val) } @@ -191,6 +198,10 @@ pub mod sync { make_write!(write_u32, write_u32_le, u32); make_write!(write_u64, write_u64_le, u64); + fn write_bool(&mut self, val: bool) -> io::Result<()> { + WriteBytesExt::write_u8(self, if val { 1 } else { 0 }) + } + fn write_padding(&mut self) -> io::Result { let pos = self.stream_position()?; let size = 16 - (pos % 16) as usize; @@ -207,8 +218,8 @@ pub mod sync { } } - impl ReadExt for R {} - impl WriteExt for W {} + impl ReadExt for R {} + impl WriteExt for W {} pub(crate) fn _read_up_to(r: &mut R, buf: &mut Vec) -> Result where diff --git a/lib/sdk/src/bundle/file.rs b/lib/sdk/src/bundle/file.rs index 6d49821..6442f98 100644 --- a/lib/sdk/src/bundle/file.rs +++ b/lib/sdk/src/bundle/file.rs @@ -15,17 +15,18 @@ use super::filetype::BundleFileType; #[derive(Debug)] struct BundleFileHeader { variant: u32, - unknown_1: u8, + external: bool, size: usize, + unknown_1: u8, len_data_file_name: usize, } -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct BundleFileVariant { property: u32, data: Vec, data_file_name: Option, - // Seems to be related to whether there is a data path. + external: bool, unknown_1: u8, } @@ -39,6 +40,7 @@ impl BundleFileVariant { property: 0, data: Vec::new(), data_file_name: None, + external: false, unknown_1: 0, } } @@ -63,21 +65,30 @@ impl BundleFileVariant { self.data_file_name.as_ref() } + pub fn external(&self) -> bool { + self.external + } + + pub fn unknown_1(&self) -> u8 { + self.unknown_1 + } + #[tracing::instrument(skip_all)] fn read_header(r: &mut R) -> Result where R: Read + Seek, { let variant = r.read_u32()?; - let unknown_1 = r.read_u8()?; + let external = r.read_bool()?; let size = r.read_u32()? as usize; - r.skip_u8(1)?; + let unknown_1 = r.read_u8()?; let len_data_file_name = r.read_u32()? as usize; Ok(BundleFileHeader { size, - unknown_1, + external, variant, + unknown_1, len_data_file_name, }) } @@ -88,7 +99,7 @@ impl BundleFileVariant { W: Write + Seek, { w.write_u32(self.property)?; - w.write_u8(self.unknown_1)?; + w.write_bool(self.external)?; let len_data_file_name = self.data_file_name.as_ref().map(|s| s.len()).unwrap_or(0); @@ -216,6 +227,7 @@ impl BundleFile { property: header.variant, data, data_file_name, + external: header.external, unknown_1: header.unknown_1, }; @@ -243,7 +255,7 @@ impl BundleFile { for variant in self.variants.iter() { w.write_u32(variant.property())?; - w.write_u8(variant.unknown_1)?; + w.write_bool(variant.external)?; let len_data_file_name = variant.data_file_name().map(|s| s.len()).unwrap_or(0); From d0fefee6672ec222d5260c7a44d7e568df8edadf Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 1 Jul 2025 10:37:19 +0200 Subject: [PATCH 319/335] dtmt: Optimize dictionary creation Improve the way the dictionary can read and write its config files, as well as improve the shared access during runtime. --- crates/dtmt/src/cmd/dictionary.rs | 11 +++++++---- crates/dtmt/src/main.rs | 6 ++++-- lib/sdk/src/context.rs | 9 ++++++--- lib/sdk/src/murmur/dictionary.rs | 12 ++++++++---- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/crates/dtmt/src/cmd/dictionary.rs b/crates/dtmt/src/cmd/dictionary.rs index 4c54c34..8f0d32c 100644 --- a/crates/dtmt/src/cmd/dictionary.rs +++ b/crates/dtmt/src/cmd/dictionary.rs @@ -1,4 +1,5 @@ use std::path::PathBuf; +use std::sync::Arc; use clap::{value_parser, Arg, ArgAction, ArgMatches, Command, ValueEnum}; use cli_table::{print_stdout, WithTitle}; @@ -156,6 +157,8 @@ pub(crate) async fn run(mut ctx: sdk::Context, matches: &ArgMatches) -> Result<( BufReader::new(Box::new(f)) }; + let lookup = Arc::make_mut(&mut ctx.lookup); + let group = sdk::murmur::HashGroup::from(*group); let mut added = 0; @@ -165,15 +168,15 @@ pub(crate) async fn run(mut ctx: sdk::Context, matches: &ArgMatches) -> Result<( let total = { for line in lines.into_iter() { let value = line?; - if ctx.lookup.find(&value, group).is_some() { + if lookup.find(&value, group).is_some() { skipped += 1; } else { - ctx.lookup.add(value, group); + lookup.add(value, group); added += 1; } } - ctx.lookup.len() + lookup.len() }; let out_path = matches @@ -190,7 +193,7 @@ pub(crate) async fn run(mut ctx: sdk::Context, matches: &ArgMatches) -> Result<( }) .with_section(|| out_path.display().to_string().header("Path:"))?; - ctx.lookup + lookup .to_csv(f) .await .wrap_err("Failed to write dictionary to disk")?; diff --git a/crates/dtmt/src/main.rs b/crates/dtmt/src/main.rs index e41e802..ef3d36b 100644 --- a/crates/dtmt/src/main.rs +++ b/crates/dtmt/src/main.rs @@ -12,6 +12,7 @@ use clap::value_parser; use clap::{command, Arg}; use color_eyre::eyre; use color_eyre::eyre::{Context, Result}; +use sdk::murmur::Dictionary; use serde::{Deserialize, Serialize}; use tokio::fs::File; use tokio::io::BufReader; @@ -107,8 +108,9 @@ async fn main() -> Result<()> { let r = BufReader::new(f); let mut ctx = ctx.write().await; - if let Err(err) = ctx.lookup.from_csv(r).await { - tracing::error!("{:#}", err); + match Dictionary::from_csv(r).await { + Ok(lookup) => ctx.lookup = Arc::new(lookup), + Err(err) => tracing::error!("{:#}", err), } }) }; diff --git a/lib/sdk/src/context.rs b/lib/sdk/src/context.rs index 1500290..8c10b3c 100644 --- a/lib/sdk/src/context.rs +++ b/lib/sdk/src/context.rs @@ -1,8 +1,11 @@ +use std::ffi::OsString; +use std::path::PathBuf; use std::process::Command; -use std::{ffi::OsString, path::PathBuf}; +use std::sync::Arc; use crate::murmur::{Dictionary, HashGroup, IdString64, Murmur32, Murmur64}; +#[derive(Clone)] pub struct CmdLine { cmd: OsString, args: Vec, @@ -52,7 +55,7 @@ impl From<&CmdLine> for Command { } pub struct Context { - pub lookup: Dictionary, + pub lookup: Arc, pub ljd: Option, pub revorb: Option, pub ww2ogg: Option, @@ -62,7 +65,7 @@ pub struct Context { impl Context { pub fn new() -> Self { Self { - lookup: Dictionary::new(), + lookup: Arc::new(Dictionary::new()), ljd: None, revorb: None, ww2ogg: None, diff --git a/lib/sdk/src/murmur/dictionary.rs b/lib/sdk/src/murmur/dictionary.rs index 267f0a4..c1b5636 100644 --- a/lib/sdk/src/murmur/dictionary.rs +++ b/lib/sdk/src/murmur/dictionary.rs @@ -48,6 +48,7 @@ struct Row { group: HashGroup, } +#[derive(Clone)] pub struct Entry { value: String, long: Murmur64, @@ -73,6 +74,7 @@ impl Entry { } } +#[derive(Clone)] pub struct Dictionary { entries: Vec, } @@ -88,10 +90,12 @@ impl Dictionary { Self { entries: vec![] } } - pub async fn from_csv(&mut self, r: R) -> Result<()> + pub async fn from_csv(r: R) -> Result where R: AsyncRead + std::marker::Unpin + std::marker::Send, { + let mut entries = vec![]; + let r = AsyncDeserializer::from_reader(r); let mut records = r.into_deserialize::(); @@ -112,10 +116,10 @@ impl Dictionary { group: record.group, }; - self.entries.push(entry); + entries.push(entry); } - Ok(()) + Ok(Self { entries }) } pub async fn to_csv(&self, w: W) -> Result<()> @@ -161,7 +165,7 @@ impl Dictionary { self.entries.push(entry); } - pub fn find(&mut self, value: &String, group: HashGroup) -> Option<&Entry> { + pub fn find(&self, value: &String, group: HashGroup) -> Option<&Entry> { self.entries .iter() .find(|e| e.value == *value && e.group == group) From f3aeec196d2ab7e4a246366141d83d1660aca880 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 1 Jul 2025 10:38:46 +0200 Subject: [PATCH 320/335] dtmt: Guess game directory from bundle path When no explicit game directory is provided, try to guess it from the bundle path given as argument during extraction. --- crates/dtmt/src/cmd/bundle/extract.rs | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/crates/dtmt/src/cmd/bundle/extract.rs b/crates/dtmt/src/cmd/bundle/extract.rs index 75f1360..b595dba 100644 --- a/crates/dtmt/src/cmd/bundle/extract.rs +++ b/crates/dtmt/src/cmd/bundle/extract.rs @@ -287,6 +287,34 @@ where P1: AsRef + std::fmt::Debug, P2: AsRef + std::fmt::Debug, { + let ctx = if ctx.game_dir.is_some() { + tracing::debug!( + "Got game directory from config: {}", + ctx.game_dir.as_ref().unwrap().display() + ); + + ctx + } else { + let game_dir = path + .as_ref() + .parent() + .and_then(|parent| parent.parent()) + .map(|p| p.to_path_buf()); + + tracing::info!( + "No game directory configured, guessing from bundle path: {:?}", + game_dir + ); + + Arc::new(sdk::Context { + game_dir, + lookup: Arc::clone(&ctx.lookup), + ljd: ctx.ljd.clone(), + revorb: ctx.revorb.clone(), + ww2ogg: ctx.ww2ogg.clone(), + }) + }; + let bundle = { let data = fs::read(path.as_ref()).await?; let name = Bundle::get_name_from_path(&ctx, path.as_ref()); From 400079237aa4f0cb4f8ff34e2537e877f07c6af7 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 1 Jul 2025 10:40:18 +0200 Subject: [PATCH 321/335] oodle: Allow providing the chunk size This makes the library itself more flexible, while still keeping things relatively straightforward with a single constant for the common chunk size. --- lib/oodle/src/lib.rs | 3 ++- lib/sdk/src/bundle/filetype.rs | 3 ++- lib/sdk/src/bundle/mod.rs | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/oodle/src/lib.rs b/lib/oodle/src/lib.rs index 871daab..7edadee 100644 --- a/lib/oodle/src/lib.rs +++ b/lib/oodle/src/lib.rs @@ -52,6 +52,7 @@ impl From for bindings::OodleLZ_CheckCRC { #[tracing::instrument(skip(data))] pub fn decompress( data: I, + out_size: usize, fuzz_safe: OodleLZ_FuzzSafe, check_crc: OodleLZ_CheckCRC, ) -> Result> @@ -59,7 +60,7 @@ where I: AsRef<[u8]>, { let data = data.as_ref(); - let mut out = vec![0; CHUNK_SIZE]; + let mut out = vec![0; out_size]; let verbosity = if tracing::enabled!(tracing::Level::INFO) { bindings::OodleLZ_Verbosity_OodleLZ_Verbosity_Minimal diff --git a/lib/sdk/src/bundle/filetype.rs b/lib/sdk/src/bundle/filetype.rs index 68ff6b5..a7a25dc 100644 --- a/lib/sdk/src/bundle/filetype.rs +++ b/lib/sdk/src/bundle/filetype.rs @@ -1,4 +1,5 @@ -use color_eyre::{eyre, Result}; +use color_eyre::eyre; +use color_eyre::Result; use serde::Serialize; use crate::murmur::Murmur64; diff --git a/lib/sdk/src/bundle/mod.rs b/lib/sdk/src/bundle/mod.rs index edb71bb..03a04c7 100644 --- a/lib/sdk/src/bundle/mod.rs +++ b/lib/sdk/src/bundle/mod.rs @@ -162,6 +162,7 @@ impl Bundle { // TODO: Optimize to not reallocate? let mut raw_buffer = oodle::decompress( &compressed_buffer, + oodle::CHUNK_SIZE, OodleLZ_FuzzSafe::No, OodleLZ_CheckCRC::No, ) @@ -359,6 +360,7 @@ where // TODO: Optimize to not reallocate? let mut raw_buffer = oodle::decompress( &compressed_buffer, + oodle::CHUNK_SIZE, OodleLZ_FuzzSafe::No, OodleLZ_CheckCRC::No, )?; From a36a59d90785aab39424c402cee7322f1a07c050 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 1 Jul 2025 10:48:54 +0200 Subject: [PATCH 322/335] sdk: Improve file decompilation debug output --- lib/sdk/src/bundle/file.rs | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/lib/sdk/src/bundle/file.rs b/lib/sdk/src/bundle/file.rs index 6442f98..18d329f 100644 --- a/lib/sdk/src/bundle/file.rs +++ b/lib/sdk/src/bundle/file.rs @@ -117,6 +117,26 @@ impl BundleFileVariant { } } +impl std::fmt::Debug for BundleFileVariant { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut out = f.debug_struct("BundleFileVariant"); + out.field("property", &self.property); + + if self.data.len() <= 5 { + out.field("data", &format!("{:x?}", &self.data)); + } else { + out.field( + "data", + &format!("{:x?}.. ({} bytes)", &self.data[..5], &self.data.len()), + ); + } + + out.field("data_file_name", &self.data_file_name) + .field("external", &self.external) + .finish() + } +} + bitflags! { #[derive(Default, Clone, Copy, Debug)] pub struct Properties: u32 { @@ -215,6 +235,7 @@ impl BundleFile { let s = r .read_string_len(header.len_data_file_name) .wrap_err("Failed to read data file name")?; + Some(s) } else { None @@ -371,18 +392,16 @@ impl BundleFile { Ok(files) } - #[tracing::instrument(name = "File::decompiled", skip_all)] + #[tracing::instrument( + name = "File::decompiled", + skip_all, + fields(file = self.name(false, None), file_type = self.file_type().ext_name(), variants = self.variants.len()) + )] pub async fn decompiled(&self, ctx: &crate::Context) -> Result> { let file_type = self.file_type(); - if tracing::enabled!(tracing::Level::DEBUG) { - tracing::debug!( - name = self.name(true, None), - variants = self.variants.len(), - "Attempting to decompile" - ); - } - + // The `Strings` type handles all variants combined. + // For the other ones, each variant will be its own file. if file_type == BundleFileType::Strings { return strings::decompile(ctx, &self.variants); } From d61ffdfef2e87d916ff95480663ec69bf44e4a6e Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 1 Jul 2025 11:37:04 +0200 Subject: [PATCH 323/335] Apply clippy lints --- crates/dtmm/src/controller/deploy.rs | 2 +- crates/dtmm/src/controller/game.rs | 2 +- crates/dtmm/src/controller/import.rs | 10 +++++----- crates/dtmm/src/main.rs | 4 ++-- crates/dtmm/src/state/delegate.rs | 11 +++++------ crates/dtmm/src/ui/theme/colors.rs | 2 +- crates/dtmm/src/ui/widget/controller.rs | 1 + crates/dtmm/src/ui/window/dialog.rs | 4 ++-- crates/dtmm/src/ui/window/main.rs | 2 +- crates/dtmt/src/cmd/bundle/db.rs | 12 ++++++------ crates/dtmt/src/cmd/bundle/extract.rs | 2 +- crates/dtmt/src/cmd/bundle/inject.rs | 6 +++--- crates/dtmt/src/cmd/bundle/list.rs | 2 +- crates/dtmt/src/cmd/new.rs | 2 +- crates/dtmt/src/main.rs | 1 - lib/dtmt-shared/src/log.rs | 4 ++-- lib/nexusmods/src/lib.rs | 2 +- lib/oodle/build.rs | 2 +- lib/sdk/src/binary.rs | 4 ++-- lib/sdk/src/murmur/types.rs | 4 ++-- 20 files changed, 39 insertions(+), 40 deletions(-) diff --git a/crates/dtmm/src/controller/deploy.rs b/crates/dtmm/src/controller/deploy.rs index 481b07c..8b36ae2 100644 --- a/crates/dtmm/src/controller/deploy.rs +++ b/crates/dtmm/src/controller/deploy.rs @@ -469,7 +469,7 @@ async fn patch_boot_bundle(state: Arc, deployment_info: &str) -> Re } .instrument(tracing::trace_span!("read boot bundle")) .await - .wrap_err_with(|| format!("Failed to read bundle '{}'", BOOT_BUNDLE_NAME))?; + .wrap_err_with(|| format!("Failed to read bundle '{BOOT_BUNDLE_NAME}'"))?; { tracing::trace!("Adding mod package file to boot bundle"); diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index b93d985..a5853bb 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -208,7 +208,7 @@ pub(crate) async fn reset_mod_deployment(state: ActionState) -> Result<()> { for p in paths { let path = bundle_dir.join(p); - let backup = bundle_dir.join(format!("{}.bak", p)); + let backup = bundle_dir.join(format!("{p}.bak")); let res = async { tracing::debug!( diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 6fc9693..36f3268 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -363,7 +363,7 @@ fn extract_legacy_mod( for i in 0..file_count { let mut f = archive .by_index(i) - .wrap_err_with(|| format!("Failed to get file at index {}", i))?; + .wrap_err_with(|| format!("Failed to get file at index {i}"))?; let Some(name) = f.enclosed_name().map(|p| p.to_path_buf()) else { let err = eyre::eyre!("File name in archive is not a safe path value.").suggestion( @@ -426,7 +426,7 @@ pub(crate) async fn import_from_file(state: ActionState, info: FileInfo) -> Resu let mod_info = api .mods_id(id) .await - .wrap_err_with(|| format!("Failed to query mod {} from Nexus", id))?; + .wrap_err_with(|| format!("Failed to query mod {id} from Nexus"))?; let version = match api.file_version(id, timestamp).await { Ok(version) => version, @@ -461,13 +461,13 @@ pub(crate) async fn import_from_file(state: ActionState, info: FileInfo) -> Resu pub(crate) async fn import_from_nxm(state: ActionState, uri: String) -> Result { let url = uri .parse() - .wrap_err_with(|| format!("Invalid Uri '{}'", uri))?; + .wrap_err_with(|| format!("Invalid Uri '{uri}'"))?; let api = NexusApi::new(state.nexus_api_key.to_string())?; let (mod_info, file_info, data) = api .handle_nxm(url) .await - .wrap_err_with(|| format!("Failed to download mod from NXM uri '{}'", uri))?; + .wrap_err_with(|| format!("Failed to download mod from NXM uri '{uri}'"))?; let nexus = NexusInfo::from(mod_info); import_mod(state, Some((nexus, file_info.version)), data).await @@ -524,7 +524,7 @@ pub(crate) async fn import_mod( let data = api .picture(url) .await - .wrap_err_with(|| format!("Failed to download Nexus image from '{}'", url))?; + .wrap_err_with(|| format!("Failed to download Nexus image from '{url}'"))?; let img = image_data_to_buffer(&data)?; diff --git a/crates/dtmm/src/main.rs b/crates/dtmm/src/main.rs index 54e101a..41a9253 100644 --- a/crates/dtmm/src/main.rs +++ b/crates/dtmm/src/main.rs @@ -47,7 +47,7 @@ fn notify_nxm_download( .to_ns_name::() .expect("Invalid socket name"), ) - .wrap_err_with(|| format!("Failed to connect to '{}'", IPC_ADDRESS)) + .wrap_err_with(|| format!("Failed to connect to '{IPC_ADDRESS}'")) .suggestion("Make sure the main window is open.")?; tracing::debug!("Connected to main process at '{}'", IPC_ADDRESS); @@ -159,7 +159,7 @@ fn main() -> Result<()> { loop { let res = server.accept().wrap_err_with(|| { - format!("IPC server failed to listen on '{}'", IPC_ADDRESS) + format!("IPC server failed to listen on '{IPC_ADDRESS}'") }); match res { diff --git a/crates/dtmm/src/state/delegate.rs b/crates/dtmm/src/state/delegate.rs index f3c4711..62fb319 100644 --- a/crates/dtmm/src/state/delegate.rs +++ b/crates/dtmm/src/state/delegate.rs @@ -108,20 +108,19 @@ impl std::fmt::Debug for AsyncAction { match self { AsyncAction::DeployMods(_) => write!(f, "AsyncAction::DeployMods(_state)"), AsyncAction::ResetDeployment(_) => write!(f, "AsyncAction::ResetDeployment(_state)"), - AsyncAction::AddMod(_, info) => write!(f, "AsyncAction::AddMod(_state, {:?})", info), + AsyncAction::AddMod(_, info) => write!(f, "AsyncAction::AddMod(_state, {info:?})"), AsyncAction::DeleteMod(_, info) => { - write!(f, "AsyncAction::DeleteMod(_state, {:?})", info) + write!(f, "AsyncAction::DeleteMod(_state, {info:?})") } AsyncAction::SaveSettings(_) => write!(f, "AsyncAction::SaveSettings(_state)"), AsyncAction::CheckUpdates(_) => write!(f, "AsyncAction::CheckUpdates(_state)"), AsyncAction::LoadInitial((path, is_default)) => write!( f, - "AsyncAction::LoadInitial(({:?}, {:?}))", - path, is_default + "AsyncAction::LoadInitial(({path:?}, {is_default:?}))" ), AsyncAction::Log(_) => write!(f, "AsyncAction::Log(_)"), AsyncAction::NxmDownload(_, uri) => { - write!(f, "AsyncAction::NxmDownload(_state, {})", uri) + write!(f, "AsyncAction::NxmDownload(_state, {uri})") } } } @@ -448,7 +447,7 @@ impl AppDelegate for Delegate { if let Err(err) = open::that_detached(Arc::as_ref(url)) { tracing::error!( "{:?}", - Report::new(err).wrap_err(format!("Failed to open url '{}'", url)) + Report::new(err).wrap_err(format!("Failed to open url '{url}'")) ); } diff --git a/crates/dtmm/src/ui/theme/colors.rs b/crates/dtmm/src/ui/theme/colors.rs index 1051539..c78644e 100644 --- a/crates/dtmm/src/ui/theme/colors.rs +++ b/crates/dtmm/src/ui/theme/colors.rs @@ -76,7 +76,7 @@ impl ColorExt for Color { fn darken(&self, fac: f32) -> Self { let (r, g, b, a) = self.as_rgba(); let rgb = Rgb::from(r as f32, g as f32, b as f32); - let rgb = rgb.lighten(-1. * fac); + let rgb = rgb.lighten(-fac); Self::rgba( rgb.get_red() as f64, rgb.get_green() as f64, diff --git a/crates/dtmm/src/ui/widget/controller.rs b/crates/dtmm/src/ui/widget/controller.rs index f789b5a..f3b8a2e 100644 --- a/crates/dtmm/src/ui/widget/controller.rs +++ b/crates/dtmm/src/ui/widget/controller.rs @@ -5,6 +5,7 @@ use druid::{ use crate::state::{State, ACTION_SET_DIRTY, ACTION_START_SAVE_SETTINGS}; +#[allow(dead_code)] pub struct DisabledButtonController; impl Controller> for DisabledButtonController { diff --git a/crates/dtmm/src/ui/window/dialog.rs b/crates/dtmm/src/ui/window/dialog.rs index 11df4d5..511d1de 100644 --- a/crates/dtmm/src/ui/window/dialog.rs +++ b/crates/dtmm/src/ui/window/dialog.rs @@ -34,9 +34,9 @@ pub fn error(err: Report, _parent: WindowHandle) -> WindowDesc { // The second to last one, the context to the root cause let context = err.chain().nth(count - 2).unwrap(); - (format!("{first}!"), format!("{}: {}", context, root)) + (format!("{first}!"), format!("{context}: {root}")) } else { - ("An error occurred!".to_string(), format!("{}: {}", first, root)) + ("An error occurred!".to_string(), format!("{first}: {root}")) } } }; diff --git a/crates/dtmm/src/ui/window/main.rs b/crates/dtmm/src/ui/window/main.rs index 022a780..3955bb1 100644 --- a/crates/dtmm/src/ui/window/main.rs +++ b/crates/dtmm/src/ui/window/main.rs @@ -348,7 +348,7 @@ fn build_mod_details_info() -> impl Widget { let nexus_link = Maybe::or_empty(|| { let link = Label::raw().lens(NexusInfo::id.map( |id| { - let url = format!("https://nexusmods.com/warhammer40kdarktide/mods/{}", id); + let url = format!("https://nexusmods.com/warhammer40kdarktide/mods/{id}"); let mut builder = RichTextBuilder::new(); builder .push("Open on Nexusmods") diff --git a/crates/dtmt/src/cmd/bundle/db.rs b/crates/dtmt/src/cmd/bundle/db.rs index b537991..2398573 100644 --- a/crates/dtmt/src/cmd/bundle/db.rs +++ b/crates/dtmt/src/cmd/bundle/db.rs @@ -94,10 +94,10 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { match bundle_name { IdString64::String(name) => { - println!("{:016x} {}", bundle_hash, name); + println!("{bundle_hash:016x} {name}"); } IdString64::Hash(hash) => { - println!("{:016x}", hash); + println!("{hash:016x}"); } } @@ -110,7 +110,7 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { println!("\t{:016x}.{:<12} {}", file.name, extension, name); } IdString64::Hash(hash) => { - println!("\t{:016x}.{}", hash, extension); + println!("\t{hash:016x}.{extension}"); } } } @@ -127,10 +127,10 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { match bundle_name { IdString64::String(name) => { - println!("{:016x} {}", bundle_hash, name); + println!("{bundle_hash:016x} {name}"); } IdString64::Hash(hash) => { - println!("{:016x}", hash); + println!("{hash:016x}"); } } } @@ -158,7 +158,7 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { for bundle in bundles { found = true; - println!("{:016x}", bundle); + println!("{bundle:016x}"); } if !found { diff --git a/crates/dtmt/src/cmd/bundle/extract.rs b/crates/dtmt/src/cmd/bundle/extract.rs index b595dba..f20733d 100644 --- a/crates/dtmt/src/cmd/bundle/extract.rs +++ b/crates/dtmt/src/cmd/bundle/extract.rs @@ -473,7 +473,7 @@ where } } Err(err) => { - let err = err.wrap_err(format!("Failed to decompile file {}", name)); + let err = err.wrap_err(format!("Failed to decompile file {name}")); tracing::error!("{:?}", err); } }; diff --git a/crates/dtmt/src/cmd/bundle/inject.rs b/crates/dtmt/src/cmd/bundle/inject.rs index 23e3f8b..1fba83d 100644 --- a/crates/dtmt/src/cmd/bundle/inject.rs +++ b/crates/dtmt/src/cmd/bundle/inject.rs @@ -147,7 +147,7 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { let patch_number = matches .get_one::("patch") - .map(|num| format!("{:03}", num)); + .map(|num| format!("{num:03}")); let output_path = matches .get_one::("output") @@ -156,7 +156,7 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { let mut output_path = bundle_path.clone(); if let Some(patch_number) = patch_number.as_ref() { - output_path.set_extension(format!("patch_{:03}", patch_number)); + output_path.set_extension(format!("patch_{patch_number:03}")); } output_path @@ -196,7 +196,7 @@ pub(crate) async fn run(ctx: sdk::Context, matches: &ArgMatches) -> Result<()> { span.record("output_path", output_path.display().to_string()); span.record("raw", sub_matches.get_flag("raw")); span.record("target_name", target_name.display().to_string()); - span.record("file_type", format!("{:?}", file_type)); + span.record("file_type", format!("{file_type:?}")); } } diff --git a/crates/dtmt/src/cmd/bundle/list.rs b/crates/dtmt/src/cmd/bundle/list.rs index 558126b..d511f35 100644 --- a/crates/dtmt/src/cmd/bundle/list.rs +++ b/crates/dtmt/src/cmd/bundle/list.rs @@ -38,7 +38,7 @@ enum OutputFormat { fn format_byte_size(size: usize) -> String { if size < 1024 { - format!("{} Bytes", size) + format!("{size} Bytes") } else if size < 1024 * 1024 { format!("{} kB", size / 1024) } else if size < 1024 * 1024 * 1024 { diff --git a/crates/dtmt/src/cmd/new.rs b/crates/dtmt/src/cmd/new.rs index 571b0cb..9d69b74 100644 --- a/crates/dtmt/src/cmd/new.rs +++ b/crates/dtmt/src/cmd/new.rs @@ -164,7 +164,7 @@ pub(crate) async fn run(_ctx: sdk::Context, matches: &ArgMatches) -> Result<()> .iter() .map(|(path_tmpl, content_tmpl)| { env.render_str(path_tmpl, &render_ctx) - .wrap_err_with(|| format!("Failed to render template: {}", path_tmpl)) + .wrap_err_with(|| format!("Failed to render template: {path_tmpl}")) .and_then(|path| { env.render_named_str(&path, content_tmpl, &render_ctx) .wrap_err_with(|| format!("Failed to render template '{}'", &path)) diff --git a/crates/dtmt/src/main.rs b/crates/dtmt/src/main.rs index ef3d36b..e2cb718 100644 --- a/crates/dtmt/src/main.rs +++ b/crates/dtmt/src/main.rs @@ -1,6 +1,5 @@ #![feature(io_error_more)] #![feature(let_chains)] -#![feature(result_flattening)] #![feature(test)] #![windows_subsystem = "console"] diff --git a/lib/dtmt-shared/src/log.rs b/lib/dtmt-shared/src/log.rs index 9c95c63..e5afc6c 100644 --- a/lib/dtmt-shared/src/log.rs +++ b/lib/dtmt-shared/src/log.rs @@ -19,7 +19,7 @@ pub const TIME_FORMAT: &[FormatItem] = format_description!("[hour]:[minute]:[sec pub fn format_fields(w: &mut Writer<'_>, field: &Field, val: &dyn std::fmt::Debug) -> Result { if field.name() == "message" { - write!(w, "{:?}", val) + write!(w, "{val:?}") } else { Ok(()) } @@ -70,7 +70,7 @@ where writer, "[{}] [{:>5}] ", time, - color.bold().paint(format!("{}", level)) + color.bold().paint(format!("{level}")) )?; ctx.field_format().format_fields(writer.by_ref(), event)?; diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index cddf6a0..5c243e6 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -99,7 +99,7 @@ impl Api { #[tracing::instrument(skip(self))] pub async fn mods_id(&self, id: u64) -> Result { - let url = BASE_URL_GAME.join(&format!("mods/{}.json", id))?; + let url = BASE_URL_GAME.join(&format!("mods/{id}.json"))?; let req = self.client.get(url); self.send(req).await } diff --git a/lib/oodle/build.rs b/lib/oodle/build.rs index 1a1d4e9..03b6463 100644 --- a/lib/oodle/build.rs +++ b/lib/oodle/build.rs @@ -11,7 +11,7 @@ fn main() { } else { "oo2core_win64" }; - println!("cargo:rustc-link-lib=static={}", lib_name); + println!("cargo:rustc-link-lib=static={lib_name}"); } else { println!("cargo:rustc-link-lib=static=oo2corelinux64"); println!("cargo:rustc-link-lib=stdc++"); diff --git a/lib/sdk/src/binary.rs b/lib/sdk/src/binary.rs index 9ce9d23..f96b3e0 100644 --- a/lib/sdk/src/binary.rs +++ b/lib/sdk/src/binary.rs @@ -247,7 +247,7 @@ pub mod sync { fn read_string_len(mut r: impl Read, len: usize) -> Result { let mut buf = vec![0; len]; r.read_exact(&mut buf) - .wrap_err_with(|| format!("Failed to read {} bytes", len))?; + .wrap_err_with(|| format!("Failed to read {len} bytes"))?; let res = match CStr::from_bytes_until_nul(&buf) { Ok(s) => { @@ -259,6 +259,6 @@ pub mod sync { res.wrap_err("Invalid binary for UTF8 string") .with_section(|| format!("{}", String::from_utf8_lossy(&buf)).header("ASCI:")) - .with_section(|| format!("{:x?}", buf).header("Bytes:")) + .with_section(|| format!("{buf:x?}").header("Bytes:")) } } diff --git a/lib/sdk/src/murmur/types.rs b/lib/sdk/src/murmur/types.rs index c66e2cf..4e6965b 100644 --- a/lib/sdk/src/murmur/types.rs +++ b/lib/sdk/src/murmur/types.rs @@ -50,7 +50,7 @@ impl fmt::LowerHex for Murmur64 { impl fmt::Display for Murmur64 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:016X}", self) + write!(f, "{self:016X}") } } @@ -158,7 +158,7 @@ impl fmt::LowerHex for Murmur32 { impl fmt::Display for Murmur32 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:08X}", self) + write!(f, "{self:08X}") } } From 1326222eb090bfa1c2c30797bd17e76510027441 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 1 Jul 2025 15:01:14 +0200 Subject: [PATCH 324/335] Improve Renovate config --- .ci/image/Dockerfile | 8 +++++--- .renovaterc | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/.ci/image/Dockerfile b/.ci/image/Dockerfile index f115929..b995065 100644 --- a/.ci/image/Dockerfile +++ b/.ci/image/Dockerfile @@ -1,6 +1,7 @@ # https://jake-shadle.github.io/xwin/ -FROM debian:bullseye-slim as xwin +FROM debian:bullseye-slim AS xwin +# renovate: datasource=github-releases depName=xwin packageName=Jake-Shadle/xwin ARG XWIN_VERSION=0.5.2 ARG XWIN_PREFIX="xwin-$XWIN_VERSION-x86_64-unknown-linux-musl" ADD https://github.com/Jake-Shadle/xwin/releases/download/$XWIN_VERSION/$XWIN_PREFIX.tar.gz /root/$XWIN_PREFIX.tar.gz @@ -31,7 +32,7 @@ RUN set -eux; \ # And to keep that to a minimum, we still delete the stuff we don't need. rm -rf /root/.xwin-cache; -FROM rust:slim-bullseye as linux +FROM rust:slim-bullseye AS linux RUN set -eux; \ apt-get update; \ @@ -58,8 +59,9 @@ WORKDIR /src/dtmt COPY lib/oodle/*.so lib/oodle/*.a /src/ -FROM linux as msvc +FROM linux AS msvc +# renovate: datasource=github-releases depName=llvm packageName=llvm/llvm-project ARG LLVM_VERSION=18 ENV KEYRINGS /usr/local/share/keyrings diff --git a/.renovaterc b/.renovaterc index 7ad59cd..6d8dec6 100644 --- a/.renovaterc +++ b/.renovaterc @@ -10,5 +10,35 @@ "baseBranches": [ "$default", "/^release\\/.*/" + ], + "ignorePaths": [ + "lib/color_eyre/**", + "lib/ansi-parser/**", + "lib/luajit2-sys/**", + "**/target/**" + ], + "customManagers": [ + { + "customType": "regex", + "description": "Update _VERSION variables in Dockerfiles", + "fileMatch": [ + "(^|/|\\.)Dockerfile$", + "(^|/)Dockerfile\\.[^/]*$" + ], + "matchStrings": [ + "# renovate: datasource=(?[a-z-]+?)(?: depName=(?.+?))? packageName=(?.+?)(?: versioning=(?[a-z-]+?))?\\s(?:ENV|ARG) .+?_VERSION=(?.+?)\\s" + ] + } + ], + "packageRules": [ + { + "matchDatasources": [ + "github-releases" + ], + "matchPackageNames": [ + "llvm/llvm-project" + ], + "extractVersion": "^llvmorg-(?\\d+)\\.\\d+\\.\\d+$" + } ] } From b42b4f01d297a172b85b31b21850a54e915ccc87 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 1 Jul 2025 15:44:23 +0200 Subject: [PATCH 325/335] Prepare to absorb luajit2-sys submodule I had to pin the version of the LuaJIT library to a specific commit, and have reworked the build scripts heavily to work with the XWin setup, sacrificing the more flexible approach that existed before. As such it is heavily customized to the specific usage here. There is no reason to assume that it will be reconciled with the main project in the future. --- .gitmodules | 3 --- lib/luajit2-sys | 1 - 2 files changed, 4 deletions(-) delete mode 160000 lib/luajit2-sys diff --git a/.gitmodules b/.gitmodules index e1964a8..4cd74de 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "lib/luajit2-sys"] - path = lib/luajit2-sys - url = https://github.com/sclu1034/luajit2-sys.git [submodule "lib/color-eyre"] path = lib/color-eyre url = https://github.com/sclu1034/color-eyre.git diff --git a/lib/luajit2-sys b/lib/luajit2-sys deleted file mode 160000 index 6d94a4d..0000000 --- a/lib/luajit2-sys +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6d94a4dd2c296bf1f044ee4c70fb10dca4c1c241 From 7500f9010506bfd28d400aba66cedaf0fedc4ae9 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 1 Jul 2025 16:15:56 +0200 Subject: [PATCH 326/335] luajit2-sys: Remove unused files Stuff that isn't needed anymore after the merge into the workspace. --- .gitmodules | 7 +- lib/luajit2-sys/.azure-pipelines.yml | 59 -- lib/luajit2-sys/.cargo/config | 9 - lib/luajit2-sys/.gitignore | 7 - lib/luajit2-sys/.gitmodules | 3 - lib/luajit2-sys/README.md | 47 -- lib/luajit2-sys/ci/azure-install-rust.yml | 23 - lib/luajit2-sys/ci/azure-test-all.yml | 36 -- lib/luajit2-sys/etc/Makefile | 721 ---------------------- lib/luajit2-sys/examples/hello.lua | 3 - lib/luajit2-sys/examples/lua.rs | 54 -- lib/luajit2-sys/tests/test.rs | 40 -- 12 files changed, 5 insertions(+), 1004 deletions(-) delete mode 100644 lib/luajit2-sys/.azure-pipelines.yml delete mode 100644 lib/luajit2-sys/.cargo/config delete mode 100644 lib/luajit2-sys/.gitignore delete mode 100644 lib/luajit2-sys/.gitmodules delete mode 100644 lib/luajit2-sys/README.md delete mode 100644 lib/luajit2-sys/ci/azure-install-rust.yml delete mode 100644 lib/luajit2-sys/ci/azure-test-all.yml delete mode 100644 lib/luajit2-sys/etc/Makefile delete mode 100644 lib/luajit2-sys/examples/hello.lua delete mode 100644 lib/luajit2-sys/examples/lua.rs delete mode 100644 lib/luajit2-sys/tests/test.rs diff --git a/.gitmodules b/.gitmodules index 4cd74de..558929a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,8 +1,11 @@ [submodule "lib/color-eyre"] path = lib/color-eyre url = https://github.com/sclu1034/color-eyre.git - branch = "fork" + branch = "fork" [submodule "lib/ansi-parser"] path = lib/ansi-parser url = https://gitlab.com/lschwiderski/ansi-parser.git - branch = "issue/outdated-nom" + branch = "issue/outdated-nom" +[submodule "lib/luajit2-sys/luajit"] + path = lib/luajit2-sys/luajit + url = https://github.com/LuaJIT/LuaJIT.git diff --git a/lib/luajit2-sys/.azure-pipelines.yml b/lib/luajit2-sys/.azure-pipelines.yml deleted file mode 100644 index 2445ee1..0000000 --- a/lib/luajit2-sys/.azure-pipelines.yml +++ /dev/null @@ -1,59 +0,0 @@ -trigger: - - master - -pr: - branches: - include: - - master - -schedules: - - cron: "0 12 * * 0" - displayName: Weekly Sunday build - branches: - include: - - master - always: true - -jobs: - - job: Windows - pool: - vmImage: vs2017-win2016 - steps: - - template: ci/azure-install-rust.yml - - template: ci/azure-test-all.yml - strategy: - matrix: - stable-x86_64-msvc: - TOOLCHAIN: stable-x86_64-pc-windows-msvc - # stable-x86_64-gnu: - # TOOLCHAIN: stable-x86_64-pc-windows-gnu - # stable-i686-msvc: - # TOOLCHAIN: stable-i686-pc-windows-msvc - # stable-i686-gnu: - # TOOLCHAIN: stable-i686-pc-windows-gnu - - - job: Linux - pool: - vmImage: ubuntu-16.04 - steps: - - template: ci/azure-install-rust.yml - - template: ci/azure-test-all.yml - strategy: - matrix: - stable-x86_64: - TOOLCHAIN: stable-x86_64-unknown-linux-gnu - # stable-i686: - # TOOLCHAIN: stable-i686-unknown-linux-gnu - nightly-x86_64: - TOOLCHAIN: nightly - - - job: MacOS - pool: - vmImage: macOS-10.14 - steps: - - template: ci/azure-install-rust.yml - - template: ci/azure-test-all.yml - strategy: - matrix: - stable-x86_64: - TOOLCHAIN: stable-x86_64-apple-darwin \ No newline at end of file diff --git a/lib/luajit2-sys/.cargo/config b/lib/luajit2-sys/.cargo/config deleted file mode 100644 index 134419d..0000000 --- a/lib/luajit2-sys/.cargo/config +++ /dev/null @@ -1,9 +0,0 @@ -[target.x86_64-apple-darwin] -rustflags = [ - "-C", - "link-arg=-pagezero_size 10000", - -] - -# "-C", -# "link-arg=-image_base 100000000", \ No newline at end of file diff --git a/lib/luajit2-sys/.gitignore b/lib/luajit2-sys/.gitignore deleted file mode 100644 index faa27f7..0000000 --- a/lib/luajit2-sys/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock - -*.iml -.idea -.vscode \ No newline at end of file diff --git a/lib/luajit2-sys/.gitmodules b/lib/luajit2-sys/.gitmodules deleted file mode 100644 index e7ae3a7..0000000 --- a/lib/luajit2-sys/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "luajit"] - path = luajit - url = https://github.com/LuaJIT/LuaJIT.git diff --git a/lib/luajit2-sys/README.md b/lib/luajit2-sys/README.md deleted file mode 100644 index 873b88e..0000000 --- a/lib/luajit2-sys/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Rust LuaJIT Bindings - -[![crates.io](https://img.shields.io/crates/v/luajit2-sys.svg)](https://crates.io/crates/luajit2-sys) -[![docs.rs](https://docs.rs/luajit2-sys/badge.svg)](https://docs.rs/luajit2-sys) -[![build](https://dev.azure.com/aloucks/aloucks/_apis/build/status/aloucks.luajit2-sys?branchName=master)](https://dev.azure.com/aloucks/aloucks/_build/latest?definitionId=3&branchName=master) - -```toml -[dependencies] -luajit2-sys = "0.0.2" -``` - -## Exported Cargo Environment Variables - -||| -| -- | -- | -| `DEP_LUAJIT_INCLUDE` | Path to the LuaJIT source and headers | -| `DEP_LUAJIT_LIB_NAME` | Platform specfic lib name (`lua51` on Windows and `luajit` everywhere else) | - -## Example - -```rust -use luajit2_sys as sys; -use std::ffi::CStr; - -fn main() { - unsafe { - let lua = sys::luaL_newstate(); - sys::luaL_openlibs(lua); - let script_data = b"return 1 + 2"; - let script_name = b"run_script\0"; - sys::luaL_loadbuffer( - lua, - script_data.as_ptr() as _, - script_data.len() as _, - script_name.as_ptr() as _, - ); - sys::lua_pcall(lua, 0, 1, 0); - let idx = sys::lua_gettop(lua); - let s = sys::lua_tostring(lua, idx); - let result = CStr::from_ptr(s).to_string_lossy().to_string(); - sys::lua_close(lua); - - println!("result: {}", result); - } -} -``` - diff --git a/lib/luajit2-sys/ci/azure-install-rust.yml b/lib/luajit2-sys/ci/azure-install-rust.yml deleted file mode 100644 index cd39cc1..0000000 --- a/lib/luajit2-sys/ci/azure-install-rust.yml +++ /dev/null @@ -1,23 +0,0 @@ -steps: - - bash: | - set -e -x - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $TOOLCHAIN - source $HOME/.cargo/env - echo "##vso[task.prependpath]$HOME/.cargo/bin" - rustup --version - displayName: Install rustup - condition: eq(variables['Agent.OS'], 'Darwin') - - bash: | - set -e -x - rustup --version - rustup default $TOOLCHAIN - rustup update --no-self-update $TOOLCHAIN - rustup toolchain install stable - rustup component add rustfmt --toolchain stable - displayName: Configure rust - - bash: | - set -x - rustc -Vv - cargo -Vv - cargo +stable fmt --version - displayName: Query rustc, cargo, and rustfmt versions \ No newline at end of file diff --git a/lib/luajit2-sys/ci/azure-test-all.yml b/lib/luajit2-sys/ci/azure-test-all.yml deleted file mode 100644 index 877ab5a..0000000 --- a/lib/luajit2-sys/ci/azure-test-all.yml +++ /dev/null @@ -1,36 +0,0 @@ -steps: - - checkout: self - submodules: true -# - script: | -# call "C:\Program Files (x86)\Microsoft Visual Studio\2017\VC\Auxiliary\Build\vcvarsall.bat" x86 -# displayName: Call vcvarsalls.bat x86 -# condition: eq(variables['TOOLCHAIN'], 'stable-i686-pc-windows-msvc') -# - bash: pacman -Syu -# condition: eq(variables['TOOLCHAIN'], 'stable-x86_64-pc-windows-gnu') -# displayName: Update msys -# - bash: sudo apt install gcc-multilib -# condition: eq(variables['TOOLCHAIN'], 'stable-i686-unknown-linux-gnu') -# displayName: Install gcc-multilib - - bash: | - set -e -x - cargo +stable fmt --all -- --check - displayName: Check formatting - - bash: | - set -e -x - cargo test --no-run - displayName: Build everything - env: - RUST_BACKTRACE: 1 - CARGO_INCREMENTAL: 0 - - bash: | - set -e -x - cargo test - displayName: Run unit tests - env: - RUST_BACKTRACE: 1 - CARGO_INCREMENTAL: 0 - - bash: | - pwd - /usr/bin/find ./target - displayName: List files in target - condition: always() diff --git a/lib/luajit2-sys/etc/Makefile b/lib/luajit2-sys/etc/Makefile deleted file mode 100644 index 2046c6c..0000000 --- a/lib/luajit2-sys/etc/Makefile +++ /dev/null @@ -1,721 +0,0 @@ -############################################################################## -# LuaJIT Makefile. Requires GNU Make. -# -# Please read doc/install.html before changing any variables! -# -# Suitable for POSIX platforms (Linux, *BSD, OSX etc.). -# Also works with MinGW and Cygwin on Windows. -# Please check msvcbuild.bat for building with MSVC on Windows. -# -# Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h -############################################################################## - -MAJVER= 2 -MINVER= 1 -RELVER= 0 -ABIVER= 5.1 -NODOTABIVER= 51 - -############################################################################## -############################# COMPILER OPTIONS ############################# -############################################################################## -# These options mainly affect the speed of the JIT compiler itself, not the -# speed of the JIT-compiled code. Turn any of the optional settings on by -# removing the '#' in front of them. Make sure you force a full recompile -# with "make clean", followed by "make" if you change any options. -# -DEFAULT_CC = gcc -# -# LuaJIT builds as a native 32 or 64 bit binary by default. -CC= $(DEFAULT_CC) -# -# Use this if you want to force a 32 bit build on a 64 bit multilib OS. -#CC= $(DEFAULT_CC) -m32 -# -# Since the assembler part does NOT maintain a frame pointer, it's pointless -# to slow down the C part by not omitting it. Debugging, tracebacks and -# unwinding are not affected -- the assembler part has frame unwind -# information and GCC emits it where needed (x64) or with -g (see CCDEBUG). -CCOPT= -O2 -fomit-frame-pointer -# Use this if you want to generate a smaller binary (but it's slower): -#CCOPT= -Os -fomit-frame-pointer -# Note: it's no longer recommended to use -O3 with GCC 4.x. -# The I-Cache bloat usually outweighs the benefits from aggressive inlining. -# -# Target-specific compiler options: -# -# x86/x64 only: For GCC 4.2 or higher and if you don't intend to distribute -# the binaries to a different machine you could also use: -march=native -# -CCOPT_x86= -march=i686 -msse -msse2 -mfpmath=sse -CCOPT_x64= -CCOPT_arm= -CCOPT_arm64= -CCOPT_ppc= -CCOPT_mips= -# -CCDEBUG= -# Uncomment the next line to generate debug information: -#CCDEBUG= -g -# -CCWARN= -Wall -# Uncomment the next line to enable more warnings: -#CCWARN+= -Wextra -Wdeclaration-after-statement -Wredundant-decls -Wshadow -Wpointer-arith -# -############################################################################## - -############################################################################## -################################ BUILD MODE ################################ -############################################################################## -# The default build mode is mixed mode on POSIX. On Windows this is the same -# as dynamic mode. -# -# Mixed mode creates a static + dynamic library and a statically linked luajit. -#BUILDMODE= mixed -# -# Static mode creates a static library and a statically linked luajit. -BUILDMODE= static -# -# Dynamic mode creates a dynamic library and a dynamically linked luajit. -# Note: this executable will only run when the library is installed! -#BUILDMODE= dynamic -# -############################################################################## - -############################################################################## -################################# FEATURES ################################# -############################################################################## -# Enable/disable these features as needed, but make sure you force a full -# recompile with "make clean", followed by "make". -XCFLAGS= -# -# Permanently disable the FFI extension to reduce the size of the LuaJIT -# executable. But please consider that the FFI library is compiled-in, -# but NOT loaded by default. It only allocates any memory, if you actually -# make use of it. -#XCFLAGS+= -DLUAJIT_DISABLE_FFI -# -# Features from Lua 5.2 that are unlikely to break existing code are -# enabled by default. Some other features that *might* break some existing -# code (e.g. __pairs or os.execute() return values) can be enabled here. -# Note: this does not provide full compatibility with Lua 5.2 at this time. -#XCFLAGS+= -DLUAJIT_ENABLE_LUA52COMPAT -# -# Disable the JIT compiler, i.e. turn LuaJIT into a pure interpreter. -#XCFLAGS+= -DLUAJIT_DISABLE_JIT -# -# Some architectures (e.g. PPC) can use either single-number (1) or -# dual-number (2) mode. Uncomment one of these lines to override the -# default mode. Please see LJ_ARCH_NUMMODE in lj_arch.h for details. -#XCFLAGS+= -DLUAJIT_NUMMODE=1 -#XCFLAGS+= -DLUAJIT_NUMMODE=2 -# -# Enable GC64 mode for x64. -#XCFLAGS+= -DLUAJIT_ENABLE_GC64 -# -############################################################################## - -############################################################################## -############################ DEBUGGING SUPPORT ############################# -############################################################################## -# Enable these options as needed, but make sure you force a full recompile -# with "make clean", followed by "make". -# Note that most of these are NOT suitable for benchmarking or release mode! -# -# Use the system provided memory allocator (realloc) instead of the -# bundled memory allocator. This is slower, but sometimes helpful for -# debugging. This option cannot be enabled on x64 without GC64, since -# realloc usually doesn't return addresses in the right address range. -# OTOH this option is mandatory for Valgrind's memcheck tool on x64 and -# the only way to get useful results from it for all other architectures. -#XCFLAGS+= -DLUAJIT_USE_SYSMALLOC -# -# This define is required to run LuaJIT under Valgrind. The Valgrind -# header files must be installed. You should enable debug information, too. -# Use --suppressions=lj.supp to avoid some false positives. -#XCFLAGS+= -DLUAJIT_USE_VALGRIND -# -# This is the client for the GDB JIT API. GDB 7.0 or higher is required -# to make use of it. See lj_gdbjit.c for details. Enabling this causes -# a non-negligible overhead, even when not running under GDB. -#XCFLAGS+= -DLUAJIT_USE_GDBJIT -# -# Turn on assertions for the Lua/C API to debug problems with lua_* calls. -# This is rather slow -- use only while developing C libraries/embeddings. -#XCFLAGS+= -DLUA_USE_APICHECK -# -# Turn on assertions for the whole LuaJIT VM. This significantly slows down -# everything. Use only if you suspect a problem with LuaJIT itself. -#XCFLAGS+= -DLUA_USE_ASSERT -# -############################################################################## -# You probably don't need to change anything below this line! -############################################################################## - -############################################################################## -# Host system detection. -############################################################################## - -ifeq (Windows,$(findstring Windows,$(OS))$(MSYSTEM)$(TERM)) - HOST_SYS= Windows - HOST_RM= del -else - HOST_SYS:= $(shell uname -s) - ifneq (,$(findstring MINGW,$(HOST_SYS))) - HOST_SYS= Windows - HOST_MSYS= mingw - endif - ifneq (,$(findstring MSYS,$(HOST_SYS))) - HOST_SYS= Windows - HOST_MSYS= mingw - endif - ifneq (,$(findstring CYGWIN,$(HOST_SYS))) - HOST_SYS= Windows - HOST_MSYS= cygwin - endif -endif - -############################################################################## -# Flags and options for host and target. -############################################################################## - -# You can override the following variables at the make command line: -# CC HOST_CC STATIC_CC DYNAMIC_CC -# CFLAGS HOST_CFLAGS TARGET_CFLAGS -# LDFLAGS HOST_LDFLAGS TARGET_LDFLAGS TARGET_SHLDFLAGS -# LIBS HOST_LIBS TARGET_LIBS -# CROSS HOST_SYS TARGET_SYS TARGET_FLAGS -# -# Cross-compilation examples: -# make HOST_CC="gcc -m32" CROSS=i586-mingw32msvc- TARGET_SYS=Windows -# make HOST_CC="gcc -m32" CROSS=powerpc-linux-gnu- - -ASOPTIONS= $(CCOPT) $(CCWARN) $(XCFLAGS) $(CFLAGS) -CCOPTIONS= $(CCDEBUG) $(ASOPTIONS) -LDOPTIONS= $(CCDEBUG) $(LDFLAGS) - -HOST_CC= $(CC) -HOST_RM?= rm -f -# If left blank, minilua is built and used. You can supply an installed -# copy of (plain) Lua 5.1 or 5.2, plus Lua BitOp. E.g. with: HOST_LUA=lua -HOST_LUA= - -HOST_XCFLAGS= -I. -HOST_XLDFLAGS= -HOST_XLIBS= -HOST_ACFLAGS= $(CCOPTIONS) $(HOST_XCFLAGS) $(TARGET_ARCH) $(HOST_CFLAGS) -HOST_ALDFLAGS= $(LDOPTIONS) $(HOST_XLDFLAGS) $(HOST_LDFLAGS) -HOST_ALIBS= $(HOST_XLIBS) $(LIBS) $(HOST_LIBS) - -STATIC_CC = $(CROSS)$(CC) -fPIC -DYNAMIC_CC = $(CROSS)$(CC) -fPIC -TARGET_CC= $(STATIC_CC) -TARGET_STCC= $(STATIC_CC) -TARGET_DYNCC= $(DYNAMIC_CC) -TARGET_LD= $(CROSS)$(CC) -TARGET_AR= $(CROSS)ar rcus -TARGET_STRIP= $(CROSS)strip - -TARGET_LIBPATH= $(or $(PREFIX),/usr/local)/$(or $(MULTILIB),lib) -TARGET_SONAME= libluajit-$(ABIVER).so.$(MAJVER) -TARGET_DYLIBNAME= libluajit-$(ABIVER).$(MAJVER).dylib -TARGET_DYLIBPATH= $(TARGET_LIBPATH)/$(TARGET_DYLIBNAME) -TARGET_DLLNAME= lua$(NODOTABIVER).dll -TARGET_XSHLDFLAGS= -shared -fPIC -Wl,-soname,$(TARGET_SONAME) -TARGET_DYNXLDOPTS= - -TARGET_LFSFLAGS= -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -TARGET_XCFLAGS= $(TARGET_LFSFLAGS) -U_FORTIFY_SOURCE -TARGET_XLDFLAGS= -TARGET_XLIBS= -lm -TARGET_TCFLAGS= $(CCOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS) -TARGET_ACFLAGS= $(CCOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS) -TARGET_ASFLAGS= $(ASOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS) -TARGET_ALDFLAGS= $(LDOPTIONS) $(TARGET_XLDFLAGS) $(TARGET_FLAGS) $(TARGET_LDFLAGS) -TARGET_ASHLDFLAGS= $(LDOPTIONS) $(TARGET_XSHLDFLAGS) $(TARGET_FLAGS) $(TARGET_SHLDFLAGS) -TARGET_ALIBS= $(TARGET_XLIBS) $(LIBS) $(TARGET_LIBS) - -TARGET_TESTARCH=$(shell $(TARGET_CC) $(TARGET_TCFLAGS) -E lj_arch.h -dM) -ifneq (,$(findstring LJ_TARGET_X64 ,$(TARGET_TESTARCH))) - TARGET_LJARCH= x64 -else -ifneq (,$(findstring LJ_TARGET_X86 ,$(TARGET_TESTARCH))) - TARGET_LJARCH= x86 -else -ifneq (,$(findstring LJ_TARGET_ARM ,$(TARGET_TESTARCH))) - TARGET_LJARCH= arm -else -ifneq (,$(findstring LJ_TARGET_ARM64 ,$(TARGET_TESTARCH))) - ifneq (,$(findstring __AARCH64EB__ ,$(TARGET_TESTARCH))) - TARGET_ARCH= -D__AARCH64EB__=1 - endif - TARGET_LJARCH= arm64 -else -ifneq (,$(findstring LJ_TARGET_PPC ,$(TARGET_TESTARCH))) - ifneq (,$(findstring LJ_LE 1,$(TARGET_TESTARCH))) - TARGET_ARCH= -DLJ_ARCH_ENDIAN=LUAJIT_LE - else - TARGET_ARCH= -DLJ_ARCH_ENDIAN=LUAJIT_BE - endif - TARGET_LJARCH= ppc -else -ifneq (,$(findstring LJ_TARGET_MIPS ,$(TARGET_TESTARCH))) - ifneq (,$(findstring MIPSEL ,$(TARGET_TESTARCH))) - TARGET_ARCH= -D__MIPSEL__=1 - endif - ifneq (,$(findstring LJ_TARGET_MIPS64 ,$(TARGET_TESTARCH))) - TARGET_LJARCH= mips64 - else - TARGET_LJARCH= mips - endif -else - $(error Unsupported target architecture) -endif -endif -endif -endif -endif -endif - -ifneq (,$(findstring LJ_TARGET_PS3 1,$(TARGET_TESTARCH))) - TARGET_SYS= PS3 - TARGET_ARCH+= -D__CELLOS_LV2__ - TARGET_XCFLAGS+= -DLUAJIT_USE_SYSMALLOC - TARGET_XLIBS+= -lpthread -endif - -TARGET_XCFLAGS+= $(CCOPT_$(TARGET_LJARCH)) -TARGET_ARCH+= $(patsubst %,-DLUAJIT_TARGET=LUAJIT_ARCH_%,$(TARGET_LJARCH)) - -ifneq (,$(PREFIX)) -ifneq (/usr/local,$(PREFIX)) - TARGET_XCFLAGS+= -DLUA_ROOT=\"$(PREFIX)\" - ifneq (/usr,$(PREFIX)) - TARGET_DYNXLDOPTS= -Wl,-rpath,$(TARGET_LIBPATH) - endif -endif -endif -ifneq (,$(MULTILIB)) - TARGET_XCFLAGS+= -DLUA_MULTILIB=\"$(MULTILIB)\" -endif -ifneq (,$(LMULTILIB)) - TARGET_XCFLAGS+= -DLUA_LMULTILIB=\"$(LMULTILIB)\" -endif - -############################################################################## -# Target system detection. -############################################################################## - -TARGET_SYS?= $(HOST_SYS) -ifeq (Windows,$(TARGET_SYS)) - TARGET_STRIP+= --strip-unneeded - TARGET_XSHLDFLAGS= -shared - TARGET_DYNXLDOPTS= -else - TARGET_AR+= 2>/dev/null -ifeq (,$(shell $(TARGET_CC) -o /dev/null -c -x c /dev/null -fno-stack-protector 2>/dev/null || echo 1)) - TARGET_XCFLAGS+= -fno-stack-protector -endif -ifeq (Darwin,$(TARGET_SYS)) - ifeq (,$(MACOSX_DEPLOYMENT_TARGET)) -# export MACOSX_DEPLOYMENT_TARGET=10.4 - endif - TARGET_STRIP+= -x - TARGET_XSHLDFLAGS= -dynamiclib -single_module -undefined dynamic_lookup -fPIC - TARGET_DYNXLDOPTS= - TARGET_XSHLDFLAGS+= -install_name $(TARGET_DYLIBPATH) -compatibility_version $(MAJVER).$(MINVER) -current_version $(MAJVER).$(MINVER).$(RELVER) - ifeq (x64,$(TARGET_LJARCH)) - TARGET_XLDFLAGS+= -pagezero_size 10000 -image_base 100000000 - TARGET_XSHLDFLAGS+= -image_base 7fff04c4a000 - endif -else -ifeq (iOS,$(TARGET_SYS)) - TARGET_STRIP+= -x - TARGET_XSHLDFLAGS= -dynamiclib -single_module -undefined dynamic_lookup -fPIC - TARGET_DYNXLDOPTS= - TARGET_XSHLDFLAGS+= -install_name $(TARGET_DYLIBPATH) -compatibility_version $(MAJVER).$(MINVER) -current_version $(MAJVER).$(MINVER).$(RELVER) - ifeq (arm64,$(TARGET_LJARCH)) - TARGET_XCFLAGS+= -fno-omit-frame-pointer - endif -else - ifneq (SunOS,$(TARGET_SYS)) - ifneq (PS3,$(TARGET_SYS)) - TARGET_XLDFLAGS+= -Wl,-E - endif - endif - ifeq (Linux,$(TARGET_SYS)) - TARGET_XLIBS+= -ldl - endif - ifeq (GNU/kFreeBSD,$(TARGET_SYS)) - TARGET_XLIBS+= -ldl - endif -endif -endif -endif - -ifneq ($(HOST_SYS),$(TARGET_SYS)) - ifeq (Windows,$(TARGET_SYS)) - HOST_XCFLAGS+= -malign-double -DLUAJIT_OS=LUAJIT_OS_WINDOWS - else - ifeq (Linux,$(TARGET_SYS)) - HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_LINUX - else - ifeq (Darwin,$(TARGET_SYS)) - HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX - else - ifeq (iOS,$(TARGET_SYS)) - HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX - else - HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OTHER - endif - endif - endif - endif -endif - -ifneq (,$(CCDEBUG)) - TARGET_STRIP= @: -endif - -############################################################################## -# Files and pathnames. -############################################################################## - -MINILUA_O= host/minilua.o -MINILUA_LIBS= -lm -MINILUA_T= host/minilua -MINILUA_X= $(MINILUA_T) - -ifeq (,$(HOST_LUA)) - HOST_LUA= $(MINILUA_X) - DASM_DEP= $(MINILUA_T) -endif - -DASM_DIR= ../dynasm -DASM= $(HOST_LUA) $(DASM_DIR)/dynasm.lua -DASM_XFLAGS= -DASM_AFLAGS= -DASM_ARCH= $(TARGET_LJARCH) - -ifneq (,$(findstring LJ_LE 1,$(TARGET_TESTARCH))) - DASM_AFLAGS+= -D ENDIAN_LE -else - DASM_AFLAGS+= -D ENDIAN_BE -endif -ifneq (,$(findstring LJ_ARCH_BITS 64,$(TARGET_TESTARCH))) - DASM_AFLAGS+= -D P64 -endif -ifneq (,$(findstring LJ_HASJIT 1,$(TARGET_TESTARCH))) - DASM_AFLAGS+= -D JIT -endif -ifneq (,$(findstring LJ_HASFFI 1,$(TARGET_TESTARCH))) - DASM_AFLAGS+= -D FFI -endif -ifneq (,$(findstring LJ_DUALNUM 1,$(TARGET_TESTARCH))) - DASM_AFLAGS+= -D DUALNUM -endif -ifneq (,$(findstring LJ_ARCH_HASFPU 1,$(TARGET_TESTARCH))) - DASM_AFLAGS+= -D FPU - TARGET_ARCH+= -DLJ_ARCH_HASFPU=1 -else - TARGET_ARCH+= -DLJ_ARCH_HASFPU=0 -endif -ifeq (,$(findstring LJ_ABI_SOFTFP 1,$(TARGET_TESTARCH))) - DASM_AFLAGS+= -D HFABI - TARGET_ARCH+= -DLJ_ABI_SOFTFP=0 -else - TARGET_ARCH+= -DLJ_ABI_SOFTFP=1 -endif -ifneq (,$(findstring LJ_NO_UNWIND 1,$(TARGET_TESTARCH))) - DASM_AFLAGS+= -D NO_UNWIND - TARGET_ARCH+= -DLUAJIT_NO_UNWIND -endif -DASM_AFLAGS+= -D VER=$(subst LJ_ARCH_VERSION_,,$(filter LJ_ARCH_VERSION_%,$(subst LJ_ARCH_VERSION ,LJ_ARCH_VERSION_,$(TARGET_TESTARCH)))) -ifeq (Windows,$(TARGET_SYS)) - DASM_AFLAGS+= -D WIN -endif -ifeq (x64,$(TARGET_LJARCH)) - ifeq (,$(findstring LJ_FR2 1,$(TARGET_TESTARCH))) - DASM_ARCH= x86 - endif -else -ifeq (arm,$(TARGET_LJARCH)) - ifeq (iOS,$(TARGET_SYS)) - DASM_AFLAGS+= -D IOS - endif -else -ifeq (ppc,$(TARGET_LJARCH)) - ifneq (,$(findstring LJ_ARCH_SQRT 1,$(TARGET_TESTARCH))) - DASM_AFLAGS+= -D SQRT - endif - ifneq (,$(findstring LJ_ARCH_ROUND 1,$(TARGET_TESTARCH))) - DASM_AFLAGS+= -D ROUND - endif - ifneq (,$(findstring LJ_ARCH_PPC32ON64 1,$(TARGET_TESTARCH))) - DASM_AFLAGS+= -D GPR64 - endif - ifeq (PS3,$(TARGET_SYS)) - DASM_AFLAGS+= -D PPE -D TOC - endif - ifneq (,$(findstring LJ_ARCH_PPC64 ,$(TARGET_TESTARCH))) - DASM_ARCH= ppc64 - endif -endif -endif -endif - -DASM_FLAGS= $(DASM_XFLAGS) $(DASM_AFLAGS) -DASM_DASC= vm_$(DASM_ARCH).dasc - -BUILDVM_O= host/buildvm.o host/buildvm_asm.o host/buildvm_peobj.o \ - host/buildvm_lib.o host/buildvm_fold.o -BUILDVM_T= host/buildvm -BUILDVM_X= $(BUILDVM_T) - -HOST_O= $(MINILUA_O) $(BUILDVM_O) -HOST_T= $(MINILUA_T) $(BUILDVM_T) - -LJVM_S= lj_vm.S -LJVM_O= lj_vm.o -LJVM_BOUT= $(LJVM_S) -LJVM_MODE= elfasm - -LJLIB_O= lib_base.o lib_math.o lib_bit.o lib_string.o lib_table.o \ - lib_io.o lib_os.o lib_package.o lib_debug.o lib_jit.o lib_ffi.o -LJLIB_C= $(LJLIB_O:.o=.c) - -LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \ - lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \ - lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_strscan.o \ - lj_strfmt.o lj_strfmt_num.o lj_api.o lj_profile.o \ - lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o lj_load.o \ - lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \ - lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \ - lj_mcode.o lj_snap.o lj_record.o lj_crecord.o lj_ffrecord.o \ - lj_asm.o lj_trace.o lj_gdbjit.o \ - lj_ctype.o lj_cdata.o lj_cconv.o lj_ccall.o lj_ccallback.o \ - lj_carith.o lj_clib.o lj_cparse.o \ - lj_lib.o lj_alloc.o lib_aux.o \ - $(LJLIB_O) lib_init.o - -LJVMCORE_O= $(LJVM_O) $(LJCORE_O) -LJVMCORE_DYNO= $(LJVMCORE_O:.o=_dyn.o) - -LIB_VMDEF= jit/vmdef.lua -LIB_VMDEFP= $(LIB_VMDEF) - -LUAJIT_O= luajit.o -LUAJIT_A= libluajit.a -LUAJIT_SO= libluajit.so -LUAJIT_T= luajit - -ALL_T= $(LUAJIT_T) $(LUAJIT_A) $(LUAJIT_SO) $(HOST_T) -ALL_HDRGEN= lj_bcdef.h lj_ffdef.h lj_libdef.h lj_recdef.h lj_folddef.h \ - host/buildvm_arch.h -ALL_GEN= $(LJVM_S) $(ALL_HDRGEN) $(LIB_VMDEFP) -WIN_RM= *.obj *.lib *.exp *.dll *.exe *.manifest *.pdb *.ilk -ALL_RM= $(ALL_T) $(ALL_GEN) *.o host/*.o $(WIN_RM) - -############################################################################## -# Build mode handling. -############################################################################## - -# Mixed mode defaults. -TARGET_O= $(LUAJIT_A) -TARGET_T= $(LUAJIT_T) $(LUAJIT_SO) -TARGET_DEP= $(LIB_VMDEF) $(LUAJIT_SO) - -ifeq (Windows,$(TARGET_SYS)) - TARGET_DYNCC= $(STATIC_CC) - LJVM_MODE= peobj - LJVM_BOUT= $(LJVM_O) - LUAJIT_T= luajit.exe - ifeq (cygwin,$(HOST_MSYS)) - LUAJIT_SO= cyg$(TARGET_DLLNAME) - else - LUAJIT_SO= $(TARGET_DLLNAME) - endif - # Mixed mode is not supported on Windows. And static mode doesn't work well. - # C modules cannot be loaded, because they bind to lua51.dll. - ifneq (static,$(BUILDMODE)) - BUILDMODE= dynamic - TARGET_XCFLAGS+= -DLUA_BUILD_AS_DLL - endif -endif -ifeq (Darwin,$(TARGET_SYS)) - LJVM_MODE= machasm -endif -ifeq (iOS,$(TARGET_SYS)) - LJVM_MODE= machasm -endif -ifeq (SunOS,$(TARGET_SYS)) - BUILDMODE= static -endif -ifeq (PS3,$(TARGET_SYS)) - BUILDMODE= static -endif - -ifeq (Windows,$(HOST_SYS)) - MINILUA_T= host/minilua.exe - BUILDVM_T= host/buildvm.exe - ifeq (,$(HOST_MSYS)) - MINILUA_X= host\minilua - BUILDVM_X= host\buildvm - ALL_RM:= $(subst /,\,$(ALL_RM)) - endif -endif - -ifeq (static,$(BUILDMODE)) - TARGET_DYNCC= @: - TARGET_T= $(LUAJIT_T) - TARGET_DEP= $(LIB_VMDEF) -else -ifeq (dynamic,$(BUILDMODE)) - ifneq (Windows,$(TARGET_SYS)) - TARGET_CC= $(DYNAMIC_CC) - endif - TARGET_DYNCC= @: - LJVMCORE_DYNO= $(LJVMCORE_O) - TARGET_O= $(LUAJIT_SO) - TARGET_XLDFLAGS+= $(TARGET_DYNXLDOPTS) -else -ifeq (Darwin,$(TARGET_SYS)) - TARGET_DYNCC= @: - LJVMCORE_DYNO= $(LJVMCORE_O) -endif -ifeq (iOS,$(TARGET_SYS)) - TARGET_DYNCC= @: - LJVMCORE_DYNO= $(LJVMCORE_O) -endif -endif -endif - -Q= @ -E= @echo -#Q= -#E= @: - -############################################################################## -# Make targets. -############################################################################## - -default all: $(TARGET_T) - -amalg: - @grep "^[+|]" ljamalg.c - $(MAKE) all "LJCORE_O=ljamalg.o" - -clean: - $(HOST_RM) $(ALL_RM) - -libbc: - ./$(LUAJIT_T) host/genlibbc.lua -o host/buildvm_libbc.h $(LJLIB_C) - $(MAKE) all - -depend: - @for file in $(ALL_HDRGEN); do \ - test -f $$file || touch $$file; \ - done - @$(HOST_CC) $(HOST_ACFLAGS) -MM *.c host/*.c | \ - sed -e "s| [^ ]*/dasm_\S*\.h||g" \ - -e "s|^\([^l ]\)|host/\1|" \ - -e "s| lj_target_\S*\.h| lj_target_*.h|g" \ - -e "s| lj_emit_\S*\.h| lj_emit_*.h|g" \ - -e "s| lj_asm_\S*\.h| lj_asm_*.h|g" >Makefile.dep - @for file in $(ALL_HDRGEN); do \ - test -s $$file || $(HOST_RM) $$file; \ - done - -.PHONY: default all amalg clean libbc depend - -############################################################################## -# Rules for generated files. -############################################################################## - -$(MINILUA_T): $(MINILUA_O) - $(E) "HOSTLINK $@" - $(Q)$(HOST_CC) $(HOST_ALDFLAGS) -o $@ $(MINILUA_O) $(MINILUA_LIBS) $(HOST_ALIBS) - -host/buildvm_arch.h: $(DASM_DASC) $(DASM_DEP) $(DASM_DIR)/*.lua - $(E) "DYNASM $@" - $(Q)$(DASM) $(DASM_FLAGS) -o $@ $(DASM_DASC) - -host/buildvm.o: $(DASM_DIR)/dasm_*.h - -$(BUILDVM_T): $(BUILDVM_O) - $(E) "HOSTLINK $@" - $(Q)$(HOST_CC) $(HOST_ALDFLAGS) -o $@ $(BUILDVM_O) $(HOST_ALIBS) - -$(LJVM_BOUT): $(BUILDVM_T) - $(E) "BUILDVM $@" - $(Q)$(BUILDVM_X) -m $(LJVM_MODE) -o $@ - -lj_bcdef.h: $(BUILDVM_T) $(LJLIB_C) - $(E) "BUILDVM $@" - $(Q)$(BUILDVM_X) -m bcdef -o $@ $(LJLIB_C) - -lj_ffdef.h: $(BUILDVM_T) $(LJLIB_C) - $(E) "BUILDVM $@" - $(Q)$(BUILDVM_X) -m ffdef -o $@ $(LJLIB_C) - -lj_libdef.h: $(BUILDVM_T) $(LJLIB_C) - $(E) "BUILDVM $@" - $(Q)$(BUILDVM_X) -m libdef -o $@ $(LJLIB_C) - -lj_recdef.h: $(BUILDVM_T) $(LJLIB_C) - $(E) "BUILDVM $@" - $(Q)$(BUILDVM_X) -m recdef -o $@ $(LJLIB_C) - -$(LIB_VMDEF): $(BUILDVM_T) $(LJLIB_C) - $(E) "BUILDVM $@" - $(Q)$(BUILDVM_X) -m vmdef -o $(LIB_VMDEFP) $(LJLIB_C) - -lj_folddef.h: $(BUILDVM_T) lj_opt_fold.c - $(E) "BUILDVM $@" - $(Q)$(BUILDVM_X) -m folddef -o $@ lj_opt_fold.c - -############################################################################## -# Object file rules. -############################################################################## - -%.o: %.c - $(E) "CC $@" - $(Q)$(TARGET_DYNCC) $(TARGET_ACFLAGS) -c -o $(@:.o=_dyn.o) $< - $(Q)$(TARGET_CC) $(TARGET_ACFLAGS) -c -o $@ $< - -%.o: %.S - $(E) "ASM $@" - $(Q)$(TARGET_DYNCC) $(TARGET_ASFLAGS) -c -o $(@:.o=_dyn.o) $< - $(Q)$(TARGET_CC) $(TARGET_ASFLAGS) -c -o $@ $< - -$(LUAJIT_O): - $(E) "CC $@" - $(Q)$(TARGET_STCC) $(TARGET_ACFLAGS) -c -o $@ $< - -$(HOST_O): %.o: %.c - $(E) "HOSTCC $@" - $(Q)$(HOST_CC) $(HOST_ACFLAGS) -c -o $@ $< - -include Makefile.dep - -############################################################################## -# Target file rules. -############################################################################## - -$(LUAJIT_A): $(LJVMCORE_O) - $(E) "AR $@" - $(Q)$(TARGET_AR) $@ $(LJVMCORE_O) - -# The dependency on _O, but linking with _DYNO is intentional. -$(LUAJIT_SO): $(LJVMCORE_O) - $(E) "DYNLINK $@" - $(Q)$(TARGET_LD) $(TARGET_ASHLDFLAGS) -o $@ $(LJVMCORE_DYNO) $(TARGET_ALIBS) - $(Q)$(TARGET_STRIP) $@ - -$(LUAJIT_T): $(TARGET_O) $(LUAJIT_O) $(TARGET_DEP) - $(E) "LINK $@" - $(Q)$(TARGET_LD) $(TARGET_ALDFLAGS) -o $@ $(LUAJIT_O) $(TARGET_O) $(TARGET_ALIBS) - $(Q)$(TARGET_STRIP) $@ - $(E) "OK Successfully built LuaJIT" - -############################################################################## diff --git a/lib/luajit2-sys/examples/hello.lua b/lib/luajit2-sys/examples/hello.lua deleted file mode 100644 index 87a4120..0000000 --- a/lib/luajit2-sys/examples/hello.lua +++ /dev/null @@ -1,3 +0,0 @@ -print("Hello from lua") - -return 1 + 2 diff --git a/lib/luajit2-sys/examples/lua.rs b/lib/luajit2-sys/examples/lua.rs deleted file mode 100644 index 0784409..0000000 --- a/lib/luajit2-sys/examples/lua.rs +++ /dev/null @@ -1,54 +0,0 @@ -use std::env; -use std::ffi::{CStr, CString}; -use std::ptr; - -use luajit2_sys as sys; - -unsafe fn run_script(script_name: String, script_src: String) { - let lua = sys::luaL_newstate(); - assert_ne!(lua, ptr::null_mut()); - sys::luaL_openlibs(lua); - let script_data = script_src.as_bytes(); - let script_name = CString::new(script_name).unwrap(); - let mut error = sys::luaL_loadbuffer( - lua, - script_data.as_ptr() as _, - script_data.len() as _, - script_name.as_ptr() as _, - ); - if error != 0 { - eprintln!("luaL_loadbuffer failed"); - } else { - error = sys::lua_pcall(lua, 0, 1, 0); - if error != 0 { - eprintln!("lua_pcall failed"); - } - } - let idx = sys::lua_gettop(lua); - if sys::lua_isnoneornil(lua, idx) != 1 { - let s = sys::lua_tostring(lua, idx); - assert_ne!(s, ptr::null(), "lua_tostring returned null"); - let result = CStr::from_ptr(s).to_string_lossy().to_string(); - println!("script result: {}", result); - } - sys::lua_close(lua); -} - -fn main() { - if let Some(script_name) = env::args().skip(1).next() { - let script_src = std::fs::read_to_string(&script_name) - .unwrap_or_else(|e| panic!("failed to read file: '{}' {:?}", &script_name, e)); - unsafe { - run_script(script_name, script_src); - } - } else { - println!( - "{} FILE", - env::current_exe() - .unwrap() - .file_name() - .unwrap() - .to_string_lossy() - ); - } -} diff --git a/lib/luajit2-sys/tests/test.rs b/lib/luajit2-sys/tests/test.rs deleted file mode 100644 index 8a018ea..0000000 --- a/lib/luajit2-sys/tests/test.rs +++ /dev/null @@ -1,40 +0,0 @@ -use luajit2_sys as sys; - -#[test] -fn run_script() { - use std::ffi::CStr; - use std::ptr; - - unsafe { - let lua = sys::luaL_newstate(); - assert_ne!(lua, ptr::null_mut()); - sys::luaL_openlibs(lua); - let script_data = b"return 1 + 2"; - let script_name = b"run_script\0"; - let mut error = sys::luaL_loadbuffer( - lua, - script_data.as_ptr() as _, - script_data.len() as _, - script_name.as_ptr() as _, - ); - if error != 0 { - eprintln!("luaL_loadbuffer failed"); - } else { - error = sys::lua_pcall(lua, 0, 1, 0); - if error != 0 { - eprintln!("lua_pcall failed"); - } - } - - let idx = sys::lua_gettop(lua); - println!("lua_gettop = {}", idx); - - let s = sys::lua_tostring(lua, idx); - assert_ne!(s, ptr::null(), "lua_tostring returned null"); - - let result = CStr::from_ptr(s).to_string_lossy().to_string(); - sys::lua_close(lua); - - assert_eq!("3", result); - } -} From 3bf4b7f0d1e9ee171310d0a43bc750171607e116 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Tue, 1 Jul 2025 16:17:05 +0200 Subject: [PATCH 327/335] luajit2-sys: Move dependencies to workspace --- Cargo.lock | 12 ++++++------ Cargo.toml | 4 ++++ lib/luajit2-sys/Cargo.toml | 8 ++++---- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b25b0b3..9fd5a96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -379,9 +379,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.13" +version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72db2f7947ecee9b03b510377e8bb9077afa27176fdbff55c51027e976fdcc48" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "jobserver", "libc", @@ -2173,9 +2173,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libloading" @@ -2184,7 +2184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -4581,7 +4581,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 99ce493..15e26c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,10 @@ ansi-parser = "0.9.1" ansi_term = "0.12.1" async-recursion = "1.0.5" bincode = "2.0.0" +bindgen = "0.70.1" bitflags = "2.5.0" byteorder = "1.4.3" +cc = "1.2.27" clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } cli-table = { version = "0.5.0", default-features = false, features = ["derive"] } color-eyre = { path = "lib/color-eyre" } @@ -28,11 +30,13 @@ druid = { version = "0.8", features = ["im", "serde", "image", "png", "jpeg", "b druid-widget-nursery = "0.1" dtmt-shared = { path = "lib/dtmt-shared" } fastrand = "2.1.0" +fs_extra = "1.1.0" futures = "0.3.25" futures-util = "0.3.24" glob = "0.3.0" interprocess = "2.1.0" lazy_static = "1.4.0" +libc = "0.2.174" luajit2-sys = { path = "lib/luajit2-sys" } minijinja = { version = "2.0.1", default-features = false, features = ["serde"] } nanorand = "0.8.0" diff --git a/lib/luajit2-sys/Cargo.toml b/lib/luajit2-sys/Cargo.toml index 69e935b..65dd61a 100644 --- a/lib/luajit2-sys/Cargo.toml +++ b/lib/luajit2-sys/Cargo.toml @@ -12,9 +12,9 @@ documentation = "https://docs.rs/luajit2-sys" links = "luajit" [dependencies] -libc = "0.2" +libc = { workspace = true } [build-dependencies] -bindgen = "0.70.1" -cc = "1" -fs_extra = "1.1.0" +bindgen = { workspace = true } +cc = { workspace = true } +fs_extra = { workspace = true } From b2a98a39ce93f9caeb25faf424178a13635f4dbf Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 2 Jul 2025 13:31:19 +0200 Subject: [PATCH 328/335] luajit2-sys: Apply clippy lints --- lib/luajit2-sys/build.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/luajit2-sys/build.rs b/lib/luajit2-sys/build.rs index d291dd7..fc564db 100644 --- a/lib/luajit2-sys/build.rs +++ b/lib/luajit2-sys/build.rs @@ -114,7 +114,7 @@ fn build_gcc(src_dir: &str) { fn build_msvc(src_dir: &str, out_dir: &str) { let mut cc = Build::new(); - // cc can't handle many of the `cland-dl`-specific flags, so + // cc can't handle many of the `clang-dl`-specific flags, so // we need to port them manually from a `make -n` run. cc.out_dir(out_dir) // `llvm-as` (which the clang-based toolchain for MSVC would use to compile `lj_vm.S` @@ -145,7 +145,7 @@ fn build_msvc(src_dir: &str, out_dir: &str) { fn main() { let luajit_dir = format!("{}/luajit", env!("CARGO_MANIFEST_DIR")); let out_dir = env::var("OUT_DIR").unwrap(); - let src_dir = format!("{}/luajit/src", out_dir); + let src_dir = format!("{out_dir}/luajit/src"); dbg!(&luajit_dir); dbg!(&out_dir); @@ -164,20 +164,20 @@ fn main() { // sources to re-compile just the library. if env::var("CARGO_CFG_WINDOWS").is_ok() { build_msvc(&src_dir, &out_dir); - println!("cargo:rustc-link-search={}", out_dir); + println!("cargo:rustc-link-search={out_dir}"); } else { - println!("cargo:rustc-link-search=native={}", src_dir); + println!("cargo:rustc-link-search=native={src_dir}"); } - println!("cargo:lib-name={}", LIB_NAME); - println!("cargo:include={}", src_dir); - println!("cargo:rustc-link-lib=static={}", LIB_NAME); + println!("cargo:lib-name={LIB_NAME}"); + println!("cargo:include={src_dir}"); + println!("cargo:rustc-link-lib=static={LIB_NAME}"); let mut bindings = bindgen::Builder::default(); for header in LUAJIT_HEADERS { - println!("cargo:rerun-if-changed={}/src/{}", luajit_dir, header); - bindings = bindings.header(format!("{}/src/{}", luajit_dir, header)); + println!("cargo:rerun-if-changed={luajit_dir}/src/{header}"); + bindings = bindings.header(format!("{luajit_dir}/src/{header}")); } let bindings = bindings From f47dd4ecb8e47cb6910a37e9c4dd663ca154f97d Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 2 Jul 2025 14:39:47 +0200 Subject: [PATCH 329/335] Update color-eyre --- lib/color-eyre | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/color-eyre b/lib/color-eyre index 228b8ca..bdefeef 160000 --- a/lib/color-eyre +++ b/lib/color-eyre @@ -1 +1 @@ -Subproject commit 228b8ca37ee79ab9afa45c40da415e4dcb029751 +Subproject commit bdefeef09803df45bdf6dae7f3ae289e58427e3a From 71051a384b98fc55eb494dc37f2eea89d0e6c106 Mon Sep 17 00:00:00 2001 From: Lucas Schwiderski Date: Wed, 2 Jul 2025 16:18:25 +0200 Subject: [PATCH 330/335] luajit2-sys: Compile in parallel --- Cargo.toml | 2 +- lib/luajit2-sys/build.rs | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 15e26c6..608bcda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ bincode = "2.0.0" bindgen = "0.70.1" bitflags = "2.5.0" byteorder = "1.4.3" -cc = "1.2.27" +cc = { version = "1.2.27", features = ["parallel"] } clap = { version = "4.0.15", features = ["color", "derive", "std", "cargo", "string", "unicode"] } cli-table = { version = "0.5.0", default-features = false, features = ["derive"] } color-eyre = { path = "lib/color-eyre" } diff --git a/lib/luajit2-sys/build.rs b/lib/luajit2-sys/build.rs index fc564db..ea6026b 100644 --- a/lib/luajit2-sys/build.rs +++ b/lib/luajit2-sys/build.rs @@ -82,6 +82,11 @@ const LUAJIT_SRC: [&str; 65] = [ fn build_gcc(src_dir: &str) { let mut buildcmd = Command::new("make"); + if let Ok(flags) = env::var("CARGO_MAKEFLAGS") { + buildcmd.env("MAKEFLAGS", flags); + } else { + buildcmd.arg("-j8"); + } buildcmd.current_dir(src_dir); buildcmd.stderr(Stdio::inherit()); buildcmd.arg("--no-silent"); @@ -102,14 +107,10 @@ fn build_gcc(src_dir: &str) { let mut child = buildcmd.spawn().expect("failed to run make"); - if !child + child .wait() .map(|status| status.success()) - .map_err(|_| false) - .unwrap_or(false) - { - panic!("Failed to build luajit"); - } + .expect("Failed to build LuaJIT"); } fn build_msvc(src_dir: &str, out_dir: &str) { From ef24d9bee6016f39fdf12c1f61b345bf74e126d9 Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 2 Jul 2025 14:46:46 +0000 Subject: [PATCH 331/335] fix(deps): update rust crate reqwest to v0.12.22 --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9fd5a96..352b727 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2184,7 +2184,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -3077,9 +3077,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.21" +version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8cea6b35bcceb099f30173754403d2eba0a5dc18cea3630fccd88251909288" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ "base64 0.22.1", "bytes", @@ -4581,7 +4581,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] From db0a597342fe3740f316efe802af2ce73341c64d Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 2 Jul 2025 14:46:49 +0000 Subject: [PATCH 332/335] chore(deps): update dependency xwin to v0.6.6 --- .ci/image/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/image/Dockerfile b/.ci/image/Dockerfile index b995065..96347ce 100644 --- a/.ci/image/Dockerfile +++ b/.ci/image/Dockerfile @@ -2,7 +2,7 @@ FROM debian:bullseye-slim AS xwin # renovate: datasource=github-releases depName=xwin packageName=Jake-Shadle/xwin -ARG XWIN_VERSION=0.5.2 +ARG XWIN_VERSION=0.6.6 ARG XWIN_PREFIX="xwin-$XWIN_VERSION-x86_64-unknown-linux-musl" ADD https://github.com/Jake-Shadle/xwin/releases/download/$XWIN_VERSION/$XWIN_PREFIX.tar.gz /root/$XWIN_PREFIX.tar.gz From 501b216be3f8696a226c6c4931679b6c14c70779 Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 2 Jul 2025 14:46:54 +0000 Subject: [PATCH 333/335] chore(deps): update rust crate bindgen to 0.72.0 --- Cargo.lock | 24 ++---------------------- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9fd5a96..a09eacb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -227,26 +227,6 @@ dependencies = [ "virtue", ] -[[package]] -name = "bindgen" -version = "0.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" -dependencies = [ - "bitflags 2.9.1", - "cexpr", - "clang-sys", - "itertools", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.100", -] - [[package]] name = "bindgen" version = "0.72.0" @@ -2241,7 +2221,7 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" name = "luajit2-sys" version = "0.0.2" dependencies = [ - "bindgen 0.70.1", + "bindgen", "cc", "fs_extra", "libc", @@ -2546,7 +2526,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" name = "oodle" version = "0.1.0" dependencies = [ - "bindgen 0.72.0", + "bindgen", "color-eyre", "tracing", ] diff --git a/Cargo.toml b/Cargo.toml index 608bcda..35f8d72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ ansi-parser = "0.9.1" ansi_term = "0.12.1" async-recursion = "1.0.5" bincode = "2.0.0" -bindgen = "0.70.1" +bindgen = "0.72.0" bitflags = "2.5.0" byteorder = "1.4.3" cc = { version = "1.2.27", features = ["parallel"] } From 56d9866301d15e03ecb16aff83ce2575fcfd8ede Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 2 Jul 2025 14:47:03 +0000 Subject: [PATCH 334/335] chore(deps): update rust crate tokio to v1.46.0 --- Cargo.lock | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9fd5a96..8206d78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2014,6 +2014,17 @@ dependencies = [ "unic-langid", ] +[[package]] +name = "io-uring" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "ipnet" version = "2.9.0" @@ -2184,7 +2195,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -3870,16 +3881,18 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.1" +version = "1.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "1140bb80481756a8cbe10541f37433b459c5aa1e727b4c020fbfebdc25bf3ec4" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", "tracing", From 1e2c20ce0b37f65ddee4f07537b02dc117caa9db Mon Sep 17 00:00:00 2001 From: Renovate Date: Wed, 2 Jul 2025 15:01:33 +0000 Subject: [PATCH 335/335] chore(deps): update dependency llvm to v20 --- .ci/image/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/image/Dockerfile b/.ci/image/Dockerfile index b995065..17865f2 100644 --- a/.ci/image/Dockerfile +++ b/.ci/image/Dockerfile @@ -62,7 +62,7 @@ COPY lib/oodle/*.so lib/oodle/*.a /src/ FROM linux AS msvc # renovate: datasource=github-releases depName=llvm packageName=llvm/llvm-project -ARG LLVM_VERSION=18 +ARG LLVM_VERSION=20 ENV KEYRINGS /usr/local/share/keyrings ADD https://apt.llvm.org/llvm-snapshot.gpg.key /root/llvm-snapshot.gpg.key