const std = @import("std"); const mem = std.mem; const stdout = std.io.getStdOut().writer(); const stderr = std.io.getStdErr().writer(); const utils = @import("utils.zig"); const print = utils.print; const exit = utils.exit; const pErr = utils.pErr; const walkers = @import("walker.zig"); const BitWalker = walkers.BitWalker; const BitWalkerUint = walkers.BitWalkerUint; fn usage() void { pErr("reader ", .{}); exit("", .{}, 1); } fn HuffmanGraph(comptime valueType: type) type { return struct { const Node = struct { const NodeSelf = @This(); left: ?*NodeSelf, right: ?*NodeSelf, value: ?valueType, allocator: mem.Allocator, fn init(allocator: mem.Allocator) !*NodeSelf { var node = try allocator.create(NodeSelf); node.left = null; node.right = null; node.value = null; node.allocator = allocator; return node; } fn deinit(self: *NodeSelf) void { if (self.left) |left| { left.deinit(); } if (self.right) |right| { right.deinit(); } self.allocator.destroy(self); } fn print(self: *NodeSelf, curDepth: usize, targetDepth: usize) void { if (curDepth != targetDepth) { if (self.left) |l| { l.print(curDepth + 1, targetDepth); } else { utils.printf(" , ", .{}); } if (self.right) |r| { r.print(curDepth + 1, targetDepth); } else { utils.printf(" . ", .{}); } return; } if (self.value) |v| { utils.printf(" {any} ", .{v}); } else { utils.printf(" _ ", .{}); } } fn depth(self: *NodeSelf) usize { var d: usize = 0; if (self.right) |r| { d = r.depth(); } if (self.left) |l| { d = std.math.max(d, l.depth()); } return d + 1; } }; root: *Node, allocator: mem.Allocator, stored_depth: ?usize = null, iterNode: *Node, const Self = @This(); fn init(allocator: mem.Allocator) !Self { var root = try Node.init(allocator); return Self{ .root = root, .iterNode = root, .allocator = allocator, }; } fn print(self: *Self) void { for (0..(self.depth() + 1)) |i| { self.root.print(0, i); utils.printf("\n", .{}); } } fn addValue(self: *Self, code: u64, size: u8, value: valueType) !void { var walker = try BitWalkerUint(u64, true).init(code, size); var curNode: *Node = self.root; while (walker.walkBit()) |bit| { if (bit == 1) { if (curNode.left) |nextNode| { curNode = nextNode; } else { curNode.left = try Node.init(self.allocator); curNode = curNode.left.?; } } else { if (curNode.right) |nextNode| { curNode = nextNode; } else { curNode.right = try Node.init(self.allocator); curNode = curNode.right.?; } } } curNode.value = value; self.stored_depth = null; } fn depth(self: *Self) usize { if (self.stored_depth) |d| { return d; } self.stored_depth = self.root.depth() - 1; return self.stored_depth.?; } fn iter(self: *Self, bit: u1) anyerror!?valueType { var node = self.iterNode; var nextNode: ?*Node = null; if (bit == 1) { nextNode = node.left; } else { nextNode = node.right; } if (nextNode) |new_node| { if (new_node.value) |value| { self.iterNode = self.root; return value; } else { self.iterNode = new_node; return null; } } return error.InvalidBitSequence; } fn deinit(self: *Self) void { self.root.deinit(); } }; } const DynamicDecoder = struct { const Self = @This(); len_to_read: usize, codes: [19]u64, walker: *BitWalker, allocator: mem.Allocator, graph: HuffmanGraph(u64), fn init(walker: *BitWalker, len_to_read: usize, allocator: mem.Allocator) !Self { const list: [19]u8 = .{ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; var lenList: [19]u3 = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; var bl_count: [7]u3 = .{ 0, 0, 0, 0, 0, 0, 0 }; var max: u8 = 0; for (0..len_to_read) |i| { var data: u3 = @intCast(u3, try walker.walk(3)); lenList[i] = data; if (data == 0) { continue; } bl_count[data] += 1; if (data > max) { max = data; } } var next_code: [19]u64 = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; var code: u64 = 0; for (1..(max + 1)) |bits| { code = @shlExact(code + bl_count[bits - 1], 1); next_code[bits] = code; } var codes: [19]u64 = .{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; for (0..19) |n| { var len = lenList[n]; if (len != 0) { codes[n] = next_code[len]; next_code[len] += 1; } } var graph = try HuffmanGraph(u64).init(allocator); for (0..19) |i| { if (lenList[i] == 0) continue; try graph.addValue(codes[i], lenList[i], list[i]); } return .{ .len_to_read = len_to_read, .codes = codes, .walker = walker, .allocator = allocator, .graph = graph, }; } fn deinit(self: *Self) void { self.graph.deinit(); } }; const BlockData = struct { const Self = @This(); bitw: *BitWalker, allocator: mem.Allocator, last: bool, blockType: u8, fn init(allocator: mem.Allocator, bitw: *BitWalker) !Self { return Self{ .bitw = bitw, .allocator = allocator, .last = try bitw.walk(1) == 1, .blockType = try bitw.walk(2), }; } fn uncompress(self: *Self) !void { if (self.blockType != 2) { return error.unsuported_block_type; } try self.dynamic_huffman; } fn dynamic_huffman() !void { var bitw = self.bitw; var number_of_literal_codes: u32 = @as(u32, try bitw.walk(5)) + 257; var number_of_dist_codes = try bitw.walk(5) + 1; var number_of_length_codes = try bitw.walk(4) + 4; print("number of literal codes: {}", .{number_of_literal_codes}); print("number of dist codes: {}", .{number_of_dist_codes}); print("number_of_length_codes: {}", .{number_of_length_codes}); var dynamic_decoder = try DynamicDecoder.init(bitw, number_of_length_codes, self.allocator); defer dynamic_decoder.deinit(); dynamic_decoder.graph.print(); var code_len: usize = 0; while (code_len < number_of_literal_codes) { var decode_value = try dynamic_decoder.graph.iter(try bitw.bitWalk()); while (decode_value == null) { decode_value = try dynamic_decoder.graph.iter(try bitw.bitWalk()); } print("Test {any}", .{decode_value}); if (decode_value.? == 16) { return error.not_implemented; } else if (decode_value.? == 17) { code_len += try bitw.walk(3); } else if (decode_value.? == 18) { code_len += try bitw.walk(7); } else { code_len += 1; } } return error.todo; } fn read_len_code() void {} }; const LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50; const ZipFileHeader = struct { version: u16, general: u16, compression_method: u16, last_mod_time: u16, last_mod_date: u16, crc_32: u32, compressed_size: u32, uncompressed_size: u32, file_name_length: u16, extra_field_length: u16, file_name: []u8, extra_field: []u8, compressed_content: []u8, uncompressed_content: []u8, decompressed: bool, allocator: std.mem.Allocator, const Self = @This(); fn init(allocator: std.mem.Allocator, reader: std.fs.File.Reader) !Self { if (try reader.readInt(u32, .Big) == LOCAL_FILE_HEADER_SIGNATURE) { return error.InvalidError; } var self = Self{ .allocator = allocator, .version = try reader.readInt(u16, .Little), .general = try reader.readInt(u16, .Little), .compression_method = try reader.readInt(u16, .Little), .last_mod_time = try reader.readInt(u16, .Little), .last_mod_date = try reader.readInt(u16, .Little), .crc_32 = try reader.readInt(u32, .Little), .compressed_size = try reader.readInt(u32, .Little), .uncompressed_size = try reader.readInt(u32, .Little), .file_name_length = try reader.readInt(u16, .Little), .extra_field_length = try reader.readInt(u16, .Little), .file_name = undefined, .extra_field = undefined, .compressed_content = undefined, .uncompressed_content = undefined, .decompressed = false, }; self.file_name = try allocator.alloc(u8, self.file_name_length); self.extra_field = try allocator.alloc(u8, self.extra_field_length); self.compressed_content = try allocator.alloc(u8, self.compressed_size); _ = try reader.read(self.file_name); _ = try reader.read(self.extra_field); _ = try reader.read(self.compressed_content); return self; } fn extract(self: *Self) !void { if (self.decompressed) { return error.AlreadyDecompressed; } if (self.compression_method == 0) { return error.uncompressed_file; } if (self.compression_method != 8) { return error.unsuported_compression_method; } self.uncompressed_content = try self.allocator.alloc(u8, self.uncompressed_size); errdefer self.allocator.free(self.uncompressed_content); var bitw = BitWalker.init(&self.compressed_content, false); var block = try BlockData.init(self.allocator, &bitw); if (block.last) { print("last block", .{}); } else { print("not last block", .{}); } try block.uncompress(); self.decompressed = true; } fn deinit(self: *Self) void { self.allocator.free(self.file_name); self.allocator.free(self.extra_field); self.allocator.free(self.compressed_content); if (self.decompressed) { self.allocator.free(self.uncompressed_content); } } }; pub fn main() !void { var args = std.process.args(); var filePath: ?[]const u8 = null; // Skip the current path _ = args.next(); while (args.next()) |arg| { if (filePath == null) { filePath = arg; } else { pErr("Invalid argument: {s}", .{arg}); usage(); } } if (filePath == null) { pErr("File path not provided. Please provide a path", .{}); usage(); } var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); var allocator = gpa.allocator(); var book_path = try std.fs.realpathAlloc(allocator, filePath.?); defer allocator.free(book_path); var file = std.fs.openFileAbsolute(book_path, .{}) catch |err| { exit("Please provide a file path! Error: {?}", .{err}, 1); return err; }; defer file.close(); var stat = try file.stat(); if (stat.kind != .File) { exit("Please provide a valid file", .{}, 1); } var reader = file.reader(); var first_file = try ZipFileHeader.init(allocator, reader); defer first_file.deinit(); if (!std.mem.eql(u8, first_file.file_name, "mimetype")) { exit("Invalid file provided", .{}, 1); } if (!std.mem.startsWith(u8, first_file.compressed_content, "application/epub")) { exit("Invalid file provided", .{}, 1); } print("H: {}", .{first_file.compression_method}); var second_file = try ZipFileHeader.init(allocator, reader); defer second_file.deinit(); try second_file.extract(); print("G: {s}", .{second_file.file_name}); print("GI: {}", .{second_file.compression_method}); print("xml stuff:\n{s}", .{second_file.compressed_content}); }