Initial work

This commit is contained in:
jonko0493 2025-05-06 12:48:20 -07:00
parent e9bef0c403
commit 593a4ba58c
14 changed files with 576 additions and 0 deletions

View File

@ -0,0 +1,38 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile
{
"name": "Existing Dockerfile",
"build": {
// Sets the run context to one level up instead of the .devcontainer folder.
"context": "..",
// Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
"dockerfile": "../Dockerfile"
},
"runArgs": ["--privileged", "--add-host=host.docker.internal:host-gateway"],
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.cpptools",
"ms-vscode.cpptools-extension-pack",
"ms-azuretools.vscode-docker",
"ms-python.python",
"ms-vscode.hexeditor"
]
}
},
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [3333],
// Uncomment the next line to run commands after the container is created.
"postCreateCommand": "python3 ./build.py -c -b"
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "devcontainer"
}

7
.gitignore vendored
View File

@ -1,3 +1,10 @@
*.nds
build/
*_readelf.txt
.ninja_deps
.ninja_log
build.ninja
# Prerequisites
*.d

36
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,36 @@
{
"configurations": [
{
"name": "NDS (Linux)",
"includePath": [
"${workspaceFolder}",
"${workspaceFolder}/libs/**",
"/opt/wonderful/toolchain/gcc-arm-none-eabi/include/**",
"/opt/wonderful/thirdparty/blocksds/core/libs/dswifi/include/**",
"/opt/wonderful/thirdparty/blocksds/core/libs/libnds/include/**",
"/opt/wonderful/thirdparty/blocksds/core/libs/maxmod/include"
],
"defines": [
"_DEBUG",
"_UNICODE",
"ARM7",
"ARM9"
],
"browse": {
"path": [
"${workspaceFolder}",
"${workspaceFolder}/libs/**",
"/opt/wonderful/toolchain/gcc-arm-none-eabi/include/**",
"/opt/wonderful/thirdparty/blocksds/core/libs/libnds/include/**"
],
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
},
"cStandard": "c11",
"cppStandard": "c++20",
"compilerPath": "/opt/wonderful/toolchain/gcc-arm-none-eabi/bin/arm-none-eabi-g++",
"intelliSenseMode": "gcc-arm"
}
],
"version": 4
}

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"files.associations": {
"memory": "cpp",
"unistd.h": "c"
}
}

14
Dockerfile Normal file
View File

@ -0,0 +1,14 @@
FROM skylyrac/blocksds:slim-latest
RUN apt update && \
apt install python3-full python3-pip ninja-build wget libxxf86vm1 libxfixes3 libxi6 libxkbcommon-x11-0 libsm6 libgl1 libopengl-dev unzip -y
RUN git clone https://github.com/AntonioND/architectds.git && \
cd ./architectds && \
pip3 install wheel setuptools --break-system-packages && \
python3 setup.py bdist_wheel && \
pip3 install dist/architectds-*-py3-none-any.whl --break-system-packages && \
cd ../ && \
rm -rf architectds
ENTRYPOINT [ "/bin/bash" ]

32
build.py Normal file
View File

@ -0,0 +1,32 @@
from architectds import *
arm9 = Arm9Binary(
sourcedirs=['src/arm9'],
libs=['nds9'],
cxxflags='-Werror -Wno-psabi -fpermissive -std=gnu++20',
libdirs=['${BLOCKSDS}/libs/libnds']
)
arm9.generate_elf()
example_lib = Arm9DynamicLibrary(
name='example',
main_binary=arm9,
sourcedirs=['src/lib_example'],
cxxflags='-Werror -Wno-psabi -fpermissive -std=gnu++20',
libdirs=['${BLOCKSDS}/libs/libnds']
)
example_lib.generate_dsl()
nitrofs = NitroFS()
nitrofs.add_arm9_dsl(example_lib)
nitrofs.generate_image()
nds = NdsRom(
binaries=[arm9, example_lib, nitrofs],
nds_path='dyn_lib_bug_min_repro.nds',
game_title='Dynamic Library',
game_subtitle='Bug Repro'
)
nds.generate_nds()
nds.run_command_line_arguments()

84
src/arm9/loader.cpp Normal file
View File

