diff --git a/src/directory.zig b/src/directory.zig index 11fc4cc..0f41d87 100644 --- a/src/directory.zig +++ b/src/directory.zig @@ -3,7 +3,7 @@ const io = std.io; const InodeType = @import("inode/inode.zig").InodeType; -const DirHeader = packed struct { +const DirHeader = extern struct { count: u32, inode_block_start: u32, inode_num: u32, @@ -48,12 +48,9 @@ pub fn readDirectory(alloc: std.mem.Allocator, rdr: io.AnyReader, size: u64) !st var out: std.StringHashMap(DirEntry) = .init(alloc); errdefer out.deinit(); var red_size: u64 = 3; - var hdr: DirHeader align(4) = undefined; - // rdr.readStruct reads 16 bytes, due to alignment. get around this by manually reading the memory and decoding - var hdr_tmp: [12]u8 = [_]u8{0} ** 12; + var hdr: DirHeader = undefined; while (red_size < size) { - _ = try rdr.readAll(&hdr_tmp); - hdr = std.mem.bytesToValue(DirHeader, &hdr_tmp); + hdr = try rdr.readStruct(DirHeader); red_size += 12; var i: u32 = 0; try out.ensureUnusedCapacity(hdr.count + 1); diff --git a/src/file.zig b/src/file.zig index efb2241..60e9d8e 100644 --- a/src/file.zig +++ b/src/file.zig @@ -26,12 +26,12 @@ pub const File = struct { pub fn deinit(self: *File, alloc: std.mem.Allocator) void { self.inode.deinit(); alloc.free(self.name); - if (self.hasEntries) { - var iter = self.dirEntries.iterator(); + if (self.dirEntries != null) { + var iter = self.dirEntries.?.iterator(); while (iter.next()) |ent| { ent.value_ptr.deinit(alloc); } - self.dirEntries.deinit(); + self.dirEntries.?.deinit(); } } @@ -52,7 +52,7 @@ pub const File = struct { try self.readDirEntries(reader); const split_idx = std.mem.indexOf(u8, clean_path, "/") orelse clean_path.len; const name = clean_path[0..split_idx]; - const ent = self.dirEntries.get(name); + const ent = self.dirEntries.?.get(name); if (ent == null) { return FileError.NotFound; } @@ -95,7 +95,6 @@ pub const File = struct { defer meta_rdr.deinit(); try meta_rdr.skip(offset); self.dirEntries = try directory.readDirectory(reader.alloc, meta_rdr.any(), size); - self.hasEntries = true; } pub fn read(self: *File, bytes: []u8) !usize { @@ -117,15 +116,17 @@ fn fileFromDirEntry(read: *Reader, ent: DirEntry) !File { try meta_rdr.skip(ent.offset); // Copy name so we can clean-up the DirEntrys without causing issues. const name = try read.alloc.alloc(u8, ent.name.len); - std.mem.copyForwards(u8, name, ent.name); + errdefer read.alloc.free(name); + @memcpy(name, ent.name); var out: File = .{ .name = name, - .inode = .init( + .inode = try .init( read.alloc, meta_rdr.any(), read.super.block_size, ), }; + errdefer out.deinit(read.alloc); out.data_rdr = switch (out.inode.data) { .file, .ext_file => try .init(&out, read), else => null, diff --git a/src/reader.zig b/src/reader.zig index de3f216..ddbbd4c 100644 --- a/src/reader.zig +++ b/src/reader.zig @@ -2,11 +2,13 @@ const std = @import("std"); const inode = @import("inode/inode.zig"); +const Table = @import("table.zig").Table; const FileHolder = @import("readers/file_holder.zig").FileHolder; const Superblock = @import("superblock.zig").Superblock; const File = @import("file.zig").File; const MetadataReader = @import("readers/metadata.zig").MetadataReader; const DirEntry = @import("directory.zig").DirEntry; +const FragEntry = @import("readers/data.zig").FragEntry; pub const Reader = struct { alloc: std.mem.Allocator, @@ -14,6 +16,10 @@ pub const Reader = struct { super: Superblock, root: File, + frag_table: Table(FragEntry), + export_table: Table(inode.InodeRef), + id_table: Table(u32), + pub fn init(alloc: std.mem.Allocator, filepath: []const u8, offset: u64) !Reader { var holder: FileHolder = try .init(filepath, offset); const super: Superblock = try holder.reader().readStruct(Superblock); @@ -23,7 +29,25 @@ pub const Reader = struct { .holder = holder, .super = super, .root = undefined, + .frag_table = undefined, + .export_table = undefined, + .id_table = undefined, }; + out.frag_table = .init( + &out, + super.frag_table_start, + super.frag_count, + ); + out.export_table = .init( + &out, + super.export_table_start, + super.inode_count, + ); + out.id_table = .init( + &out, + super.id_table_start, + super.id_count, + ); out.root = try out.fileFromRef(super.root_ref, ""); return out; } diff --git a/src/readers/data.zig b/src/readers/data.zig index 12cea94..787bd91 100644 --- a/src/readers/data.zig +++ b/src/readers/data.zig @@ -5,6 +5,9 @@ const File = @import("../file.zig").File; const Reader = @import("../reader.zig").Reader; const BlockSize = @import("../inode/file.zig").BlockSize; const DecompressionType = @import("../decompress.zig").DecompressType; +const FileOffsetReader = @import("../readers/file_holder.zig").FileOffsetReader; + +pub const FragEntry = packed struct { start: u64, size: BlockSize, _: u32 }; const DataReaderError = error{ EOF, @@ -13,25 +16,25 @@ const DataReaderError = error{ pub const DataReader = struct { alloc: std.mem.Allocator, decomp: DecompressionType, - rdr: io.AnyReader, + rdr: FileOffsetReader, block_size: u32, sizes: []BlockSize, - frag_rdr: ?io.AnyReader, + frag_rdr: ?DataReader = null, next_block_num: u32 = 0, cur_bloc: []u8 = undefined, cur_offset: u32 = 0, pub fn init(fil: *File, reader: *Reader) !DataReader { - const data_start: u64 = 0; - const sizes: []BlockSize = undefined; - const size: u64 = 0; - const frag_idx: u32 = 0; - const frag_offset: u32 = 0; + var data_start: u64 = 0; + var sizes: []BlockSize = undefined; + var size: u64 = 0; + var frag_idx: u32 = 0; + var frag_offset: u32 = 0; switch (fil.inode.data) { .file => |f| { sizes = try reader.alloc.alloc(BlockSize, f.blocks.len); - std.mem.copyForwards(BlockSize, sizes, f.blocks); + @memcpy(sizes, f.blocks); data_start = f.data_start; size = f.size; frag_idx = f.frag_idx; @@ -39,7 +42,7 @@ pub const DataReader = struct { }, .ext_file => |f| { sizes = try reader.alloc.alloc(BlockSize, f.blocks.len); - std.mem.copyForwards(BlockSize, sizes, f.blocks); + @memcpy(sizes, f.blocks); data_start = f.data_start; size = f.size; frag_idx = f.frag_idx; @@ -47,11 +50,37 @@ pub const DataReader = struct { }, else => return File.FileError.NotNormalFile, } - //TODO: set-up frag_rdr + var out: DataReader = .{ + .alloc = reader.alloc, + .decomp = reader.super.decomp, + .rdr = reader.holder.readerAt(data_start), + .block_size = reader.super.block_size, + .sizes = sizes, + }; + errdefer out.deinit(); + if (frag_idx != 0xFFFFFFFF) { + const frag_entry = try reader.frag_table.getValue(frag_idx); + out.frag_rdr = try .fromFragEntry(reader, frag_entry); + try out.frag_rdr.?.skip(frag_offset); + } + return out; + } + fn fromFragEntry(reader: *Reader, ent: FragEntry) !DataReader { + const size = try reader.alloc.alloc(BlockSize, 1); + size[0] = ent.size; + return .{ + .alloc = reader.alloc, + .decomp = reader.super.decomp, + .rdr = reader.holder.readerAt(ent.start), + .block_size = reader.super.block_size, + .sizes = size, + }; } pub fn deinit(self: *DataReader) void { + self.alloc.free(self.sizes); if (self.cur_bloc.len > 0) self.alloc.free(self.cur_bloc); + if (self.frag_rdr != null) self.frag_rdr.?.deinit(); } pub fn skip(self: *DataReader, offset: u32) !void { @@ -73,11 +102,31 @@ pub const DataReader = struct { const siz = self.sizes[self.next_block_num]; self.next_block_num += 1; if (self.next_block_num == self.sizes.len - 1 and self.frag_rdr != null) { + try self.sizeBlock(self.sizes % self.block_size); _ = try self.frag_rdr.?.readAll(self.cur_bloc); return; } - if (siz.size == 0) {} - if (siz.not_compressed) {} + if (siz.size == 0) { + try self.sizeBlock(self.block_size); + @memset(self.cur_bloc, 0); + return; + } + if (siz.not_compressed) { + try self.sizeBlock(siz.size); + _ = try self.rdr.any().readAll(self.cur_bloc); + } else { + self.alloc.free(self.cur_bloc); + var limit = std.io.limitedReader(self.reader, siz.size); + var dat = try self.decomp.decompress(self.alloc, limit.reader().any()); + self.block = try dat.toOwnedSlice(); + } + } + + fn sizeBlock(self: *DataReader, size: u32) !void { + if (!self.alloc.resize(u8, size)) { + self.alloc.free(self.cur_bloc); + self.cur_bloc = try self.alloc.alloc(u8, size); + } } pub fn read(self: *DataReader, bytes: []u8) !usize { diff --git a/src/readers/file_holder.zig b/src/readers/file_holder.zig index 2d812c6..befc2cb 100644 --- a/src/readers/file_holder.zig +++ b/src/readers/file_holder.zig @@ -29,7 +29,7 @@ pub const FileHolder = struct { } }; -const FileOffsetReader = struct { +pub const FileOffsetReader = struct { file: *File, offset: u64, diff --git a/src/table.zig b/src/table.zig new file mode 100644 index 0000000..b1458a7 --- /dev/null +++ b/src/table.zig @@ -0,0 +1,62 @@ +const std = @import("std"); + +const Reader = @import("reader.zig").Reader; +const DecompressType = @import("decompress.zig").DecompressType; +const FileHolder = @import("readers/file_holder.zig").FileHolder; +const FileOffsetReader = @import("readers/file_holder.zig").FileOffsetReader; +const MetadataReader = @import("readers/metadata.zig").MetadataReader; + +const TableError = error{InvalidIndex}; + +pub fn Table( + comptime T: type, +) type { + return struct { + alloc: std.mem.Allocator, + decomp: DecompressType, + holder: *FileHolder, + table: []T = &[0]T{}, + offset: u64, + item_count: u32, + + pub fn init(read: *Reader, offset: u64, item_count: u32) Self { + return .{ + .alloc = read.alloc, + .decomp = read.super.decomp, + .holder = &read.holder, + .offset = offset, + .item_count = item_count, + }; + } + pub fn deinit(self: *Self, alloc: std.mem.Allocator) void { + alloc.free(self.table); + } + + pub fn getValue(self: *Self, i: u64) !T { + if (i >= self.item_count) return TableError.InvalidIndex; + if (self.table.len - 1 > i) return self.table[i]; + var meta_rdr: MetadataReader = undefined; + var offset_rdr: FileOffsetReader = undefined; + var meta_offset: u64 = 0; + var to_read: u32 = 0; + while (self.table.len < i) { + _ = try self.holder.file.preadAll(std.mem.sliceAsBytes(&meta_offset), self.offset); + self.offset += 8; + offset_rdr = self.holder.readerAt(meta_offset); + meta_rdr = .init(self.alloc, self.decomp, offset_rdr.any()); + defer meta_rdr.deinit(); + to_read = @min(self.item_count - self.table.len, comptime blk: { + break :blk 8192 / @sizeOf(T); + }); + if (!self.alloc.resize(self.table, self.table.len + to_read)) { + const alloc_size = self.table.len + to_read; + self.alloc.free(self.table); + self.table = try self.alloc.alloc(T, alloc_size); + } + _ = try meta_rdr.any().readAll(std.mem.asBytes(self.table[self.table.len - to_read ..])); + } + return self.table[i]; + } + const Self: type = @This(); + }; +}