0.6.5 beta

This commit is contained in:
JeffRuLz 2019-06-09 13:46:10 -05:00 committed by GitHub
parent 0e938a3f1f
commit 2ab11e7ebc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 3477 additions and 116 deletions

152
Makefile
View File

@ -1,26 +1,18 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/ds_rules
export TARGET := $(shell basename $(CURDIR))
export TOPDIR := $(CURDIR)
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
# MAXMOD_SOUNDBANK contains a directory of music and sound effect files
#---------------------------------------------------------------------------------
TARGET := $(shell basename $(CURDIR))
BUILD := build
SOURCES := src
DATA := data
INCLUDES := include
# specify a directory which contains the nitro filesystem
# this is relative to the Makefile
NITRO_FILES :=
# These set the information text in the nds file
GAME_TITLE := TMFH
GAME_SUBTITLE1 := Title Manager for HiyaCFW
GAME_SUBTITLE2 := JeffRuLz
@ -28,76 +20,7 @@ GAME_SUBTITLE2 := JeffRuLz
GAME_CODE := TMFH
GAME_LABEL := TITLEMANFH
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -mthumb -mthumb-interwork -march=armv5te -mtune=arm946e-s
CFLAGS := -g -Wall -O2\
-fomit-frame-pointer\
-ffast-math \
$(ARCH)
CFLAGS += $(INCLUDE) -DARM9
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project (order is important)
#---------------------------------------------------------------------------------
LIBS := -lfat -lnds9
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBNDS)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
include $(DEVKITARM)/ds_rules
icons := $(wildcard *.bmp)
@ -108,43 +31,40 @@ else
export GAME_ICON := $(CURDIR)/icon.bmp
endif
endif
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
@echo built ... $(notdir $@)
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).elf $(TARGET).nds
.PHONY: checkarm7 checkarm9 clean
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).nds : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
all: checkarm7 checkarm9 $(TARGET).nds
$(OUTPUT).nds: $(OUTPUT).elf
@ndstool -u "00030004" \
-g "$(GAME_CODE)" "00" "$(GAME_LABEL)" \
-c $(OUTPUT).nds -9 $(OUTPUT).elf \
-b $(GAME_ICON) "$(GAME_TITLE);$(GAME_SUBTITLE1);$(GAME_SUBTITLE2)"
#---------------------------------------------------------------------------------
%.bin.o : %.bin
checkarm7:
$(MAKE) -C arm7
#---------------------------------------------------------------------------------
@echo $(notdir $<)
$(bin2o)
-include $(DEPSDIR)/*.d
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------
checkarm9:
$(MAKE) -C arm9
#---------------------------------------------------------------------------------
$(TARGET).nds : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET).elf
ndstool -c $(TARGET).nds -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \
-u "00030004" \
-g "$(GAME_CODE)" "00" "$(GAME_LABEL)" \
-b $(GAME_ICON) "$(GAME_TITLE);$(GAME_SUBTITLE1);$(GAME_SUBTITLE2)" \
$(_ADDFILES)
#---------------------------------------------------------------------------------
arm7/$(TARGET).elf:
$(MAKE) -C arm7
#---------------------------------------------------------------------------------
arm9/$(TARGET).elf:
$(MAKE) -C arm9
#---------------------------------------------------------------------------------
clean:
$(MAKE) -C arm9 clean
$(MAKE) -C arm7 clean
rm -f $(TARGET).nds $(TARGET).arm7 $(TARGET).arm9

127
arm7/Makefile Normal file
View File

@ -0,0 +1,127 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/ds_rules
#---------------------------------------------------------------------------------
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
# DATA is a list of directories containing binary files
# all directories are relative to this makefile
#---------------------------------------------------------------------------------
BUILD := build
SOURCES := src
INCLUDES := include build
DATA :=
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -mthumb-interwork
CFLAGS := -g -Wall -O2\
-mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\
-ffast-math \
$(ARCH)
CFLAGS += $(INCLUDE) -DARM7
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -fno-rtti
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=ds_arm7.specs -g $(ARCH) -Wl,--nmagic -Wl,-Map,$(notdir $*).map
#LIBS := -ldswifi7 -lmm7 -lnds7
LIBS := -lnds7
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBNDS)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export ARM7ELF := $(CURDIR)/$(TARGET).elf
export DEPSDIR := $(CURDIR)/$(BUILD)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) *.elf
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(ARM7ELF) : $(OFILES)
@echo linking $(notdir $@)
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

98
arm7/src/main.c Normal file
View File

@ -0,0 +1,98 @@
/*---------------------------------------------------------------------------------
default ARM7 core
Copyright (C) 2005 - 2010
Michael Noland (joat)
Jason Rogers (dovoto)
Dave Murphy (WinterMute)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
---------------------------------------------------------------------------------*/
#include <nds.h>
//#include <dswifi7.h>
//#include <maxmod7.h>
//---------------------------------------------------------------------------------
void VblankHandler(void) {
//---------------------------------------------------------------------------------
// Wifi_Update();
}
//---------------------------------------------------------------------------------
void VcountHandler() {
//---------------------------------------------------------------------------------
inputGetAndSend();
}
volatile bool exitflag = false;
//---------------------------------------------------------------------------------
void powerButtonCB() {
//---------------------------------------------------------------------------------
exitflag = true;
}
//---------------------------------------------------------------------------------
int main() {
//---------------------------------------------------------------------------------
// clear sound registers
dmaFillWords(0, (void*)0x04000400, 0x100);
REG_SOUNDCNT |= SOUND_ENABLE;
writePowerManagement(PM_CONTROL_REG, ( readPowerManagement(PM_CONTROL_REG) & ~PM_SOUND_MUTE ) | PM_SOUND_AMP );
powerOn(POWER_SOUND);
readUserSettings();
ledBlink(0);
irqInit();
// Start the RTC tracking IRQ
initClockIRQ();
fifoInit();
touchInit();
// mmInstall(FIFO_MAXMOD);
SetYtrigger(80);
// installWifiFIFO();
installSoundFIFO();
installSystemFIFO();
irqSet(IRQ_VCOUNT, VcountHandler);
irqSet(IRQ_VBLANK, VblankHandler);
irqEnable( IRQ_VBLANK | IRQ_VCOUNT | IRQ_NETWORK);
setPowerButtonCB(powerButtonCB);
// Keep the ARM7 mostly idle
while (!exitflag) {
if ( 0 == (REG_KEYINPUT & (KEY_SELECT | KEY_START | KEY_L | KEY_R))) {
exitflag = true;
}
swiWaitForVBlank();
}
return 0;
}

127
arm9/Makefile Normal file
View File

