From e108486c17c33af715e593fdd12c57fe77cb674b Mon Sep 17 00:00:00 2001
From: kayomn <kayomn@kayomn.net>
Date: Tue, 11 Oct 2022 01:03:02 +0100
Subject: [PATCH] Add data validation to Oar entries

---
 src/oar.zig | 36 +++++++++++++++++++++++++++++++++++-
 src/sys.zig | 20 +++++++-------------
 2 files changed, 42 insertions(+), 14 deletions(-)

diff --git a/src/oar.zig b/src/oar.zig
index 7660c8c..b85304a 100644
--- a/src/oar.zig
+++ b/src/oar.zig
@@ -7,8 +7,42 @@ const sys = @import("./sys.zig");
 /// Typically, following this block in memory is the file data it holds the meta-information for.
 ///
 pub const Entry = extern struct {
+    signature: [3]u8,
+    revision: u8,
     name_length: u8,
     name_buffer: [255]u8 = std.mem.zeroes([255]u8),
     file_size: u64,
-    padding: [248]u8,
+    padding: [244]u8,
+
+    ///
+    /// Returns `true` if `entry` correctly identifies itself as a valid Oar entry, otherwise
+    /// `false`.
+    ///
+    pub fn isValid(entry: Entry) bool {
+        return std.mem.eql(u8, &entry.signature, "oar");
+    }
+
+    ///
+    /// Attempts to read an [Entry] from `file_access` at its current cursor position.
+    ///
+    /// Returns the read [Entry] value or `null` if the end of file is reached before completing the
+    /// read.
+    ///
+    pub fn read(file_access: *sys.FileAccess) sys.FileAccess.Error!?Entry {
+        var entry = std.mem.zeroes(Entry);
+        const origin = try file_access.queryCursor();
+
+        if ((try file_access.read(std.mem.asBytes(&entry))) != @sizeOf(Entry)) {
+            try file_access.seek(origin);
+
+            return null;
+        }
+
+        return entry;
+    }
+
+    ///
+    /// Magic identifier used to validate [Entry] data.
+    ///
+    const signature_magic = "oar";
 };
diff --git a/src/sys.zig b/src/sys.zig
index edaf8db..fa7b1a3 100644
--- a/src/sys.zig
+++ b/src/sys.zig
@@ -459,22 +459,16 @@ pub const FileSystem = union(enum) {
                     const file_access = @ptrCast(*FileAccess, ext.SDL_RWFromFile(
                         &path_buffer, "rb") orelse return error.FileNotFound);
 
-                    while (true) {
-                        var entry = std.mem.zeroes(oar.Entry);
-                        const entry_buffer = std.mem.asBytes(&entry);
+                    while (oar.Entry.read(file_access) catch return error.FileNotFound) |entry| {
+                        if (!entry.isValid()) break;
 
-                        if ((file_access.read(entry_buffer) catch return
-                            error.FileNotFound) != entry_buffer.len) return error.FileNotFound;
+                        if (std.mem.eql(u8, entry.name_buffer[0 .. entry.name_length],
+                            path.buffer[0 .. path.length])) return file_access;
 
-                        if (std.mem.eql(u8, entry.name_buffer[0 .. entry.
-                            name_length], path.buffer[0 .. path.length])) {
-
-                            return file_access;
-                        }
-
-                        file_access.seek(math.roundUp(u64, entry.file_size,
-                            entry_buffer.len)) catch return error.FileNotFound;
+                        file_access.seek(entry.file_size) catch break;
                     }
+
+                    return error.FileNotFound;
                 },
 
                 .native => |native| {