mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-08 07:05:40 +12:00
Attempt to add RomFS dumping
This commit is contained in:
parent
f9dc9ac94d
commit
abe4675477
10 changed files with 180 additions and 8 deletions
|
@ -53,6 +53,7 @@ include_directories(third_party/xxhash/include)
|
||||||
include_directories(third_party/httplib)
|
include_directories(third_party/httplib)
|
||||||
include_directories(third_party/stb)
|
include_directories(third_party/stb)
|
||||||
include_directories(third_party/opengl)
|
include_directories(third_party/opengl)
|
||||||
|
include_directories(third_party/mio/single_include)
|
||||||
|
|
||||||
add_compile_definitions(NOMINMAX) # Make windows.h not define min/max macros because third-party deps don't like it
|
add_compile_definitions(NOMINMAX) # Make windows.h not define min/max macros because third-party deps don't like it
|
||||||
add_compile_definitions(WIN32_LEAN_AND_MEAN) # Make windows.h not include literally everything
|
add_compile_definitions(WIN32_LEAN_AND_MEAN) # Make windows.h not include literally everything
|
||||||
|
@ -141,7 +142,7 @@ set(SOURCE_FILES src/emulator.cpp src/io_file.cpp src/config.cpp
|
||||||
src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp
|
src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp
|
||||||
src/core/memory.cpp src/renderer.cpp src/core/renderer_null/renderer_null.cpp
|
src/core/memory.cpp src/renderer.cpp src/core/renderer_null/renderer_null.cpp
|
||||||
src/http_server.cpp src/stb_image_write.c src/core/cheats.cpp src/core/action_replay.cpp
|
src/http_server.cpp src/stb_image_write.c src/core/cheats.cpp src/core/action_replay.cpp
|
||||||
src/discord_rpc.cpp src/lua.cpp
|
src/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp
|
||||||
)
|
)
|
||||||
set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp)
|
set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp)
|
||||||
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
|
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
|
||||||
|
@ -219,7 +220,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
|
||||||
include/applets/applet.hpp include/applets/mii_selector.hpp include/math_util.hpp include/services/soc.hpp
|
include/applets/applet.hpp include/applets/mii_selector.hpp include/math_util.hpp include/services/soc.hpp
|
||||||
include/services/news_u.hpp include/applets/software_keyboard.hpp include/applets/applet_manager.hpp include/fs/archive_user_save_data.hpp
|
include/services/news_u.hpp include/applets/software_keyboard.hpp include/applets/applet_manager.hpp include/fs/archive_user_save_data.hpp
|
||||||
include/services/amiibo_device.hpp include/services/nfc_types.hpp include/swap.hpp include/services/csnd.hpp include/services/nwm_uds.hpp
|
include/services/amiibo_device.hpp include/services/nfc_types.hpp include/swap.hpp include/services/csnd.hpp include/services/nwm_uds.hpp
|
||||||
include/fs/archive_system_save_data.hpp include/lua_manager.hpp
|
include/fs/archive_system_save_data.hpp include/lua_manager.hpp include/memory_mapped_file.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
cmrc_add_resource_library(
|
cmrc_add_resource_library(
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "cpu.hpp"
|
#include "cpu.hpp"
|
||||||
#include "crypto/aes_engine.hpp"
|
#include "crypto/aes_engine.hpp"
|
||||||
#include "discord_rpc.hpp"
|
#include "discord_rpc.hpp"
|
||||||
|
#include "fs/romfs.hpp"
|
||||||
#include "io_file.hpp"
|
#include "io_file.hpp"
|
||||||
#include "lua_manager.hpp"
|
#include "lua_manager.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
@ -120,6 +121,7 @@ class Emulator {
|
||||||
void initGraphicsContext() { gpu.initGraphicsContext(window); }
|
void initGraphicsContext() { gpu.initGraphicsContext(window); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
RomFS::DumpingResult dumpRomFS(const std::filesystem::path& path);
|
||||||
void setOutputSize(u32 width, u32 height) { gpu.setOutputSize(width, height); }
|
void setOutputSize(u32 width, u32 height) { gpu.setOutputSize(width, height); }
|
||||||
|
|
||||||
EmulatorConfig& getConfig() { return config; }
|
EmulatorConfig& getConfig() { return config; }
|
||||||
|
|
|
@ -18,5 +18,12 @@ namespace RomFS {
|
||||||
std::vector<std::unique_ptr<RomFSNode>> files;
|
std::vector<std::unique_ptr<RomFSNode>> files;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Result codes when dumping RomFS. These are used by the frontend to print appropriate error messages if RomFS dumping fails
|
||||||
|
enum class DumpingResult {
|
||||||
|
Success = 0,
|
||||||
|
InvalidFormat = 1, // ROM is a format that doesn't support RomFS, such as ELF
|
||||||
|
NoRomFS = 2
|
||||||
|
};
|
||||||
|
|
||||||
std::unique_ptr<RomFSNode> parseRomFSTree(uintptr_t romFS, u64 romFSSize);
|
std::unique_ptr<RomFSNode> parseRomFSTree(uintptr_t romFS, u64 romFSSize);
|
||||||
} // namespace RomFS
|
} // namespace RomFS
|
|
@ -57,6 +57,7 @@ struct NCCH {
|
||||||
FSInfo exeFS;
|
FSInfo exeFS;
|
||||||
FSInfo romFS;
|
FSInfo romFS;
|
||||||
CodeSetInfo text, data, rodata;
|
CodeSetInfo text, data, rodata;
|
||||||
|
FSInfo partitionInfo;
|
||||||
|
|
||||||
// Contents of the .code file in the ExeFS
|
// Contents of the .code file in the ExeFS
|
||||||
std::vector<u8> codeFile;
|
std::vector<u8> codeFile;
|
||||||
|
|
42
include/memory_mapped_file.hpp
Normal file
42
include/memory_mapped_file.hpp
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
#include "helpers.hpp"
|
||||||
|
#include "mio/mio.hpp"
|
||||||
|
|
||||||
|
// Minimal RAII wrapper over memory mapped files
|
||||||
|
|
||||||
|
class MemoryMappedFile {
|
||||||
|
std::filesystem::path filePath = ""; // path of our file
|
||||||
|
mio::mmap_sink map; // mmap sink for our file
|
||||||
|
|
||||||
|
u8* pointer = nullptr; // Pointer to the contents of the memory mapped file
|
||||||
|
bool opened = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool exists() const { return opened; }
|
||||||
|
u8* data() const { return pointer; }
|
||||||
|
|
||||||
|
std::error_code flush();
|
||||||
|
MemoryMappedFile();
|
||||||
|
MemoryMappedFile(const std::filesystem::path& path);
|
||||||
|
|
||||||
|
~MemoryMappedFile();
|
||||||
|
// Returns true on success
|
||||||
|
bool open(const std::filesystem::path& path);
|
||||||
|
void close();
|
||||||
|
|
||||||
|
// TODO: For memory-mapped output files we'll need some more stuff such as a constructor that takes path/size/shouldCreate as parameters
|
||||||
|
|
||||||
|
u8& operator[](size_t index) { return pointer[index]; }
|
||||||
|
const u8& operator[](size_t index) const { return pointer[index]; }
|
||||||
|
|
||||||
|
auto begin() { return map.begin(); }
|
||||||
|
auto end() { return map.end(); }
|
||||||
|
auto cbegin() { return map.cbegin(); }
|
||||||
|
auto cend() { return map.cend(); }
|
||||||
|
|
||||||
|
mio::mmap_sink& getSink() { return map; }
|
||||||
|
};
|
|
@ -39,6 +39,7 @@ class MainWindow : public QMainWindow {
|
||||||
void swapEmuBuffer();
|
void swapEmuBuffer();
|
||||||
void emuThreadMainLoop();
|
void emuThreadMainLoop();
|
||||||
void selectROM();
|
void selectROM();
|
||||||
|
void dumpRomFS();
|
||||||
|
|
||||||
// Tracks whether we are using an OpenGL-backed renderer or a Vulkan-backed renderer
|
// Tracks whether we are using an OpenGL-backed renderer or a Vulkan-backed renderer
|
||||||
bool usingGL = false;
|
bool usingGL = false;
|
||||||
|
|
|
@ -26,6 +26,7 @@ bool NCCH::loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSIn
|
||||||
|
|
||||||
codeFile.clear();
|
codeFile.clear();
|
||||||
saveData.clear();
|
saveData.clear();
|
||||||
|
partitionInfo = info;
|
||||||
|
|
||||||
size = u64(*(u32*)&header[0x104]) * mediaUnit; // TODO: Maybe don't type pun because big endian will break
|
size = u64(*(u32*)&header[0x104]) * mediaUnit; // TODO: Maybe don't type pun because big endian will break
|
||||||
exheaderSize = *(u32*)&header[0x180];
|
exheaderSize = *(u32*)&header[0x180];
|
||||||
|
|
|
@ -582,3 +582,62 @@ void Emulator::updateDiscord() {
|
||||||
#else
|
#else
|
||||||
void Emulator::updateDiscord() {}
|
void Emulator::updateDiscord() {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void printNode(const RomFS::RomFSNode& node, int indentation) {
|
||||||
|
for (int i = 0; i < indentation; i++) {
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
printf("%s/\n", std::string(node.name.begin(), node.name.end()).c_str());
|
||||||
|
|
||||||
|
for (auto& file : node.files) {
|
||||||
|
for (int i = 0; i <= indentation; i++) {
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
printf("%s\n", std::string(file->name.begin(), file->name.end()).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
indentation++;
|
||||||
|
for (auto& directory : node.directories) {
|
||||||
|
printNode(*directory, indentation);
|
||||||
|
}
|
||||||
|
indentation--;
|
||||||
|
}
|
||||||
|
|
||||||
|
RomFS::DumpingResult Emulator::dumpRomFS(const std::filesystem::path& path) {
|
||||||
|
using namespace RomFS;
|
||||||
|
|
||||||
|
if (romType != ROMType::NCSD && romType != ROMType::CXI && romType != ROMType::HB_3DSX) {
|
||||||
|
return DumpingResult::InvalidFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contents of RomFS as raw bytes
|
||||||
|
std::vector<u8> romFS;
|
||||||
|
u64 size;
|
||||||
|
|
||||||
|
if (romType == ROMType::HB_3DSX) {
|
||||||
|
auto hb3dsx = memory.get3DSX();
|
||||||
|
if (!hb3dsx->hasRomFs()) {
|
||||||
|
return DumpingResult::NoRomFS;
|
||||||
|
}
|
||||||
|
size = hb3dsx->romFSSize;
|
||||||
|
|
||||||
|
romFS.resize(size);
|
||||||
|
hb3dsx->readRomFSBytes(&romFS[0], 0, size);
|
||||||
|
} else {
|
||||||
|
auto cxi = memory.getCXI();
|
||||||
|
if (!cxi->hasRomFS()) {
|
||||||
|
return DumpingResult::NoRomFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u64 offset = cxi->romFS.offset;
|
||||||
|
size = cxi->romFS.size;
|
||||||
|
|
||||||
|
romFS.resize(size);
|
||||||
|
cxi->readFromFile(memory.CXIFile, cxi->partitionInfo, &romFS[0], offset - cxi->fileOffset, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<RomFSNode> node = parseRomFSTree((uintptr_t)&romFS[0], size);
|
||||||
|
printNode(*node, 0);
|
||||||
|
|
||||||
|
return DumpingResult::Success;
|
||||||
|
}
|
37
src/memory_mapped_file.cpp
Normal file
37
src/memory_mapped_file.cpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#include "memory_mapped_file.hpp"
|
||||||
|
|
||||||
|
MemoryMappedFile::MemoryMappedFile() : opened(false), filePath(""), pointer(nullptr) {}
|
||||||
|
MemoryMappedFile::MemoryMappedFile(const std::filesystem::path& path) { open(path); }
|
||||||
|
MemoryMappedFile::~MemoryMappedFile() { close(); }
|
||||||
|
|
||||||
|
// TODO: This should probably also return the error one way or another eventually
|
||||||
|
bool MemoryMappedFile::open(const std::filesystem::path& path) {
|
||||||
|
std::error_code error;
|
||||||
|
map = mio::make_mmap_sink(path.string(), 0, mio::map_entire_file, error);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
opened = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
filePath = path;
|
||||||
|
pointer = (u8*)map.data();
|
||||||
|
opened = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryMappedFile::close() {
|
||||||
|
if (opened) {
|
||||||
|
opened = false;
|
||||||
|
pointer = nullptr; // Set the pointer to nullptr to avoid errors related to lingering pointers
|
||||||
|
|
||||||
|
map.unmap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::error_code MemoryMappedFile::flush() {
|
||||||
|
std::error_code ret;
|
||||||
|
map.sync(ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -16,13 +16,19 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
|
||||||
menuBar = new QMenuBar(this);
|
menuBar = new QMenuBar(this);
|
||||||
setMenuBar(menuBar);
|
setMenuBar(menuBar);
|
||||||
|
|
||||||
|
// Create menu bar menus
|
||||||
auto fileMenu = menuBar->addMenu(tr("File"));
|
auto fileMenu = menuBar->addMenu(tr("File"));
|
||||||
|
auto emulationMenu = menuBar->addMenu(tr("Emulation"));
|
||||||
|
auto toolsMenu = menuBar->addMenu(tr("Tools"));
|
||||||
|
auto helpMenu = menuBar->addMenu(tr("Help"));
|
||||||
|
auto aboutMenu = menuBar->addMenu(tr("About"));
|
||||||
|
|
||||||
|
// Create and bind actions for them
|
||||||
auto pandaAction = fileMenu->addAction(tr("panda..."));
|
auto pandaAction = fileMenu->addAction(tr("panda..."));
|
||||||
connect(pandaAction, &QAction::triggered, this, &MainWindow::selectROM);
|
connect(pandaAction, &QAction::triggered, this, &MainWindow::selectROM);
|
||||||
|
|
||||||
auto emulationMenu = menuBar->addMenu(tr("Emulation"));
|
auto dumpRomFSAction = toolsMenu->addAction(tr("Dump RomFS"));
|
||||||
auto helpMenu = menuBar->addMenu(tr("Help"));
|
connect(dumpRomFSAction, &QAction::triggered, this, &MainWindow::dumpRomFS);
|
||||||
auto aboutMenu = menuBar->addMenu(tr("About"));
|
|
||||||
|
|
||||||
// Set up theme selection
|
// Set up theme selection
|
||||||
setTheme(Theme::Dark);
|
setTheme(Theme::Dark);
|
||||||
|
@ -71,6 +77,7 @@ void MainWindow::emuThreadMainLoop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
needToLoadROM.store(false, std::memory_order::seq_cst);
|
needToLoadROM.store(false, std::memory_order::seq_cst);
|
||||||
|
emu->dumpRomFS("");
|
||||||
}
|
}
|
||||||
|
|
||||||
emu->runFrame();
|
emu->runFrame();
|
||||||
|
@ -98,8 +105,8 @@ void MainWindow::selectROM() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto path =
|
auto path = QFileDialog::getOpenFileName(
|
||||||
QFileDialog::getOpenFileName(this, tr("Select 3DS ROM to load"), "", tr("Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.3dsx *.elf *.axf)"));
|
this, tr("Select 3DS ROM to load"), {}, tr("Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.3dsx *.elf *.axf)"), {});
|
||||||
|
|
||||||
if (!path.isEmpty()) {
|
if (!path.isEmpty()) {
|
||||||
romToLoad = path.toStdU16String();
|
romToLoad = path.toStdU16String();
|
||||||
|
@ -176,3 +183,17 @@ void MainWindow::setTheme(Theme theme) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::dumpRomFS() {
|
||||||
|
// TODO: LOCK FILE MUTEX HERE
|
||||||
|
auto folder = QFileDialog::getExistingDirectory(
|
||||||
|
this, tr("Select folder to dump RomFS files to"), "", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks
|
||||||
|
);
|
||||||
|
|
||||||
|
if (folder.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path path(folder.toStdU16String());
|
||||||
|
//RomFS::DumpingResult res = emu->dumpRomFS(path);
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue