diff --git a/assets/mods/core.tar b/assets/mods/core.tar new file mode 100644 index 0000000..b44f5d0 Binary files /dev/null and b/assets/mods/core.tar differ diff --git a/assets/mods/core.wasm b/assets/mods/core.wasm deleted file mode 100755 index da6e6d1..0000000 Binary files a/assets/mods/core.wasm and /dev/null differ diff --git a/src/mods/vm.zig b/src/mods/vm.zig index ab83b02..d291ae4 100644 --- a/src/mods/vm.zig +++ b/src/mods/vm.zig @@ -103,9 +103,6 @@ pub const Runtime = struct { pub fn init(allocator: Allocator, module: Module, global_runtime: *wasm.GlobalRuntime) !Runtime { // if memory max is not set the memory is allowed to grow but it is not supported at the moment const max = module.memory.max orelse module.memory.min; - if (module.memory.max == null) { - std.log.warn("Growing memory is not yet supported, usign the minimum memory\n", .{}); - } const memory = try allocator.alloc(u8, max); @memset(memory, 0); @memcpy(memory[0..module.data.len], module.data); @@ -336,8 +333,12 @@ pub const Runtime = struct { .memorysize => @panic("UNIMPLEMENTED"), .memorygrow => { const newPages = self.stack.pop().?.i32; + const newSize = (self.memory.len / Parser.PAGE_SIZE) + @as(usize, @intCast(newPages)); + if (self.module.memory.max != null and newSize > self.module.memory.max.?){ + std.debug.panic("Mod failed to stay within memory range\n", .{}); + } const oldPages: i32 = @intCast(self.memory.len / Parser.PAGE_SIZE); - self.memory = try allocator.realloc(self.memory, self.memory.len + @as(usize, @intCast(newPages * Parser.PAGE_SIZE))); + self.memory = try allocator.realloc(self.memory, newSize * Parser.PAGE_SIZE); try self.stack.append(.{ .i32 = oldPages }); }, .memoryinit => @panic("UNIMPLEMENTED"), diff --git a/src/sideros.zig b/src/sideros.zig index c027860..03c8f98 100644 --- a/src/sideros.zig +++ b/src/sideros.zig @@ -21,32 +21,82 @@ var camera: rendering.Camera = .{ }; var input: ecs.Input = .{ .key_pressed = .{false} ** @intFromEnum(ecs.Input.KeyCode.menu) }; var resources: ecs.Resources = undefined; +const ModInfo = struct { + name: []const u8, + runtime: mods.Runtime, + modIdx: u32, +}; +var loadedMods: std.ArrayListUnmanaged(ModInfo) = .{}; + +fn openOrCreateDir(fs: std.fs.Dir, path: []const u8) !std.fs.Dir { + var dir: std.fs.Dir = undefined; + dir = fs.openDir(path, .{.iterate = true}) catch |err| { + if (err == std.fs.Dir.OpenError.FileNotFound) { + try fs.makeDir(path); + dir = try fs.openDir(path, .{.iterate = true}); + return dir; + } else { + return err; + } + }; + return dir; +} + +fn untarToDirAndGetFile(fs: std.fs.Dir, name: []const u8) !std.fs.File { + var modDir = try openOrCreateDir(fs,name); + defer modDir.close(); + var buffer: [1024]u8 = undefined; + var tarFile = try fs.openFile(try std.fmt.bufPrint(&buffer, "{s}.tar", .{name}), .{}); + defer tarFile.close(); + const tarData = try tarFile.readToEndAlloc(allocator, 1_000_000); + var tarReader = std.io.Reader.fixed(tarData); + try std.tar.pipeToFileSystem(modDir, &tarReader, .{}); + return try fs.openFile(try std.fmt.bufPrint(&buffer, "{s}/main.wasm", .{name}), .{}); +} fn init_mods() void { - var global_runtime = mods.GlobalRuntime.init(allocator); - const file = std.fs.cwd().openFile("assets/mods/core.wasm", .{}) catch @panic("Couldn't open assets/mods/core.wasm"); - // const file = std.fs.cwd().openFile("./test.wasm", .{}) catch @panic("Couldn't open test.wasm"); - const all = file.readToEndAlloc(allocator, 1_000_000) catch @panic("Unable to read the file"); // 1 MB - defer allocator.free(all); - var parser = mods.Parser.init(allocator, all) catch @panic("Failed to init parser"); - defer parser.deinit(); - parser.parseModule() catch |err| { - std.debug.panic("[ERROR]: error {any} at byte {x}(0x{x})\n", .{ err, parser.byte_idx, parser.bytes[parser.byte_idx] }); - }; - const module = parser.module(); + var modsDir = std.fs.cwd().openDir("./assets/mods", .{.iterate = true}) catch @panic("Failed to open assets/mods"); + defer modsDir.close(); - for (0..parser.globalTypes.len) |i| { - global_runtime.addGlobal(@intCast(i), parser.globalTypes[i], parser.globalValues[i]) catch @panic("Failed to add runtime global"); + var modsDirIter = modsDir.iterate(); + while (modsDirIter.next() catch @panic("Failed to get next iteration of mods directory")) |entry| { + if (entry.kind != std.fs.File.Kind.file){ + std.debug.panic("TODO: Search recursively for mods\n", .{}); + } + const extension = entry.name.ptr[entry.name.len - 4..entry.name.len]; + if (!std.mem.eql(u8, extension, ".tar")){ + std.debug.print("[WARNING]: Found non tar extension in mods directory\n", .{}); + continue; + } + const fullDir = std.fmt.allocPrint(allocator, "assets/mods/{s}", .{entry.name.ptr[0..entry.name.len - 4]}) catch @panic("Failed to allocate for fullDir"); + var global_runtime = mods.GlobalRuntime.init(allocator); + + var file = untarToDirAndGetFile(std.fs.cwd(), fullDir) catch @panic("Failed to load mod"); + defer file.close(); + const all = file.readToEndAlloc(allocator, 1_000_000) catch @panic("Unable to read main file"); + defer allocator.free(all); + var parser = mods.Parser.init(allocator, all) catch @panic("Failed to init parser"); + defer parser.deinit(); + parser.parseModule() catch |err| { + std.debug.panic("[ERROR]: error {any} at byte {x}(0x{x})\n", .{ err, parser.byte_idx, parser.bytes[parser.byte_idx] }); + }; + const module = parser.module(); + + for (0..parser.globalTypes.len) |i| { + global_runtime.addGlobal(@intCast(i), parser.globalTypes[i], parser.globalValues[i]) catch @panic("Failed to add runtime global"); + } + + var runtime = mods.Runtime.init(allocator, module, &global_runtime) catch @panic("Failed to init runtime"); + + const modIdx: u32 = @intCast(loadedMods.items.len); + var parameters = [_]mods.VM.Value{.{ .i32 = @intCast(modIdx) }}; + runtime.externalCall(allocator, .init, ¶meters) catch @panic("Failed to call to init"); + const result = runtime.stack.pop().?; + std.debug.print("Result of {s} init: {any}\n", .{fullDir, result}); + loadedMods.append(allocator, .{.name = fullDir, .runtime = runtime, .modIdx = modIdx}) catch @panic("Failed to append to loadedMods"); + std.fs.cwd().deleteTree(fullDir) catch std.debug.panic("Failed to delete {s}", .{fullDir}); } - - var runtime = mods.Runtime.init(allocator, module, &global_runtime) catch @panic("Failed to init runtime"); - defer runtime.deinit(allocator); - - var parameters = [_]mods.VM.Value{.{ .i32 = 17 }}; - runtime.externalCall(allocator, .init, ¶meters) catch @panic("Failed to call to init"); - const result = runtime.stack.pop().?; - std.debug.print("Result of init: {any}\n", .{result}); } export fn sideros_init(init: api.GameInit) callconv(.c) void { @@ -71,6 +121,14 @@ export fn sideros_update(gameUpdate: api.GameUpdate) callconv(.c) void { } export fn sideros_cleanup() callconv(.c) void { + for (loadedMods.items) |info| { + var runtime = info.runtime; + // runtime.externalCall(allocator, .deinit, &.{}); + // const result = runtime.stack.pop().?; + // std.debug.print("Result of {s} init: {any}\n", .{info.name, result}); + runtime.deinit(allocator); + } + loadedMods.deinit(allocator); renderer.deinit(); pool.deinit(); if (gpa.deinit() != .ok) @panic("Memory leaked");