@ -0,0 +1,127 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/ds_rules
#---------------------------------------------------------------------------------
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
# DATA is a list of directories containing binary files
# all directories are relative to this makefile
#---------------------------------------------------------------------------------
BUILD := build
SOURCES := src
INCLUDES := include
DATA :=
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -mthumb -mthumb-interwork
CFLAGS := -g -Wall -O2\
-march=armv5te -mtune=arm946e-s -fomit-frame-pointer\
-ffast-math \
$(ARCH)
CFLAGS += $(INCLUDE) -DARM9
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH) -march=armv5te -mtune=arm946e-s
LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS := -lfat -lnds9
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBNDS)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export ARM9ELF := $(CURDIR)/$(TARGET).elf
export DEPSDIR := $(CURDIR)/$(BUILD)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) *.elf *.nds* *.bin
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(ARM9ELF) : $(OFILES)
@echo linking $(notdir $@)
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPSDIR)/*.d
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

267
arm9/src/app.c Normal file
View File

@ -0,0 +1,267 @@
#include "app.h"
#include "main.h"
#include "storage.h"
#include <nds.h>
#include <malloc.h>
#include <stdio.h>
static void _getTitle(tNDSBanner* b, char* out, bool full)
{
int lang = PersonalData->language;
//not japanese or chinese
if (lang == 0 || lang == 6)
lang = 1;
for (int i = 0; i < 128; i++)
{
u16 c = b->titles[lang][i];
//remove accents
if (c == 0x00F3)
c = 'o';
if (c == 0x00E1)
c = 'a';
out[i] = (char)c;
if (!full && out[i] == '\n')
{
out[i] = '\0';
break;
}
}
out[128] = '\0';
}
bool getAppTitle(char* fpath, char* out)
{
FILE* f = fopen(fpath, "rb");
if (!f)
{
return false;
}
else
{
tNDSHeader* header = (tNDSHeader*)malloc(sizeof(tNDSHeader));
tNDSBanner* banner = (tNDSBanner*)malloc(sizeof(tNDSBanner));
fseek(f, 0, SEEK_SET);
fread(header, sizeof(tNDSHeader), 1, f);
fseek(f, header->bannerOffset, SEEK_SET);
fread(banner, sizeof(tNDSBanner), 1, f);
_getTitle(banner, out, false);
free(banner);
free(header);
}
fclose(f);
return true;
}
bool getAppFullTitle(char* fpath, char* out)
{
FILE* f = fopen(fpath, "rb");
if (!f)
{
return false;
}
else
{
tNDSHeader* header = (tNDSHeader*)malloc(sizeof(tNDSHeader));
tNDSBanner* banner = (tNDSBanner*)malloc(sizeof(tNDSBanner));
fseek(f, 0, SEEK_SET);
fread(header, sizeof(tNDSHeader), 1, f);
fseek(f, header->bannerOffset, SEEK_SET);
fread(banner, sizeof(tNDSBanner), 1, f);
_getTitle(banner, out, true);
free(banner);
free(header);
}
fclose(f);
return true;
}
bool getAppLabel(char* fpath, char* out)
{
FILE* f = fopen(fpath, "rb");
if (!f)
{
return false;
}
else
{
tNDSHeader* header = (tNDSHeader*)malloc(sizeof(tNDSHeader));
fseek(f, 0, SEEK_SET);
fread(header, sizeof(tNDSHeader), 1, f);
sprintf(out, "%.12s", header->gameTitle);
free(header);
}
fclose(f);
return true;
}
bool getGameCode(char* fpath, char* out)
{
FILE* f = fopen(fpath, "rb");
if (!f)
{
return false;
}
else
{
tNDSHeader* header = (tNDSHeader*)malloc(sizeof(tNDSHeader));
fseek(f, 0, SEEK_SET);
fread(header, sizeof(tNDSHeader), 1, f);
sprintf(out, "%.4s", header->gameCode);
free(header);
}
fclose(f);
return true;
}
bool getTid(char* fpath, u32* low, u32* high)
{
FILE* f = fopen(fpath, "rb");
if (!f)
{
return false;
}
else
{
tDSiHeader* header = (tDSiHeader*)malloc(sizeof(tDSiHeader));
fseek(f, 0, SEEK_SET);
fread(header, sizeof(tDSiHeader), 1, f);
if (low != NULL)
*low = header->tid_low;
if (high != NULL)
*high = header->tid_high;
free(header);
}
fclose(f);
return true;
}
void printAppInfo(char const* path)
{
clearScreen(&topScreen);
if (!path) return;
tDSiHeader* header = (tDSiHeader*)malloc(sizeof(tDSiHeader));
tNDSBanner* banner = (tNDSBanner*)malloc(sizeof(tNDSBanner));
FILE* f = fopen(path, "rb");
if (f)
{
if (fread(header, sizeof(tDSiHeader), 1, f) != 1)
{
iprintf("Could not read dsi header.\n");
}
else
{
fseek(f, header->ndshdr.bannerOffset, SEEK_SET);
if (fread(banner, sizeof(tNDSBanner), 1, f) != 1)
{
iprintf("Could not read banner.\n");
}
else
{
//proper title
{
char gameTitle[128+1];
_getTitle(banner, gameTitle, true);
iprintf("%s\n\n", gameTitle);
}
//file size
{
iprintf("Size: ");
printBytes(getFileSize(f));
iprintf("\n");
}
iprintf("Label: %.12s\n", header->ndshdr.gameTitle);
iprintf("Game Code: %.4s\n", header->ndshdr.gameCode);
//system type
{
iprintf("Unit Code: ");
switch (header->ndshdr.unitCode)
{
case 0: iprintf("NDS"); break;
case 2: iprintf("NDS+DSi"); break;
case 3: iprintf("DSi"); break;
default: iprintf("unknown");
}
iprintf("\n");
}
//application type
{
iprintf("Program Type: ");
switch (header->ndshdr.reserved1[7])
{
case 0x3: iprintf("Normal"); break;
case 0xB: iprintf("Sys"); break;
case 0xF: iprintf("Debug/Sys"); break;
default: iprintf("unknown");
}
iprintf("\n");
}
//DSi title ids
{
if (header->tid_high == 0x00030004 ||
header->tid_high == 0x00030005 ||
header->tid_high == 0x00030015 ||
header->tid_high == 0x00030017 ||
header->tid_high == 0x00030000)
{
iprintf("Title ID: %08x %08x\n", (unsigned int)header->tid_high, (unsigned int)header->tid_low);
}
}
//print full file path
iprintf("\n%s\n", path);
}
}
}
fclose(f);
free(banner);
free(header);
}

17
arm9/src/app.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef APP_H
#define APP_H
#include <nds/ndstypes.h>
bool getAppTitle(char* fpath, char* out);
bool getAppFullTitle(char* fpath, char* out);
bool getAppLabel(char* fpath, char* out);
bool getGameCode(char* fpath, char* out);
bool getTid(char* fpath, u32* low, u32* high);
void printAppInfo(char const* path);
#endif

252
arm9/src/backupmenu.c Normal file
View File

@ -0,0 +1,252 @@
#include "main.h"
#include "menu.h"
#include "storage.h"
#include "message.h"
#include <dirent.h>
#include <sys/stat.h>
enum {
BACKUP_MENU_RESTORE,
BACKUP_MENU_DELETE,
BACKUP_MENU_BACK
};
static void generateList(Menu* m);
static int subMenu();
static void restore(Menu* m);
static bool delete(Menu* m);
void backupMenu()
{
Menu* m = newMenu();
generateList(m);
//no files found
if (m->itemCount <= 0)
{
messageBox("No backups found.\n");
}
else
{
while (1)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
{
if (m->changePage != 0)
generateList(m);
printMenu(m);
}
if (keysDown() & KEY_B || m->itemCount <= 0)
break;
else if (keysDown() & KEY_A)
{
switch (subMenu())
{
case BACKUP_MENU_RESTORE:
restore(m);
break;
case BACKUP_MENU_DELETE:
{
if (delete(m))
{
resetMenu(m);
generateList(m);
}
}
break;
}
printMenu(m);
}
}
}
freeMenu(m);
}
static int subMenu()
{
int result = -1;
Menu* m = newMenu();
addMenuItem(m, "Restore", NULL);
addMenuItem(m, "Delete", NULL);
addMenuItem(m, "Back - [B]", NULL);
printMenu(m);
while (1)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
printMenu(m);
if (keysDown() & KEY_B)
break;
else if (keysDown() & KEY_A)
{
result = m->cursor;
break;
}
}
freeMenu(m);
return result;
}
static void generateList(Menu* m)
{
if (!m) return;
//reset menu
clearMenu(m);
m->page += sign(m->changePage);
m->changePage = 0;
bool done = false;
struct dirent* ent;
DIR* dir = opendir(BACKUP_PATH);
if (dir)
{
int count = 0;
while ( (ent = readdir(dir)) && done == false)
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
//current item is not on page
if (count < m->page * ITEMS_PER_PAGE)
count += 1;
else
{
if (m->itemCount >= ITEMS_PER_PAGE)
done = true;
else
{
char* path = (char*)malloc(strlen(BACKUP_PATH) + strlen(ent->d_name) + 8);
sprintf(path, "%s/%s", BACKUP_PATH, ent->d_name);
addMenuItem(m, ent->d_name, path);
free(path);
}
}
}
}
}
closedir(dir);
m->nextPage = done;
if (m->cursor >= m->itemCount)
m->cursor = m->itemCount - 1;
printMenu(m);
}
static void restore(Menu* m)
{
char* fpath = m->items[m->cursor];
bool choice = NO;
{
char str[] = "Are you sure you want to restore\n";
char* msg = (char*)malloc(strlen(str) + strlen(fpath) + 1);
sprintf(msg, "%s%s", str, fpath);
choice = choiceBox(msg);
free(msg);
}
if (choice == YES)
{
if (!fpath)
{
messageBox("Failed to restore backup.\n");
}
else
{
clearScreen(&bottomScreen);
if (!copyDir(fpath, "/title"))
{
messagePrint("\nFailed to restore backup.\n");
}
else
{
messagePrint("\nBackup restored.\n");
}
}
}
}
static bool delete(Menu* m)
{
if (!m) return false;
char* fpath = m->items[m->cursor];
bool result = false;
bool choice = NO;
{
char str[] = "Are you sure you want to delete\n";
char* msg = (char*)malloc(strlen(str) + strlen(fpath) + 1);
sprintf(msg, "%s\n%s", str, fpath);
choice = choiceBox(msg);
free(msg);
}
if (choice == YES)
{
if (!fpath)
{
messageBox("Failed to delete backup.\n");
}
else
{
if (!dirExists(fpath))
{
messageBox("Failed to delete backup.\n");
}
else
{
clearScreen(&bottomScreen);
if (deleteDir(fpath))
{
result = true;
messagePrint("\nBackup deleted.\n");
}
else
{
messagePrint("\nError deleting backup.\n");
}
}
}
}
return result;
}

114
arm9/src/fat12.c Normal file
View File

@ -0,0 +1,114 @@
#include "fat12.h"
#include <string.h>
#include <malloc.h>
static u32 _getClusterSize(u32 sizebytes)
{
if (sizebytes < 573440)
return 512;
else if (sizebytes < 5472256)
return 2048;
else if (sizebytes < 17301504)
return 4096;
else
return 2048;
}
static u16 _getMaxFiles(u32 sizebytes)
{
if (sizebytes < 573440)
return 16;
else
return 256;
}
//wip
static u16 _getFatz(u32 sizebytes)
{
if (sizebytes <= 0x4000) //16 kb
return 1;
else if (sizebytes <= 0x200000) //2 mb
return 3;
else
return 6;
}
bool initFatHeader(FILE* f)
{
if (!f)
return false;
//get size
fseek(f, 0, SEEK_END);
u32 size = ftell(f);
FATHeader* h = (FATHeader*)malloc(sizeof(FATHeader));
h->BS_JmpBoot[0] = 0xE9;
h->BS_JmpBoot[1] = 0;
h->BS_JmpBoot[2] = 0;
h->BS_OEMName[0] = 'M';
h->BS_OEMName[1] = 'S';
h->BS_OEMName[2] = 'W';
h->BS_OEMName[3] = 'I';
h->BS_OEMName[4] = 'N';
h->BS_OEMName[5] = '4';
h->BS_OEMName[6] = '.';
h->BS_OEMName[7] = '1';
h->BPB_BytesPerSec = 512;
h->BPB_SecPerClus = _getClusterSize(size) / h->BPB_BytesPerSec;
h->BPB_RsvdSecCnt = 1;
h->BPB_NumFATs = 2;
h->BPB_RootEntCnt = _getMaxFiles(size) * 2;
h->BPB_TotSec16 = size / h->BPB_BytesPerSec; //
h->BPB_Media = 0xF8;
h->BPB_FATSz16 = _getFatz(size); //
h->BPB_SecPerTrk = 0;
h->BPB_NumHeads = 0;
h->BPB_HiddSec = 0;
h->BPB_TotSec32 = 0;
h->BS_DrvNum = 2;
h->BS_Reserved1 = 0;
h->BS_BootSig = 0x29;
h->BS_VolID = 305419896;
h->BS_VolLab[0] = 'V';
h->BS_VolLab[1] = 'O';
h->BS_VolLab[2] = 'L';
h->BS_VolLab[3] = 'U';
h->BS_VolLab[4] = 'M';
h->BS_VolLab[5] = 'E';
h->BS_VolLab[6] = 'L';
h->BS_VolLab[7] = 'A';
h->BS_VolLab[8] = 'B';
h->BS_VolLab[9] = 'E';
h->BS_VolLab[10] = 'L';
h->BS_FilSysType[0] = 'F';
h->BS_FilSysType[1] = 'A';
h->BS_FilSysType[2] = 'T';
h->BS_FilSysType[3] = '1';
h->BS_FilSysType[4] = '2';
h->BS_FilSysType[5] = ' ';
h->BS_FilSysType[6] = ' ';
h->BS_FilSysType[7] = ' ';
memset(h->BS_BootCode, 0, 448);
h->BS_BootSign = 0xAA55;
fseek(f, 0, SEEK_SET);
fwrite(h, sizeof(FATHeader), 1, f);
free(h);
return true;
}

77
arm9/src/fat12.h Normal file
View File

@ -0,0 +1,77 @@
#ifndef FAT12_H
#define FAT12_H
#include <nds/ndstypes.h>
#include <stdio.h>
//http://elm-chan.org/docs/fat_e.html
#pragma pack(push, 1)
typedef struct
{
u8 BS_JmpBoot[3]; //0x0000
u8 BS_OEMName[8]; //0x0003
u16 BPB_BytesPerSec; //0x000B
u8 BPB_SecPerClus; //0x000D
u16 BPB_RsvdSecCnt; //0x000E
u8 BPB_NumFATs;
u16 BPB_RootEntCnt;
u16 BPB_TotSec16;
u8 BPB_Media;
u16 BPB_FATSz16;
u16 BPB_SecPerTrk;
u16 BPB_NumHeads;
u32 BPB_HiddSec;
u32 BPB_TotSec32;
u8 BS_DrvNum;
u8 BS_Reserved1;
u8 BS_BootSig;
u32 BS_VolID;
u8 BS_VolLab[11];
u8 BS_FilSysType[8];
u8 BS_BootCode[448];
u16 BS_BootSign;
} FATHeader;
#pragma pack(push, 0)
bool initFatHeader(FILE* f);
/*
typedef struct
{
u16 bytesPerSector; //0x0B - Bytes per Sector. The size of a hardware sector. For most disks in use in the United States, the value of this field is 512.
u8 sectorsPerCluster; //0x0D - Sectors Per Cluster. The number of sectors in a cluster. The default cluster size for a volume depends on the volume size and the file system.
u16 reservedSectors; //0x0E - Reserved Sectors. The number of sectors from the Partition Boot Sector to the start of the first file allocation table, including the Partition Boot Sector. The minimum value is 1. If the value is greater than 1, it means that the bootstrap code is too long to fit completely in the Partition Boot Sector.
u8 numberOfTables; //0x10 - Number of file allocation tables (FATs). The number of copies of the file allocation table on the volume. Typically, the value of this field is 2.
u16 rootEntries; //0x11 - Root Entries. The total number of file name entries that can be stored in the root folder of the volume. One entry is always used as a Volume Label. Files with long filenames use up multiple entries per file. Therefore, the largest number of files in the root folder is typically 511, but you will run out of entries sooner if you use long filenames.
u16 smallSectors; //0x13 - Small Sectors. The number of sectors on the volume if the number fits in 16 bits (65535). For volumes larger than 65536 sectors, this field has a value of 0 and the Large Sectors field is used instead.
u8 mediaType; //0x15 - Media Type. Provides information about the media being used. A value of 0xF8 indicates a hard disk.
u16 sectorsPerTable; //0x16 - Sectors per file allocation table (FAT). Number of sectors occupied by each of the file allocation tables on the volume. By using this information, together with the Number of FATs and Reserved Sectors, you can compute where the root folder begins. By using the number of entries in the root folder, you can also compute where the user data area of the volume begins.
u16 sectorsPerTrack; //0x18 - Sectors per Track. The apparent disk geometry in use when the disk was low-level formatted.
u16 numberOfHeads; //0x1A - Number of Heads. The apparent disk geometry in use when the disk was low-level formatted.
u32 hiddenSectors; //0x1C - Hidden Sectors. Same as the Relative Sector field in the Partition Table.
u32 largeSectors; //0x20 - Large Sectors. If the Small Sectors field is zero, this field contains the total number of sectors in the volume. If Small Sectors is nonzero, this field contains zero.
} FATBiosBlock;
typedef struct
{
FATBiosBlock fatBiosBlock;
u8 physicalDiskNumber; //0x24 - Physical Disk Number. This is related to the BIOS physical disk number. Floppy drives are numbered starting with 0x00 for the A disk. Physical hard disks are numbered starting with 0x80. The value is typically 0x80 for hard disks, regardless of how many physical disk drives exist, because the value is only relevant if the device is the startup disk.
u8 currentHead; //0x25 - Current Head. Not used by the FAT file system.
u8 signature; //0x26 - Signature. Must be either 0x28 or 0x29 in order to be recognized by Windows NT.
u8 volumeSerialNumber[4]; //0x27 - Volume Serial Number. A unique number that is created when you format the volume.
u8 volumeLabel[11]; //0x2B - Volume Label. This field was used to store the volume label, but the volume label is now stored as special file in the root directory.
u8 systemID[8]; //0x36 - System ID. Either FAT12 or FAT16, depending on the format of the disk.
} FATBiosBlockExtended;
typedef struct
{
u8 jump[3]; //0x0000
u8 OEMName[8]; //0x0003
FATBiosBlockExtended biosBlock; //0x000B
u8 bootstrapCode[448]; //0x003E
u16 endOfSector; //0x01FE
} FATBootSector;
*/
#endif

