mirror of
https://github.com/rvtr/TDT.git
synced 2025-10-31 13:51:07 -04:00
0.6.5 beta
This commit is contained in:
parent
0e938a3f1f
commit
2ab11e7ebc
152
Makefile
152
Makefile
@ -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
127
arm7/Makefile
Normal 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
98
arm7/src/main.c
Normal 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
127
arm9/Makefile
Normal 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
267
arm9/src/app.c
Normal 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
17
arm9/src/app.h
Normal 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
252
arm9/src/backupmenu.c
Normal 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
114
arm9/src/fat12.c
Normal 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
77
arm9/src/fat12.h
Normal 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
482
arm9/src/install.c
Normal 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
8
arm9/src/install.h
Normal 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
257
arm9/src/installmenu.c
Normal 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
134
arm9/src/main.c
Normal 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
22
arm9/src/main.h
Normal 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
182
arm9/src/maketmd.c
Normal 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
9
arm9/src/maketmd.h
Normal 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
168
arm9/src/menu.c
Normal 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
29
arm9/src/menu.h
Normal 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
104
arm9/src/message.c
Normal 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
17
arm9/src/message.h
Normal 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
463
arm9/src/storage.c
Normal 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
47
arm9/src/storage.h
Normal 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
70
arm9/src/testmenu.c
Normal 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
370
arm9/src/titlemenu.c
Normal 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;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user