352 lines
10 KiB
Zig
352 lines
10 KiB
Zig
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;
|