482
arm9/src/install.c Normal file
View File

@ -0,0 +1,482 @@
#include "install.h"
#include "fat12.h"
#include "main.h"
#include "message.h"
#include "maketmd.h"
#include "storage.h"
#include <sys/stat.h>
static bool _titleIsUsed(tDSiHeader* h)
{
if (!h) return false;
char path[64];
sprintf(path, "/title/%08x/%08x/", (unsigned int)h->tid_high, (unsigned int)h->tid_low);
return dirExists(path);
}
//patch homebrew roms if gameCode is #### or null
static bool _patchGameCode(tDSiHeader* h)
{
if (!h) return false;
if ((strcmp(h->ndshdr.gameCode, "####") == 0 && h->tid_low == 0x23232323) || (!*h->ndshdr.gameCode && h->tid_low == 0))
{
iprintf("Fixing Game Code...");
swiWaitForVBlank();
//set as standard app
h->tid_high = 0x00030004;
do {
do {
//generate a random game code
for (int i = 0; i < 4; i++)
h->ndshdr.gameCode[i] = 'A' + (rand() % 26);
}
while (h->ndshdr.gameCode[0] == 'A'); //first letter shouldn't be A
//correct title id
h->tid_low = ( (h->ndshdr.gameCode[0] << 24) | (h->ndshdr.gameCode[1] << 16) | (h->ndshdr.gameCode[2] << 8) | h->ndshdr.gameCode[3] );
}
while (_titleIsUsed(h));
iprintf("Done\n");
return true;
}
return false;
}
static bool _iqueHack(tDSiHeader* h)
{
if (!h) return false;
if (h->ndshdr.reserved1[8] == 0x80)
{
iprintf("iQue Hack...");
h->ndshdr.reserved1[8] = 0x00;
iprintf("Done\n");
return true;
}
return false;
}
static unsigned long long _getSize(FILE* f, tDSiHeader* h)
{
iprintf("Install Size: ");
swiWaitForVBlank();
unsigned long long size = 0;
//get app size
if (f)
size = getFileSize(f);
//add save file size
if (h)
{
size += h->public_sav_size;
size += h->private_sav_size;
//banner.sav
if (h->appflags & 0x4)
size += 0x4000;
}
printBytes(size);
iprintf("\n");
return size;
}
static bool _checkSdSpace(unsigned long long size)
{
iprintf("Enough room on SD card?...");
swiWaitForVBlank();
if (getSDCardFree() < size)
{
iprintf("No\n");
return false;
}
iprintf("Yes\n");
return true;
}
static bool _checkDsiSpace(unsigned long long size)
{
iprintf("Enough room on DSi?...");
swiWaitForVBlank();
if (getDsiFree() < size)
{
iprintf("No\n");
return false;
}
iprintf("Yes\n");
return true;
}
static bool _openMenuSlot()
{
iprintf("Open DSi menu slot?...");
swiWaitForVBlank();
if (getMenuSlotsFree() <= 0)
{
iprintf("No\n");
return choicePrint("Try installing anyway?");
}
iprintf("Yes\n");
return true;
}
static bool _isDSiRom(tDSiHeader* h)
{
//high title id must be one of four
if (h->tid_high != 0x00030004 &&
h->tid_high != 0x00030005 &&
h->tid_high != 0x00030015 &&
h->tid_high != 0x00030017)
{
iprintf("Error: This is not a DSi rom.\n");
return false;
}
return true;
}
static void _createPublicSav(tDSiHeader* h, char* dataPath)
{
if (!h) return;
if (h->public_sav_size > 0)
{
iprintf("Creating public.sav...");
swiWaitForVBlank();
if (!dataPath)
{
iprintf("Failed\n");
}
else
{
char* publicPath = (char*)malloc(strlen(dataPath) + strlen("/public.sav") + 1);
sprintf(publicPath, "%s/public.sav", dataPath);
FILE* f = fopen(publicPath, "wb");
if (!f)
{
iprintf("Failed\n");
}
else
{
fseek(f, h->public_sav_size-1, SEEK_SET);
fputc(0, f);
initFatHeader(f);
iprintf("Done\n");
}
fclose(f);
free(publicPath);
}
}
}
static void _createPrivateSav(tDSiHeader* h, char* dataPath)
{
if (!h) return;
if (h->private_sav_size > 0)
{
iprintf("Creating private.sav...");
swiWaitForVBlank();
if (!dataPath)
{
iprintf("Failed\n");
}
else
{
char* privatePath = (char*)malloc(strlen(dataPath) + strlen("/private.sav") + 1);
sprintf(privatePath, "%s/private.sav", dataPath);
FILE* f = fopen(privatePath, "wb");
if (!f)
{
iprintf("Failed\n");
}
else
{
fseek(f, h->private_sav_size-1, SEEK_SET);
fputc(0, f);
initFatHeader(f);
iprintf("Done\n");
}
fclose(f);
free(privatePath);
}
}
}
static void _createBannerSav(tDSiHeader* h, char* dataPath)
{
if (!h) return;
if (h->appflags & 0x4)
{
iprintf("Creating banner.sav...");
swiWaitForVBlank();
if (!dataPath)
{
iprintf("Failed\n");
}
else
{
char* bannerPath = (char*)malloc(strlen(dataPath) + strlen("/banner.sav") + 1);
sprintf(bannerPath, "%s/banner.sav", dataPath);
FILE* f = fopen(bannerPath, "wb");
if (!f)
{
iprintf("Failed\n");
}
else
{
fseek(f, 0x4000 - 1, SEEK_SET);
fputc(0, f);
iprintf("Done\n");
}
fclose(f);
free(bannerPath);
}
}
}
bool install(char* fpath, bool systemTitle)
{
bool result = false;
//confirmation message
{
char msg[512];
sprintf(msg, "Are you sure you want to install\n%s\n", fpath);
if (choiceBox(msg) == NO)
return false;
}
//start installation
clearScreen(&bottomScreen);
iprintf("Installing %s\n\n", fpath); swiWaitForVBlank();
tDSiHeader* header = (tDSiHeader*)malloc(sizeof(tDSiHeader));
FILE* f = fopen(fpath, "rb");
if (!f)
{
iprintf("Error: could not open file.\n");
goto error;
}
else
{
bool fixHeader = false;
//read header
fseek(f, 0, SEEK_SET);
fread(header, sizeof(tDSiHeader), 1, f);
if (_patchGameCode(header))
fixHeader = true;
if (!_isDSiRom(header))
goto error;
unsigned long long fileSize = getFileSize(f);
unsigned long long installSize = _getSize(f, header);
//do not need file opened anymore
fclose(f);
if (!_checkSdSpace(installSize))
goto error;
if (!_openMenuSlot())
goto error;
//system title patch
if (systemTitle)
{
iprintf("System Title Patch...");
swiWaitForVBlank();
header->tid_high = 0x00030015;
iprintf("Done\n");
fixHeader = true;
}
//skip nand check if system title
if (header->tid_high != 0x00030015)
{
if (!_checkDsiSpace(installSize))
{
if (choicePrint("Install as system title?"))
{
header->tid_high = 0x00030015;
fixHeader = true;
}
else
{
if (choicePrint("Try installing anyway?") == NO)
goto error;
}
}
}
if (_iqueHack(header))
fixHeader = true;
//check if title is free
if (_titleIsUsed(header))
{
char msg[512];
sprintf(msg, "Title %08x is already used.\nInstall anyway?", (unsigned int)header->tid_low);
if (choicePrint(msg) == NO)
goto error;
}
//create title directory /title/XXXXXXXX/XXXXXXXX
char dirPath[32];
sprintf(dirPath, "/title/%08x/%08x", (unsigned int)header->tid_high, (unsigned int)header->tid_low);
mkdir(dirPath, 0777);
//content folder /title/XXXXXXXX/XXXXXXXXX/content
{
char contentPath[64];
sprintf(contentPath, "%s/content", dirPath);
mkdir(contentPath, 0777);
//create 00000000.app
{
iprintf("Creating 00000000.app...");
swiWaitForVBlank();
char appPath[80];
sprintf(appPath, "%s/00000000.app", contentPath);
//copy nds file to app
{
if (copyFile(fpath, appPath) == 0)
{
iprintf("Failed\n");
goto error;
}
iprintf("Done\n");
}
//pad out banner if it is the last part of the file
{
if (header->ndshdr.bannerOffset == fileSize - 0x1C00)
{
iprintf("Padding banner...");
swiWaitForVBlank();
if (padFile(appPath, 0x7C0) == false)
iprintf("Failed\n");
else
iprintf("Done\n");
}
}
//update header
{
if (fixHeader)
{
iprintf("Fixing header...");
swiWaitForVBlank();
//fix header checksum
header->ndshdr.headerCRC16 = swiCRC16(0xFFFF, header, 0x15E);
//fix RSA signature
u8 buffer[20];
swiSHA1Calc(&buffer, header, 0xE00);
memcpy(&(header->rsa_signature[0x6C]), buffer, 20);
f = fopen(appPath, "r+");
if (!f)
{
iprintf("Failed\n");
}
else
{
fseek(f, 0, SEEK_SET);
fwrite(header, sizeof(tDSiHeader), 1, f);
iprintf("Done\n");
}
fclose(f);
}
}
//make TMD
{
char tmdPath[80];
sprintf(tmdPath, "%s/title.tmd", contentPath);
if (maketmd(appPath, tmdPath) != 0)
goto error;
}
}
}
//data folder
{
char dataPath[64];
sprintf(dataPath, "%s/data", dirPath);
mkdir(dataPath, 0777);
_createPublicSav(header, dataPath);
_createPrivateSav(header, dataPath);
_createBannerSav(header, dataPath);
}
//end
result = true;
iprintf("\nInstallation complete.\nBack - [B]\n");
keyWait(KEY_A | KEY_B);
}
goto complete;
error:
messagePrint("\nInstallation failed.\n");
complete:
fclose(f);
free(header);
return result;
}

