ebook-reader/src/main.zig

475 lines
13 KiB
Zig

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 <path>", .{});
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});
}