diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index db4610d9..434adb40 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,10 +48,7 @@ jobs: submodules: recursive - name: Setup dependencies run: | - vcpkg install sdl2[vulkan]:x64-windows - vcpkg install fmt:x64-windows - vcpkg install nlohmann-json:x64-windows - vcpkg install mio:x64-windows + vcpkg install sdl2[vulkan]:x64-windows fmt:x64-windows nlohmann-json:x64-windows mio:x64-windows - name: Build Kaizen run: | cmake -B build -T clangcl -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -S src diff --git a/.gitmodules b/.gitmodules index 805592e4..703a4c98 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "external/fmt"] path = external/fmt url = https://github.com/fmtlib/fmt +[submodule "external/unarr"] + path = external/unarr + url = https://github.com/selmf/unarr diff --git a/external/parallel-rdp/ParallelRDPWrapper.cpp b/external/parallel-rdp/ParallelRDPWrapper.cpp index 7a6b90c6..4af147db 100644 --- a/external/parallel-rdp/ParallelRDPWrapper.cpp +++ b/external/parallel-rdp/ParallelRDPWrapper.cpp @@ -154,11 +154,14 @@ void LoadParallelRDP(const u8* rdram) { fragLayout.sets[0].fp_mask = 1; fragLayout.sets[0].array_size[0] = 1; - u32* fullscreenQuadVert, *fullscreenQuadFrag; - auto sizeVert = Util::ReadFileBinary("resources/vert.spv", &fullscreenQuadVert); - auto sizeFrag = Util::ReadFileBinary("resources/frag.spv", &fullscreenQuadFrag); + auto fullscreenQuadVert = Util::ReadFileBinary("resources/vert.spv"); + auto fullscreenQuadFrag = Util::ReadFileBinary("resources/frag.spv"); + auto sizeVert = fullscreenQuadVert.size(); + auto sizeFrag = fullscreenQuadFrag.size(); - fullscreen_quad_program = wsi->get_device().request_program(fullscreenQuadVert, sizeVert, fullscreenQuadFrag, sizeFrag, &vertLayout, &fragLayout); + fullscreen_quad_program = wsi->get_device().request_program(reinterpret_cast(fullscreenQuadVert.data()), sizeVert, + reinterpret_cast(fullscreenQuadFrag.data()), sizeFrag, + &vertLayout, &fragLayout); auto aligned_rdram = reinterpret_cast(rdram); uintptr_t offset = 0; diff --git a/external/unarr b/external/unarr new file mode 160000 index 00000000..569ffdb0 --- /dev/null +++ b/external/unarr @@ -0,0 +1 @@ +Subproject commit 569ffdb063ce8a76ab069444af175e5953d90c93 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 71405396..ab6969b0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,6 +36,7 @@ include_directories( ../external/imgui/imgui ../external/imgui/imgui/backends ../external/discord-rpc/include + ../external/unarr ${SDL2_INCLUDE_DIRS} ) @@ -63,6 +64,7 @@ add_subdirectory(../external/discord-rpc discord-rpc) add_subdirectory(../external/imgui imgui) add_subdirectory(../external/nativefiledialog-extended nfd) add_subdirectory(../external/parallel-rdp parallel-rdp) +add_subdirectory(../external/unarr unarr) add_executable(kaizen main.cpp) @@ -77,4 +79,4 @@ file(REMOVE ${PROJECT_BINARY_DIR}/resources/shader.vert) target_link_libraries(kaizen PUBLIC frontend frontend-imgui - discord-rpc imgui nfd parallel-rdp backend fmt::fmt mio::mio nlohmann_json::nlohmann_json core registers interpreter mem mmio rsp SDL2::SDL2main SDL2::SDL2) + discord-rpc imgui nfd parallel-rdp backend fmt::fmt unarr mio::mio nlohmann_json::nlohmann_json core registers interpreter mem mmio rsp SDL2::SDL2main SDL2::SDL2) diff --git a/src/backend/Core.cpp b/src/backend/Core.cpp index 959590ad..03a2c21c 100644 --- a/src/backend/Core.cpp +++ b/src/backend/Core.cpp @@ -20,15 +20,21 @@ void Core::LoadROM(const std::string& rom_) { cpu.Reset(); pause = false; romLoaded = true; - - cpu.mem.LoadROM(rom); + + auto extension = fs::path(rom).extension().string(); + + bool isArchive = std::any_of(std::begin(ARCHIVE_TYPES), std::end(ARCHIVE_TYPES), [&](auto i) { + return extension == i; + }); + + cpu.mem.LoadROM(isArchive, rom); GameDB::match(cpu.mem); cpu.mem.mmio.vi.isPal = cpu.mem.IsROMPAL(); cpu.mem.mmio.si.pif.InitDevices(cpu.mem.saveType); - cpu.mem.mmio.si.pif.LoadMempak(rom_); - cpu.mem.mmio.si.pif.LoadEeprom(cpu.mem.saveType, rom_); - cpu.mem.flash.Load(cpu.mem.saveType, rom_); - cpu.mem.LoadSRAM(cpu.mem.saveType, rom_); + cpu.mem.mmio.si.pif.LoadMempak(rom); + cpu.mem.mmio.si.pif.LoadEeprom(cpu.mem.saveType, rom); + cpu.mem.flash.Load(cpu.mem.saveType, rom); + cpu.mem.LoadSRAM(cpu.mem.saveType, rom); cpu.mem.mmio.si.pif.ExecutePIF(cpu.mem, cpu.regs); } diff --git a/src/backend/core/Mem.cpp b/src/backend/core/Mem.cpp index 0681ce25..87353e8a 100644 --- a/src/backend/core/Mem.cpp +++ b/src/backend/core/Mem.cpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace n64 { Mem::Mem() { @@ -76,29 +77,75 @@ FORCE_INLINE void SetROMCIC(u32 checksum, ROM& rom) { } } -void Mem::LoadROM(const std::string& filename) { - std::ifstream file(filename, std::ios::binary); - file.unsetf(std::ios::skipws); +std::vector Mem::OpenArchive(const std::string &path, size_t& sizeAdjusted) { + auto stream = ar_open_file(fs::path(path).u8string().c_str()); - if(!file.is_open()) { - Util::panic("Unable to open {}!", filename); + if(!stream) { + Util::panic("Could not open archive! Are you sure it's an archive?"); } - file.seekg(0, std::ios::end); - size_t size = file.tellg(); - file.seekg(0, std::ios::beg); + ar_archive* archive = ar_open_zip_archive(stream, false); - size_t sizeAdjusted = Util::NextPow2(size); - rom.mask = sizeAdjusted - 1; - u8* buf = (u8*)malloc(sizeAdjusted); - file.read(reinterpret_cast(buf), size); - file.close(); + if(!archive) archive = ar_open_rar_archive(stream); + if(!archive) archive = ar_open_7z_archive(stream); + if(!archive) archive = ar_open_tar_archive(stream); + + if(!archive) { + ar_close(stream); + Util::panic("Could not open archive! Are you sure it's a supported archive? (7z, zip, rar and tar are supported)"); + } + + std::vector buf{}; + + while(ar_parse_entry(archive)) { + auto filename = ar_entry_get_name(archive); + auto extension = fs::path(filename).extension(); + + if(std::any_of(std::begin(ROM_EXTENSIONS), std::end(ROM_EXTENSIONS), [&](auto x) { + return extension == x; + })) { + auto size = ar_entry_get_size(archive); + sizeAdjusted = Util::NextPow2(size); + buf.resize(sizeAdjusted); + ar_entry_uncompress(archive, buf.data(), size); + break; + } else { + ar_close_archive(archive); + ar_close(stream); + Util::panic("Could not find any rom image in the archive!"); + } + } + + ar_close_archive(archive); + ar_close(stream); + return buf; +} + +std::vector Mem::OpenROM(const std::string& filename, size_t& sizeAdjusted) { + auto buf = Util::ReadFileBinary(filename); + sizeAdjusted = Util::NextPow2(buf.size()); + return buf; +} + +void Mem::LoadROM(bool isArchive, const std::string& filename) { + size_t sizeAdjusted; + u8* buf; + std::vector temp{}; + if(isArchive) { + temp = OpenArchive(filename, sizeAdjusted); + } else { + temp = OpenROM(filename, sizeAdjusted); + } + + buf = (u8*)calloc(sizeAdjusted, 1); + std::copy(temp.begin(), temp.end(), buf); u32 endianness = be32toh(*reinterpret_cast(buf)); Util::SwapN64Rom(sizeAdjusted, buf, endianness); memcpy(rom.cart, buf, sizeAdjusted); rom.size = sizeAdjusted; + rom.mask = sizeAdjusted - 1; memcpy(&rom.header, buf, sizeof(ROMHeader)); memcpy(rom.gameNameCart, rom.header.imageName, sizeof(rom.header.imageName)); diff --git a/src/backend/core/Mem.hpp b/src/backend/core/Mem.hpp index 6b7118b9..db5c7fee 100644 --- a/src/backend/core/Mem.hpp +++ b/src/backend/core/Mem.hpp @@ -134,7 +134,9 @@ struct Mem { Mem(); void Reset(); void LoadSRAM(SaveType, fs::path); - void LoadROM(const std::string&); + static std::vector OpenROM(const std::string&, size_t&); + static std::vector OpenArchive(const std::string&, size_t&); + void LoadROM(bool, const std::string&); [[nodiscard]] auto GetRDRAM() const -> u8* { return mmio.rdp.rdram; } diff --git a/src/common.hpp b/src/common.hpp index 4fcb5ff3..652c68d1 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -20,6 +20,9 @@ using m128i = __m128i; #define HALF_ADDRESS(addr) ((addr) ^ 2) #define BYTE_ADDRESS(addr) ((addr) ^ 3) +#define ARCHIVE_TYPES {".zip",".7z",".rar",".tar"} +#define ROM_EXTENSIONS {".n64",".z64",".v64",".N64",".Z64",".V64"} + #define RD(x) (((x) >> 11) & 0x1F) #define RT(x) (((x) >> 16) & 0x1F) #define RS(x) (((x) >> 21) & 0x1F) diff --git a/src/frontend/App.cpp b/src/frontend/App.cpp index 3b3d1c13..2423eb58 100644 --- a/src/frontend/App.cpp +++ b/src/frontend/App.cpp @@ -49,7 +49,7 @@ void App::Run() { switch(event.key.keysym.sym) { case SDLK_o: { nfdchar_t* outpath; - const nfdu8filteritem_t filter {"Nintendo 64 roms", "n64,z64,v64,N64,Z64,V64"}; + const nfdu8filteritem_t filter {"Nintendo 64 roms/archives", "n64,z64,v64,N64,Z64,V64,zip,tar,rar,7z"}; nfdresult_t result = NFD_OpenDialog(&outpath, &filter, 1, nullptr); if(result == NFD_OKAY) { LoadROM(outpath); diff --git a/src/utils/File.hpp b/src/utils/File.hpp index 03716fac..dd21ba54 100644 --- a/src/utils/File.hpp +++ b/src/utils/File.hpp @@ -3,21 +3,9 @@ #include namespace Util { -FORCE_INLINE auto ReadFileBinary(const std::string& path, u32** buf) { +FORCE_INLINE std::vector ReadFileBinary(const std::string& path) { std::ifstream file(path, std::ios::binary); - file.unsetf(std::ios::skipws); - if(!file.is_open()) { - panic("Could not load file '{}'!", path); - } - - file.seekg(0, std::ios::end); - size_t size = file.tellg(); - file.seekg(0, std::ios::beg); - - *buf = (u32*)calloc(size, 1); - file.read(reinterpret_cast(*buf), size); - file.close(); - return size; + return {std::istreambuf_iterator{file}, {}}; } FORCE_INLINE size_t NextPow2(size_t num) {