8
arm9/src/install.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef INSTALL_H
#define INSTALL_H
#include <nds/ndstypes.h>
bool install(char* fpath, bool systemTitle);
#endif

257
arm9/src/installmenu.c Normal file
View File

@ -0,0 +1,257 @@
#include "main.h"
#include "app.h"
#include "install.h"
#include "menu.h"
#include "storage.h"
#include "message.h"
#include <dirent.h>
enum {
INSTALL_MENU_INSTALL,
INSTALL_MENU_SYSTEM_TITLE,
INSTALL_MENU_DELETE,
INSTALL_MENU_BACK
};
static void generateList(Menu* m);
static void printItem(Menu* m);
static int subMenu();
static bool delete(Menu* m);
void installMenu()
{
Menu* m = newMenu();
generateList(m);
//no files found
if (m->itemCount <= 0)
{
clearScreen(&bottomScreen);
iprintf("No files found.\n");
iprintf("Place .nds, .app, or .dsi files in %s\n", ROM_PATH);
iprintf("\nBack - [B]\n");
keyWait(KEY_B | KEY_A | KEY_START);
}
else
{
while (1)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
{
if (m->changePage != 0)
generateList(m);
printMenu(m);
printItem(m);
}
//back
if (keysDown() & KEY_B || m->itemCount <= 0)
{
break;
}
//selection
else if (keysDown() & KEY_A)
{
switch (subMenu())
{
case INSTALL_MENU_INSTALL:
{
if (install(m->items[m->cursor], false))
{
resetMenu(m);
generateList(m);
}
}
break;
case INSTALL_MENU_SYSTEM_TITLE:
{
if (install(m->items[m->cursor], true))
{
resetMenu(m);
generateList(m);
}
}
break;
case INSTALL_MENU_DELETE:
{
if (delete(m))
{
resetMenu(m);
generateList(m);
}
}
break;
case INSTALL_MENU_BACK:
break;
}
printMenu(m);
}
}
}
freeMenu(m);
}
static void generateList(Menu* m)
{
if (!m) return;
//reset menu
clearMenu(m);
m->page += sign(m->changePage);
m->changePage = 0;
bool done = false;
struct dirent* ent;
DIR* dir = opendir(ROM_PATH);
if (dir)
{
int count = 0;
//scan /dsi/
while ( (ent = readdir(dir)) && !done)
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type != DT_DIR)
{
if (strstr(ent->d_name, ".nds") != NULL ||
strstr(ent->d_name, ".app") != NULL ||
strstr(ent->d_name, ".dsi") != NULL ||
strstr(ent->d_name, ".NDS") != NULL ||
strstr(ent->d_name, ".APP") != NULL ||
strstr(ent->d_name, ".DSI") != NULL)
{
if (count < m->page * ITEMS_PER_PAGE)
count += 1;
else
{
if (m->itemCount >= ITEMS_PER_PAGE)
done = true;
else
{
char* fpath = (char*)malloc(strlen(ROM_PATH) + strlen(ent->d_name) + 8);
sprintf(fpath, "%s/%s", ROM_PATH, ent->d_name);
addMenuItem(m, ent->d_name, fpath);
free(fpath);
}
}
}
}
}
}
closedir(dir);
m->nextPage = done;
if (m->cursor >= m->itemCount)
m->cursor = m->itemCount - 1;
printItem(m);
printMenu(m);
}
static void printItem(Menu* m)
{
if (!m) return;
printAppInfo(m->items[m->cursor]);
}
static int subMenu()
{
int result = -1;
Menu* m = newMenu();
addMenuItem(m, "Install", NULL);
addMenuItem(m, "Install as System Title", NULL);
addMenuItem(m, "Delete", NULL);
addMenuItem(m, "Back - [B]", NULL);
printMenu(m);
while (1)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
printMenu(m);
if (keysDown() & KEY_B)
{
result = -1;
break;
}
else if (keysDown() & KEY_A)
{
result = m->cursor;
break;
}
}
freeMenu(m);
return result;
}
static bool delete(Menu* m)
{
if (!m) return false;
char* fpath = m->items[m->cursor];
bool result = false;
bool choice = NO;
{
char str[] = "Are you sure you want to delete\n";
char* msg = (char*)malloc(strlen(str) + strlen(fpath) + 1);
sprintf(msg, "%s%s", str, fpath);
choice = choiceBox(msg);
free(msg);
}
if (choice == YES)
{
if (!fpath)
{
messageBox("Could not delete file.");
}
else
{
if (remove(fpath) == 0)
{
result = true;
messageBox("File deleted.");
}
else
{
messageBox("Could not delete file.");
}
}
}
return result;
}