@ -0,0 +1,84 @@
#include "loader.hpp"
typedef bool (fnIsFlagSet)(Save *, u16);
typedef void (fnSetFlag)(Save *, u16);
typedef void (fnClearFlag)(Save *, u16);
typedef u8 (fnGetGlobal)(Save *, u16);
typedef void (fnSetGlobal)(Save *, u16, u8);
typedef int (fnGetMemUsed)();
typedef int (fnGetMemFree)();
fnIsFlagSet *isFlagSetFn;
fnSetFlag *setFlagFn;
fnClearFlag *clearFlagFn;
fnGetGlobal *getGlobalFn;
fnSetGlobal *setGlobalFn;
fnGetMemUsed *getMemUsedFn;
fnGetMemFree *getMemFreeFn;
namespace Loader
{
void *exampleLib;
void loadExampleLib()
{
exampleLib = dlopen("nitro:/dsl/example.dsl", RTLD_NOW | RTLD_LOCAL);
isFlagSetFn = (fnIsFlagSet *)dlsym(exampleLib, "isFlagSet");
setFlagFn = (fnSetFlag *)dlsym(exampleLib, "setFlag");
clearFlagFn = (fnClearFlag *)dlsym(exampleLib, "clearFlag");
getGlobalFn = (fnGetGlobal *)dlsym(exampleLib, "getGlobal");
setGlobalFn = (fnSetGlobal *)dlsym(exampleLib, "setGlobal");
getMemUsedFn = (fnGetMemUsed *)dlsym(exampleLib, "getMemUsed");
getMemFreeFn = (fnGetMemFree *)dlsym(exampleLib, "getMemFree");
}
void unloadExmapleLib()
{
dlclose(exampleLib);
}
}
namespace SaveExtensions
{
bool isFlagSet(Save *save, u16 flag)
{
return isFlagSetFn(save, flag);
}
void setFlag(Save *save, u16 flag)
{
setFlagFn(save, flag);
}
void clearFlag(Save *save, u16 flag)
{
clearFlagFn(save, flag);
}
u8 getGlobal(Save *save, u16 global)
{
return getGlobalFn(save, global);
}
void setGlobal(Save *save, u16 global, u8 value)
{
setGlobalFn(save, global, value);
}
}
namespace Debug
{
int getMemUsed()
{
return getMemUsedFn();
}
int getMemFree()
{
return getMemFreeFn();
}
}

26
src/arm9/loader.hpp Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include <dlfcn.h>
#include "save.hpp"
namespace Loader
{
void loadExampleLib();
void unloadExmapleLib();
}
namespace SaveExtensions
{
bool isFlagSet(Save *save, u16 flag);
void setFlag(Save *save, u16 flag);
void clearFlag(Save *save, u16 flag);
u8 getGlobal(Save *save, u16 global);
void setGlobal(Save *save, u16 global, u8 value);
}
namespace Debug
{
int getMemUsed();
int getMemFree();
}

73
src/arm9/main.cpp Normal file
View File

@ -0,0 +1,73 @@
#include "main.hpp"
void wait_forever(void)
{
while (1)
swiWaitForVBlank();
}
int main()
{
defaultExceptionHandler();
PrintConsole topScreen;
PrintConsole bottomScreen;
videoSetMode(MODE_0_2D);
videoSetModeSub(MODE_0_2D);
vramSetBankA(VRAM_A_MAIN_BG);
vramSetBankC(VRAM_C_SUB_BG);
consoleInit(&topScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, true, true);
consoleInit(&bottomScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, false, true);
consoleSelect(&topScreen);
bool init_ok = nitroFSInit(NULL);
if (!init_ok)
{
perror("nitroFSInit()");
wait_forever();
}
// Initialize FAT for save files
if (!fatInitDefault())
{
perror("fatInitDefault()");
wait_forever();
}
swiWaitForVBlank();
printf("Creating save file...\n");
save = new Save();
printf("Loading example library...");
Loader::loadExampleLib();
SaveExtensions::setFlag(save, 5);
if (SaveExtensions::isFlagSet(save, 5))
{
printf("Flag 5 was set.");
}
SaveExtensions::setGlobal(save, 5, 20);
printf("Global 5 was set to %d", SaveExtensions::getGlobal(save, 5));
printf("Mem used: %d\nMem free: %d", Debug::getMemUsed(), Debug::getMemFree());
printf("Unloading example lib...");
Loader::unloadExmapleLib();
printf("Press START to exit to loader\n");
while (1)
{
swiWaitForVBlank();
scanKeys();
if (keysHeld() & KEY_START)
break;
}
return 0;
}

24
src/arm9/main.hpp Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <memory>
#include <string>
#include <vector>
#include <iostream>
#include <regex>
#include <fat.h>
#include <filesystem.h>
#include <nds.h>
#include <nds/arm9/dldi.h>
#include "loader.hpp"
#include "save.hpp"
Save *save;
int main();
void update();
void render();

106
src/arm9/save.cpp Normal file
View File

