mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-06 22:25:41 +12:00
More NCSD loading
This commit is contained in:
parent
9a040e1cde
commit
f6c2e390c1
11 changed files with 142 additions and 17 deletions
|
@ -44,7 +44,7 @@ else()
|
|||
message(FATAL_ERROR "THIS IS NOT x64 WAIT FOR THE KVM IMPLEMENTATION")
|
||||
endif()
|
||||
|
||||
set(SOURCE_FILES src/main.cpp src/emulator.cpp src/core/CPU/cpu_dynarmic.cpp src/core/memory.cpp src/core/elf.cpp)
|
||||
set(SOURCE_FILES src/main.cpp src/emulator.cpp src/core/CPU/cpu_dynarmic.cpp src/core/memory.cpp)
|
||||
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
|
||||
src/core/kernel/memory_management.cpp src/core/kernel/ports.cpp
|
||||
src/core/kernel/events.cpp src/core/kernel/threads.cpp
|
||||
|
@ -57,6 +57,7 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services
|
|||
set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp src/core/PICA/shader_unit.cpp
|
||||
src/core/PICA/shader_interpreter.cpp src/core/PICA/renderer_opengl.cpp
|
||||
)
|
||||
set(LOADER_SOURCE_FILES src/core/loader/elf.cpp src/core/loader/ncsd.cpp src/core/loader/ncch.cpp)
|
||||
|
||||
set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp include/termcolor.hpp
|
||||
include/cpu.hpp include/cpu_dynarmic.hpp include/memory.hpp include/kernel/kernel.hpp
|
||||
|
@ -80,11 +81,12 @@ set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
|
|||
#add_library(Alber ${HEADER_FILES})
|
||||
source_group("Header Files\\Core" FILES ${HEADER_FILES})
|
||||
source_group("Source Files\\Core" FILES ${SOURCE_FILES})
|
||||
source_group("Source Files\\Loader" FILES ${LOADER_SOURCE_FILES})
|
||||
source_group("Source Files\\Core\\Kernel" FILES ${KERNEL_SOURCE_FILES})
|
||||
source_group("Source Files\\Core\\Services" FILES ${SERVICE_SOURCE_FILES})
|
||||
source_group("Source Files\\Core\\PICA" FILES ${PICA_SOURCE_FILES})
|
||||
source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES})
|
||||
|
||||
add_executable(Alber ${SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${SERVICE_SOURCE_FILES} ${PICA_SOURCE_FILES}
|
||||
add_executable(Alber ${SOURCE_FILES} ${LOADER_SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${SERVICE_SOURCE_FILES} ${PICA_SOURCE_FILES}
|
||||
${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES})
|
||||
target_link_libraries(Alber PRIVATE dynarmic SDL2-static)
|
|
@ -10,7 +10,7 @@
|
|||
#include "PICA/gpu.hpp"
|
||||
|
||||
enum class ROMType {
|
||||
None, ELF, Cart
|
||||
None, ELF, NCSD
|
||||
};
|
||||
|
||||
class Emulator {
|
||||
|
@ -28,7 +28,9 @@ class Emulator {
|
|||
bool running = true;
|
||||
|
||||
// Keep the handle for the ROM here to reload when necessary and to prevent deleting it
|
||||
std::ifstream loadedROM;
|
||||
// This is currently only used for ELFs, NCSDs use the IOFile API instead
|
||||
std::ifstream loadedELF;
|
||||
NCSD loadedNCSD;
|
||||
|
||||
public:
|
||||
Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory), memory(cpu.getTicksRef()) {
|
||||
|
@ -51,6 +53,7 @@ public:
|
|||
void runFrame();
|
||||
|
||||
bool loadROM(const std::filesystem::path& path);
|
||||
bool loadNCSD(const std::filesystem::path& path);
|
||||
bool loadELF(const std::filesystem::path& path);
|
||||
bool loadELF(std::ifstream& file);
|
||||
void initGraphicsContext() { gpu.initGraphicsContext(); }
|
||||
|
|
|
@ -9,10 +9,12 @@
|
|||
#define fseeko _fseeki64
|
||||
#define ftello _ftelli64
|
||||
#define fileno _fileno
|
||||
|
||||
#pragma warning(disable : 4996)
|
||||
#endif
|
||||
|
||||
#ifdef _CRT_SECURE_NO_WARNINGS
|
||||
#undef _CRT_SECURE_NO_WARNINGS
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
class IOFile {
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
#pragma once
|
||||
#include "helpers.hpp"
|
||||
|
||||
struct NCCH {
|
||||
bool isNew3DS = false;
|
||||
bool initialized = false;
|
||||
bool compressExeFS = false;
|
||||
bool mountRomFS = false;
|
||||
bool encrypted = false;
|
||||
|
||||
static constexpr u64 mediaUnit = 0x200;
|
||||
u64 size = 0; // Size of NCCH converted to bytes
|
||||
u32 stackSize = 0;
|
||||
u32 bssSize = 0;
|
||||
|
||||
// Header: 0x200 byte NCCH header
|
||||
// Header: 0x200 + 0x800 byte NCCH header + exheadr
|
||||
// Returns true on success, false on failure
|
||||
bool loadFromHeader(u8* header);
|
||||
};
|
|
@ -1,3 +1,4 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include "helpers.hpp"
|
||||
#include "io_file.hpp"
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include "helpers.hpp"
|
||||
#include "handles.hpp"
|
||||
#include "loader/ncsd.hpp"
|
||||
|
||||
namespace PhysicalAddrs {
|
||||
enum : u32 {
|
||||
|
@ -121,6 +123,7 @@ public:
|
|||
void* getReadPointer(u32 address);
|
||||
void* getWritePointer(u32 address);
|
||||
std::optional<u32> loadELF(std::ifstream& file);
|
||||
std::optional<NCSD> loadNCSD(const std::filesystem::path& path);
|
||||
|
||||
u8 read8(u32 vaddr);
|
||||
u16 read16(u32 vaddr);
|
||||
|
|
30
src/core/loader/ncch.cpp
Normal file
30
src/core/loader/ncch.cpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include "loader/ncch.hpp"
|
||||
|
||||
bool NCCH::loadFromHeader(u8* header) {
|
||||
const u8* exheader = &header[0x200]; // Extended NCCH header
|
||||
|
||||
if (header[0x100] != 'N' || header[0x101] != 'C' || header[0x102] != 'C' || header[0x103] != 'H') {
|
||||
printf("Invalid header on NCCH\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
size = u64(*(u32*)&header[0x104]) * mediaUnit; // TODO: Maybe don't type pun because big endian will break
|
||||
|
||||
// Read NCCH flags
|
||||
isNew3DS = header[0x188 + 4] == 2;
|
||||
mountRomFS = (header[0x188 + 7] & 0x1) != 0x1;
|
||||
encrypted = (header[0x188 + 7] & 0x4) != 0x4;
|
||||
|
||||
compressExeFS = (exheader[0xD] & 1) != 0;
|
||||
|
||||
if (compressExeFS) {
|
||||
Helpers::panic("Compressed ExeFS");
|
||||
}
|
||||
|
||||
if (encrypted) {
|
||||
Helpers::panic("Encrypted NCCH partition");
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
68
src/core/loader/ncsd.cpp
Normal file
68
src/core/loader/ncsd.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
#include <optional>
|
||||
#include "loader/ncsd.hpp"
|
||||
#include "memory.hpp"
|
||||
|
||||
std::optional<NCSD> Memory::loadNCSD(const std::filesystem::path& path) {
|
||||
NCSD ncsd;
|
||||
if (!ncsd.file.open(path, "rb"))
|
||||
return std::nullopt;
|
||||
|
||||
u8 magic[4]; // Must be "NCSD"
|
||||
ncsd.file.seek(0x100);
|
||||
auto [success, bytes] = ncsd.file.readBytes(magic, 4);
|
||||
|
||||
if (!success || bytes != 4) {
|
||||
printf("Failed to read NCSD magic\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (magic[0] != 'N' || magic[1] != 'C' || magic[2] != 'S' || magic[3] != 'D') {
|
||||
printf("NCSD with wrong magic value\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::tie(success, bytes) = ncsd.file.readBytes(&ncsd.size, 4);
|
||||
if (!success || bytes != 4) {
|
||||
printf("Failed to read NCSD size\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ncsd.size *= NCSD::mediaUnit; // Convert size to bytes
|
||||
|
||||
// Read partition data
|
||||
ncsd.file.seek(0x120);
|
||||
// 2 u32s per partition (offset and length), 8 partitions total
|
||||
constexpr size_t partitionDataSize = 8 * 2; // Size of partition in u32s
|
||||
u32 partitionData[8 * 2];
|
||||
std::tie(success, bytes) = ncsd.file.read(partitionData, partitionDataSize, sizeof(u32));
|
||||
if (!success || bytes != partitionDataSize) {
|
||||
printf("Failed to read NCSD partition data\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
auto& partition = ncsd.partitions[i];
|
||||
partition.offset = u64(partitionData[i * 2]) * NCSD::mediaUnit;
|
||||
partition.length = u64(partitionData[i * 2 + 1]) * NCSD::mediaUnit;
|
||||
|
||||
if (partition.length != 0) { // Initialize the NCCH of each partition
|
||||
ncsd.file.seek(partition.offset);
|
||||
|
||||
// 0x200 bytes for the NCCH header and another 0x800 for the exheader
|
||||
constexpr u64 headerSize = 0x200 + 0x800;
|
||||
u8 ncchHeader[headerSize];
|
||||
std::tie(success, bytes) = ncsd.file.readBytes(ncchHeader, headerSize);
|
||||
if (!success || bytes != headerSize) {
|
||||
printf("Failed to read NCCH header\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!partition.ncch.loadFromHeader(ncchHeader)) {
|
||||
printf("Invalid NCCH partition\n");
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ncsd;
|
||||
}
|
|
@ -11,7 +11,7 @@ void Emulator::reset() {
|
|||
// Otherwise resetting the kernel or cpu might nuke them
|
||||
cpu.setReg(13, VirtualAddrs::StackTop); // Set initial SP
|
||||
if (romType == ROMType::ELF) { // Reload ELF if we're using one
|
||||
loadELF(loadedROM);
|
||||
loadELF(loadedELF);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,26 +54,38 @@ bool Emulator::loadROM(const std::filesystem::path& path) {
|
|||
if (extension == ".elf" || extension == ".axf")
|
||||
return loadELF(path);
|
||||
else if (extension == ".3ds")
|
||||
Helpers::panic("3DS file");
|
||||
return loadNCSD(path);
|
||||
else {
|
||||
printf("Unknown file type\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Emulator::loadNCSD(const std::filesystem::path& path) {
|
||||
romType = ROMType::NCSD;
|
||||
std::optional<NCSD> opt = memory.loadNCSD(path);
|
||||
|
||||
if (!opt.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
loadedNCSD = opt.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Emulator::loadELF(const std::filesystem::path& path) {
|
||||
loadedROM.open(path, std::ios_base::binary); // Open ROM in binary mode
|
||||
loadedELF.open(path, std::ios_base::binary); // Open ROM in binary mode
|
||||
romType = ROMType::ELF;
|
||||
|
||||
return loadELF(loadedROM);
|
||||
return loadELF(loadedELF);
|
||||
}
|
||||
|
||||
bool Emulator::loadELF(std::ifstream& file) {
|
||||
// Rewind ifstream
|
||||
loadedROM.clear();
|
||||
loadedROM.seekg(0);
|
||||
loadedELF.clear();
|
||||
loadedELF.seekg(0);
|
||||
|
||||
std::optional<u32> entrypoint = memory.loadELF(loadedROM);
|
||||
std::optional<u32> entrypoint = memory.loadELF(loadedELF);
|
||||
if (!entrypoint.has_value())
|
||||
return false;
|
||||
|
||||
|
|
|
@ -9,10 +9,10 @@ int main (int argc, char *argv[]) {
|
|||
|
||||
emu.initGraphicsContext();
|
||||
|
||||
auto elfPath = std::filesystem::current_path() / (argc > 1 ? argv[1] : "Metroid2.3ds");
|
||||
if (!emu.loadROM(elfPath)) {
|
||||
auto romPath = std::filesystem::current_path() / (argc > 1 ? argv[1] : "SuperMario3DLand.3ds");
|
||||
if (!emu.loadROM(romPath)) {
|
||||
// For some reason just .c_str() doesn't show the proper path
|
||||
Helpers::panic("Failed to load ROM file: %s", elfPath.string().c_str());
|
||||
Helpers::panic("Failed to load ROM file: %s", romPath.string().c_str());
|
||||
}
|
||||
|
||||
emu.run();
|
||||
|
|
Loading…
Add table
Reference in a new issue