134
arm9/src/main.c Normal file
View File

@ -0,0 +1,134 @@
#include "main.h"
#include "menu.h"
#include "message.h"
#include <time.h>
#define VERSION "0.6.5 beta"
enum {
MAIN_MENU_INSTALL,
MAIN_MENU_TITLES,
MAIN_MENU_BACKUP,
MAIN_MENU_TEST,
MAIN_MENU_EXIT
};
static void _setupScreens()
{
REG_DISPCNT = MODE_FB0;
VRAM_A_CR = VRAM_ENABLE;
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);
clearScreen(&bottomScreen);
VRAM_A[100] = 0xFFFF;
}
static int _mainMenu(int cursor)
{
//top screen
clearScreen(&topScreen);
iprintf("\tTitle Manager for HiyaCFW\n");
iprintf("\nversion %s\n", VERSION);
iprintf("\x1b[23;0HJeff - 2018-2019");
//menu
Menu* m = newMenu();
addMenuItem(m, "Install", NULL);
addMenuItem(m, "Titles", NULL);
addMenuItem(m, "Restore", NULL);
addMenuItem(m, "Test", NULL);
addMenuItem(m, "Shut Down", NULL);
m->cursor = cursor;
//bottom screen
printMenu(m);
while (1)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
printMenu(m);
if (keysDown() & KEY_A)
break;
}
int result = m->cursor;
freeMenu(m);
return result;
}
int main(int argc, char **argv)
{
srand(time(0));
_setupScreens();
//DSi check
if (!isDSiMode())
{
messageBox("Error: This app is only for the DSi.\n");
return 0;
}
//setup sd card access
if (!fatInitDefault())
{
messageBox("fatInitDefault()...Failed\n");
return 0;
}
//main menu
bool programEnd = false;
int cursor = 0;
while (!programEnd)
{
cursor = _mainMenu(cursor);
switch (cursor)
{
case MAIN_MENU_INSTALL:
installMenu();
break;
case MAIN_MENU_TITLES:
titleMenu();
break;
case MAIN_MENU_BACKUP:
backupMenu();
break;
case MAIN_MENU_TEST:
testMenu();
break;
case MAIN_MENU_EXIT:
programEnd = true;
break;
}
}
return 0;
}
void clearScreen(PrintConsole* screen)
{
consoleSelect(screen);
consoleClear();
}

22
arm9/src/main.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef MAIN_H
#define MAIN_H
#include <nds.h>
#include <fat.h>
#include <stdio.h>
void installMenu();
void titleMenu();
void backupMenu();
void testMenu();
PrintConsole topScreen;
PrintConsole bottomScreen;
void clearScreen(PrintConsole* screen);
#define abs(X) ( (X) < 0 ? -(X): (X) )
#define sign(X) ( ((X) > 0) - ((X) < 0) )
#define repeat(X) for (int _I_ = 0; _I_ < (X); _I_++)
#endif

182
arm9/src/maketmd.c Normal file
View File

@ -0,0 +1,182 @@
/*---------------------------------------------------------------------------------
maketmd.cpp -- TMD Creator for DSiWare Homebrew
Copyright (C) 2018
Przemyslaw Skryjomski (Tuxality)
Big thanks to:
Apache Thunder
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
---------------------------------------------------------------------------------*/
/* September 2018 - Jeff - Translated from C++ to C and uses libnds instead of openssl
Original: github.com/Tuxality/maketmd
*/
#include "maketmd.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <nds/sha1.h>
#include <nds/ndstypes.h>
#include <machine/endian.h>
//#define TMD_CREATOR_VER "0.2"
#define TMD_SIZE 0x208
#define SHA_BUFFER_SIZE 0x200
#define SHA_DIGEST_LENGTH 0x14
void tmd_create(uint8_t* tmd, FILE* app)
{
// Phase 1 - offset 0x18C (Title ID, first part)
{
fseek(app, 0x234, SEEK_SET);
uint32_t value;
fread(&value, 4, 1, app);
value = __bswap32(value);
memcpy(tmd + 0x18c, &value, 4);
}
// Phase 2 - offset 0x190 (Title ID, second part)
{
// We can take this also from 0x230, but reversed
fseek(app, 0x0C, SEEK_SET);
fread((char*)&tmd[0x190], 4, 1, app);
}
// Phase 3 - offset 0x198 (Group ID = '01')
{
fseek(app, 0x10, SEEK_SET);
fread((char*)&tmd[0x198], 2, 1, app);
}
// Phase 4 - offset 0x1AA (fill-in 0x80 value, 0x10 times)
{
for(size_t i = 0; i<0x10; i++) {
tmd[0x1AA + i] = 0x80;
}
}
// Phase 5 - offset 0x1DE (number of contents = 1)
{
tmd[0x1DE] = 0x00;
tmd[0x1DF] = 0x01;
}
// Phase 6 - offset 0x1EA (type of content = 1)
{
tmd[0x1EA] = 0x00;
tmd[0x1EB] = 0x01;
}
// Phase 7 - offset, 0x1EC (file size, 8B)
uint32_t filesize = 0;
uint32_t fileread = 0;
{
fseek(app, 0, SEEK_END);
filesize = ftell(app);
uint32_t size = __bswap32(filesize);
// We only use 4B for size as for now
memcpy((tmd + 0x1F0), &size, sizeof(u32));
}
// Phase 8 - offset, 0x1F4 (SHA1 sum, 20B)
{
// Makes use of libnds
fseek(app, 0, SEEK_SET);
uint8_t buffer[SHA_BUFFER_SIZE] = { 0 };
uint32_t buffer_read = 0;
swiSHA1context_t ctx;
swiSHA1Init(&ctx);
do {
buffer_read = fread((char*)&buffer[0], 1, SHA_BUFFER_SIZE, app);
fileread += buffer_read;
swiSHA1Update(&ctx, buffer, buffer_read);
printProgressBar((float)fileread / (float)filesize);
}
while(buffer_read == SHA_BUFFER_SIZE);
clearProgressBar();
consoleSelect(&bottomScreen);
swiSHA1Final(buffer, &ctx);
//Store SHA1 sum
memcpy((tmd + 0x1F4), buffer, SHA_DIGEST_LENGTH);
}
}
int maketmd(char* input, char* tmdPath)
{
iprintf("MakeTMD for DSiWare Homebrew\n");
iprintf("by Przemyslaw Skryjomski\n\t(Tuxality)\n");
if(input == NULL || tmdPath == NULL) {
iprintf("\nUsage: %s file.app <file.tmd>\n", "maketmd");
return 1;
}
// APP file (input)
FILE* app = fopen(input, "rb");
if(!app) {
iprintf("Error at opening %s for reading.\n", input);
return 1;
}
// TMD file (output)
FILE* tmd = fopen(tmdPath, "wb");
if (!tmd)
{
fclose(app);
iprintf("Error at opening %s for writing.\n", tmdPath);
return 1;
}
// Allocate memory for TMD
uint8_t* tmd_template = (uint8_t*)malloc(sizeof(uint8_t) * TMD_SIZE);
memset(tmd_template, 0, sizeof(uint8_t) * TMD_SIZE); // zeroed
// Prepare TMD template then write to file
tmd_create(tmd_template, app);
fwrite((const char*)(&tmd_template[0]), TMD_SIZE, 1, tmd);
// Free allocated memory for TMD
free(tmd_template);
// This is done in dtor, but we additionally flush tmd.
fclose(app);
fclose(tmd);
return 0;
}

