init (messy)

This commit is contained in:
Jeeves 2025-05-27 22:59:15 -06:00
commit 18bff3cc00
10 changed files with 926 additions and 0 deletions

1
.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
* text=auto eol=lf

16
.gitignore vendored Normal file
View file

@ -0,0 +1,16 @@
# Zig-specific build artifacts
.zig-cache/
zig-cache/
zig-out/
/release/
/debug/
/build/
/build-*/
/docgen_tmp/
# Temporary project-specific dirs
vsh/
dumped/
menu/
font/
reference/

46
build.zig Normal file
View file

@ -0,0 +1,46 @@
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const raylib = b.dependency("raylib", .{
.target = target,
.optimize = optimize,
.shared = false,
});
const exe_mod = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
exe_mod.strip = false;
exe_mod.linkLibrary(raylib.artifact("raylib"));
const exe = b.addExecutable(.{
.name = "rex",
.root_module = exe_mod,
});
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
const exe_unit_tests = b.addTest(.{
.root_module = exe_mod,
});
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_exe_unit_tests.step);
}

17
build.zig.zon Normal file
View file

@ -0,0 +1,17 @@
.{
.name = .rex,
.version = "0.0.0",
.fingerprint = 0xee6003c042470dd0,
.minimum_zig_version = "0.14.0",
.dependencies = .{
.raylib = .{
.url = "git+https://github.com/raysan5/raylib#8d9c1cecb7f53aef720e2ee0d1558ffc39fa7eef",
.hash = "raylib-5.5.0-whq8uKXONARKPKfYVo14WktbUTIM3OYxdVPOPTdsQejc",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

20
build.zig.zon2json-lock Normal file
View file

@ -0,0 +1,20 @@
{
"raylib-5.5.0-whq8uKXONARKPKfYVo14WktbUTIM3OYxdVPOPTdsQejc": {
"name": "raylib",
"url": "git+https://github.com/raysan5/raylib#8d9c1cecb7f53aef720e2ee0d1558ffc39fa7eef",
"hash": "sha256-NCUpCi5Kjs/mC97pAeRpPO4/1e5vaMZ2xEWrAXZV2Vs=",
"rev": "8d9c1cecb7f53aef720e2ee0d1558ffc39fa7eef"
},
"N-V-__8AABHMqAWYuRdIlflwi8gksPnlUMQBiSxAqQAAZFms": {
"name": "xcode_frameworks",
"url": "git+https://github.com/hexops/xcode-frameworks#9a45f3ac977fd25dff77e58c6de1870b6808c4a7",
"hash": "sha256-xveFYoQu0BT+ZtEsyca/zdQ/so9jPK56SeX3xmF3iro=",
"rev": "9a45f3ac977fd25dff77e58c6de1870b6808c4a7"
},
"N-V-__8AALRTBQDo_pUJ8IQ-XiIyYwDKQVwnr7-7o5kvPDGE": {
"name": "emsdk",
"url": "git+https://github.com/emscripten-core/emsdk#3.1.50",
"hash": "sha256-YUwb8yfz26Tfz4wyN13MBsdyA40ygJwfxHWt4eFMvQE=",
"rev": "e2627e265d940db5ea58dffa63e490375bfc92e5"
}
}

78
flake.lock generated Normal file
View file

@ -0,0 +1,78 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1747273150,
"narHash": "sha256-8Vp3dEOmH4Idj0wnj66uJZbi4xiIgKxx+d6gD9KIBzc=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "7f8abfe980eb2ec5f3257c34aa4b4f0c3ca7002e",
"type": "github"
},
"original": {
"owner": "nixos",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"zig2nix": "zig2nix"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"zig2nix": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1747273718,
"narHash": "sha256-cB0Abljln7nvmou3fecVKtIQZc0B8DpYBrw6CKbovgQ=",
"owner": "Cloudef",
"repo": "zig2nix",
"rev": "ea9a1a0f86a7addf8dedb03a38215b14c6f429b3",
"type": "github"
},
"original": {
"owner": "Cloudef",
"repo": "zig2nix",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

90
flake.nix Normal file
View file

@ -0,0 +1,90 @@
{
description = "Zig project flake";
inputs = {
zig2nix.url = "github:Cloudef/zig2nix";
};
outputs = { zig2nix, ... }: let
flake-utils = zig2nix.inputs.flake-utils;
in (flake-utils.lib.eachDefaultSystem (system: let
# Zig flake helper
# Check the flake.nix in zig2nix project for more options:
# <https://github.com/Cloudef/zig2nix/blob/master/flake.nix>
env = zig2nix.outputs.zig-env.${system} {};
in with builtins; with env.pkgs.lib; rec {
# Produces clean binaries meant to be ship'd outside of nix
# nix build .#foreign
packages.foreign = env.package {
src = cleanSource ./.;
# Packages required for compiling
nativeBuildInputs = with env.pkgs; [];
# Packages required for linking
buildInputs = with env.pkgs; [
raylib
wayland
wayland-scanner
libxkbcommon
xorg.libXcursor
xorg.libXext
xorg.libXfixes
xorg.libXi
xorg.libXinerama
xorg.libXrandr
xorg.libXrender
];
# Smaller binaries and avoids shipping glibc.
zigPreferMusl = true;
};
# nix build .
packages.default = packages.foreign.override (attrs: {
# Prefer nix friendly settings.
zigPreferMusl = false;
# Executables required for runtime
# These packages will be added to the PATH
zigWrapperBins = with env.pkgs; [];
# Libraries required for runtime
# These packages will be added to the LD_LIBRARY_PATH
zigWrapperLibs = attrs.buildInputs or [];
});
# For bundling with nix bundle for running outside of nix
# example: https://github.com/ralismark/nix-appimage
apps.bundle = {
type = "app";
program = "${packages.foreign}/bin/default";
};
# nix run .
apps.default = env.app [] "zig build run -- \"$@\"";
# nix run .#build
apps.build = env.app [] "zig build \"$@\"";
# nix run .#test
apps.test = env.app [] "zig build test -- \"$@\"";
# nix run .#docs
apps.docs = env.app [] "zig build docs -- \"$@\"";
# nix run .#zig2nix
apps.zig2nix = env.app [] "zig2nix \"$@\"";
# nix develop
devShells.default = env.mkShell {
# Packages required for compiling, linking and running
# Libraries added here will be automatically added to the LD_LIBRARY_PATH and PKG_CONFIG_PATH
nativeBuildInputs = []
++ packages.default.nativeBuildInputs
++ packages.default.buildInputs
++ packages.default.zigWrapperBins
++ packages.default.zigWrapperLibs;
};
}));
}

4
src/c.zig Normal file
View file

@ -0,0 +1,4 @@
pub const raylib = @cImport({
@cInclude("raylib.h");
@cInclude("raymath.h");
});

302
src/main.zig Normal file
View file

@ -0,0 +1,302 @@
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
raylib.SetConfigFlags(raylib.FLAG_VSYNC_HINT | raylib.FLAG_WINDOW_RESIZABLE);
raylib.InitWindow(@intFromFloat(screen_width), @intFromFloat(screen_height), "ReX");
defer raylib.CloseWindow();
raylib.SetWindowMinSize(480, 272);
raylib.SetWindowMaxSize(1920, 1080);
scales.recalculate();
global_font = raylib.LoadFontEx("font/SCE-PS3-RD-R-LATIN.TTF", 32, 0, 250);
raylib.SetTextureFilter(global_font.texture, raylib.TEXTURE_FILTER_TRILINEAR);
// const camera = createCamera();
var background = Background.init();
var column = Column.init(
allocator,
raylib.LoadTextureFromImage(raylib.GenImageChecked(64, 64, 8, 8, raylib.BLACK, raylib.WHITE)),
"Game",
);
defer column.deinit();
var item = Item.init(
raylib.LoadTexture("menu/game/CometCrash/ICON0.PNG"),
"Comet Crash",
"",
);
try column.appendItem(&item);
raylib.SetTargetFPS(120);
while (!raylib.WindowShouldClose()) {
if (raylib.IsWindowResized()) {
screen_width = @floatFromInt(raylib.GetScreenWidth());
screen_height = @floatFromInt(raylib.GetScreenHeight());
scales.recalculate();
}
if (raylib.IsKeyPressed('Z')) item.setBig(!item.big);
raylib.BeginDrawing();
defer raylib.EndDrawing();
background.draw();
column.draw();
// {
// raylib.BeginMode3D(camera);
// defer raylib.EndMode3D();
// }
raylib.DrawFPS(1, 1);
const debug_text = try std.fmt.allocPrint(allocator, "screen size = {d}x{d}", .{ screen_width, screen_height });
defer allocator.free(debug_text);
raylib.DrawText(@ptrCast(debug_text), 80, 2, 8, raylib.GREEN);
}
}
var global_font: raylib.Font = undefined;
var screen_width: f32 = 480;
var screen_height: f32 = 272;
var scales: Scales = undefined;
pub const Scales = struct {
item_icon_scale: f32,
item_title_font_size: f32,
item_subtitle_font_size: f32,
column_icon_scale: f32,
column_title_font_size: f32,
column_position_center: raylib.Vector2,
column_position_spacing: f32,
pub fn recalculate(self: *Scales) void {
self.item_icon_scale = screen_height * 0.72 / screen_height;
self.column_icon_scale = screen_height * 0.75 / screen_height;
self.column_title_font_size = screen_height * 13 / screen_height;
self.column_position_center = .{
.x = std.math.lerp(0.0, screen_width, 0.18),
.y = std.math.lerp(0.0, screen_height, 0.15),
};
self.column_position_spacing = 64;
}
};
pub const Column = struct {
icon: raylib.Texture2D,
title: []const u8,
items: std.ArrayList(*Item),
pub fn init(allocator: Allocator, icon: raylib.Texture2D, title: []const u8) Column {
raylib.SetTextureFilter(icon, raylib.TEXTURE_FILTER_BILINEAR);
return .{
.icon = icon,
.title = title,
.items = .init(allocator),
};
}
pub fn deinit(self: *Column) void {
self.items.deinit();
}
pub fn draw(self: *Column) void {
const icon_position = scales.column_position_center;
const icon_scale = scales.column_icon_scale;
const icon_width = @as(f32, @floatFromInt(self.icon.width)) * icon_scale;
const icon_height = @as(f32, @floatFromInt(self.icon.height)) * icon_scale;
const title_font_size = scales.column_title_font_size;
const title_font_spacing = 1.0;
const title_size = raylib.MeasureTextEx(global_font, @ptrCast(self.title), title_font_size, title_font_spacing);
const title_position = raylib.Vector2{
.x = icon_position.x + icon_width / 2.0 - title_size.x / 2.0,
.y = icon_position.y + icon_height + 6,
};
var y: f32 = scales.column_position_center.y + icon_height + title_size.y + 32;
for (self.items.items) |item| {
item.position = .{ .x = scales.column_position_center.x, .y = y };
// item.draw();
y += 64;
}
raylib.DrawTextureEx(self.icon, icon_position, 0, icon_scale, raylib.WHITE);
raylib.DrawTextEx(global_font, @ptrCast(self.title), title_position, title_font_size, title_font_spacing, raylib.WHITE);
}
pub fn appendItem(self: *Column, item: *Item) !void {
try self.items.append(item);
}
pub fn insertItem(self: *Column, idx: usize, item: *Item) !void {
try self.items.insert(idx, item);
}
pub fn removeItem(self: *Column, idx: usize) void {
_ = try self.items.orderedRemove(idx);
}
};
pub const Item = struct {
time: f32 = 0.0,
start_scale: f32,
position: raylib.Vector2 = .{ .x = 0, .y = 0 },
scale: f32,
icon: raylib.Texture2D,
title: []const u8,
subtitle: []const u8,
big: bool = true,
pub fn init(texture: raylib.Texture2D, title: []const u8, subtitle: []const u8) Item {
raylib.SetTextureFilter(texture, raylib.TEXTURE_FILTER_BILINEAR);
return .{
.icon = texture,
.title = title,
.subtitle = subtitle,
.scale = scales.item_icon_scale,
.start_scale = scales.item_icon_scale,
};
}
pub fn draw(self: *Item) void {
self.time += raylib.GetFrameTime();
self.scale = std.math.lerp(
self.start_scale,
if (self.big) scales.item_icon_scale else scales.item_icon_scale * 0.5,
easeOutExpo(self.time / 0.333),
);
const title_pos = raylib.Vector2{
.x = self.position.x + 16 + @as(f32, @floatFromInt(self.icon.width)) * self.scale,
.y = (self.position.y + @as(f32, @floatFromInt(self.icon.height)) * self.scale) / 2.0,
};
raylib.DrawTextureEx(self.icon, self.position, 0, self.scale, raylib.WHITE);
raylib.DrawTextEx(global_font, @ptrCast(self.title), title_pos, 18.0, 1, raylib.WHITE);
}
pub fn setBig(self: *Item, big: bool) void {
self.big = big;
self.time = 0;
self.start_scale = self.scale;
}
fn easeOutExpo(x: f32) f32 {
return 1.0 - std.math.pow(f32, 2, -10 * std.math.clamp(x, 0.0, 1.0));
}
};
/// Draws the dynamic gradient background.
// TODO shift based on time of day
// TODO image wallpaper
// TODO slideshow wallpaper
// TODO animated image wallpaper
pub const Background = struct {
top_left: Color,
top_right: Color,
bottom_right: Color,
bottom_left: Color,
pub fn init() Background {
var self: Background = undefined;
self.setColors(NIGHT_08);
return self;
}
pub fn setColors(self: *Background, colors: [4]Color) void {
self.top_left = colors[0];
self.bottom_right = colors[1];
self.top_right = colors[2];
self.bottom_left = colors[3];
}
pub fn draw(self: *Background) void {
raylib.DrawRectangleGradientEx(
.{
.x = 0,
.y = 0,
.width = screen_width,
.height = screen_height,
},
self.top_left.toRaylib(),
self.bottom_left.toRaylib(),
self.top_right.toRaylib(),
self.bottom_right.toRaylib(),
);
}
pub const Color = struct {
r: f32,
g: f32,
b: f32,
fn toRaylib(self: Color) raylib.Color {
return .{
.r = @intFromFloat(self.r * 255.0),
.g = @intFromFloat(self.g * 255.0),
.b = @intFromFloat(self.b * 255.0),
.a = 255,
};
}
};
pub const DAY_06 = [4]Color{
.{ .r = 0.408, .g = 0.333, .b = 0.643 },
.{ .r = 0.518, .g = 0.365, .b = 0.855 },
.{ .r = 0.761, .g = 0.510, .b = 0.851 },
.{ .r = 0.569, .g = 0.325, .b = 0.620 },
};
pub const DAY_08 = [4]Color{
.{ .r = 0.243, .g = 0.608, .b = 0.831 },
.{ .r = 0.039, .g = 0.690, .b = 0.878 },
.{ .r = 0.016, .g = 0.306, .b = 0.694 },
.{ .r = 0.000, .g = 0.027, .b = 0.310 },
};
pub const NIGHT_08 = [4]Color{
.{ .r = 0.000, .g = 0.145, .b = 0.349 },
.{ .r = 0.000, .g = 0.008, .b = 0.106 },
.{ .r = 0.251, .g = 0.494, .b = 0.576 },
.{ .r = 0.008, .g = 0.537, .b = 0.612 },
};
};
fn createCamera() raylib.Camera3D {
var camera = raylib.Camera3D{};
camera.position.x = 0;
camera.position.y = 10;
camera.position.z = 0;
// camera pointing down, oriented correctly
// TODO this works but looks weird. do better.
camera.target.x = 0;
camera.target.y = 0;
camera.target.z = -0.000000000000001;
camera.up.x = 0;
camera.up.y = 1;
camera.up.z = 0;
camera.fovy = 1;
camera.projection = raylib.CAMERA_ORTHOGRAPHIC;
return camera;
}
const std = @import("std");
const Allocator = std.mem.Allocator;
const c = @import("c.zig");
const raylib = c.raylib;

352
src/rco.zig Normal file
View file

@ -0,0 +1,352 @@
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var rco_file = try std.fs.cwd().openFile("vsh/resource/explore_plugin_game.rco", .{});
defer rco_file.close();
var rco = try RcoFile.init(allocator, rco_file);
defer rco.deinit();
try rco_file.seekTo(rco.toc_main_tree_offset.?);
const packed_size = try rco_file.reader().readInt(u32, .big);
const unpacked_size = try rco_file.reader().readInt(u32, .big);
const longest_text_size = try rco_file.reader().readInt(u32, .big);
std.debug.print("{d} {d} {d}\n", .{ packed_size, unpacked_size, longest_text_size });
const packed_buf = try allocator.alloc(u8, packed_size);
defer allocator.free(packed_buf);
_ = try rco_file.readAll(packed_buf);
var fb = std.io.fixedBufferStream(packed_buf);
var al = std.ArrayList(u8).init(allocator);
defer al.deinit();
try std.compress.zlib.decompress(fb.reader(), al.writer());
if (al.items.len != unpacked_size) return error.DecompressedSizeDoesNotMatch;
var nfb = std.io.fixedBufferStream(al.items);
while (nfb.pos < try nfb.getEndPos()) {
try parseTocEntry(nfb.reader());
}
std.debug.print("{any}\n", .{rco});
}
fn parseTocEntry(reader: anytype) !void {
const entry_type: TocEntryType = @enumFromInt(try reader.readInt(u16, .big));
try reader.skipBytes(2, .{});
const entry_label_offset = try reader.readInt(u32, .big);
const attributes_offset = try reader.readInt(u32, .big);
const children_offset = try reader.readInt(u32, .big);
const children_number = try reader.readInt(u32, .big);
const next_sibling_offset = try reader.readInt(u32, .big);
const prev_sibling_offset = try reader.readInt(u32, .big);
const parent_offset = try reader.readInt(u32, .big);
try reader.skipBytes(8, .{});
_ = .{ entry_label_offset, attributes_offset, children_offset, children_number, next_sibling_offset, prev_sibling_offset, parent_offset };
std.log.debug("Found TOC Entry: {any}", .{entry_type});
switch (entry_type) {
.MainTree,
.ScriptTree,
.TextTree,
.ImageTree,
.ModelTree,
.SoundTree,
.FontTree,
.ObjectTree,
.AnimTree,
=> {},
.Text => {
const text = try RcoFile.Text.init(reader);
std.debug.print("Text: {any}\n", .{text});
},
.Image => {
const image = try RcoFile.ImageTree.Image.init(reader);
std.debug.print("Image: {any}\n", .{image});
},
else => {},
}
}
pub const TocEntryType = enum(u16) {
MainTree = 0x0101,
ScriptTree = 0x0200,
TextTree = 0x0300,
Text = 0x0301,
ImageTree = 0x0400,
Image = 0x0401,
ModelTree = 0x0500,
Model = 0x0501,
SoundTree = 0x0600,
Sound = 0x0601,
FontTree = 0x0700,
Font = 0x0701,
ObjectTree = 0x0800,
Page = 0x0801,
Plane = 0x0802,
Button = 0x0803,
XMenu,
XMList,
Progress,
Scroll,
MList,
MItem,
_object_unk1 = 0x080B,
XItem,
TextObject,
ModelObject,
Spin,
Action,
ItemSpin,
Group,
LList,
LItem,
Edit,
Clock,
IList,
IItem,
Icon,
UButton,
_object_unk2 = 0x081B,
CheckboxGroup,
CheckboxItem,
Meter,
EditBox = 0x081F,
AnimTree = 0x0900,
Anim = 0x0901,
MoveTo = 0x0902,
Recolour = 0x0903,
Rotate = 0x0904,
Resize = 0x0905,
Fade = 0x0906,
Delay = 0x0907,
FireEvent = 0x0908,
Lock = 0x0909,
Unlock = 0x090A,
SlideOut = 0x090B,
_,
};
pub const RcoFile = struct {
allocator: Allocator,
file: std.fs.File,
compression: Compression,
toc_main_tree_offset: ?u32,
toc_script_tree_offset: ?u32,
toc_text_tree_offset: ?u32,
toc_sound_tree_offset: ?u32,
toc_model_tree_offset: ?u32,
toc_image_tree_offset: ?u32,
toc_font_tree_offset: ?u32,
toc_object_tree_offset: ?u32,
toc_anim_tree_offset: ?u32,
pub fn parse(self: *RcoFile) !void {
const rco_header = try self.file.reader().readStructEndian(RcoHeader, .big);
self.compression = @enumFromInt(rco_header.prf_compress);
self.toc_main_tree_offset = parseOffset(rco_header.toc_main_tree_offset);
self.toc_script_tree_offset = parseOffset(rco_header.toc_script_tree_offset);
self.toc_text_tree_offset = parseOffset(rco_header.toc_text_tree_offset);
self.toc_sound_tree_offset = parseOffset(rco_header.toc_sound_tree_offset);
self.toc_model_tree_offset = parseOffset(rco_header.toc_model_tree_offset);
self.toc_image_tree_offset = parseOffset(rco_header.toc_image_tree_offset);
self.toc_font_tree_offset = parseOffset(rco_header.toc_font_tree_offset);
self.toc_object_tree_offset = parseOffset(rco_header.toc_object_tree_offset);
self.toc_anim_tree_offset = parseOffset(rco_header.toc_anim_tree_offset);
}
pub fn init(allocator: Allocator, file: std.fs.File) !RcoFile {
var self: RcoFile = undefined;
self.allocator = allocator;
self.file = file;
try self.parse();
return self;
}
pub fn deinit(_: *RcoFile) void {}
fn parseOffset(offset: u32) ?u32 {
return if (offset == std.math.maxInt(u32)) null else offset;
}
pub fn format(
self: RcoFile,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
try writer.writeAll("RcoFile{\n");
try writer.print(" .compression = {any},\n", .{self.compression});
try self.fmtField("toc_main_tree_offset", writer);
try self.fmtField("toc_script_tree_offset", writer);
try self.fmtField("toc_text_tree_offset", writer);
try self.fmtField("toc_sound_tree_offset", writer);
try self.fmtField("toc_model_tree_offset", writer);
try self.fmtField("toc_image_tree_offset", writer);
try self.fmtField("toc_font_tree_offset", writer);
try self.fmtField("toc_object_tree_offset", writer);
try self.fmtField("toc_anim_tree_offset", writer);
try writer.writeAll("}");
}
inline fn fmtField(self: RcoFile, comptime field_name: []const u8, writer: anytype) !void {
const field = @field(self, field_name);
const format_string = std.fmt.comptimePrint(" .{s} = {{s}}{{?X}},\n", .{field_name});
try writer.print(format_string, .{
if (field != null) "0x" else "",
field,
});
}
pub const Compression = enum(u32) {
none = 0,
zlib = 0x10,
rlz = 0x20,
};
pub const Text = struct {
language: Language,
encoding: Encoding,
number_of: u32,
pub const Language = enum(u16) {
Japanese = 0,
English_US = 1,
French = 2,
Spanish_Spain = 3,
German = 4,
Italian = 5,
Dutch = 6,
Portuguese_Portugal = 7,
Russian = 8,
Korean = 9,
Chinese_Traditional = 10,
Chinese_Simplified = 11,
Finnish = 12,
Swedish = 13,
Danish = 14,
Norwegian = 15,
Polish = 16,
Portuguese_Brazil = 17,
English_UK = 18,
Turkish = 19,
};
pub const Encoding = enum(u16) {
utf8 = 0,
utf16 = 1,
utf32 = 2,
};
pub const String = extern struct {
label_offset: u32,
string_length: u32,
string_offset: u32,
};
};
pub const ImageTree = struct {
pub const Image = struct {
format: Format,
compression: Image.Compression,
size: u32,
offset: u32,
uncompressed_size: ?u32,
pub fn init(reader: anytype) !Image {
var self: Image = undefined;
self.format = @enumFromInt(try reader.readInt(u16, .big));
self.compression = @enumFromInt(try reader.readInt(u16, .big));
self.size = try reader.readInt(u32, .big);
self.offset = try reader.readInt(u32, .big);
try reader.skipBytes(4, .{});
self.uncompressed_size = if (self.compression == .none) null else try reader.readInt(u32, .big);
return self;
}
pub const Format = enum(u16) {
png = 0,
jpeg = 1,
tiff = 2,
gif = 3,
bmp = 4,
gim = 5,
};
pub const Compression = enum(u16) {
none = 0,
zlib = 1,
rlz = 2,
};
};
};
};
pub const RcoHeader = extern struct {
prf_signature: [4]u8,
prf_version: [4]u8,
prf_unk: u32 = 0,
prf_compress: u32,
toc_main_tree_offset: u32,
toc_script_tree_offset: u32,
toc_text_tree_offset: u32,
toc_sound_tree_offset: u32,
toc_model_tree_offset: u32,
toc_image_tree_offset: u32,
toc_unk: u32 = std.math.maxInt(u32),
toc_font_tree_offset: u32,
toc_object_tree_offset: u32,
toc_anim_tree_offset: u32,
str_text_table_offset: u32,
str_text_table_length: u32,
str_text_label_offset: u32,
str_text_label_length: u32,
str_text_event_offset: u32,
str_text_event_length: u32,
ptr_text_table_offset: u32,
ptr_text_table_length: u32,
ptr_image_table_offset: u32,
ptr_image_table_length: u32,
ptr_model_table_offset: u32,
ptr_model_table_length: u32,
ptr_sound_table_offset: u32,
ptr_sound_table_length: u32,
ptr_object_table_offset: u32,
ptr_object_table_length: u32,
ptr_anim_table_offset: u32,
ptr_anim_table_length: u32,
dat_image_table_offset: u32,
dat_image_table_length: u32,
dat_sound_table_offset: u32,
dat_sound_table_length: u32,
dat_model_table_offset: u32,
dat_model_table_length: u32,
dat_unk: [3]u32 = [_]u32{std.math.maxInt(u32)} ** 3,
};
const std = @import("std");
const Allocator = std.mem.Allocator;