First commit

This commit is contained in:
UserDeploy 2025-03-23 11:11:19 +07:00
commit 81f55e53d4
16 changed files with 3338 additions and 0 deletions

44
.gitignore vendored Normal file
View file

@ -0,0 +1,44 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
*.fls
# Let git use modules
externals/dynarmic/
build/
uni_libs/
# My test files
*.bin
samples/
src/libfiles/
src/test/*

21
.vscode/c_cpp_properties.json vendored Normal file
View file

@ -0,0 +1,21 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${default}"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"windowsSdkVersion": "10.0.22621.0",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64",
"configurationProvider": "ms-vscode.cmake-tools"
}
],
"version": 4
}

111
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,111 @@
{
"files.associations": {
"*.ejs": "html",
"*.c": "c",
"*.s": "ca65",
"*.h": "c",
"memory": "cpp",
"algorithm": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"exception": "cpp",
"filesystem": "cpp",
"format": "cpp",
"forward_list": "cpp",
"fstream": "cpp",
"functional": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"iterator": "cpp",
"limits": "cpp",
"list": "cpp",
"locale": "cpp",
"map": "cpp",
"new": "cpp",
"optional": "cpp",
"ostream": "cpp",
"random": "cpp",
"ratio": "cpp",
"set": "cpp",
"span": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"string": "cpp",
"system_error": "cpp",
"thread": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"utility": "cpp",
"variant": "cpp",
"vector": "cpp",
"xfacet": "cpp",
"xhash": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstring": "cpp",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "cpp",
"cwctype": "cpp",
"numbers": "cpp",
"codecvt": "cpp",
"complex": "cpp",
"condition_variable": "cpp",
"coroutine": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"deque": "cpp",
"future": "cpp",
"mutex": "cpp",
"strstream": "cpp",
"typeindex": "cpp",
"*.ipp": "cpp",
"any": "cpp",
"cfenv": "cpp",
"cinttypes": "cpp",
"expected": "cpp",
"numeric": "cpp",
"queue": "cpp",
"ranges": "cpp",
"regex": "cpp",
"shared_mutex": "cpp",
"source_location": "cpp",
"stack": "cpp",
"stdfloat": "cpp"
},
"C_Cpp.default.compilerPath": "d:\\Daffin\\vs_com_2022\\VC\\Tools\\MSVC\\14.42.34433\\bin\\Hostx64\\x64\\cl.exe",
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"dotnet.defaultSolution": "disable"
}

62
CMakeLists.txt Normal file
View file

@ -0,0 +1,62 @@
cmake_minimum_required(VERSION 3.10.0)
project(qmgdec VERSION 0.1.0 LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 20)
if(POLICY CMP0167)
cmake_policy(SET CMP0167 OLD)
endif()
# 01 - Boost first
set(BOOST_INCLUDEDIR "${PROJECT_SOURCE_DIR}/externals/dynarmic/externals/ext-boost")
find_package(Boost 1.57 REQUIRED)
# 02 - Dynarmic
set(DYNARMIC_TESTS OFF CACHE BOOL "")
set(DYNARMIC_FRONTENDS "A32;A64" CACHE STRING "")
set(DYNARMIC_USE_PRECOMPILED_HEADERS ON CACHE BOOL "")
add_subdirectory("${PROJECT_SOURCE_DIR}/externals/dynarmic")
# 03 - UTF-8 msvc
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
# 04 - My code
add_subdirectory(src)
#add_library(dynarmic STATIC IMPORTED)
# set_target_properties(dynarmic PROPERTIES
# IMPORTED_LOCATION "deps/dynarmic/"
# IMPORTED_IMPLIB "${DEPENDENCIES_DIR}/allegro/lib/allegro.lib"
# INTERFACE_INCLUDE_DIRECTORIES "${DEPENDENCIES_DIR}/allegro/include"
# )
# # include_directories("${PROJECT_SOURCE_DIR}/deps/dynarmic/include")
# # target_link_libraries(dynarmic INTERFACE dynarmic::dynarmic)
# # # The dynarmic package's cmake files are helpfully completely silent
# # # so we have to inform the user of its status ourselves
# # if(TARGET dynarmic::dynarmic)
# # message(STATUS "dynarmic: found him!")
# # include_directories("${PROJECT_SOURCE_DIR}/deps/dynarmic/include")
# # endif()
#include_directories("${PROJECT_SOURCE_DIR}/ld/ext-boost")
#add_executable(qmgdec qmgtest.cpp)
#target_link_libraries(qmgdec PUBLIC dynarmic::dynarmic)
#add_executable(ifgdec ifgtest.cpp)
#target_link_libraries(ifgdec PUBLIC dynarmic::dynarmic)
# target_sources(sources
# PUBLIC
# FILE_SET CXX_MODULES
# FILES
# main.ixx
# PRIVATE
# main.cpp
# )
# target_compile_features(sources PUBLIC cxx_std_20)

1146
bindings/python/dynemu.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
#include <stdint.h>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef void Emulator;
Emulator *dynemu_open();
void dynemu_delete(Emulator *e);
void dynemu_mmap(Emulator *e, u32 vaddr, u32 size);
size_t dynemu_upload_file(Emulator *e, const char *filename, int64_t dest, u32 size);
u8 dynemu_upload_fls_memory(Emulator *e, const u8 *fls);
void dynemu_upload_memstub(Emulator *e, u32 dest, u32 mem_start, u32 mem_size, u8 is_big);
void dynemu_upload_memstub_nooffset(Emulator *e, u32 mem_start, u32 mem_size, u8 is_big);
u8 dynemu_read_u8(Emulator *e, u32 vaddr);
u16 dynemu_read_u16(Emulator *e, u32 vaddr, u8 is_big);
u32 dynemu_read_u32(Emulator *e, u32 vaddr, u8 is_big);
u64 dynemu_read_u64(Emulator *e, u32 vaddr, u8 is_big);
void dynemu_write_u8(Emulator *e, u32 vaddr, u8 value);
void dynemu_write_u16(Emulator *e, u32 vaddr, u16 value, u8 is_big);
void dynemu_write_u32(Emulator *e, u32 vaddr, u32 value, u8 is_big);
void dynemu_write_u64(Emulator *e, u32 vaddr, u64 value, u8 is_big);
void dynemu_read_bytes(Emulator *e, u32 vaddr, u8 *output, u32 size);
void dynemu_write_bytes(Emulator *e, u32 vaddr, const u8 *input, u32 size);
u32 dynemu_execute(Emulator *e, u32 pc, u8 is_big);
u32 dynemu_read_reg(Emulator *e, u8 reg);
void dynemu_write_reg(Emulator *e, u8 reg, u32 value);

View file

@ -0,0 +1,69 @@
from ctypes import *
class Dynemu():
def __init__(self):
self._dynemu = dynemu_open()
def __del__(self):
dynemu_delete(self._dynemu)
def memory_map(self, vaddr: int, size: int):
dynemu_mmap(self._dynemu, vaddr, size)
def upload_file(self, filename: str, dest: int, size: int):
return dynemu_upload_file(self._dynemu, filename, dest, size)
def upload_fls_memory(self, data: bytes|bytearray):
temp = (c_ubyte * len(data)).from_buffer(bytearray(data))
return dynemu_upload_fls_memory(self._dynemu, temp)
def upload_memstub(self, dest: int=-1, mem_start: int=0xa0000000, mem_size: int=0x2000000, is_big: bool=False):
if dest == -1:
dynemu_upload_memstub_nooffset(self._dynemu, mem_start, mem_size, is_big)
else:
dynemu_upload_memstub(self._dynemu, dest, mem_start, mem_size, is_big)
def read_u8(self, vaddr: int):
return dynemu_read_u8(self._dynemu, vaddr)
def read_u16(self, vaddr: int, is_big: bool=False):
return dynemu_read_u16(self._dynemu, vaddr, is_big)
def read_u32(self, vaddr: int, is_big: bool=False):
return dynemu_read_u32(self._dynemu, vaddr, is_big)
def read_u64(self, vaddr: int, is_big: bool=False):
return dynemu_read_u64(self._dynemu, vaddr, is_big)
def write_u8(self, vaddr: int, value: int):
return dynemu_write_u8(self._dynemu, vaddr, value)
def write_u16(self, vaddr: int, value: int, is_big: bool=False):
return dynemu_write_u16(self._dynemu, vaddr, value, is_big)
def write_u32(self, vaddr: int, value: int, is_big: bool=False):
return dynemu_write_u32(self._dynemu, vaddr, value, is_big)
def write_u64(self, vaddr: int, value: int, is_big: bool=False):
return dynemu_write_u64(self._dynemu, vaddr, value, is_big)
def read_bytes(self, vaddr: int, size: int):
temp = (c_ubyte * size)()
dynemu_read_bytes(self._dynemu, vaddr, temp, size)
return bytes(temp)
def write_bytes(self, vaddr: int, data: bytes|bytearray):
temp = (c_ubyte * len(data)).from_buffer(bytearray(data))
dynemu_write_bytes(self._dynemu, vaddr, temp, len(data))
def execute(self, pc: int, is_big: bool=False):
return dynemu_execute(self._dynemu, pc, is_big)
def get_reg(self, reg: int):
return dynemu_read_reg(self._dynemu, reg)
def set_reg(self, reg: int, value: int):
dynemu_write_reg(self._dynemu, reg, value)

View file

@ -0,0 +1,2 @@
@echo off
ctypesgen.exe -ldynemu_shared bindings.h -o ..\dynemu.py --insert-file=bindings.py

6
src/CMakeLists.txt Normal file
View file

@ -0,0 +1,6 @@
include_directories(.)
set_property(DIRECTORY APPEND PROPERTY
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
add_subdirectory(test)
add_subdirectory(lib)

15
src/lib/CMakeLists.txt Normal file
View file

@ -0,0 +1,15 @@
add_library(dynemu OBJECT libdynemu.cpp)
add_dependencies(dynemu dynarmic mcl)
target_link_libraries(dynemu PUBLIC dynarmic mcl)
set_property(TARGET dynemu PROPERTY POSITION_INDEPENDENT_CODE 1)
# shared and static libraries built from the same object files
add_library(dynemu_shared SHARED $<TARGET_OBJECTS:dynemu>)
add_library(dynemu_static STATIC $<TARGET_OBJECTS:dynemu>)
add_dependencies(dynemu_shared dynarmic mcl)
target_link_libraries(dynemu_shared PUBLIC dynarmic mcl)
add_dependencies(dynemu_static dynarmic mcl)
target_link_libraries(dynemu_static PUBLIC dynarmic mcl)

109
src/lib/libdynemu.cpp Normal file
View file

@ -0,0 +1,109 @@
#include "shared/sys_emulator.hpp"
#include "libdynemu.hpp"
extern "C" {
Emulator *dynemu_open() {
auto temp = new Emulator();
return temp;
}
void dynemu_delete(Emulator *e) {
delete e;
}
void dynemu_mmap(Emulator *e, u32 vaddr, u32 size) {
return e->env.CreateMemoryMap(vaddr, size);
}
size_t dynemu_upload_file(Emulator *e, const char *filename, std::int64_t dest, u32 size) {
return e->env.UploadToMemory(filename, dest, size);
}
bool dynemu_upload_fls_memory(Emulator *e, const u8 *fls) {
return e->env.UploadFLSFromBytes(fls);
}
void dynemu_upload_memstub(Emulator *e, u32 dest, u32 mem_start, u32 mem_size, bool is_big) {
return e->env.UploadOSStub(dest, mem_start, mem_size, is_big);
}
void dynemu_upload_memstub_nooffset(Emulator *e, u32 mem_start, u32 mem_size, bool is_big) {
return e->env.UploadOSStub(mem_start, mem_size, is_big);
}
u8 dynemu_read_u8(Emulator *e, u32 vaddr) {
return e->env.MemoryRead8(vaddr);
}
u16 dynemu_read_u16(Emulator *e, u32 vaddr, bool is_big) {
if (is_big) {
return e->env.MemoryRead16_Big(vaddr);
} else {
return e->env.MemoryRead16(vaddr);
}
}
u32 dynemu_read_u32(Emulator *e, u32 vaddr, bool is_big) {
if (is_big) {
return e->env.MemoryRead32_Big(vaddr);
} else {
return e->env.MemoryRead32(vaddr);
}
}
u64 dynemu_read_u64(Emulator *e, u32 vaddr, bool is_big) {
if (is_big) {
return e->env.MemoryRead64_Big(vaddr);
} else {
return e->env.MemoryRead64(vaddr);
}
}
void dynemu_write_u8(Emulator *e, u32 vaddr, u8 value) {
e->env.MemoryWrite8(vaddr, value);
}
void dynemu_write_u16(Emulator *e, u32 vaddr, u16 value, bool is_big) {
if (is_big) {
e->env.MemoryWrite16_Big(vaddr, value);
} else {
e->env.MemoryWrite16(vaddr, value);
}
}
void dynemu_write_u32(Emulator *e, u32 vaddr, u32 value, bool is_big) {
if (is_big) {
e->env.MemoryWrite32_Big(vaddr, value);
} else {
e->env.MemoryWrite32(vaddr, value);
}
}
void dynemu_write_u64(Emulator *e, u32 vaddr, u64 value, bool is_big) {
if (is_big) {
e->env.MemoryWrite64_Big(vaddr, value);
} else {
e->env.MemoryWrite64(vaddr, value);
}
}
void dynemu_read_bytes(Emulator *e, u32 vaddr, u8 *output, u32 size) {
e->env.MemoryReadBytes(vaddr, output, size);
}
void dynemu_write_bytes(Emulator *e, u32 vaddr, const u8 *input, u32 size) {
e->env.MemoryWriteBytes(vaddr, input, size);
}
u32 dynemu_execute(Emulator *e, u32 pc, bool is_big) {
return e->env.Execute(pc, is_big);
}
u32 dynemu_read_reg(Emulator *e, u8 reg) {
return e->env.cpu->Regs()[reg];
}
void dynemu_write_reg(Emulator *e, u8 reg, u32 value) {
e->env.cpu->Regs()[reg] = value;
}
}

40
src/lib/libdynemu.hpp Normal file
View file

@ -0,0 +1,40 @@
#include <cstdint>
#pragma once
#if defined(_MSC_VER)
// Microsoft
#define EXPORT __declspec(dllexport)
#define IMPORT __declspec(dllimport)
#elif defined(__GNUC__)
// GCC
#define EXPORT __attribute__((visibility("default")))
#define IMPORT
#else
// do nothing and hope for the best?
#define EXPORT
#define IMPORT
#pragma warning Unknown dynamic link import/export semantics.
#endif
extern "C" {
EXPORT Emulator *dynemu_open();
EXPORT void dynemu_delete(Emulator *e);
EXPORT void dynemu_mmap(Emulator *e, u32 vaddr, u32 size);
EXPORT size_t dynemu_upload_file(Emulator *e, const char *filename, int64_t dest, u32 size);
EXPORT bool dynemu_upload_fls_memory(Emulator *e, const u8 *fls);
EXPORT void dynemu_upload_memstub(Emulator *e, u32 dest, u32 mem_start, u32 mem_size, bool is_big);
EXPORT void dynemu_upload_memstub_nooffset(Emulator *e, u32 mem_start, u32 mem_size, bool is_big);
EXPORT u8 dynemu_read_u8(Emulator *e, u32 vaddr);
EXPORT u16 dynemu_read_u16(Emulator *e, u32 vaddr, bool is_big);
EXPORT u32 dynemu_read_u32(Emulator *e, u32 vaddr, bool is_big);
EXPORT u64 dynemu_read_u64(Emulator *e, u32 vaddr, bool is_big);
EXPORT void dynemu_write_u8(Emulator *e, u32 vaddr, u8 value);
EXPORT void dynemu_write_u16(Emulator *e, u32 vaddr, u16 value, bool is_big);
EXPORT void dynemu_write_u32(Emulator *e, u32 vaddr, u32 value, bool is_big);
EXPORT void dynemu_write_u64(Emulator *e, u32 vaddr, u64 value, bool is_big);
EXPORT void dynemu_read_bytes(Emulator *e, u32 vaddr, u8 *output, u32 size);
EXPORT void dynemu_write_bytes(Emulator *e, u32 vaddr, const u8 *input, u32 size);
EXPORT u32 dynemu_execute(Emulator *e, u32 pc, bool is_big);
EXPORT u32 dynemu_read_reg(Emulator *e, u8 reg);
EXPORT void dynemu_write_reg(Emulator *e, u8 reg, u32 value);
}

1015
src/shared/os_wrapper.hpp Normal file

File diff suppressed because it is too large Load diff

511
src/shared/sys_emulator.hpp Normal file
View file

@ -0,0 +1,511 @@
/* (c) 2025 Wrapper Inc. - The New Emulation Library */
#include <iostream>
#include <fstream>
#include <format>
#include "os_wrapper.hpp"
#include "dynarmic/interface/A32/a32.h"
#include "dynarmic/interface/A32/config.h"
#include "dynarmic/interface/exclusive_monitor.h"
#include "mcl/assert.hpp"
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
#define DEBUG 0
#define BLOCK_ALLIGN 0x10000
class MemMap final {
public:
u64 start;
u64 size;
std::unique_ptr<u8[]> data;
};
struct FlashPartition {
u32 start_vaddr;
u32 size;
std::unique_ptr<u8[]> data;
};
struct FlashLoader {
u16 magic;
u32 count;
std::unique_ptr<FlashPartition[]> data;
};
class MyEmulator final : public Dynarmic::A32::UserCallbacks {
private:
u8 find_memory_map_id(u32 vaddr) {
for (int i = 0; i < mem_map.size(); i++) {
if (vaddr >= mem_map[i].start && vaddr < (mem_map[i].start + mem_map[i].size)) return i;
}
std::cerr << std::format("Memory map {:#08x} OOB, Emulation cannot continue", vaddr) << std::endl;
throw std::out_of_range("Data abort");
}
bool is_mmap_exists(u32 vaddr) {
for (int i = 0; i < mem_map.size(); i++) {
if (vaddr >= mem_map[i].start && vaddr < (mem_map[i].start + mem_map[i].size)) return true;
}
return false;
}
template <std::size_t N>
bool CheckExecOverridePC(const std::array<u32, N> &list, u32 pc) {
for (int i = 0; i < N; i++) {
if (pc == list[i]) return true;
}
return false;
}
public:
std::array<MemMap, 64> mem_map{};
u32 map_index = 0;
u32 os_offset = 0xa8000000;
Dynarmic::A32::Jit *cpu;
/*
void CreateMemoryMap(u32 vaddr, u32 size)
This function creates a memory map for this specific vaddr (virtual address) with specific size
*/
void CreateMemoryMap(u32 vaddr, u32 size) {
if (!is_mmap_exists(vaddr)) mem_map[map_index++] = {vaddr, size, std::make_unique<u8[]>(size)};
}
/*
void RoutineReplaceWithBXLR(const std::array<u32, N> &list, bool is_thumb, bool is_big)
This function replaces each instruction on the list with "bx lr" instruction
Currently, this has no use outside for hooking functions
*/
template <std::size_t N>
void RoutineReplaceWithBXLR(const std::array<u32, N> &list, bool is_thumb, bool is_big) {
for (int i = 0; i < N; i++) {
if (is_thumb) {
MemoryWrite16(list[i], is_big ? 0x7047 : 0x4770);
} else {
MemoryWrite32(list[i], is_big ? 0x1eff2fe1 : 0xe12fff1e);
}
}
}
/*
size_t UploadToMemory(std::string filename, std::int64_t dest, u32 size)
This function uploads a file into memory, automatically creating a memory map if not exists.
Setting dest to -1 will use a multi-map parser
Returns:
0: NOK
1 or FileSize: OK
*/
size_t UploadToMemory(std::string filename, std::int64_t dest, u32 size) {
std::ifstream file(filename, std::ios::binary);
u8 buf[2048];
u32 offset = 0;
int to_read = 0;
if (!file.is_open()) {
std::cerr << std::format("Unable to open {}: {}", filename, std::strerror(errno)) << std::endl;
return 0;
}
if (dest != -1) {
file.seekg(0, file.end);
size_t fileSize = file.tellg();
file.seekg(0, file.beg);
if (size < fileSize && !is_mmap_exists((u32)dest)) {
std::cerr << "Code file too large to fit into memory" << std::endl;
return 0;
}
CreateMemoryMap((u32)dest, size);
while ((size_t)file.tellg() < fileSize) {
to_read = (int)((fileSize - file.tellg()) >= 2048 ? 2048 : (fileSize - file.tellg()));
file.read((char *)buf, to_read);
MemoryWriteBytes((u32)dest + offset, buf, to_read);
offset += to_read;
}
return fileSize;
} else {
FlashLoader fl{};
file.read((char *)&fl.magic, sizeof(fl.magic));
file.read((char *)&fl.count, sizeof(fl.count));
if (fl.magic != 0x4c46) {
std::cerr << "Invalid FL magic check" << std::endl;
return 0;
}
fl.data = std::make_unique<FlashPartition[]>(fl.count);
for (u32 i = 0; i < fl.count; i++) {
file.read((char *)&fl.data.get()[i].start_vaddr, sizeof(fl.data.get()[i].start_vaddr));
file.read((char *)&fl.data.get()[i].size, sizeof(fl.data.get()[i].size));
fl.data.get()[i].data = std::make_unique<u8[]>(fl.data.get()[i].size);
file.read((char *)fl.data.get()[i].data.get(), fl.data.get()[i].size);
CreateMemoryMap(fl.data.get()[i].start_vaddr, fl.data.get()[i].size + ((fl.data.get()[i].size % BLOCK_ALLIGN) ? (BLOCK_ALLIGN - (fl.data.get()[i].size % BLOCK_ALLIGN)) : 0));
MemoryWriteBytes(fl.data.get()[i].start_vaddr, fl.data.get()[i].data.get(), fl.data.get()[i].size);
}
return 1;
}
}
/*
bool UploadFLSFromBytes(const u8 *fls)
This function uploads a multi-part code file from memory.
Returns:
0: NOK
1: OK
*/
bool UploadFLSFromBytes(const u8 *fls) {
FlashLoader fl{};
memcpy(&fl.magic, fls, sizeof(fl.magic));
memcpy(&fl.count, fls + sizeof(fl.magic), sizeof(fl.count));
if (fl.magic != 0x4c46) {
std::cerr << "Invalid FL magic check" << std::endl;
return false;
}
fl.data = std::make_unique<FlashPartition[]>(fl.count);
auto offset = sizeof(fl.magic) + sizeof(fl.count);
for (u32 i = 0; i < fl.count; i++) {
memcpy(&fl.data.get()[i].start_vaddr, fls + offset, sizeof(fl.data.get()[i].start_vaddr));
memcpy(&fl.data.get()[i].size, fls + offset + sizeof(fl.data.get()[i].start_vaddr), sizeof(fl.data.get()[i].size));
fl.data.get()[i].data = std::make_unique<u8[]>(fl.data.get()[i].size);
memcpy((char *)fl.data.get()[i].data.get(), fls + offset + sizeof(fl.data.get()[i].start_vaddr) + sizeof(fl.data.get()[i].size), fl.data.get()[i].size);
offset += sizeof(fl.data.get()[i].start_vaddr) + sizeof(fl.data.get()[i].size) + fl.data.get()[i].size;
CreateMemoryMap(fl.data.get()[i].start_vaddr, fl.data.get()[i].size + ((fl.data.get()[i].size % BLOCK_ALLIGN) ? (BLOCK_ALLIGN - (fl.data.get()[i].size % BLOCK_ALLIGN)) : 0));
MemoryWriteBytes(fl.data.get()[i].start_vaddr, fl.data.get()[i].data.get(), fl.data.get()[i].size);
}
return true;
}
/*
bool UploadFLSFromBytes(const std::array<u8, N> &fls)
This function uploads a multi-part code file from memory.
Returns:
0: NOK
1: OK
*/
template <std::size_t N>
bool UploadFLSFromBytes(const std::array<u8, N> &fls) {
return UploadFLSFromBytes(fls.data());
}
/*
void UploadOSStub(u32 dest, u32 mem_start, u32 mem_size, bool is_big)
This function uploads a stub libc to a specific dest virtual address, along with setting up the regions assosiated with the dynamic memory functions.
Endianness were determined by is_big parameter.
*/
void UploadOSStub(u32 dest, u32 mem_start, u32 mem_size, bool is_big) {
if (is_big) {
MemoryWriteBytes(dest, OS_LoopLoader_BE.data(), (u32)OS_LoopLoader_BE.size());
MemoryWrite32_Big(dest + MEM_INIT_REG_OFFSET, mem_start);
MemoryWrite32_Big(dest + MEM_INIT_REG_SIZE, mem_size);
} else {
MemoryWriteBytes(dest, OS_LoopLoader.data(), (u32)OS_LoopLoader.size());
MemoryWrite32(dest + MEM_INIT_REG_OFFSET, mem_start);
MemoryWrite32(dest + MEM_INIT_REG_SIZE, mem_size);
}
os_offset = dest;
}
/*
void UploadOSStub(u32 mem_start, u32 mem_size, bool is_big)
This function uploads a stub libc to the configured os_offset parameter virtual address, along with setting up the regions assosiated with the dynamic memory functions.
Endianness were determined by is_big parameter.
*/
void UploadOSStub(u32 mem_start, u32 mem_size, bool is_big) {
UploadOSStub(os_offset, mem_start, mem_size, is_big);
}
/*
bool PreCodeReadHook(bool is_thumb, u32 pc, Dynarmic::A32::IREmitter& ir)
Custom function used for end of code detection.
*/
bool PreCodeReadHook(bool is_thumb, u32 pc, Dynarmic::A32::IREmitter& ir) override {
#if DEBUG
std::cout << std::format("executing at {:#08x}", pc) << std::endl;
std::cout << std::format("REG P0 {:#08x}", cpu->Regs()[0]) << std::endl;
std::cout << std::format("REG P1 {:#08x}", cpu->Regs()[1]) << std::endl;
std::cout << std::format("REG P2 {:#08x}", cpu->Regs()[2]) << std::endl;
std::cout << std::format("REG P12 {:#08x}", cpu->Regs()[12]) << std::endl;
std::cout << std::format("REG P15 {:#08x}", cpu->Regs()[15]) << std::endl;
#endif
if (pc == (os_offset + ROUTE_EXIT)) cpu->HaltExecution();
return true;
}
/*
u8 MemoryRead8(u32 vaddr)
Reads an 8-bit value from vaddr
Returns: byte[vaddr]
*/
u8 MemoryRead8(u32 vaddr) override {
#if DEBUG
if (cpu->IsExecuting()) printf("Reading at 0x%08x\n", vaddr);
#endif
try {
u8 mem_now = find_memory_map_id(vaddr);
return mem_map[mem_now].data.get()[vaddr - mem_map[mem_now].start];
} catch (std::out_of_range) {
cpu->HaltExecution(Dynarmic::HaltReason::MemoryAbort);
return 0xff; // Undefined behavior can occur
}
}
/*
void MemoryReadBytes(u32 vaddr, u8 *output, u32 size)
Copy data from memory map to a single u8 pointer.
*/
void MemoryReadBytes(u32 vaddr, u8 *output, u32 size) {
u8 mem_now = find_memory_map_id(vaddr);
ASSERT(((vaddr - mem_map[mem_now].start) + size) <= mem_map[mem_now].size);
memcpy(output, mem_map[mem_now].data.get() + (vaddr - mem_map[mem_now].start), size);
}
/*
u16 MemoryRead16(u32 vaddr)
Reads a 16-bit value from vaddr
Returns: short[vaddr]
*/
u16 MemoryRead16(u32 vaddr) override {
return u16(MemoryRead8(vaddr)) | u16(MemoryRead8(vaddr + 1)) << 8;
}
/*
u32 MemoryRead32(u32 vaddr)
Reads a 32-bit value from vaddr
Returns: word[vaddr]
*/
u32 MemoryRead32(u32 vaddr) override {
return u32(MemoryRead16(vaddr)) | u32(MemoryRead16(vaddr + 2)) << 16;
}
/*
u64 MemoryRead64(u32 vaddr)
Reads a 64-bit value from vaddr
Returns: dword[vaddr]
*/
u64 MemoryRead64(u32 vaddr) override {
return u64(MemoryRead32(vaddr)) | u64(MemoryRead32(vaddr + 4)) << 32;
}
/*
u16 MemoryRead16_Big(u32 vaddr)
Reads a 16-bit value from vaddr
Returns: short[vaddr]
*/
u16 MemoryRead16_Big(u32 vaddr) {
return u16(MemoryRead8(vaddr + 1)) | u16(MemoryRead8(vaddr)) << 8;
}
/*
u32 MemoryRead32_Big(u32 vaddr)
Reads a 32-bit value from vaddr
Returns: word[vaddr]
*/
u32 MemoryRead32_Big(u32 vaddr) {
return u32(MemoryRead16_Big(vaddr + 2)) | u32(MemoryRead16_Big(vaddr)) << 16;
}
/*
u64 MemoryRead64_Big(u32 vaddr)
Reads a 64-bit value from vaddr
Returns: dword[vaddr]
*/
u64 MemoryRead64_Big(u32 vaddr) {
return u64(MemoryRead32_Big(vaddr + 4)) | u64(MemoryRead32_Big(vaddr)) << 32;
}
/*
void MemoryWrite8(u32 vaddr, u8 value)
Writes an 8-bit value to vaddr
*/
void MemoryWrite8(u32 vaddr, u8 value) override {
#if DEBUG
if (cpu->IsExecuting()) printf("Writing at 0x%08x 0x%02x\n", vaddr, value);
#endif
try {
u8 mem_now = find_memory_map_id(vaddr);
mem_map[mem_now].data.get()[vaddr - mem_map[mem_now].start] = value;
} catch (std::out_of_range) {
cpu->HaltExecution(Dynarmic::HaltReason::MemoryAbort);
}
}
/*
void MemoryWriteBytes(u32 vaddr, const u8 *input, u32 size)
Copy data from a single u8 pointer into memory map.
*/
void MemoryWriteBytes(u32 vaddr, const u8 *input, u32 size) {
u8 mem_now = find_memory_map_id(vaddr);
ASSERT(((vaddr - mem_map[mem_now].start) + size) <= mem_map[mem_now].size);
memcpy(mem_map[mem_now].data.get() + (vaddr - mem_map[mem_now].start), input, size);
}
/*
void MemoryWriteBytes(u32 vaddr, const std::array<u8, N> &data)
Copy data from a single u8 pointer into memory map.
*/
template <std::size_t N>
void MemoryWriteBytes(u32 vaddr, const std::array<u8, N> &data) {
MemoryWriteBytes(vaddr, data.data(), N);
}
/*
void MemoryWrite16(u32 vaddr, u16 value)
Writes an 16-bit value to vaddr
*/
void MemoryWrite16(u32 vaddr, u16 value) override {
MemoryWrite8(vaddr, u8(value));
MemoryWrite8(vaddr + 1, u8(value >> 8));
}
/*
void MemoryWrite32(u32 vaddr, u32 value)
Writes an 32-bit value to vaddr
*/
void MemoryWrite32(u32 vaddr, u32 value) override {
MemoryWrite16(vaddr, u16(value));
MemoryWrite16(vaddr + 2, u16(value >> 16));
}
/*
void MemoryWrite64(u32 vaddr, u64 value)
Writes an 64-bit value to vaddr
*/
void MemoryWrite64(u32 vaddr, u64 value) override {
MemoryWrite32(vaddr, u32(value));
MemoryWrite32(vaddr + 4, u32(value >> 32));
}
/*
void MemoryWrite16_Big(u32 vaddr, u16 value)
Writes an 16-bit value to vaddr
*/
void MemoryWrite16_Big(u32 vaddr, u16 value) {
MemoryWrite8(vaddr + 1, u8(value));
MemoryWrite8(vaddr, u8(value >> 8));
}
/*
void MemoryWrite32_Big(u32 vaddr, u32 value)
Writes an 32-bit value to vaddr
*/
void MemoryWrite32_Big(u32 vaddr, u32 value) {
MemoryWrite16_Big(vaddr + 2, u16(value));
MemoryWrite16_Big(vaddr, u16(value >> 16));
}
/*
void MemoryWrite64_Big(u32 vaddr, u64 value)
Writes an 64-bit value to vaddr
*/
void MemoryWrite64_Big(u32 vaddr, u64 value) {
MemoryWrite32_Big(vaddr + 4, u32(value));
MemoryWrite32_Big(vaddr, u32(value >> 32));
}
/*
void InterpreterFallback(u32 pc, size_t num_instructions)
*/
void InterpreterFallback(u32 pc, size_t num_instructions) override {
// This is never called in practice.
std::terminate();
}
/*
void CallSVC(u32 swi)
*/
void CallSVC(u32 swi) override {
// Do something.
}
/*
u32 Execute(u32 pc, bool is_big)
Executes from specific PC
Returns: R0 register
*/
u32 Execute(u32 pc, bool is_big) {
cpu->Regs()[14] = os_offset + ROUTE_EXIT;
cpu->Regs()[15] = pc & ~1;
cpu->SetCpsr(((pc & 1) ? 0x30 : 0x1d0) | (is_big ? 0x200 : 0x0));
Dynarmic::HaltReason returnCode = cpu->Run();
cpu->ClearCache();
return cpu->Regs()[0];
}
/*
u32 Execute(u32 pc)
Executes from specific PC
Returns: R0 register
*/
u32 Execute(u32 pc) {
return Execute(pc, false);
}
/*
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception)
*/
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
std::cerr << std::format("decoder: exception at {:#08x}: ", pc);
std::cerr << std::format("{}", (int)exception) << std::endl;
std::terminate();
// Do something.
}
/*
void AddTicks(u64 ticks)
*/
void AddTicks(u64 ticks) override {}
/*
GetTicksRemaining()
*/
u64 GetTicksRemaining() override {return 8000;}
};
class Emulator {
private:
std::unique_ptr<Dynarmic::A32::Jit> cpu;
std::unique_ptr<Dynarmic::ExclusiveMonitor> mon;
public:
MyEmulator env;
Emulator() {
Dynarmic::A32::UserConfig user_config;
user_config.callbacks = &env;
user_config.enable_cycle_counting = false;
user_config.arch_version = Dynarmic::A32::ArchVersion::v7;
mon = std::make_unique<Dynarmic::ExclusiveMonitor>(1);
user_config.global_monitor = mon.get();
cpu = std::make_unique<Dynarmic::A32::Jit>(user_config);
env.cpu = cpu.get();
};
};