9
arm9/src/maketmd.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef MAKETMD_H
#define MAKETMD_H
#include "main.h"
#include "storage.h"
int maketmd(char* input, char* tmdPath);
#endif

168
arm9/src/menu.c Normal file
View File

@ -0,0 +1,168 @@
#include "menu.h"
#include "main.h"
Menu* newMenu()
{
Menu* m = (Menu*)malloc(sizeof(Menu));
m->cursor = 0;
m->page = 0;
m->itemCount = 0;
m->nextPage = false;
m->changePage = 0;
for (int i = 0; i < ITEMS_PER_PAGE; i++)
{
m->labels[i] = NULL;
m->items[i] = NULL;
}
return m;
}
void freeMenu(Menu* m)
{
if (!m) return;
clearMenu(m);
free(m);
m = NULL;
}
void addMenuItem(Menu* m, char const* label, char const* value)
{
if (!m) return;
int i = m->itemCount;
if (i >= ITEMS_PER_PAGE) return;
if (label)
{
m->labels[i] = (char*)malloc(32);
sprintf(m->labels[i], "%.31s", label);
}
if (value)
{
m->items[i] = (char*)malloc(strlen(value)+1);
sprintf(m->items[i], "%s", value);
}
m->itemCount += 1;
}
void resetMenu(Menu* m)
{
m->cursor = 0;
m->page = 0;
m->changePage = 0;
m->nextPage = 0;
}
void clearMenu(Menu* m)
{
if (!m) return;
for (int i = 0; i < ITEMS_PER_PAGE; i++)
{
if (m->labels[i])
{
free(m->labels[i]);
m->labels[i] = NULL;
}
if (m->items[i])
{
free(m->items[i]);
m->items[i] = NULL;
}
}
m->itemCount = 0;
}
void printMenu(Menu* m)
{
clearScreen(&bottomScreen);
if (!m) return;
for (int i = 0; i < m->itemCount; i++)
{
if (m->labels[i])
iprintf(" %.30s\n", m->labels[i]);
else
iprintf(" \n");
}
//cursor
iprintf("\x1b[%d;0H>", m->cursor);
//scroll arrows
if (m->page > 0)
iprintf("\x1b[0;31H^");
if (m->nextPage)
iprintf("\x1b[22;31Hv");
}
static void _moveCursor(Menu* m, int dir)
{
if (m->changePage != 0)
return;
m->cursor += sign(dir);
if (m->cursor < 0)
{
if (m->page <= 0)
m->cursor = 0;
else
{
m->changePage = -1;
m->cursor = ITEMS_PER_PAGE - 1;
}
}
else if (m->cursor > m->itemCount-1)
{
if (m->cursor >= ITEMS_PER_PAGE)
{
m->changePage = 1;
m->cursor = 0;
}
else
{
m->cursor = m->itemCount-1;
}
}
}
bool moveCursor(Menu* m)
{
if (!m) return false;
m->changePage = 0;
int lastCursor = m->cursor;
if (keysDown() & KEY_DOWN)
_moveCursor(m, 1);
else if (keysDown() & KEY_UP)
_moveCursor(m, -1);
if (keysDown() & KEY_RIGHT)
{
repeat(10)
_moveCursor(m, 1);
}
else if (keysDown() & KEY_LEFT)
{
repeat(10)
_moveCursor(m, -1);
}
return !(lastCursor == m->cursor);
}

29
arm9/src/menu.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef MENU_H
#define MENU_H
#include <nds/ndstypes.h>
#define ITEMS_PER_PAGE 23
typedef struct {
int cursor;
int page;
int itemCount;
bool nextPage;
int changePage;
char* labels[ITEMS_PER_PAGE];
char* items[ITEMS_PER_PAGE];
} Menu;
Menu* newMenu();
void freeMenu(Menu* m);
void addMenuItem(Menu* m, char const* label, char const* value);
void resetMenu(Menu* m);
void clearMenu(Menu* m);
void printMenu(Menu* m);
bool moveCursor(Menu* m);
#endif

104
arm9/src/message.c Normal file
View File

@ -0,0 +1,104 @@
#include "message.h"
#include "main.h"
void keyWait(u32 key)
{
while (1)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & key)
break;
}
}
bool choiceBox(char* message)
{
const int choiceRow = 10;
int cursor = 0;
clearScreen(&bottomScreen);
iprintf("%s\n", message);
iprintf("\x1b[%d;0H\tYes\n\tNo\n", choiceRow);
while (1)
{
swiWaitForVBlank();
scanKeys();
//Clear cursor
iprintf("\x1b[%d;0H ", choiceRow + cursor);
if (keysDown() & (KEY_UP | KEY_DOWN))
cursor = !cursor;
//Print cursor
iprintf("\x1b[%d;0H>", choiceRow + cursor);
if (keysDown() & (KEY_A | KEY_START))
break;
if (keysDown() & KEY_B)
{
cursor = 1;
break;
}
}
scanKeys();
return (cursor == 0)? YES: NO;
}
bool choicePrint(char* message)
{
bool choice = NO;
iprintf("\n%s\n", message);
iprintf("Yes - [A]\nNo - [B]\n");
while (1)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & KEY_A)
{
choice = YES;
break;
}
else if (keysDown() & KEY_B)
{
choice = NO;
break;
}
}
scanKeys();
return choice;
}
void messageBox(char* message)
{
clearScreen(&bottomScreen);
messagePrint(message);
}
void messagePrint(char* message)
{
iprintf("%s\n", message);
iprintf("\nOkay - [A]\n");
while (1)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & (KEY_A | KEY_B | KEY_START))
break;
}
scanKeys();
}

17
arm9/src/message.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef MESSAGE_H
#define MESSAGE_H
#include <nds/ndstypes.h>
enum {
YES = true,
NO = false
};
void keyWait(u32 key);
bool choiceBox(char* message);
bool choicePrint(char* message);
void messageBox(char* message);
void messagePrint(char* message);
#endif

463
arm9/src/storage.c Normal file
View File

