init (messy)
This commit is contained in:
commit
18bff3cc00
10 changed files with 926 additions and 0 deletions
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
* text=auto eol=lf
|
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal 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
46
build.zig
Normal 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
17
build.zig.zon
Normal 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
20
build.zig.zon2json-lock
Normal 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
78
flake.lock
generated
Normal 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
90
flake.nix
Normal 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
4
src/c.zig
Normal file
|
@ -0,0 +1,4 @@
|
|||
pub const raylib = @cImport({
|
||||
@cInclude("raylib.h");
|
||||
@cInclude("raymath.h");
|
||||
});
|
302
src/main.zig
Normal file
302
src/main.zig
Normal 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
352
src/rco.zig
Normal 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;
|
Loading…
Add table
Add a link
Reference in a new issue