18
utils/fls2bin.py Normal file
View file

@ -0,0 +1,18 @@
from construct import *
import sys
import os
FlashLoader = Struct(
"magic" / Const(b"FL"),
"count" / Int32ul,
"regions" / Array(this.count, Struct(
"start" / Int32ul,
"size" / Int32ul,
"data" / Bytes(this.size)
))
)
if __name__ == "__main__":
fls = FlashLoader.parse_file(sys.argv[1])
for reg in fls.regions:
open(os.path.splitext(sys.argv[1])[0] + f"_0x{reg.start:08x}.bin", "wb").write(reg.data)

141
utils/packer_template.py Normal file
View file

@ -0,0 +1,141 @@
import struct
from keystone import *
# ("file_name", 0xOFFSET)
files = [
]
asm = Ks(KS_ARCH_ARM, KS_MODE_ARM)
asm_t = Ks(KS_ARCH_ARM, KS_MODE_THUMB)
asm_be = Ks(KS_ARCH_ARM, KS_MODE_ARM + KS_MODE_BIG_ENDIAN)
asm_be_t = Ks(KS_ARCH_ARM, KS_MODE_THUMB + KS_MODE_BIG_ENDIAN)
# Override addresses
MEMCPY_ADDRESS = []
MALLOC_ADDRESS = []
FREE_ADDRESS = []
MEMSET_ADDRESS = []
CALLOC_ADDRESS = []
REALLOC_ADDRESS = []
MEMMOVE_ADDRESS = []
MEMCPY_ADDRESS_T = []
MALLOC_ADDRESS_T = []
FREE_ADDRESS_T = []
MEMSET_ADDRESS_T = []
CALLOC_ADDRESS_T = []
REALLOC_ADDRESS_T = []
MEMMOVE_ADDRESS_T = []
ROUTE_INIT_REGION = 0x0
ROUTE_MALLOC = 0x4
ROUTE_MEMCPY = 0x8
ROUTE_FREE = 0xc
ROUTE_MEMSET = 0x10
ROUTE_CALLOC = 0x14
ROUTE_REALLOC = 0x18
ROUTE_MEMMOVE = 0x1c
ROUTE_EXIT = 0x20
ROUTE_MALLOC_T = 0x24
ROUTE_MEMCPY_T = 0x2c
ROUTE_FREE_T = 0x34
ROUTE_MEMSET_T = 0x3c
ROUTE_CALLOC_T = 0x44
ROUTE_REALLOC_T = 0x4c
ROUTE_MEMMOVE_T = 0x54
ROUTE_EXIT_T = 0x5c
ROUTE_MEM_BASE = 0x0
if __name__ == "__main__":
f = open("(OUTPUT_FILE).fls", "wb")
# 01 - Files
f.write(b"FL")
f.write(struct.pack("<L", 0))
for fl in files:
flashName, offset = fl
flash = open(flashName, "rb").read()
f.write(struct.pack("<LL", offset, len(flash))+flash)
tCount = len(files)
# 02 - Patching (ARM)
for a in MALLOC_ADDRESS:
b, c = asm.asm(f"b 0x{ROUTE_MEM_BASE + ROUTE_MALLOC:02x}", a, True)
f.write(struct.pack("<LL", a, len(b)) + b)
tCount += 1
for a in MEMCPY_ADDRESS:
b, c = asm.asm(f"b 0x{ROUTE_MEM_BASE + ROUTE_MEMCPY:02x}", a, True)
f.write(struct.pack("<LL", a, len(b)) + b)
tCount += 1
for a in FREE_ADDRESS:
b, c = asm.asm(f"b 0x{ROUTE_MEM_BASE + ROUTE_FREE:02x}", a, True)
f.write(struct.pack("<LL", a, len(b)) + b)
tCount += 1
for a in MEMSET_ADDRESS:
b, c = asm.asm(f"b 0x{ROUTE_MEM_BASE + ROUTE_MEMSET:02x}", a, True)
f.write(struct.pack("<LL", a, len(b)) + b)
tCount += 1
for a in CALLOC_ADDRESS:
b, c = asm.asm(f"b 0x{ROUTE_MEM_BASE + ROUTE_CALLOC:02x}", a, True)
f.write(struct.pack("<LL", a, len(b)) + b)
tCount += 1
for a in REALLOC_ADDRESS:
b, c = asm.asm(f"b 0x{ROUTE_MEM_BASE + ROUTE_REALLOC:02x}", a, True)
f.write(struct.pack("<LL", a, len(b)) + b)
tCount += 1
for a in MEMMOVE_ADDRESS:
b, c = asm.asm(f"b 0x{ROUTE_MEM_BASE + ROUTE_MEMMOVE:02x}", a, True)
f.write(struct.pack("<LL", a, len(b)) + b)
tCount += 1
# 03 - Patching thumb
for a in MALLOC_ADDRESS_T:
b, c = asm_t.asm("push {r4-r7, lr}; " + f"blx 0x{ROUTE_MEM_BASE + ROUTE_MALLOC:02x}; " + "pop {r4-r7, pc}", a, True)
f.write(struct.pack("<LL", a, len(b)) + b)
tCount += 1
for a in MEMCPY_ADDRESS_T:
b, c = asm_t.asm("push {r4-r7, lr}; " + f"blx 0x{ROUTE_MEM_BASE + ROUTE_MEMCPY:02x}; " + "pop {r4-r7, pc}", a, True)
f.write(struct.pack("<LL", a, len(b)) + b)
tCount += 1
for a in FREE_ADDRESS_T:
b, c = asm_t.asm("push {r4-r7, lr}; " + f"blx 0x{ROUTE_MEM_BASE + ROUTE_FREE:02x}; " + "pop {r4-r7, pc}", a, True)
f.write(struct.pack("<LL", a, len(b)) + b)
tCount += 1
for a in MEMSET_ADDRESS_T:
b, c = asm_t.asm("push {r4-r7, lr}; " + f"blx 0x{ROUTE_MEM_BASE + ROUTE_MEMSET:02x}; " + "pop {r4-r7, pc}", a, True)
f.write(struct.pack("<LL", a, len(b)) + b)
tCount += 1
for a in CALLOC_ADDRESS_T:
b, c = asm_t.asm("push {r4-r7, lr}; " + f"blx 0x{ROUTE_MEM_BASE + ROUTE_CALLOC:02x}; " + "pop {r4-r7, pc}", a, True)
f.write(struct.pack("<LL", a, len(b)) + b)
tCount += 1
for a in REALLOC_ADDRESS_T:
b, c = asm_t.asm("push {r4-r7, lr}; " + f"blx 0x{ROUTE_MEM_BASE + ROUTE_REALLOC:02x}; " + "pop {r4-r7, pc}", a, True)
f.write(struct.pack("<LL", a, len(b)) + b)
tCount += 1
for a in MEMMOVE_ADDRESS_T:
b, c = asm_t.asm("push {r4-r7, lr}; " + f"blx 0x{ROUTE_MEM_BASE + ROUTE_MEMMOVE:02x}; " + "pop {r4-r7, pc}", a, True)
f.write(struct.pack("<LL", a, len(b)) + b)
tCount += 1
f.seek(2)
f.write(struct.pack("<L", tCount))
f.close()