@ -0,0 +1,463 @@
#include "storage.h"
#include "main.h"
#include "message.h"
#include <dirent.h>
#define TITLE_LIMIT 39
//printing
void printBytes(unsigned long long bytes)
{
if (bytes < 1024)
iprintf("%dB", (unsigned int)bytes);
else if (bytes < 1024 * 1024)
printf("%.2fKB", (float)bytes / 1024.f);
else if (bytes < 1024 * 1024 * 1024)
printf("%.2fMB", (float)bytes / 1024.f / 1024.f);
else
printf("%.2fGB", (float)bytes / 1024.f / 1024.f / 1024.f);
}
//progress bar
static int lastBars = 0;
void printProgressBar(float percent)
{
if (percent < 0.f) percent = 0.f;
if (percent > 1.f) percent = 1.f;
int bars = (int)(30.f * percent);
//skip redundant prints
if (bars != lastBars)
{
consoleSelect(&topScreen);
//Print frame
if (lastBars <= 0)
{
iprintf("\x1b[23;0H[");
iprintf("\x1b[23;31H]");
}
//Print bars
if (bars > 0)
{
for (int i = 0; i < bars; i++)
iprintf("\x1b[23;%dH|", 1 + i);
}
lastBars = bars;
}
}
void clearProgressBar()
{
lastBars = 0;
consoleSelect(&topScreen);
iprintf("\x1b[23;0H ");
}
//files
bool fileExists(char const* path)
{
if (!path) return false;
FILE* f = fopen(path, "rb");
if (!f)
return false;
fclose(f);
return true;
}
bool copyFile(char const* in, char const* out)
{
if (!in || !out) return false;
FILE* fin = fopen(in, "rb");
FILE* fout = fopen(out, "wb");
if (!fin || !fout)
{
fclose(fin);
fclose(fout);
return false;
}
else
{
consoleSelect(&topScreen);
int bytesRead;
int totalBytesRead = 0;
int fileSize = getFileSize(fin);
#define BUFF_SIZE 128 //Arbitrary. A value too large freezes the ds.
char* buffer = (char*)malloc(BUFF_SIZE);
while (1)
{
bytesRead = fread(buffer, 1, BUFF_SIZE, fin);
fwrite(buffer, bytesRead, 1, fout);
totalBytesRead += bytesRead;
printProgressBar( ((float)totalBytesRead / (float)fileSize) );
if (bytesRead != BUFF_SIZE)
break;
}
clearProgressBar();
consoleSelect(&bottomScreen);
free(buffer);
}
fclose(fin);
fclose(fout);
return true;
}
unsigned long long getFileSize(FILE* f)
{
if (!f) return 0;
fseek(f, 0, SEEK_END);
unsigned long long size = ftell(f);
fseek(f, 0, SEEK_SET);
return size;
}
unsigned long long getFileSizePath(char const* path)
{
if (!path) return 0;
FILE* f = fopen(path, "rb");
unsigned long long size = getFileSize(f);
fclose(f);
return size;
}
bool padFile(char const* path, int size)
{
if (!path) return false;
FILE* f = fopen(path, "ab");
if (!f)
{
return false;
}
else
{
char zero = 0;
fwrite(&zero, size, 1, f);
}
fclose(f);
return true;
}
//directories
bool dirExists(char const* path)
{
if (!path) return false;
DIR* dir = opendir(path);
if (!dir)
return false;
closedir(dir);
return true;
}
bool copyDir(char const* src, char const* dst)
{
if (!src || !dst) return false;
// iprintf("copyDir\n%s\n%s\n\n", src, dst);
bool result = true;
DIR* dir = opendir(src);
struct dirent* ent;
if (!dir)
{
return false;
}
else
{
while ( (ent = readdir(dir)) )
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
char* dsrc = (char*)malloc(strlen(src) + strlen(ent->d_name) + 1);
sprintf(dsrc, "%s/%s", src, ent->d_name);
char* ddst = (char*)malloc(strlen(dst) + strlen(ent->d_name) + 1);
sprintf(ddst, "%s/%s", dst, ent->d_name);
mkdir(ddst, 0777);
if (!copyDir(dsrc, ddst))
result = false;
free(ddst);
free(dsrc);
}
else
{
char* fsrc = (char*)malloc(strlen(src) + strlen(ent->d_name) + 1);
sprintf(fsrc, "%s/%s", src, ent->d_name);
char* fdst = (char*)malloc(strlen(dst) + strlen(ent->d_name) + 1);
sprintf(fdst, "%s/%s", dst, ent->d_name);
// iprintf("%s\n%s\n\n", fsrc, fdst);
iprintf("%s...", fdst);
if(!copyFile(fsrc, fdst))
{
iprintf("Fail\n");
result = false;
}
else
{
iprintf("Done\n");
}
free(fdst);
free(fsrc);
}
}
}
closedir(dir);
return result;
}
bool deleteDir(char const* path)
{
if (!path) return false;
if (strcmp("/", path) == 0)
{
//oh fuck no
return false;
}
bool result = true;
DIR* dir = opendir(path);
struct dirent* ent;
if (!dir)
{
result = false;
}
else
{
while ( (ent = readdir(dir)) )
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
//Delete directory
char subpath[512];
sprintf(subpath, "%s/%s", path, ent->d_name);
if (!deleteDir(subpath))
result = false;
}
else
{
//Delete file
char fpath[512];
sprintf(fpath, "%s/%s", path, ent->d_name);
iprintf("%s...", fpath);
if (remove(fpath) != 0)
{
iprintf("Fail\n");
result = false;
}
else
{
iprintf("Done\n");
}
}
}
}
closedir(dir);
iprintf("%s...", path);
if (remove(path) != 0)
{
iprintf("Fail\n");
result = false;
}
else
{
iprintf("Done\n");
}
return result;
}
unsigned long long getDirSize(const char* path)
{
if (!path) return 0;
unsigned long long size = 0;
DIR* dir = opendir(path);
struct dirent* ent;
if (dir)
{
while ((ent = readdir(dir)))
{
if(strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
char fullpath[512];
sprintf(fullpath, "%s%s/", path, ent->d_name);
size += getDirSize(fullpath);
}
else
{
char fullpath[256];
sprintf(fullpath, "%s%s", path, ent->d_name);
size += getFileSizePath(fullpath);
}
}
}
closedir(dir);
return size;
}
//home menu
int getMenuSlots()
{
//Assume the home menu has a hard limit on slots
//Find a better way to do this
return TITLE_LIMIT;
}
int getMenuSlotsFree()
{
//Get number of open menu slots by subtracting the number of directories in the title folders
//Find a better way to do this
const int NUM_OF_DIRS = 3;
const char* dirs[] = {
"00030004",
"00030005",
"00030015"
};
int freeSlots = getMenuSlots();
DIR* dir;
struct dirent* ent;
for (int i = 0; i < NUM_OF_DIRS; i++)
{
char path[256];
sprintf(path, "/title/%s", dirs[i]);
dir = opendir(path);
if (dir)
{
while ( (ent = readdir(dir)) != NULL )
{
if(strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
freeSlots -= 1;
}
}
closedir(dir);
}
return freeSlots;
}
//SD card
bool sdIsInserted()
{
//Find a better way to do this.
return true;
}
unsigned long long getSDCardSize()
{
if (sdIsInserted())
{
struct statvfs st;
if (statvfs("/", &st) == 0)
return st.f_bsize * st.f_blocks;
}
return 0;
}
unsigned long long getSDCardFree()
{
if (sdIsInserted())
{
struct statvfs st;
if (statvfs("/", &st) == 0)
return st.f_bsize * st.f_bavail;
}
return 0;
}
//internal storage
int getDsiSize()
{
//The DSi has 256MB of internal storage. Some is unavailable and used by other things.
//Find a better way to do this
return 240 * 1024 * 1024;
}
int getDsiFree()
{
//Get free space by subtracting file sizes in emulated nand folders
//Find a better way to do this
int size = getDsiSize();
size -= getDirSize("/sys/");
size -= getDirSize("/title/");
size -= getDirSize("/ticket/");
size -= getDirSize("/shared1/");
size -= getDirSize("/shared2/");
size -= getDirSize("/import/");
size -= getDirSize("/tmp/");
size -= getDirSize("/progress/");
size -= getDirSize("/photo/");
size -= getDirSize("/private/");
size += getDirSize("/title/00030015/");
return size;
}

47
arm9/src/storage.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef STORAGE_H
#define STORAGE_H
#include <nds/ndstypes.h>
#include <stdio.h>
#define BACKUP_PATH "/titlebackup"
#define ROM_PATH "/dsi"
#define BYTES_PER_BLOCK (1024*128)
//printing
void printBytes(unsigned long long bytes);
//progress bar
void printProgressBar(float percent);
void clearProgressBar();
//Files
bool fileExists(char const* path);
bool copyFile(char const* src, char const* dst);
unsigned long long getFileSize(FILE* f);
unsigned long long getFileSizePath(char const* path);
bool padFile(char const* path, int size);
//Directories
bool dirExists(char const* path);
bool copyDir(char const* src, char const* dst);
bool deleteDir(char const* path);
unsigned long long getDirSize(char const* path);
//home menu
int getMenuSlots();
int getMenuSlotsFree();
#define getMenuSlotsUsed() (getMenuSlots() - getMenuSlotsFree())
//SD card
bool sdIsInserted();
unsigned long long getSDCardSize();
unsigned long long getSDCardFree();
#define getSDCardUsedSpace() (getSDCardSize() - getSDCardFree())
//internal storage
int getDsiSize();
int getDsiFree();
#define getDsiUsed() (getDSIStorageSize() - getDSIStorageFree())
#endif

70
arm9/src/testmenu.c Normal file
View File

@ -0,0 +1,70 @@
#include "main.h"
#include "message.h"
#include "storage.h"
void testMenu()
{
//top screen
clearScreen(&topScreen);
iprintf("Storage Check Test\n\n");
//bottom screen
clearScreen(&bottomScreen);
unsigned int free = 0;
unsigned int size = 0;
//home menu slots
{
iprintf("Free Home Menu Slots:\n");
swiWaitForVBlank();
free = getMenuSlotsFree();
iprintf("\t%d / ", free);
swiWaitForVBlank();
size = getMenuSlots();
iprintf("%d\n", size);
swiWaitForVBlank();
}
//SD Card
{
iprintf("\nFree SD Space:\n\t");
swiWaitForVBlank();
unsigned long long sdfree = getSDCardFree();
printBytes(sdfree);
iprintf(" / ");
swiWaitForVBlank();
unsigned long long sdsize = getSDCardSize();
printBytes(sdsize);
iprintf("\n");
swiWaitForVBlank();
printf("\t%d / %d blocks\n", (unsigned int)(sdfree / BYTES_PER_BLOCK), (unsigned int)(sdsize / BYTES_PER_BLOCK));
}
//Emunand
{
iprintf("\nFree DSi Space:\n\t");
swiWaitForVBlank();
free = getDsiFree();
printBytes(free);
iprintf(" / ");
swiWaitForVBlank();
size = getDsiSize();
printBytes(size);
iprintf("\n");
swiWaitForVBlank();
printf("\t%.0f / %.0f blocks\n", (float)free / BYTES_PER_BLOCK, (float)size / BYTES_PER_BLOCK);
}
//end
iprintf("\nBack - [B]\n");
keyWait(KEY_B);
}

370
arm9/src/titlemenu.c Normal file
View File

@ -0,0 +1,370 @@
#include "main.h"
#include "app.h"
#include "menu.h"
#include "message.h"
#include "storage.h"
#include <dirent.h>
enum {
TITLE_MENU_BACKUP,
TITLE_MENU_DELETE,
TITLE_MENU_BACK
};
static void generateList(Menu* m);
static void printItem(Menu* m);
static int subMenu();
static void backup(Menu* m);
static bool delete(Menu* m);
void titleMenu()
{
Menu* m = newMenu();
generateList(m);
//no titles
if (m->itemCount <= 0)
{
messageBox("No titles found.");
}
else
{
while (1)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
{
if (m->changePage != 0)
generateList(m);
printMenu(m);
printItem(m);
}
if (keysDown() & KEY_B || m->itemCount <= 0)
break;
else if (keysDown() & KEY_A)
{
switch (subMenu())
{
case TITLE_MENU_BACKUP:
backup(m);
break;
case TITLE_MENU_DELETE:
{
if (delete(m))
{
resetMenu(m);
generateList(m);
}
}
break;
}
printMenu(m);
}
}
}
freeMenu(m);
}
static void generateList(Menu* m)
{
if (!m) return;
const int NUM_OF_DIRS = 3;
const char* dirs[] = {
"00030004",
"00030005",
"00030015"
};
//Reset menu
clearMenu(m);
m->page += sign(m->changePage);
m->changePage = 0;
bool done = false;
int count = 0; //used to skip to the right page
//search each category directory /title/XXXXXXXX
for (int i = 0; i < NUM_OF_DIRS && done == false; i++)
{
char* dirPath = (char*)malloc(strlen(dirs[i])+10);
sprintf(dirPath, "/title/%s", dirs[i]);
struct dirent* ent;
DIR* dir = opendir(dirPath);
if (dir)
{
while ( (ent = readdir(dir)) && done == false)
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
//scan content folder /title/XXXXXXXX/content
char* contentPath = (char*)malloc(strlen(dirPath) + strlen(ent->d_name) + 20);
sprintf(contentPath, "%s/%s/content", dirPath, ent->d_name);
struct dirent* subent;
DIR* subdir = opendir(contentPath);
if (subdir)
{
while ( (subent = readdir(subdir)) && done == false)
{
if (strcmp(".", subent->d_name) == 0 || strcmp("..", subent->d_name) == 0)
continue;
if (subent->d_type != DT_DIR)
{
//found .app file
if (strstr(subent->d_name, ".app") != NULL)
{
//current item is not on page
if (count < m->page * ITEMS_PER_PAGE)
count += 1;
else
{
if (m->itemCount >= ITEMS_PER_PAGE)
done = true;
else
{
//found requested title
char* path = (char*)malloc(strlen(contentPath) + strlen(subent->d_name) + 10);
sprintf(path, "%s/%s", contentPath, subent->d_name);
char title[128];
getAppTitle(path, title);
addMenuItem(m, title, path);
free(path);
}
}
}
}
}
}
closedir(subdir);
free(contentPath);
}
}
}
closedir(dir);
free(dirPath);
}
m->nextPage = done;
if (m->cursor >= m->itemCount)
m->cursor = m->itemCount - 1;
printItem(m);
printMenu(m);
}
static void printItem(Menu* m)
{
if (!m) return;
printAppInfo(m->items[m->cursor]);
}
static int subMenu()
{
int result = -1;
Menu* m = newMenu();
addMenuItem(m, "Backup", NULL);
addMenuItem(m, "Delete", NULL);
addMenuItem(m, "Back - [B]", NULL);
printMenu(m);
while (1)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
printMenu(m);
if (keysDown() & KEY_B)
break;
else if (keysDown() & KEY_A)
{
result = m->cursor;
break;
}
}
freeMenu(m);
return result;
}
static void backup(Menu* m)
{
char* fpath = m->items[m->cursor];
char* backname = NULL;
{
//make backup folder name
char label[16];
getAppLabel(fpath, label);
char gamecode[5];
getGameCode(fpath, gamecode);
backname = (char*)malloc(strlen(label) + strlen(gamecode) + 16);
sprintf(backname, "%s-%s", label, gamecode);
//make sure dir is unused
char* dstpath = (char*)malloc(strlen(BACKUP_PATH) + strlen(backname) + 32);
sprintf(dstpath, "%s/%s", BACKUP_PATH, backname);
int try = 1;
while (dirExists(dstpath))
{
try += 1;
sprintf(backname, "%s-%s(%d)", label, gamecode, try);
sprintf(dstpath, "%s/%s", BACKUP_PATH, backname);
}
free(dstpath);
}
bool choice = NO;
{
char str[] = "Are you sure you want to backup\n";
char* msg = (char*)malloc(strlen(str) + strlen(backname) + 1);
sprintf(msg, "%s%s", str, backname);
choice = choiceBox(msg);
free(msg);
}
if (choice == YES)
{
u32 tid_low = 1;
u32 tid_high = 1;
getTid(fpath, &tid_low, &tid_high);
char* srcpath = (char*)malloc(strlen("/title/") + 32);
sprintf(srcpath, "/title/%08x/%08x", (unsigned int)tid_high, (unsigned int)tid_low);
if (getSDCardFree() < getDirSize(srcpath))
{
messageBox("Not enough space on SD card.");
}
else
{
char* dstpath = (char*)malloc(strlen(BACKUP_PATH) + strlen(backname) + 8);
sprintf(dstpath, "%s/%s", BACKUP_PATH, backname);
//create dirs
mkdir(BACKUP_PATH, 0777); // /titlebackup
mkdir(dstpath, 0777); // /titlebackup/App Name - XXXX
free(dstpath);
dstpath = (char*)malloc(strlen(BACKUP_PATH) + strlen(backname) + 16);
sprintf(dstpath, "%s/%s/%08x", BACKUP_PATH, backname, (unsigned int)tid_high);
mkdir(dstpath, 0777); // /titlebackup/App Name - XXXX/tid_high
free(dstpath);
dstpath = (char*)malloc(strlen(BACKUP_PATH) + strlen(backname) + 32);
sprintf(dstpath, "%s/%s/%08x/%08x", BACKUP_PATH, backname, (unsigned int)tid_high, (unsigned int)tid_low);
mkdir(dstpath, 0777); // /titlebackup/App Name - XXXX/tid_high/tid_low
// iprintf("dst %s\nsrc %s", dstpath, srcpath);
// keyWait(KEY_A);
clearScreen(&bottomScreen);
if (!copyDir(srcpath, dstpath))
messagePrint("\nBackup error.");
else
messagePrint("\nBackup finished.");
free(dstpath);
}
free(srcpath);
}
free(backname);
}
static bool delete(Menu* m)
{
if (!m) return false;
char* fpath = m->items[m->cursor];
bool result = false;
bool choice = NO;
{
//get app title
char title[128];
getAppTitle(m->items[m->cursor], title);
char str[] = "Are you sure you want to delete\n";
char* msg = (char*)malloc(strlen(str) + strlen(title) + 8);
sprintf(msg, "%s%s", str, title);
choice = choiceBox(msg);
free(msg);
}
if (choice == YES)
{
if (!fpath)
{
messageBox("Failed to delete title.\n");
}
else
{
char dirPath[64];
sprintf(dirPath, "%.25s", fpath);
if (!dirExists(dirPath))
{
messageBox("Failed to delete title.\n");
}
else
{
clearScreen(&bottomScreen);
if (deleteDir(dirPath))
{
result = true;
messagePrint("\nTitle deleted.\n");
}
else
{
messagePrint("\nTitle could not be deleted.\n");
}
}
}
}
return result;
}