@ -0,0 +1,106 @@
#include "save.hpp"
// Make things unnecessarily complicated so we can use C++ stuff lol
void debugLog(string str)
{
printf(format("{}\n", str).c_str());
}
string SaveManager::getSavePath()
{
string defaultDrive = string(fatGetDefaultDrive());
if (defaultDrive == "")
{
defaultDrive = "fat:/";
}
string path = format("{}into-the-dream-spring.sav", defaultDrive);
return path;
}
void SaveManager::initializeSave(string path)
{
debugLog("Initializing save file...");
FILE *newSaveFile = fopen(path.c_str(), "wb");
u8 *cleanData = new u8[NUM_SAVE_SLOTS * SAVE_SLOT_SIZE];
fwrite(cleanData, sizeof(u8), NUM_SAVE_SLOTS * SAVE_SLOT_SIZE, newSaveFile);
fclose(newSaveFile);
delete cleanData;
}
void SaveManager::checkSave(string path)
{
if (access(path.c_str(), F_OK) != 0)
{
debugLog("Save file does not exist; initializing...");
initializeSave(path);
return;
}
FILE *saveFile = fopen(path.c_str(), "rb");
fseek(saveFile, 0, SEEK_END);
long length = ftell(saveFile);
if (length != NUM_SAVE_SLOTS * SAVE_SLOT_SIZE)
{
debugLog(format("Save file was wrong size {:d} bytes; re-initializing...", length));
fclose(saveFile);
initializeSave(path);
}
}
void SaveManager::save(int slot, Save *save)
{
string path = getSavePath();
debugLog(format("Attempting to save to {}...", path));
checkSave(path);
FILE *saveFile = fopen(path.c_str(), "rb+");
fseek(saveFile, slot * SAVE_SLOT_SIZE, SEEK_SET);
int flagWrite = fwrite(save->getSaveSlot()->flags, sizeof(u8), 512, saveFile);
debugLog(format("Wrote {:d} bytes of flags", flagWrite));
int globalWrite = fwrite(save->getSaveSlot()->globals, sizeof(u8), 512, saveFile);
debugLog(format("Wrote {:d} bytes of globals", globalWrite));
fclose(saveFile);
debugLog("Saved!");
}
Save *SaveManager::load(int slot)
{
string path = getSavePath();
debugLog(format("Attempting to load from {}...", path));
checkSave(path);
FILE *saveFile = fopen(path.c_str(), "rb");
fseek(saveFile, slot * SAVE_SLOT_SIZE, SEEK_SET);
Save *save = new Save();
int flagRead = fread(save->getSaveSlot()->flags, sizeof(u8), 512, saveFile);
debugLog(format("Read {:d} bytes of flags", flagRead));
int globalRead = fwrite(save->getSaveSlot()->globals, sizeof(u8), 512, saveFile);
debugLog(format("Read {:d} bytes of globals", globalRead));
fclose(saveFile);
return save;
}
Save::Save()
{
this->saveSlot = new SaveSlot();
}
Save::Save(SaveSlot *save)
{
this->saveSlot = save;
}
Save::~Save()
{
delete saveSlot;
}
SaveSlot *Save::getSaveSlot()
{
return this->saveSlot;
}

53
src/arm9/save.hpp Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#include <format>
#include <string>
#include <nds.h>
#include <nds/arm9/dldi.h>
#include <fat.h>
using namespace std;
#define NUM_SAVE_SLOTS 30
#define SAVE_SLOT_SIZE 1024
struct SaveSlot
{
u8 *flags;
u8 *globals;
SaveSlot()
{
flags = new u8[512];
globals = new u8[512];
}
~SaveSlot()
{
delete flags;
delete globals;
}
};
class Save
{
private:
SaveSlot *saveSlot;
public:
Save();
Save(SaveSlot *save);
~Save();
SaveSlot *getSaveSlot();
};
class SaveManager
{
private:
static string getSavePath();
static void initializeSave(string path);
static void checkSave(string path);
public:
static void save(int slot, Save *save);
static Save *load(int slot);
};

37
src/lib_example/debug.c Normal file
View File

@ -0,0 +1,37 @@
#include <nds.h>
#include <malloc.h>
#include <unistd.h>
#define SYM_PUBLIC __attribute__((visibility ("default")))
extern u8 *fake_heap_end; // current heap start
extern u8 *fake_heap_start; // current heap end
u8* getHeapStart()
{
return fake_heap_start;
}
u8* getHeapEnd()
{
return (u8*)sbrk(0);
}
u8* getHeapLimit()
{
return fake_heap_end;
}
// returns the amount of used memory in bytes
SYM_PUBLIC int getMemUsed()
{
struct mallinfo mi = mallinfo();
return mi.uordblks;
}
// returns the amount of free memory in bytes
SYM_PUBLIC int getMemFree()
{
struct mallinfo mi = mallinfo();
return mi.fordblks + (getHeapLimit() - getHeapEnd());
}

View File

@ -0,0 +1,40 @@
#include "../arm9/save.hpp"
#define SYM_PUBLIC __attribute__((visibility ("default")))
void setOrClearFlag(Save *save, u16 flag, bool set)
{
flag--;
int byte = flag >> 3;
int bit = flag & 7;
if (set)
{
save->getSaveSlot()->flags[byte] = save->getSaveSlot()->flags[byte] | (1 << bit);
}
else
{
save->getSaveSlot()->flags[byte] = save->getSaveSlot()->flags[byte] & ~(1 << bit);
}
}
SYM_PUBLIC bool isFlagSet(Save *save, u16 flag)
{
flag--;
return (save->getSaveSlot()->flags[flag >> 3] >> (flag & 7)) & 1;
}
SYM_PUBLIC void setFlag(Save *save, u16 flag)
{
setOrClearFlag(save, flag, true);
}
SYM_PUBLIC void clearFlag(Save *save, u16 flag)
{
setOrClearFlag(save, flag, false);
}
SYM_PUBLIC u8 getGlobal(Save *save, u16 global)
{
return save->getSaveSlot()->globals[--global];
}
SYM_PUBLIC void setGlobal(Save *save, u16 global, u8 value)
{
save->getSaveSlot()->globals[--global] = value;
}