Added source
After Width: | Height: | Size: 964 B |
|
@ -0,0 +1,55 @@
|
|||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
export TARGET := $(shell basename $(CURDIR))
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
# 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 := Forwarder Maker
|
||||
GAME_SUBTITLE1 := Edo9300
|
||||
#GAME_SUBTITLE2 := http://devitpro.org
|
||||
|
||||
include $(DEVKITARM)/ds_rules
|
||||
|
||||
.PHONY: checkarm7 checkarm9 clean
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all: checkarm7 checkarm9 $(TARGET).nds
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
checkarm7:
|
||||
$(MAKE) -C arm7
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
checkarm9:
|
||||
$(MAKE) -C arm9
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
$(TARGET).nds : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET).elf
|
||||
ndstool -u 00030004 -g EDO0 -c $(TARGET).nds -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \
|
||||
-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
|
|
@ -0,0 +1,126 @@
|
|||
#---------------------------------------------------------------------------------
|
||||
.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 := source
|
||||
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
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# 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
|
||||
#---------------------------------------------------------------------------------------
|
|
@ -0,0 +1,97 @@
|
|||
/*---------------------------------------------------------------------------------
|
||||
|
||||
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();
|
||||
|
||||
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;
|
||||
}
|
|
@ -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 := source
|
||||
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
|
||||
#---------------------------------------------------------------------------------------
|
|
@ -0,0 +1,241 @@
|
|||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <nds.h>
|
||||
#include <cstdio>
|
||||
#include "apppatch.h"
|
||||
#include "headers.h"
|
||||
|
||||
class CRC16 {
|
||||
std::vector<uint16_t>crc16_tab;
|
||||
uint16_t crc16_constant = 0xA001;
|
||||
bool mdflag;
|
||||
public:
|
||||
CRC16(bool modbus_flag = false) {
|
||||
// initialize the precalculated tables
|
||||
if(crc16_tab.empty()) {
|
||||
init_crc16();
|
||||
}
|
||||
mdflag = modbus_flag;
|
||||
}
|
||||
|
||||
uint16_t calculate(std::string input_data) {
|
||||
uint16_t crc_value = mdflag ? 0xffff : 0x0000;
|
||||
|
||||
for(auto c : input_data) {
|
||||
uint16_t tmp = crc_value ^ c;
|
||||
uint16_t rotated = crc_value >> 8;
|
||||
crc_value = rotated ^ crc16_tab[(tmp & 0x00ff)];
|
||||
}
|
||||
|
||||
return crc_value;
|
||||
}
|
||||
|
||||
void init_crc16() {
|
||||
for(int i = 0; i < 256; i++) {
|
||||
uint16_t crc = i;
|
||||
for(int j = 0; j < 8; j++) {
|
||||
if(crc & 0x0001)
|
||||
crc = (crc >> 1) ^ crc16_constant;
|
||||
else
|
||||
crc = crc >> 1;
|
||||
}
|
||||
crc16_tab.push_back(crc);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int32_t GetBanerSize(uint16_t banner_type) {
|
||||
switch(banner_type) {
|
||||
case NDS_BANNER_VER_ZH: {
|
||||
return NDS_BANNER_SIZE_ZH;
|
||||
break;
|
||||
}
|
||||
case NDS_BANNER_VER_ZH_KO: {
|
||||
return NDS_BANNER_SIZE_ZH_KO;
|
||||
break;
|
||||
}
|
||||
case NDS_BANNER_VER_DSi: {
|
||||
return NDS_BANNER_SIZE_DSi;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return NDS_BANNER_SIZE_ORIGINAL;
|
||||
}
|
||||
}
|
||||
void ReplaceBanner(const std::string& target, const std::string& input, const std::string& output) {
|
||||
std::string destination = target;
|
||||
std::ifstream infile(input, std::ifstream::binary);
|
||||
if(!output.empty()) {
|
||||
std::ifstream src(target, std::ios::binary);
|
||||
std::ofstream dst(output, std::ios::binary);
|
||||
dst << src.rdbuf();
|
||||
src.close();
|
||||
dst.close();
|
||||
destination = output;
|
||||
}
|
||||
|
||||
SrlHeader isrlheader;
|
||||
infile.read((char*)&isrlheader, sizeof(SrlHeader));
|
||||
|
||||
TwlHeader itwlheader = { 0 };
|
||||
if(isrlheader.rom_header_size >= 0x300) {
|
||||
infile.read((char*)&itwlheader, sizeof(SrlHeader));
|
||||
}
|
||||
infile.seekg(0, infile.beg);
|
||||
infile.seekg(isrlheader.banner_offset, infile.beg);
|
||||
int16_t ibanner_type = 0;
|
||||
infile.read((char*)&ibanner_type, sizeof(int16_t));
|
||||
infile.seekg(isrlheader.banner_offset, infile.beg);
|
||||
sNDSBannerExt ibanner;
|
||||
infile.read((char*)&ibanner, GetBanerSize(ibanner_type));
|
||||
std::fstream trfile(destination, std::fstream::binary | std::fstream::out | std::fstream::in);
|
||||
|
||||
SrlHeader tsrlheader;
|
||||
trfile.read((char*)&tsrlheader, sizeof(SrlHeader));
|
||||
|
||||
TwlHeader ttwlheader;
|
||||
trfile.read((char*)&ttwlheader, sizeof(SrlHeader));
|
||||
|
||||
trfile.seekg(tsrlheader.banner_offset, trfile.beg);
|
||||
int16_t tbanner_type = 0;
|
||||
trfile.read((char*)&tbanner_type, sizeof(int16_t));
|
||||
trfile.seekg(tsrlheader.banner_offset, trfile.beg);
|
||||
{
|
||||
int32_t diff = GetBanerSize(ibanner_type) - GetBanerSize(tbanner_type);
|
||||
std::ofstream tmp("tmp.nds", std::ios::binary);
|
||||
trfile.seekg(0, trfile.beg);
|
||||
std::string tmpstr;
|
||||
tmpstr.resize(tsrlheader.banner_offset);
|
||||
trfile.read(&tmpstr[0], tsrlheader.banner_offset);
|
||||
tmp.write(&tmpstr[0], tsrlheader.banner_offset);
|
||||
tmp.write((char*)&ibanner, GetBanerSize(ibanner_type));
|
||||
int32_t start = tsrlheader.banner_offset + GetBanerSize(tbanner_type);
|
||||
trfile.seekg(0, trfile.end);
|
||||
int32_t length = trfile.tellg();
|
||||
trfile.seekg(0, trfile.beg);
|
||||
trfile.seekg(start);
|
||||
tmpstr.clear();
|
||||
tmpstr.resize(length - start);
|
||||
trfile.read(&tmpstr[0], length - start);
|
||||
tmp.write(&tmpstr[0], length - start);
|
||||
tmp.close();
|
||||
trfile.close();
|
||||
remove(destination.c_str());
|
||||
rename("tmp.nds", destination.c_str());
|
||||
trfile.open(destination, std::fstream::binary | std::fstream::out | std::fstream::in);
|
||||
tsrlheader.application_end_offset += diff;
|
||||
ttwlheader.dsi9_rom_offset += diff;
|
||||
ttwlheader.dsi7_rom_offset += diff;
|
||||
ttwlheader.total_rom_size += diff;
|
||||
}
|
||||
tsrlheader.gamecode[0] = isrlheader.gamecode[0];
|
||||
tsrlheader.gamecode[1] = isrlheader.gamecode[1];
|
||||
tsrlheader.gamecode[2] = isrlheader.gamecode[2];
|
||||
tsrlheader.gamecode[3] = isrlheader.gamecode[3];
|
||||
ttwlheader.tid_low = tsrlheader.gamecode[3] | (tsrlheader.gamecode[2] << 8) | (tsrlheader.gamecode[1] << 16) | (tsrlheader.gamecode[0] << 24);
|
||||
trfile.seekg(0, trfile.beg);
|
||||
trfile.write((char*)&tsrlheader, sizeof(tsrlheader));
|
||||
trfile.write((char*)&ttwlheader, sizeof(ttwlheader));
|
||||
trfile.close();
|
||||
infile.close();
|
||||
}
|
||||
|
||||
/*
|
||||
Straight port of ahezard's python script to patch the header for dsiwares apps
|
||||
*/
|
||||
void Patch(const std::string& name, bool backup) {
|
||||
if(backup) {
|
||||
//make a backup of the nds
|
||||
std::ifstream src(name, std::ios::binary);
|
||||
std::ofstream dst((name + ".orig.nds"), std::ios::binary);
|
||||
dst << src.rdbuf();
|
||||
src.close();
|
||||
dst.close();
|
||||
}
|
||||
std::fstream infile(name, std::fstream::binary | std::fstream::out | std::fstream::in);
|
||||
infile.seekg(0, infile.beg);
|
||||
SrlHeader header;
|
||||
infile.read((char*)&header, sizeof(SrlHeader));
|
||||
header.dsi_flags = 0x01;
|
||||
header.nds_region = 0x00;
|
||||
header.unitcode = 0x03;
|
||||
CRC16 crc(true);
|
||||
std::string aa;
|
||||
aa.resize(0x15E);
|
||||
memcpy(&aa[0], (char*)&header, 0x15E);
|
||||
auto headercrc = crc.calculate(aa);
|
||||
header.header_crc = headercrc;
|
||||
|
||||
TwlHeader header2;
|
||||
infile.read((char*)&header2, sizeof(TwlHeader));
|
||||
|
||||
header2.access_control = 0x00000138;
|
||||
header2.scfg_ext_mask = 0x80040000;
|
||||
memset(header2.offset_0x1BC, 0, sizeof(header2.offset_0x1BC));
|
||||
header2.appflags = 0;
|
||||
|
||||
SrlSignedHeader header3 = { 0 };
|
||||
|
||||
if(header.rom_header_size >= 0x1100) {
|
||||
infile.read((char*)&header3, sizeof(SrlSignedHeader));
|
||||
}
|
||||
|
||||
infile.seekg(0x4000, infile.beg);
|
||||
memset(header3.hmac_arm7, 0xff, sizeof(header3.hmac_arm7));
|
||||
memset(header3.hmac_arm7i, 0xff, sizeof(header3.hmac_arm7i));
|
||||
|
||||
memset(header3.hmac_arm9, 0xff, sizeof(header3.hmac_arm9));
|
||||
memset(header3.hmac_arm9i, 0xff, sizeof(header3.hmac_arm9i));
|
||||
memset(header3.hmac_arm9_no_secure, 0xff, sizeof(header3.hmac_arm9_no_secure));
|
||||
|
||||
memset(header3.hmac_icon_title, 0xff, sizeof(header3.hmac_icon_title));
|
||||
memset(header3.hmac_digest_master, 0xff, sizeof(header3.hmac_digest_master));
|
||||
memset(header3.rsa_signature, 0xff, sizeof(header3.rsa_signature));
|
||||
|
||||
auto arm9FooterAddr = header.arm9_rom_offset + header.arm9_size;
|
||||
bool writefooter = false;
|
||||
|
||||
ARM9Footer footer;
|
||||
|
||||
infile.seekg(arm9FooterAddr, infile.beg);
|
||||
infile.read((char*)&footer, sizeof(ARM9Footer));
|
||||
|
||||
if(footer.nitrocode != 0xDEC00621) {
|
||||
footer.nitrocode = 0xDEC00621;
|
||||
footer.versionInfo = 0xad8;
|
||||
footer.reserved = 0;
|
||||
writefooter = true;
|
||||
}
|
||||
|
||||
|
||||
infile.seekg(0, infile.beg);
|
||||
infile.write((char*)&header, sizeof(SrlHeader));
|
||||
infile.write((char*)&header2, sizeof(TwlHeader));
|
||||
infile.write((char*)&header3, 0xC80);
|
||||
std::string dummy;
|
||||
dummy.resize(16 * 8);
|
||||
std::fill_n(&dummy[0], 16 * 8, 0xff);
|
||||
infile.write(&dummy[0], 16 * 8);
|
||||
|
||||
if(writefooter) {
|
||||
infile.seekg(arm9FooterAddr, infile.beg);
|
||||
infile.write((char*)&footer, sizeof(ARM9Footer));
|
||||
}
|
||||
infile.close();
|
||||
}
|
||||
|
||||
int PathStringReplace(std::string path) {
|
||||
std::fstream target("sd:/MakeForwarder/banner.nds", std::fstream::binary | std::fstream::out | std::fstream::in);
|
||||
if(!target.is_open())
|
||||
return 255;
|
||||
std::string str((std::istreambuf_iterator<char>(target)),
|
||||
std::istreambuf_iterator<char>());
|
||||
std::size_t found = str.find("sd:/kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk");
|
||||
if(found == std::string::npos)
|
||||
return 5;
|
||||
target.seekg(found + 4, target.beg);
|
||||
target.write(&path[0], path.size());
|
||||
target.put('\0');
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#ifndef APPPATCH_H
|
||||
#define APPPATCH_H
|
||||
|
||||
#include <string>
|
||||
|
||||
void ReplaceBanner(const std::string& target, const std::string& input, const std::string& output);
|
||||
void Patch(const std::string& name, bool backup);
|
||||
void MakeTmd(const std::string& target, const std::string& destination = "");
|
||||
int PathStringReplace(std::string path);
|
||||
|
||||
#endif //APPPATCH_H
|
|
@ -0,0 +1,223 @@
|
|||
/*-----------------------------------------------------------------
|
||||
Copyright (C) 2005 - 2017
|
||||
Michael "Chishm" Chisholm
|
||||
Dave "WinterMute" Murphy
|
||||
Claudio "sverx"
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
------------------------------------------------------------------*/
|
||||
|
||||
#include "file_browse.h"
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <nds.h>
|
||||
|
||||
#define SCREEN_COLS 32
|
||||
#define ENTRIES_PER_SCREEN 22
|
||||
#define ENTRIES_START_ROW 2
|
||||
#define ENTRY_PAGE_LENGTH 10
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct DirEntry {
|
||||
string name;
|
||||
bool isDirectory;
|
||||
} ;
|
||||
|
||||
bool nameEndsWith (const string& name, const vector<string> extensionList) {
|
||||
|
||||
if (name.size() == 0) return false;
|
||||
|
||||
if (extensionList.size() == 0) return true;
|
||||
|
||||
for (int i = 0; i < (int)extensionList.size(); i++) {
|
||||
const string ext = extensionList.at(i);
|
||||
if ( strcasecmp (name.c_str() + name.size() - ext.size(), ext.c_str()) == 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dirEntryPredicate (const DirEntry& lhs, const DirEntry& rhs) {
|
||||
|
||||
if (!lhs.isDirectory && rhs.isDirectory) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.isDirectory && !rhs.isDirectory) {
|
||||
return true;
|
||||
}
|
||||
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0;
|
||||
}
|
||||
|
||||
void getDirectoryContents (vector<DirEntry>& dirContents, const vector<string> extensionList) {
|
||||
struct stat st;
|
||||
|
||||
dirContents.clear();
|
||||
|
||||
DIR *pdir = opendir (".");
|
||||
|
||||
if (pdir == NULL) {
|
||||
iprintf ("Unable to open the directory.\n");
|
||||
} else {
|
||||
|
||||
while(true) {
|
||||
DirEntry dirEntry;
|
||||
|
||||
struct dirent* pent = readdir(pdir);
|
||||
if(pent == NULL) break;
|
||||
|
||||
stat(pent->d_name, &st);
|
||||
dirEntry.name = pent->d_name;
|
||||
dirEntry.isDirectory = (st.st_mode & S_IFDIR) ? true : false;
|
||||
|
||||
if (dirEntry.name.compare(".") != 0 && (dirEntry.isDirectory || nameEndsWith(dirEntry.name, extensionList))) {
|
||||
dirContents.push_back (dirEntry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
closedir(pdir);
|
||||
}
|
||||
|
||||
sort(dirContents.begin(), dirContents.end(), dirEntryPredicate);
|
||||
}
|
||||
|
||||
void getDirectoryContents (vector<DirEntry>& dirContents) {
|
||||
vector<string> extensionList;
|
||||
getDirectoryContents (dirContents, extensionList);
|
||||
}
|
||||
|
||||
void showDirectoryContents (const vector<DirEntry>& dirContents, int startRow) {
|
||||
char path[PATH_MAX];
|
||||
|
||||
|
||||
getcwd(path, PATH_MAX);
|
||||
|
||||
// Clear the screen
|
||||
iprintf ("\x1b[2J");
|
||||
|
||||
// Print the path
|
||||
if (strlen(path) < SCREEN_COLS) {
|
||||
iprintf ("%s", path);
|
||||
} else {
|
||||
iprintf ("%s", path + strlen(path) - SCREEN_COLS);
|
||||
}
|
||||
|
||||
// Move to 2nd row
|
||||
iprintf ("\x1b[1;0H");
|
||||
// Print line of dashes
|
||||
iprintf ("--------------------------------");
|
||||
|
||||
// Print directory listing
|
||||
for (int i = 0; i < ((int)dirContents.size() - startRow) && i < ENTRIES_PER_SCREEN; i++) {
|
||||
const DirEntry* entry = &dirContents.at(i + startRow);
|
||||
char entryName[SCREEN_COLS + 1];
|
||||
|
||||
// Set row
|
||||
iprintf ("\x1b[%d;0H", i + ENTRIES_START_ROW);
|
||||
|
||||
if (entry->isDirectory) {
|
||||
strncpy (entryName, entry->name.c_str(), SCREEN_COLS);
|
||||
entryName[SCREEN_COLS - 3] = '\0';
|
||||
iprintf (" [%s]", entryName);
|
||||
} else {
|
||||
strncpy (entryName, entry->name.c_str(), SCREEN_COLS);
|
||||
entryName[SCREEN_COLS - 1] = '\0';
|
||||
iprintf (" %s", entryName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string browseForFile (const vector<string>& extensionList) {
|
||||
int pressed = 0;
|
||||
int screenOffset = 0;
|
||||
int fileOffset = 0;
|
||||
vector<DirEntry> dirContents;
|
||||
|
||||
getDirectoryContents (dirContents, extensionList);
|
||||
showDirectoryContents (dirContents, screenOffset);
|
||||
|
||||
while (true) {
|
||||
// Clear old cursors
|
||||
for (int i = ENTRIES_START_ROW; i < ENTRIES_PER_SCREEN + ENTRIES_START_ROW; i++) {
|
||||
iprintf ("\x1b[%d;0H ", i);
|
||||
}
|
||||
// Show cursor
|
||||
iprintf ("\x1b[%d;0H*", fileOffset - screenOffset + ENTRIES_START_ROW);
|
||||
|
||||
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
|
||||
do {
|
||||
scanKeys();
|
||||
pressed = keysDownRepeat();
|
||||
swiWaitForVBlank();
|
||||
} while (!pressed);
|
||||
|
||||
if (pressed & KEY_UP) fileOffset -= 1;
|
||||
if (pressed & KEY_DOWN) fileOffset += 1;
|
||||
if (pressed & KEY_LEFT) fileOffset -= ENTRY_PAGE_LENGTH;
|
||||
if (pressed & KEY_RIGHT) fileOffset += ENTRY_PAGE_LENGTH;
|
||||
|
||||
if (fileOffset < 0) fileOffset = dirContents.size() - 1; // Wrap around to bottom of list
|
||||
if (fileOffset > ((int)dirContents.size() - 1)) fileOffset = 0; // Wrap around to top of list
|
||||
|
||||
// Scroll screen if needed
|
||||
if (fileOffset < screenOffset) {
|
||||
screenOffset = fileOffset;
|
||||
showDirectoryContents (dirContents, screenOffset);
|
||||
}
|
||||
if (fileOffset > screenOffset + ENTRIES_PER_SCREEN - 1) {
|
||||
screenOffset = fileOffset - ENTRIES_PER_SCREEN + 1;
|
||||
showDirectoryContents (dirContents, screenOffset);
|
||||
}
|
||||
|
||||
if (pressed & KEY_A) {
|
||||
DirEntry* entry = &dirContents.at(fileOffset);
|
||||
if (entry->isDirectory) {
|
||||
iprintf("Entering directory\n");
|
||||
// Enter selected directory
|
||||
chdir (entry->name.c_str());
|
||||
getDirectoryContents (dirContents, extensionList);
|
||||
screenOffset = 0;
|
||||
fileOffset = 0;
|
||||
showDirectoryContents (dirContents, screenOffset);
|
||||
} else {
|
||||
// Clear the screen
|
||||
iprintf ("\x1b[2J");
|
||||
// Return the chosen file
|
||||
char cwd[PATH_MAX];
|
||||
getcwd(cwd, PATH_MAX);
|
||||
std::string full_path;
|
||||
full_path.resize(sizeof(cwd)+entry->name.size()+10);
|
||||
sprintf(&full_path[0], "%s%s", cwd, entry->name.c_str());
|
||||
return full_path;
|
||||
}
|
||||
}
|
||||
|
||||
if (pressed & KEY_B) {
|
||||
// Go up a directory
|
||||
chdir ("..");
|
||||
getDirectoryContents (dirContents, extensionList);
|
||||
screenOffset = 0;
|
||||
fileOffset = 0;
|
||||
showDirectoryContents (dirContents, screenOffset);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*-----------------------------------------------------------------
|
||||
Copyright (C) 2005 - 2017
|
||||
Michael "Chishm" Chisholm
|
||||
Dave "WinterMute" Murphy
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
------------------------------------------------------------------*/
|
||||
|
||||
#ifndef FILE_BROWSE_H
|
||||
#define FILE_BROWSE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
std::string browseForFile (const std::vector<std::string>& extensionList);
|
||||
|
||||
|
||||
|
||||
#endif //FILE_BROWSE_H
|
|
@ -0,0 +1,175 @@
|
|||
#ifndef HEADERS
|
||||
#define HEADERS
|
||||
|
||||
struct SrlHeader {
|
||||
int8_t title[0xC];
|
||||
int8_t gamecode[0x4];
|
||||
int8_t makercode[2];
|
||||
uint8_t unitcode; // product code. 0=NDS, 2=NDS+DSi, 3=DSi
|
||||
uint8_t devicetype; // device code. 0 = normal
|
||||
uint8_t devicecap; // device size. (1<<n Mbit)
|
||||
uint8_t reserved1[0x7]; // 0x015..0x01D
|
||||
uint8_t dsi_flags;
|
||||
uint8_t nds_region;
|
||||
uint8_t romversion;
|
||||
uint8_t reserved2; // 0x01F
|
||||
uint32_t arm9_rom_offset; // points to libsyscall and rest of ARM9 binary
|
||||
uint32_t arm9_entry_address;
|
||||
uint32_t arm9_ram_address;
|
||||
uint32_t arm9_size;
|
||||
uint32_t arm7_rom_offset;
|
||||
uint32_t arm7_entry_address;
|
||||
uint32_t arm7_ram_address;
|
||||
uint32_t arm7_size;
|
||||
uint32_t fnt_offset;
|
||||
uint32_t fnt_size;
|
||||
uint32_t fat_offset;
|
||||
uint32_t fat_size;
|
||||
uint32_t arm9_overlay_offset;
|
||||
uint32_t arm9_overlay_size;
|
||||
uint32_t arm7_overlay_offset;
|
||||
uint32_t arm7_overlay_size;
|
||||
uint32_t rom_control_info1; // 0x00416657 for OneTimePROM
|
||||
uint32_t rom_control_info2; // 0x081808F8 for OneTimePROM
|
||||
uint32_t banner_offset;
|
||||
uint16_t secure_area_crc;
|
||||
uint16_t rom_control_info3; // 0x0D7E for OneTimePROM
|
||||
uint32_t offset_0x70; // magic1 (64 bit encrypted magic code to disable LFSR)
|
||||
uint32_t offset_0x74; // magic2
|
||||
uint32_t offset_0x78; // unique ID for homebrew
|
||||
uint32_t offset_0x7C; // unique ID for homebrew
|
||||
uint32_t application_end_offset; // rom size
|
||||
uint32_t rom_header_size;
|
||||
uint32_t offset_0x88; // reserved... ?
|
||||
uint32_t offset_0x8C;
|
||||
|
||||
// reserved
|
||||
uint32_t offset_0x90;
|
||||
uint32_t offset_0x94;
|
||||
uint32_t offset_0x98;
|
||||
uint32_t offset_0x9C;
|
||||
uint32_t offset_0xA0;
|
||||
uint32_t offset_0xA4;
|
||||
uint32_t offset_0xA8;
|
||||
uint32_t offset_0xAC;
|
||||
uint32_t offset_0xB0;
|
||||
uint32_t offset_0xB4;
|
||||
uint32_t offset_0xB8;
|
||||
uint32_t offset_0xBC;
|
||||
|
||||
uint8_t logo[156]; // character data
|
||||
uint16_t logo_crc;
|
||||
uint16_t header_crc;
|
||||
|
||||
// 0x160..0x17F reserved
|
||||
uint32_t debug_rom_offset;
|
||||
uint32_t debug_size;
|
||||
uint32_t debug_ram_address;
|
||||
uint32_t offset_0x16C;
|
||||
uint8_t zero[0x10];
|
||||
};
|
||||
|
||||
struct TwlHeader {
|
||||
// DSi extended stuff below
|
||||
uint8_t global_mbk_setting[5][4];
|
||||
uint32_t arm9_mbk_setting[3];
|
||||
uint32_t arm7_mbk_setting[3];
|
||||
uint32_t mbk9_wramcnt_setting;
|
||||
|
||||
uint32_t region_flags;
|
||||
uint32_t access_control;
|
||||
uint32_t scfg_ext_mask;
|
||||
uint8_t offset_0x1BC[3];
|
||||
uint8_t appflags;
|
||||
|
||||
uint32_t dsi9_rom_offset;
|
||||
uint32_t offset_0x1C4;
|
||||
uint32_t dsi9_ram_address;
|
||||
uint32_t dsi9_size;
|
||||
uint32_t dsi7_rom_offset;
|
||||
uint32_t offset_0x1D4;
|
||||
uint32_t dsi7_ram_address;
|
||||
uint32_t dsi7_size;
|
||||
|
||||
uint32_t digest_ntr_start;
|
||||
uint32_t digest_ntr_size;
|
||||
uint32_t digest_twl_start;
|
||||
uint32_t digest_twl_size;
|
||||
uint32_t sector_hashtable_start;
|
||||
uint32_t sector_hashtable_size;
|
||||
uint32_t block_hashtable_start;
|
||||
uint32_t block_hashtable_size;
|
||||
uint32_t digest_sector_size;
|
||||
uint32_t digest_block_sectorcount;
|
||||
|
||||
uint32_t banner_size;
|
||||
uint32_t offset_0x20C;
|
||||
uint32_t total_rom_size;
|
||||
uint32_t offset_0x214;
|
||||
uint32_t offset_0x218;
|
||||
uint32_t offset_0x21C;
|
||||
|
||||
uint32_t modcrypt1_start;
|
||||
uint32_t modcrypt1_size;
|
||||
uint32_t modcrypt2_start;
|
||||
uint32_t modcrypt2_size;
|
||||
|
||||
uint32_t tid_low;
|
||||
uint32_t tid_high;
|
||||
uint32_t public_sav_size;
|
||||
uint32_t private_sav_size;
|
||||
uint8_t reserved3[176];
|
||||
uint8_t age_ratings[0x10];
|
||||
};
|
||||
struct SrlSignedHeader {
|
||||
uint8_t hmac_arm9[20];
|
||||
uint8_t hmac_arm7[20];
|
||||
uint8_t hmac_digest_master[20];
|
||||
uint8_t hmac_icon_title[20];
|
||||
uint8_t hmac_arm9i[20];
|
||||
uint8_t hmac_arm7i[20];
|
||||
uint8_t reserved4[40];
|
||||
uint8_t hmac_arm9_no_secure[20];
|
||||
uint8_t reserved5[2636];
|
||||
uint8_t debug_args[0x180];
|
||||
uint8_t rsa_signature[0x80];
|
||||
};
|
||||
struct ARM9Footer {
|
||||
uint32_t nitrocode;
|
||||
uint32_t versionInfo;
|
||||
uint32_t reserved;
|
||||
};
|
||||
|
||||
struct sNDSBannerExt {
|
||||
uint16_t version; //!< version of the banner.
|
||||
uint16_t crc[4]; //!< CRC-16s of the banner.
|
||||
uint8_t reserved[22];
|
||||
uint8_t icon[512]; //!< 32*32 icon of the game with 4 bit per pixel.
|
||||
uint16_t palette[16]; //!< the palette of the icon.
|
||||
uint16_t titles[8][128]; //!< title of the game in 8 different languages.
|
||||
|
||||
// [0xA40] Reserved space, possibly for other titles.
|
||||
uint8_t reserved2[0x800];
|
||||
|
||||
// DSi-specific.
|
||||
uint8_t dsi_icon[8][512]; //!< DSi animated icon frame data.
|
||||
uint16_t dsi_palette[8][16]; //!< Palette for each DSi icon frame.
|
||||
uint16_t dsi_seq[64]; //!< DSi animated icon sequence.
|
||||
};
|
||||
// sNDSBanner version.
|
||||
enum sNDSBannerVersion {
|
||||
NDS_BANNER_VER_ORIGINAL = 0x0001,
|
||||
NDS_BANNER_VER_ZH = 0x0002,
|
||||
NDS_BANNER_VER_ZH_KO = 0x0003,
|
||||
NDS_BANNER_VER_DSi = 0x0103,
|
||||
};
|
||||
|
||||
// sNDSBanner sizes.
|
||||
enum sNDSBannerSize {
|
||||
NDS_BANNER_SIZE_ORIGINAL = 0x0840,
|
||||
NDS_BANNER_SIZE_ZH = 0x0940,
|
||||
NDS_BANNER_SIZE_ZH_KO = 0x0A40,
|
||||
NDS_BANNER_SIZE_DSi = 0x23C0,
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,399 @@
|
|||
/*
|
||||
inifile.cpp
|
||||
Copyright (C) 2007 Acekard, www.acekard.com
|
||||
Copyright (C) 2007-2009 somebody
|
||||
Copyright (C) 2009 yellow wood goblin
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include "inifile.h"
|
||||
//#include "stringtool.h"
|
||||
|
||||
static bool freadLine(FILE* f,std::string& str)
|
||||
{
|
||||
str.clear();
|
||||
__read:
|
||||
char p=0;
|
||||
|
||||
size_t readed=fread(&p,1,1,f);
|
||||
if(0==readed)
|
||||
{
|
||||
str="";
|
||||
return false;
|
||||
}
|
||||
if('\n'==p||'\r'==p)
|
||||
{
|
||||
str="";
|
||||
return true;
|
||||
}
|
||||
|
||||
while(p!='\n'&&p!='\r'&&readed)
|
||||
{
|
||||
str+=p;
|
||||
readed=fread(&p,1,1,f);
|
||||
}
|
||||
|
||||
if(str.empty()||""==str)
|
||||
{
|
||||
goto __read;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void trimString(std::string& str)
|
||||
{
|
||||
size_t first=str.find_first_not_of(" \t"),last;
|
||||
if(first==str.npos)
|
||||
{
|
||||
str="";
|
||||
}
|
||||
else
|
||||
{
|
||||
last=str.find_last_not_of(" \t");
|
||||
if(first>0||(last+1)<str.length()) str=str.substr(first,last-first+1);
|
||||
}
|
||||
}
|
||||
|
||||
CIniFile::CIniFile()
|
||||
{
|
||||
m_bLastResult=false;
|
||||
m_bModified=false;
|
||||
m_bReadOnly=false;
|
||||
}
|
||||
|
||||
CIniFile::CIniFile(const std::string& filename)
|
||||
{
|
||||
m_sFileName=filename;
|
||||
m_bLastResult=false;
|
||||
m_bModified=false;
|
||||
m_bReadOnly=false;
|
||||
m_bHasHandle=false;
|
||||
LoadIniFile(m_sFileName);
|
||||
}
|
||||
|
||||
CIniFile::~CIniFile()
|
||||
{
|
||||
if(m_FileContainer.size()>0)
|
||||
{
|
||||
m_FileContainer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void CIniFile::SetString(const std::string& Section,const std::string& Item,const std::string& Value)
|
||||
{
|
||||
if(GetFileString(Section,Item)!=Value)
|
||||
{
|
||||
SetFileString(Section,Item,Value);
|
||||
m_bModified=true;
|
||||
}
|
||||
}
|
||||
|
||||
/*void CIniFile::SetInt(const std::string& Section,const std::string& Item,int Value)
|
||||
{
|
||||
std::string strtemp=formatString("%d",Value);
|
||||
|
||||
if(GetFileString(Section,Item)!=strtemp)
|
||||
{
|
||||
SetFileString(Section,Item,strtemp);
|
||||
m_bModified=true;
|
||||
}
|
||||
}*/
|
||||
|
||||
std::string CIniFile::GetString(const std::string& Section,const std::string& Item)
|
||||
{
|
||||
return GetFileString(Section,Item);
|
||||
}
|
||||
|
||||
std::string CIniFile::GetString(const std::string& Section,const std::string& Item,const std::string& DefaultValue)
|
||||
{
|
||||
std::string temp=GetString(Section,Item);
|
||||
if(!m_bLastResult)
|
||||
{
|
||||
SetString(Section,Item,DefaultValue);
|
||||
temp=DefaultValue;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
void CIniFile::GetStringVector(const std::string& Section,const std::string& Item,std::vector< std::string >& strings,char delimiter)
|
||||
{
|
||||
std::string strValue=GetFileString(Section,Item);
|
||||
strings.clear();
|
||||
size_t pos;
|
||||
while((pos=strValue.find(delimiter),strValue.npos!=pos))
|
||||
{
|
||||
const std::string string=strValue.substr(0,pos);
|
||||
if(string.length())
|
||||
{
|
||||
strings.push_back(string);
|
||||
}
|
||||
strValue=strValue.substr(pos+1,strValue.npos);
|
||||
}
|
||||
if(strValue.length())
|
||||
{
|
||||
strings.push_back(strValue);
|
||||
}
|
||||
}
|
||||
|
||||
void CIniFile::SetStringVector(const std::string& Section,const std::string& Item,std::vector<std::string>& strings,char delimiter)
|
||||
{
|
||||
std::string strValue;
|
||||
for(size_t ii=0;ii<strings.size();++ii)
|
||||
{
|
||||
if(ii) strValue+=delimiter;
|
||||
strValue+=strings[ii];
|
||||
}
|
||||
SetString(Section,Item,strValue);
|
||||
}
|
||||
|
||||
/*int CIniFile::GetInt(const std::string& Section,const std::string& Item)
|
||||
{
|
||||
std::string value=GetFileString(Section,Item);
|
||||
if(value.size()>2&&'0'==value[0]&&('x'==value[1]||'X'==value[1]))
|
||||
return strtol(value.c_str(),NULL,16);
|
||||
else
|
||||
return strtol(value.c_str(),NULL,10);
|
||||
}
|
||||
|
||||
int CIniFile::GetInt(const std::string& Section,const std::string& Item,int DefaultValue)
|
||||
{
|
||||
int temp;
|
||||
temp=GetInt(Section,Item);
|
||||
if(!m_bLastResult)
|
||||
{
|
||||
SetInt(Section,Item,DefaultValue);
|
||||
temp=DefaultValue;
|
||||
}
|
||||
return temp;
|
||||
}*/
|
||||
|
||||
bool CIniFile::LoadIniFile(const std::string& FileName)
|
||||
{
|
||||
//dbg_printf("load %s\n",FileName.c_str());
|
||||
if(FileName!="") m_sFileName=FileName;
|
||||
|
||||
FILE* f=fopen(FileName.c_str(),"rb");
|
||||
|
||||
if(NULL==f) return false;
|
||||
|
||||
m_bHasHandle = true;
|
||||
|
||||
//check for utf8 bom.
|
||||
char bom[3];
|
||||
if(fread(bom,3,1,f)==1&&bom[0]==0xef&&bom[1]==0xbb&&bom[2]==0xbf) ;
|
||||
else fseek(f,0,SEEK_SET);
|
||||
|
||||
std::string strline("");
|
||||
m_FileContainer.clear();
|
||||
|
||||
while(freadLine(f,strline))
|
||||
{
|
||||
trimString(strline);
|
||||
if(strline!=""&&';'!=strline[0]&&'/'!=strline[0]&&'!'!=strline[0]) m_FileContainer.push_back(strline);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
m_bLastResult=false;
|
||||
m_bModified=false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CIniFile::SaveIniFileModified(const std::string& FileName)
|
||||
{
|
||||
if(m_bModified==true)
|
||||
{
|
||||
return SaveIniFile(FileName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CIniFile::HasFileHandle()
|
||||
{
|
||||
return m_bHasHandle;
|
||||
}
|
||||
|
||||
bool CIniFile::SaveIniFile(const std::string& FileName)
|
||||
{
|
||||
if(FileName!="")
|
||||
m_sFileName=FileName;
|
||||
|
||||
FILE* f=fopen(m_sFileName.c_str(),"wb");
|
||||
if(NULL==f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for(size_t ii=0;ii<m_FileContainer.size();ii++)
|
||||
{
|
||||
std::string& strline=m_FileContainer[ii];
|
||||
size_t notSpace=strline.find_first_not_of(' ');
|
||||
strline=strline.substr(notSpace);
|
||||
if(strline.find('[')==0&&ii>0)
|
||||
{
|
||||
if(!m_FileContainer[ii-1].empty()&&m_FileContainer[ii-1]!="")
|
||||
fwrite("\r\n",1,2,f);
|
||||
}
|
||||
if(!strline.empty()&&strline!="")
|
||||
{
|
||||
fwrite(strline.c_str(),1,strline.length(),f);
|
||||
fwrite("\r\n",1,2,f);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
m_bModified=false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string CIniFile::GetFileString(const std::string& Section,const std::string& Item)
|
||||
{
|
||||
std::string strline;
|
||||
std::string strSection;
|
||||
std::string strItem;
|
||||
std::string strValue;
|
||||
|
||||
size_t ii=0;
|
||||
size_t iFileLines=m_FileContainer.size();
|
||||
|
||||
if(m_bReadOnly)
|
||||
{
|
||||
cSectionCache::iterator it=m_Cache.find(Section);
|
||||
if((it!=m_Cache.end())) ii=it->second;
|
||||
}
|
||||
|
||||
m_bLastResult=false;
|
||||
|
||||
if(iFileLines>=0)
|
||||
{
|
||||
while(ii<iFileLines)
|
||||
{
|
||||
strline=m_FileContainer[ii++];
|
||||
|
||||
size_t rBracketPos=0;
|
||||
if('['==strline[0]) rBracketPos=strline.find(']');
|
||||
if(rBracketPos>0&&rBracketPos!=std::string::npos)
|
||||
{
|
||||
strSection=strline.substr(1,rBracketPos-1);
|
||||
if(m_bReadOnly) m_Cache.insert(std::make_pair(strSection,ii-1));
|
||||
if(strSection==Section)
|
||||
{
|
||||
while(ii<iFileLines)
|
||||
{
|
||||
strline=m_FileContainer[ii++];
|
||||
size_t equalsignPos=strline.find('=');
|
||||
if(equalsignPos!=strline.npos)
|
||||
{
|
||||
size_t last=equalsignPos?strline.find_last_not_of(" \t",equalsignPos-1):strline.npos;
|
||||
if(last==strline.npos) strItem="";
|
||||
else strItem=strline.substr(0,last+1);
|
||||
|
||||
if(strItem==Item)
|
||||
{
|
||||
size_t first=strline.find_first_not_of(" \t",equalsignPos+1);
|
||||
if(first==strline.npos) strValue="";
|
||||
else strValue=strline.substr(first);
|
||||
m_bLastResult=true;
|
||||
return strValue;
|
||||
}
|
||||
}
|
||||
else if('['==strline[0])
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
void CIniFile::SetFileString(const std::string& Section,const std::string& Item,const std::string& Value)
|
||||
{
|
||||
std::string strline;
|
||||
std::string strSection;
|
||||
std::string strItem;
|
||||
|
||||
if(m_bReadOnly) return;
|
||||
|
||||
size_t ii=0;
|
||||
size_t iFileLines=m_FileContainer.size();
|
||||
|
||||
while(ii<iFileLines)
|
||||
{
|
||||
strline=m_FileContainer[ii++];
|
||||
|
||||
size_t rBracketPos=0;
|
||||
if('['==strline[0]) rBracketPos=strline.find(']');
|
||||
if(rBracketPos>0&&rBracketPos!=std::string::npos)
|
||||
{
|
||||
strSection=strline.substr(1,rBracketPos-1);
|
||||
if(strSection==Section)
|
||||
{
|
||||
while(ii<iFileLines)
|
||||
{
|
||||
strline=m_FileContainer[ii++];
|
||||
size_t equalsignPos=strline.find('=');
|
||||
if(equalsignPos!=strline.npos)
|
||||
{
|
||||
size_t last=equalsignPos?strline.find_last_not_of(" \t",equalsignPos-1):strline.npos;
|
||||
if(last==strline.npos) strItem="";
|
||||
else strItem=strline.substr(0,last+1);
|
||||
|
||||
if(Item==strItem)
|
||||
{
|
||||
ReplaceLine(ii-1,Item+" = "+Value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if('['==strline[0])
|
||||
{
|
||||
InsertLine(ii-1,Item+" = "+Value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
InsertLine(ii,Item+" = "+Value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InsertLine(ii,"["+Section+"]");
|
||||
InsertLine(ii+1,Item+" = "+Value);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool CIniFile::InsertLine(size_t line,const std::string& str)
|
||||
{
|
||||
m_FileContainer.insert(m_FileContainer.begin()+line,str);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CIniFile::ReplaceLine(size_t line,const std::string& str)
|
||||
{
|
||||
m_FileContainer[line]=str;
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
inifile.h
|
||||
Copyright (C) 2007 Acekard, www.acekard.com
|
||||
Copyright (C) 2007-2009 somebody
|
||||
Copyright (C) 2009-2010 yellow wood goblin
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _INIFILE_H_
|
||||
#define _INIFILE_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
class CIniFile
|
||||
{
|
||||
public:
|
||||
CIniFile();
|
||||
CIniFile(const std::string& filename);
|
||||
virtual ~CIniFile();
|
||||
|
||||
public:
|
||||
bool LoadIniFile(const std::string& FileName);
|
||||
bool SaveIniFile(const std::string& FileName);
|
||||
bool SaveIniFileModified(const std::string& FileName);
|
||||
bool HasFileHandle();
|
||||
|
||||
std::string GetString(const std::string& Section,const std::string& Item,const std::string& DefaultValue);
|
||||
void SetString(const std::string& Section,const std::string& Item,const std::string& Value);
|
||||
int GetInt(const std::string& Section,const std::string& Item,int DefaultValue);
|
||||
void SetInt(const std::string& Section,const std::string& Item,int Value);
|
||||
void GetStringVector(const std::string& Section,const std::string& Item,std::vector<std::string>& strings,char delimiter=',');
|
||||
void SetStringVector(const std::string& Section,const std::string& Item,std::vector<std::string>& strings,char delimiter=',');
|
||||
protected:
|
||||
std::string m_sFileName;
|
||||
typedef std::vector<std::string> cStringArray;
|
||||
cStringArray m_FileContainer;
|
||||
bool m_bLastResult;
|
||||
bool m_bModified;
|
||||
bool m_bReadOnly;
|
||||
bool m_bHasHandle;
|
||||
typedef std::map<std::string,size_t> cSectionCache;
|
||||
cSectionCache m_Cache;
|
||||
|
||||
bool InsertLine(size_t line,const std::string& str);
|
||||
bool ReplaceLine(size_t line,const std::string& str);
|
||||
|
||||
void SetFileString(const std::string& Section,const std::string& Item,const std::string& Value);
|
||||
std::string GetFileString(const std::string& Section,const std::string& Item);
|
||||
|
||||
std::string GetString(const std::string& Section,const std::string& Item);
|
||||
int GetInt(const std::string& Section,const std::string& Item);
|
||||
};
|
||||
|
||||
#endif // _INIFILE_H_
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
/*---------------------------------------------------------------------------------
|
||||
|
||||
Basic template code for starting a DS app
|
||||
|
||||
---------------------------------------------------------------------------------*/
|
||||
#include <nds.h>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <unistd.h>
|
||||
#include <fat.h>
|
||||
|
||||
#include "file_browse.h"
|
||||
#include "headers.h"
|
||||
#include "utils.h"
|
||||
#include "menu.h"
|
||||
#include "inifile.h"
|
||||
#include "apppatch.h"
|
||||
|
||||
CIniFile bootsrtapconfig;
|
||||
CIniFile bootstrap_template;
|
||||
|
||||
PrintConsole upperScreen;
|
||||
PrintConsole lowerScreen;
|
||||
|
||||
void WriteMessage(std::string text, bool clear = false, PrintConsole* screen = nullptr) {
|
||||
if(screen)
|
||||
consoleSelect(screen);
|
||||
if(clear)
|
||||
consoleClear();
|
||||
if(text.size() <= DISPLAY_COLUMNS) {
|
||||
iprintf(text.c_str());
|
||||
return;
|
||||
}
|
||||
std::vector<std::string> words;
|
||||
std::string temp;
|
||||
for(int i = 0; i < (int)text.size(); i++) {
|
||||
if(text[i] == ' ' || text[i] == '\n') {
|
||||
words.push_back(temp);
|
||||
temp.clear();
|
||||
if(text[i] == '\n')
|
||||
words.push_back("\n");
|
||||
} else
|
||||
temp += text[i];
|
||||
}
|
||||
if(temp.size())
|
||||
words.push_back(temp);
|
||||
int column = 0;
|
||||
for(auto word : words) {
|
||||
if(word.size() == 1 && word[0] == '\n') {
|
||||
if(column != DISPLAY_COLUMNS)
|
||||
iprintf("\n");
|
||||
column = 0;
|
||||
return;
|
||||
}
|
||||
int chkval = column + (int)word.size();
|
||||
if(column)
|
||||
chkval++;
|
||||
if(chkval <= DISPLAY_COLUMNS) {
|
||||
if(column) {
|
||||
iprintf(" ");
|
||||
column++;
|
||||
}
|
||||
iprintf(word.c_str());
|
||||
column += (int)word.size();
|
||||
} else {
|
||||
if(column != DISPLAY_COLUMNS)
|
||||
iprintf("\n");
|
||||
column = (int)word.size();
|
||||
iprintf(word.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menu yesno;
|
||||
|
||||
void displayInit() {
|
||||
lowerScreen = *consoleDemoInit();
|
||||
videoSetMode(MODE_0_2D);
|
||||
vramSetBankA(VRAM_A_MAIN_BG);
|
||||
consoleInit(&upperScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, true, true);
|
||||
}
|
||||
|
||||
bool fileExists(const std::string& file) {
|
||||
struct stat buf;
|
||||
return (stat(file.c_str(), &buf) == 0);
|
||||
}
|
||||
|
||||
std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) {
|
||||
size_t start_pos = 0;
|
||||
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
|
||||
}
|
||||
return str;
|
||||
}
|
||||
void stop (void) {
|
||||
//---------------------------------------------------------------------------------
|
||||
while (1) {
|
||||
swiWaitForVBlank();
|
||||
}
|
||||
}
|
||||
void wait (void) {
|
||||
//---------------------------------------------------------------------------------
|
||||
while (1) {
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
int pressed = keysDown();
|
||||
if(pressed & KEY_A) break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char* GetErrorString(int code) {
|
||||
switch(code){
|
||||
case 1:
|
||||
return "fatInitDefault failed!";
|
||||
case 2:
|
||||
return "The MakeForwarder folder is missing!";
|
||||
case 3:
|
||||
return "Template.nds is missing from the MakeForwarder folder!";
|
||||
case 4:
|
||||
return "Error when reading the template.nds file, make sure to have the correct one!";
|
||||
case 5:
|
||||
return "Couldn't open the target rom, or the rom is not a valid target!";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void PrintError(int errorcode, bool halt = false) {
|
||||
consoleSetWindow(&upperScreen, 0, 3, DISPLAY_COLUMNS, 23);
|
||||
consoleSelect(&upperScreen);
|
||||
WriteMessage(GetErrorString(errorcode), true);
|
||||
if (halt)
|
||||
stop();
|
||||
else {
|
||||
WriteMessage("Press A to continue", false);
|
||||
wait();
|
||||
consoleClear();
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetHexid(std::string file, int* chk) {
|
||||
std::ifstream infile(file, std::ifstream::binary);
|
||||
infile.seekg(0xc);
|
||||
if(infile.tellg()!=0xc) {
|
||||
*chk = 1;
|
||||
return "";
|
||||
}
|
||||
std::string gameid;
|
||||
gameid.resize(4);
|
||||
infile.read(&gameid[0], 4);
|
||||
infile.close();
|
||||
return string_to_hex(gameid);
|
||||
}
|
||||
|
||||
void CreateForwarder() {
|
||||
std::vector<std::string> extensionList={".nds"};
|
||||
WriteMessage("Select the target rom", true, &upperScreen);
|
||||
consoleSelect(&lowerScreen);
|
||||
std::string file = browseForFile(extensionList);
|
||||
chdir("sd:/");
|
||||
|
||||
int chk = 0;
|
||||
std::string gameidhex = GetHexid(file, &chk);
|
||||
if (chk){
|
||||
PrintError(5);
|
||||
return;
|
||||
}
|
||||
std::string folderpath("sd:/title/00030004/" + gameidhex);
|
||||
|
||||
if(fileExists(folderpath + "/content/00000000.app")) {
|
||||
WriteMessage("A DsiWare with the same id already exists, do you want to overwrite it?", true, &upperScreen);
|
||||
int ret = yesno.DoMenu(&lowerScreen);
|
||||
consoleSelect(&upperScreen);
|
||||
consoleClear();
|
||||
if(ret)
|
||||
return;
|
||||
}
|
||||
|
||||
WriteMessage("Creating forwarder", true, &lowerScreen);
|
||||
|
||||
ReplaceBanner("sd:/MakeForwarder/template.nds", file, "sd:/MakeForwarder/banner.nds");
|
||||
|
||||
Patch("sd:/MakeForwarder/banner.nds", false);
|
||||
|
||||
MakeTmd("sd:/MakeForwarder/banner.nds", "sd:/MakeForwarder/title.tmd");
|
||||
|
||||
chk = PathStringReplace("title/00030004/" + gameidhex + "/data/");
|
||||
|
||||
if(chk) {
|
||||
PrintError(chk);
|
||||
remove("sd:/MakeForwarder/banner.nds");
|
||||
remove("sd:/MakeForwarder/title.tmd");
|
||||
return;
|
||||
}
|
||||
|
||||
if(CreatePath("title/00030004/" + gameidhex + "/data", "sd:/") && CreatePath("title/00030004/" + gameidhex + "/content", "sd:/")) {
|
||||
Movefile("sd:/MakeForwarder/banner.nds", folderpath + "/content/00000000.app");
|
||||
Movefile("sd:/MakeForwarder/title.tmd", folderpath + "/content/title.tmd");
|
||||
if(bootstrap_template.HasFileHandle()) {
|
||||
bootsrtapconfig.SaveIniFile((folderpath + "/data/config.ini").c_str());
|
||||
bootstrap_template.SetString( "NDS-BOOTSTRAP", "NDS_PATH", file.c_str());
|
||||
std::string savePath = ReplaceAll(file, ".nds", ".sav");
|
||||
bootstrap_template.SetString( "NDS-BOOTSTRAP", "SAV_PATH", savePath.c_str());
|
||||
bootstrap_template.SaveIniFile((folderpath + "/data/bootstrap.ini").c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetBootstrap() {
|
||||
std::vector<std::string> extensionList={".nds"};
|
||||
WriteMessage("Select the target bootstrap file to be used", true, &upperScreen);
|
||||
consoleSelect(&lowerScreen);
|
||||
std::string file = browseForFile(extensionList);
|
||||
chdir("sd:/");
|
||||
|
||||
size_t found = file.find_last_of("/");
|
||||
|
||||
std::string bootstrappath=file.substr(0,found+1);
|
||||
std::string bootstrapversion=file.substr(found+1);
|
||||
|
||||
bootsrtapconfig.SetString( "NDS-FORWARDER", "BOOTSTRAP_PATH", bootstrappath.c_str());
|
||||
bootsrtapconfig.SetString( "NDS-FORWARDER", "BOOTSTRAP_VERSION", bootstrapversion.c_str());
|
||||
|
||||
bootstrap_template.LoadIniFile((bootstrappath+"nds-bootstrap.ini").c_str());
|
||||
}
|
||||
|
||||
void CheckResources() {
|
||||
if (!fileExists("sd:/MakeForwarder"))
|
||||
PrintError(2, true);
|
||||
if (!fileExists("sd:/MakeForwarder/template.nds"))
|
||||
PrintError(3, true);
|
||||
std::ifstream ndstemplate("sd:/MakeForwarder/template.nds", std::ifstream::binary);
|
||||
std::string str((std::istreambuf_iterator<char>(ndstemplate)),
|
||||
std::istreambuf_iterator<char>());
|
||||
std::size_t found = str.find("sd:/kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk");
|
||||
if(found == std::string::npos)
|
||||
PrintError(4, true);
|
||||
}
|
||||
|
||||
int main() {
|
||||
displayInit();
|
||||
consoleSetWindow(&upperScreen, 0, 0, DISPLAY_COLUMNS, 3);
|
||||
WriteMessage("Forwarder maker by edo9300 v1.0", true, &upperScreen);
|
||||
consoleSetWindow(&upperScreen, 0, 3, DISPLAY_COLUMNS, 23);
|
||||
if (!fatInitDefault())
|
||||
PrintError(1, true);
|
||||
CheckResources();
|
||||
menu mainmenu;
|
||||
mainmenu.AddOption("Create Forwarder");
|
||||
mainmenu.AddOption("Set target bootstrap");
|
||||
yesno.AddOption("Yes");
|
||||
yesno.AddOption("No");
|
||||
while(1){
|
||||
swiWaitForVBlank();
|
||||
WriteMessage("Use the option \"Set target bootstrap\" to avoid configuring the created forwarders at startup", true, &upperScreen);
|
||||
int ret = mainmenu.DoMenu(&lowerScreen);
|
||||
if (ret==0)
|
||||
CreateForwarder();
|
||||
else if (ret==1)
|
||||
SetBootstrap();
|
||||
else
|
||||
break;
|
||||
}
|
||||
stop();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/*---------------------------------------------------------------------------------
|
||||
|
||||
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.
|
||||
|
||||
---------------------------------------------------------------------------------*/
|
||||
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <nds/sha1.h>
|
||||
|
||||
#define TMD_SIZE 0x208
|
||||
#define SHA_BUFFER_SIZE 0x200
|
||||
#define SHA1_DIGEST_SIZE 0x14
|
||||
|
||||
void MakeTmd(const std::string& target, const std::string& destination) {
|
||||
uint8_t tmd[TMD_SIZE] = { 0 };
|
||||
std::fstream app(target, std::ios::in | std::ios::binary);
|
||||
{
|
||||
app.seekg(0x234, app.beg);
|
||||
|
||||
uint32_t value;
|
||||
app.read((char*)&value, 4);
|
||||
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
|
||||
app.seekg(0x0C, app.beg);
|
||||
app.read((char*)&tmd[0x190], 4);
|
||||
}
|
||||
|
||||
// Phase 3 - offset 0x198 (Group ID = '01')
|
||||
{
|
||||
app.seekg(0x10, app.beg);
|
||||
app.read((char*)&tmd[0x198], 2);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
app.seekg(0, app.end);
|
||||
uint32_t size = app.tellg();
|
||||
size = __bswap32(size);
|
||||
|
||||
// We only use 4B for size as for now
|
||||
memcpy((tmd + 0x1F0), &size, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
// Phase 8 - offset, 0x1F4 (SHA1 sum, 20B)
|
||||
{
|
||||
app.seekg(0, app.beg);
|
||||
|
||||
uint8_t buffer[SHA_BUFFER_SIZE] = { 0 };
|
||||
uint32_t buffer_read = 0;
|
||||
|
||||
swiSHA1context_t ctx;
|
||||
swiSHA1Init(&ctx);
|
||||
|
||||
do {
|
||||
app.read((char*)&buffer[0], SHA_BUFFER_SIZE);
|
||||
buffer_read = app.gcount();
|
||||
|
||||
swiSHA1Update(&ctx, buffer, buffer_read);
|
||||
} while(buffer_read == SHA_BUFFER_SIZE);
|
||||
|
||||
swiSHA1Final(buffer, &ctx);
|
||||
|
||||
// Store SHA1 sum
|
||||
memcpy((tmd + 0x1F4), buffer, SHA1_DIGEST_SIZE);
|
||||
}
|
||||
std::ofstream temptmd(destination.empty() ? "title.tmd" : destination, std::fstream::binary);
|
||||
temptmd.write((const char*)tmd, TMD_SIZE);
|
||||
temptmd.close();
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*---------------------------------------------------------------------------------
|
||||
|
||||
Basic template code for starting a DS app
|
||||
|
||||
---------------------------------------------------------------------------------*/
|
||||
#include <nds.h>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "menu.h"
|
||||
|
||||
void menu::AddOption(const std::string& option){
|
||||
options.push_back(option);
|
||||
}
|
||||
int menu::DoMenu(PrintConsole* screen){
|
||||
consoleSelect(screen);
|
||||
current = 0;
|
||||
consoleClear();
|
||||
consoleSetWindow(screen, 1, 0, DISPLAY_COLUMNS-1, DISPLAY_ROWS);
|
||||
for(auto option : options)
|
||||
iprintf((option+"\n").c_str());
|
||||
consoleSetWindow(screen, 0, 0, 1, DISPLAY_ROWS);
|
||||
iprintf(">");
|
||||
while (true){
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
int pressed = keysDownRepeat();
|
||||
if(pressed & KEY_UP) {
|
||||
consoleClear();
|
||||
current--;
|
||||
if(current<0)
|
||||
current = options.size() - 1;
|
||||
for (int a = 0; a<current; a++)
|
||||
iprintf("\n");
|
||||
iprintf(">");
|
||||
}
|
||||
if(pressed & KEY_DOWN) {
|
||||
consoleClear();
|
||||
current++;
|
||||
if(current>=options.size())
|
||||
current = 0;
|
||||
for (int a = 0; a<current; a++)
|
||||
iprintf("\n");
|
||||
iprintf(">");
|
||||
}
|
||||
if(pressed & KEY_A) {
|
||||
consoleSetWindow(screen, 0, 0, DISPLAY_COLUMNS, DISPLAY_ROWS);
|
||||
consoleClear();
|
||||
return current;
|
||||
}
|
||||
if(pressed & KEY_START) {
|
||||
consoleSetWindow(screen, 0, 0, DISPLAY_COLUMNS, DISPLAY_ROWS);
|
||||
consoleClear();
|
||||
current = -1;
|
||||
return current;
|
||||
}
|
||||
}
|
||||
}
|
||||
int menu::GetLastSeletedOption(){
|
||||
return current;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef MENU_H
|
||||
#define MENU_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define DISPLAY_COLUMNS 32
|
||||
#define DISPLAY_ROWS 26
|
||||
|
||||
class menu{
|
||||
private:
|
||||
std::vector<std::string> options;
|
||||
int current;
|
||||
public:
|
||||
void AddOption(const std::string& option);
|
||||
int DoMenu(PrintConsole* screen);
|
||||
int GetLastSeletedOption();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //MENU_H
|
|
@ -0,0 +1,61 @@
|
|||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
std::string string_to_hex(const std::string& input) {
|
||||
static const char* const lut = "0123456789ABCDEF";
|
||||
size_t len = input.length();
|
||||
|
||||
std::string output;
|
||||
output.reserve(2 * len);
|
||||
for(size_t i = 0; i < len; ++i) {
|
||||
const unsigned char c = input[i];
|
||||
output.push_back(lut[c >> 4]);
|
||||
output.push_back(lut[c & 15]);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
bool Makedirectory(const std::string& path) {
|
||||
return !mkdir((const char *)path.c_str(), 0777) || errno == EEXIST;
|
||||
}
|
||||
bool Movefile(const std::string& source, const std::string& destination) {
|
||||
std::ifstream src(source, std::ios::binary);
|
||||
if(!src.is_open())
|
||||
return false;
|
||||
std::ofstream dst(destination, std::ios::binary);
|
||||
if(!dst.is_open())
|
||||
return false;
|
||||
dst << src.rdbuf();
|
||||
src.close();
|
||||
remove(source.c_str());
|
||||
return true;
|
||||
}
|
||||
bool CreatePath(const std::string& path, const std::string& workingdir = "") {
|
||||
std::vector<std::string> folders;
|
||||
std::string temp;
|
||||
for(int i = 0; i < (int)path.size(); i++) {
|
||||
if(path[i] == '/') {
|
||||
folders.push_back(temp);
|
||||
temp.clear();
|
||||
} else
|
||||
temp += path[i];
|
||||
}
|
||||
if(!temp.empty())
|
||||
folders.push_back(temp);
|
||||
temp.clear();
|
||||
for(auto folder : folders) {
|
||||
if(temp.empty() && !workingdir.empty())
|
||||
temp = workingdir + "/" + folder;
|
||||
else
|
||||
temp += "/" + folder;
|
||||
if(!Makedirectory(temp))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
After Width: | Height: | Size: 217 B |
After Width: | Height: | Size: 204 B |
After Width: | Height: | Size: 195 B |
After Width: | Height: | Size: 197 B |
After Width: | Height: | Size: 177 B |
After Width: | Height: | Size: 177 B |
After Width: | Height: | Size: 133 B |
After Width: | Height: | Size: 176 B |
After Width: | Height: | Size: 190 B |
After Width: | Height: | Size: 201 B |
After Width: | Height: | Size: 210 B |
After Width: | Height: | Size: 213 B |
After Width: | Height: | Size: 210 B |
After Width: | Height: | Size: 201 B |
After Width: | Height: | Size: 190 B |
After Width: | Height: | Size: 176 B |
After Width: | Height: | Size: 133 B |
After Width: | Height: | Size: 177 B |
After Width: | Height: | Size: 177 B |
After Width: | Height: | Size: 197 B |
After Width: | Height: | Size: 195 B |
After Width: | Height: | Size: 204 B |
|
@ -0,0 +1,457 @@
|
|||
# -*- coding: utf8 -*-
|
||||
# Patch an .nds (works with homebrew and ds demo only) to make it ready for make_cia
|
||||
#
|
||||
# 2016-02-28, Ahezard
|
||||
#
|
||||
# inspired by
|
||||
# Apache Thunder .nds edited files and comments
|
||||
# https://github.com/Relys/Project_CTR/blob/master/makerom/srl.h
|
||||
# https://dsibrew.org/wiki/DSi_Cartridge_Header
|
||||
# if the header size of the input nds file is 0x200 (homebrew)
|
||||
# the header size of the output nds file will be patched to 0x4000 (normal ds/dsi header), 0x3E00 offset
|
||||
|
||||
from struct import *
|
||||
from collections import namedtuple
|
||||
from collections import OrderedDict
|
||||
from pprint import pprint
|
||||
import os, sys
|
||||
import binascii
|
||||
import argparse
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser(description='Patch an nds in order to be ready cia conversion via make_cia --srl=.')
|
||||
parser.add_argument('file', metavar='file.nds', type=file, help='nds file to patch')
|
||||
parser.add_argument('--verbose', help='verbose mode', action="store_true")
|
||||
parser.add_argument('--out', help='output file [optionnal]')
|
||||
parser.add_argument('--read', help='print only the header content, do not patch', action="store_true")
|
||||
parser.add_argument('--extract', help='extract the content of the rom : header.bin,arm9.bin,arm7.bin,icon.bin,arm9i.bin,arm7i.bin, do not patch', action="store_true") #Not yet implemented
|
||||
parser.add_argument('--title', help='Game title')
|
||||
parser.add_argument('--code', help='Game code')
|
||||
parser.add_argument('--maker', help='Maker code')
|
||||
parser.add_argument('--mode', help='target mode, default mode is ds [ds|dsi|dsinogba]')
|
||||
parser.add_argument('--arm9', type=file, help='swap the ds arm9 binary by the one provided')
|
||||
parser.add_argument('--arm7', type=file, help='swap the ds arm7 binary by the one provided')
|
||||
parser.add_argument('--arm9EntryAddress', help='arm9 ram address of the binary provided')
|
||||
parser.add_argument('--arm7EntryAddress', help='arm7 ram address of the binary provided')
|
||||
parser.add_argument('--arm9i', type=file, help='add a dsi arm9i binary to the file, not needed for homebrew so far')
|
||||
parser.add_argument('--arm7i', type=file, help='add a dsi arm7i binary to the file, not needed for homebrew so far')
|
||||
parser.add_argument('--digest-block', type=file, help='dsi digest block table') #Not yet implemented
|
||||
parser.add_argument('--digest-sector', type=file, help='dsi digest sector table') #Not yet implemented
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.mode is None:
|
||||
args.mode = "dsi"
|
||||
|
||||
#
|
||||
# CRC16 MODULE
|
||||
#
|
||||
# includes CRC16 and CRC16 MODBUS
|
||||
#
|
||||
|
||||
from ctypes import c_ushort
|
||||
|
||||
# from https://github.com/cristianav/PyCRC/blob/master/demo.py
|
||||
class CRC16(object):
|
||||
crc16_tab = []
|
||||
|
||||
# The CRC's are computed using polynomials. Here is the most used
|
||||
# coefficient for CRC16
|
||||
crc16_constant = 0xA001 # 40961
|
||||
|
||||
def __init__(self, modbus_flag=False):
|
||||
# initialize the precalculated tables
|
||||
if not len(self.crc16_tab):
|
||||
self.init_crc16()
|
||||
self.mdflag = bool(modbus_flag)
|
||||
|
||||
def calculate(self, input_data=None):
|
||||
try:
|
||||
is_string = isinstance(input_data, str)
|
||||
is_bytes = isinstance(input_data, (bytes, bytearray))
|
||||
|
||||
if not is_string and not is_bytes:
|
||||
raise Exception("Please provide a string or a byte sequence "
|
||||
"as argument for calculation.")
|
||||
|
||||
crc_value = 0x0000 if not self.mdflag else 0xffff
|
||||
|
||||
for c in input_data:
|
||||
d = ord(c) if is_string else c
|
||||
tmp = crc_value ^ d
|
||||
rotated = crc_value >> 8
|
||||
crc_value = rotated ^ self.crc16_tab[(tmp & 0x00ff)]
|
||||
|
||||
return crc_value
|
||||
except Exception as e:
|
||||
print("EXCEPTION(calculate): {}".format(e))
|
||||
|
||||
def init_crc16(self):
|
||||
"""The algorithm uses tables with precalculated values"""
|
||||
for i in range(0, 256):
|
||||
crc = c_ushort(i).value
|
||||
for j in range(0, 8):
|
||||
if crc & 0x0001:
|
||||
crc = c_ushort(crc >> 1).value ^ self.crc16_constant
|
||||
else:
|
||||
crc = c_ushort(crc >> 1).value
|
||||
self.crc16_tab.append(crc)
|
||||
|
||||
def getSize(fileobject):
|
||||
current = fileobject.tell()
|
||||
fileobject.seek(0,2) # move the cursor to the end of the file
|
||||
size = fileobject.tell()
|
||||
fileobject.seek(current,0)
|
||||
return size
|
||||
|
||||
def skipUntilAddress(f_in,f_out, caddr, taddr):
|
||||
chunk = f_in.read(taddr-caddr)
|
||||
f_out.write(chunk)
|
||||
|
||||
def writeBlankuntilAddress(f_out, caddr, taddr):
|
||||
f_out.write("\x00"*(taddr-caddr))
|
||||
|
||||
fname=args.file.name
|
||||
args.file.close()
|
||||
|
||||
if not args.read:
|
||||
print "Patching file : "+fname
|
||||
else:
|
||||
print "Reading header of file : "+fname
|
||||
|
||||
#offset of 0x4600 created
|
||||
|
||||
# File size compute
|
||||
file = open(fname, 'rb')
|
||||
fsize=getSize(file)
|
||||
file.close()
|
||||
|
||||
#CRC header compute "CRC-16 (Modbus)"
|
||||
file = open(fname, 'rb')
|
||||
#0x15E from https://github.com/devkitPro/ndstool/ ... source/header.cpp
|
||||
hdr = file.read(0x15E)
|
||||
hdrCrc=CRC16(modbus_flag=True).calculate(hdr)
|
||||
if args.verbose:
|
||||
print("{:10s} {:20X}".format('HDR CRC-16 ModBus', hdrCrc))
|
||||
#print "origin header cr c"+hdr[0x15E:0x15F]
|
||||
#filew = open(fname+".hdr", "wb")
|
||||
#filew.write(hdr);
|
||||
#filew.close()
|
||||
file.close()
|
||||
|
||||
if args.arm9 is not None:
|
||||
arm9Fname=args.arm9.name
|
||||
args.arm9.close()
|
||||
arm9File = open(arm9Fname, 'rb')
|
||||
arm9FileSize=getSize(arm9File)
|
||||
dataArm9=arm9File.read(arm9FileSize)
|
||||
arm9File.close()
|
||||
|
||||
if args.arm7 is not None:
|
||||
arm7Fname=args.arm7.name
|
||||
args.arm7.close()
|
||||
arm7File = open(arm7Fname, 'rb')
|
||||
arm7FileSize=getSize(arm7File)
|
||||
dataArm7=arm7File.read(arm7FileSize)
|
||||
arm7File.close()
|
||||
|
||||
filer = open(fname, 'rb')
|
||||
data = filer.read(0x180)
|
||||
caddr=0x180
|
||||
|
||||
#DS Data 180 bytes
|
||||
SrlHeader = namedtuple('SrlHeader',
|
||||
"gameTitle "
|
||||
"gameCode "
|
||||
"makerCode "
|
||||
"unitCode "
|
||||
"encryptionSeedSelect "
|
||||
"deviceCapacity "
|
||||
"reserved0 "
|
||||
"dsiflags "
|
||||
"romVersion "
|
||||
"internalFlag "
|
||||
"arm9RomOffset "
|
||||
"arm9EntryAddress "
|
||||
"arm9RamAddress "
|
||||
"arm9Size "
|
||||
"arm7RomOffset "
|
||||
"arm7EntryAddress "
|
||||
"arm7RamAddress "
|
||||
"arm7Size "
|
||||
"fntOffset "
|
||||
"fntSize "
|
||||
"fatOffset "
|
||||
"fatSize "
|
||||
"arm9OverlayOffset "
|
||||
"arm9OverlaySize "
|
||||
"arm7OverlayOffset "
|
||||
"arm7OverlaySize "
|
||||
"normalCardControlRegSettings "
|
||||
"secureCardControlRegSettings "
|
||||
"icon_bannerOffset "
|
||||
"secureAreaCrc "
|
||||
"secure_transfer_timeout "
|
||||
"arm9Autoload "
|
||||
"arm7Autoload "
|
||||
"secureDisable "
|
||||
"ntrRomSize "
|
||||
"headerSize "
|
||||
"reserved1 "
|
||||
"nintendoLogo "
|
||||
"nintendoLogoCrc "
|
||||
"headerCrc "
|
||||
"debugReserved ")
|
||||
srlHeaderFormat='<12s4s2scbb7s2sbcIIIIIIIIIIIIIIIIIIIHHII8sII56s156s2sH32s'
|
||||
srlHeader=SrlHeader._make(unpack_from(srlHeaderFormat, data))
|
||||
if args.verbose:
|
||||
print "origin header crc "+hex(srlHeader.headerCrc)
|
||||
print "origin secure crc "+hex(srlHeader.secureAreaCrc)
|
||||
|
||||
#SecureArea CRC compute "CRC-16 (Modbus)"
|
||||
file = open(fname, 'rb')
|
||||
#0x15E from https://github.com/devkitPro/ndstool/ ... source/header.cpp
|
||||
file.read(0x200)
|
||||
sec = file.read(0x4000)
|
||||
secCrc=CRC16(modbus_flag=True).calculate(sec)
|
||||
if args.verbose:
|
||||
print("{:10s} {:20X}".format('SEC CRC-16 ModBus', secCrc))
|
||||
file.close()
|
||||
|
||||
if srlHeader.arm7EntryAddress>0x2400000 and not args.read and args.arm7 is None:
|
||||
print "WARNING: .nds arm7EntryAddress greater than 0x2400000 will not boot as cia"
|
||||
print "you need to recompile or swap the arm7 binary with a precompiled one with --arm7 and --arm7EntryAddress"
|
||||
|
||||
if "dsi" in args.mode :
|
||||
srlHeaderPatched=srlHeader._replace(
|
||||
dsiflags= '\x01\x00', #disable modcrypt but enable twl
|
||||
unitCode= '\x03',
|
||||
)
|
||||
|
||||
data1=pack(*[srlHeaderFormat]+srlHeaderPatched._asdict().values())
|
||||
newHdrCrc=CRC16(modbus_flag=True).calculate(data1[0:0x15E])
|
||||
srlHeaderPatched=srlHeaderPatched._replace(headerCrc=newHdrCrc)
|
||||
|
||||
if args.verbose:
|
||||
print "new header crc "+hex(newHdrCrc)
|
||||
if not args.read :
|
||||
if args.verbose:
|
||||
pprint(dict(srlHeaderPatched._asdict()))
|
||||
else:
|
||||
pprint(dict(srlHeader._asdict()))
|
||||
|
||||
data1=pack(*[srlHeaderFormat]+srlHeaderPatched._asdict().values())
|
||||
|
||||
arm9isize=0
|
||||
arm7isize=0
|
||||
|
||||
#TWL Only Data 384 bytes
|
||||
SrlTwlExtHeader = namedtuple('SrlTwlExtHeader',
|
||||
"MBK_1_5_Settings "
|
||||
"MBK_6_8_Settings_ARM9 "
|
||||
"MBK_6_8_Settings_ARM7 "
|
||||
"global_MBK_9_Setting "
|
||||
"regionFlags "
|
||||
"accessControl "
|
||||
"arm7ScfgExtMask "
|
||||
"reserved_flags "
|
||||
"arm9iRomOffset "
|
||||
"reserved2 "
|
||||
"arm9iLoadAddress "
|
||||
"arm9iSize "
|
||||
"arm7iRomOffset "
|
||||
"struct_param_baseAddress "
|
||||
"arm7iLoadAddress "
|
||||
"arm7iSize "
|
||||
"digest_ntrRegionOffset "
|
||||
"digest_ntrRegionSize "
|
||||
"digest_twlRegionOffset "
|
||||
"digest_twlRegionSize "
|
||||
"digestSectorHashtableOffset "
|
||||
"digestSectorHashtableSize "
|
||||
"digest_blockHashtableOffset "
|
||||
"digest_blockHashtableSize "
|
||||
"digestSectorSize "
|
||||
"digest_blockSectorcount "
|
||||
"iconSize " #usually 0x23C0 or 2112 in homebrew
|
||||
"unknown1 "
|
||||
"twlRomSize "
|
||||
"unknown2 "
|
||||
"modcryptArea1Offset "
|
||||
"modcryptArea1Size "
|
||||
"modcryptArea2Offset "
|
||||
"modcryptArea2Size "
|
||||
"title_id "
|
||||
"pubSaveDataSize "
|
||||
"privSaveDataSize "
|
||||
"reserved4 "
|
||||
"parentalControl ")
|
||||
srlTwlExtHeaderFormat="<20s12s12s4s4sIIII4sIIIIIIIIIIIIIIIII4sI12sIIII8sII176s16s"
|
||||
if srlHeader.headerSize<0x300:
|
||||
#homebrew
|
||||
srlTwlExtHeader=SrlTwlExtHeader._make(unpack_from(srlTwlExtHeaderFormat, "\x00" * (0x300-0x180)))
|
||||
else:
|
||||
data = filer.read(0x300-0x180)
|
||||
srlTwlExtHeader=SrlTwlExtHeader._make(unpack_from(srlTwlExtHeaderFormat, data))
|
||||
caddr=0x300
|
||||
|
||||
#pprint(dict(srlTwlExtHeader._asdict()))
|
||||
|
||||
if not args.read:
|
||||
# Fix srlTwlExtHeader
|
||||
if "dsi" in args.mode:
|
||||
arm7iRomOffset=srlHeaderPatched.arm7RomOffset
|
||||
arm9iRomOffset=srlHeaderPatched.arm9RomOffset
|
||||
arm7isize=srlHeaderPatched.arm7Size
|
||||
arm9isize=srlHeaderPatched.arm9Size
|
||||
totaldsisize=0
|
||||
arm7iname = None
|
||||
arm9iname = None
|
||||
|
||||
if args.arm9i is not None:
|
||||
arm9iname = args.arm9i.name
|
||||
arm9isize = getSize(args.arm9i)
|
||||
arm9iRomOffset=srlHeaderPatched.ntrRomSize
|
||||
if args.verbose:
|
||||
print "arm9isize : "+hex(arm9isize)
|
||||
print "arm9ioffset : "+hex(srlHeaderPatched.ntrRomSize)
|
||||
args.arm9i.close()
|
||||
totaldsisize=arm9isize
|
||||
|
||||
if args.arm7i is not None:
|
||||
arm7iname = args.arm7i.name
|
||||
arm7isize = getSize(args.arm7i)
|
||||
arm7iRomOffset=srlHeaderPatched.ntrRomSize+arm9isize
|
||||
if args.verbose:
|
||||
print "arm7isize : "+hex(arm7isize)
|
||||
print "arm9ioffset : "+hex(srlHeaderPatched.ntrRomSize+arm9isize)
|
||||
args.arm7i.close()
|
||||
totaldsisize=arm9isize+arm7isize
|
||||
|
||||
srlTwlExtHeader=srlTwlExtHeader._replace(
|
||||
accessControl= 0x00000138,
|
||||
arm7ScfgExtMask= 0x80040000,
|
||||
reserved_flags= 0x00000000
|
||||
)
|
||||
|
||||
if args.verbose or args.read:
|
||||
pprint(dict(srlTwlExtHeader._asdict()))
|
||||
|
||||
data2=pack(*[srlTwlExtHeaderFormat]+srlTwlExtHeader._asdict().values())
|
||||
|
||||
#TWL and Signed NTR 3328 bytes
|
||||
SrlSignedHeader = namedtuple('SrlSignedHeader',
|
||||
"arm9WithSecAreaSha1Hmac "
|
||||
"arm7Sha1Hmac "
|
||||
"digestMasterSha1Hmac "
|
||||
"bannerSha1Hmac "
|
||||
"arm9iSha1Hmac "
|
||||
"arm7iSha1Hmac "
|
||||
"reserved5 "
|
||||
"arm9Sha1Hmac "
|
||||
"reserved6 "
|
||||
"reserved7 "
|
||||
"signature "
|
||||
)
|
||||
srlSignedHeaderFormat="<20s20s20s20s20s20s40s20s2636s384s128s"
|
||||
if srlHeader.headerSize<0x1100:
|
||||
#homebrew
|
||||
srlSignedHeader=SrlSignedHeader._make(unpack_from(srlSignedHeaderFormat, "\x00" * (3328)))
|
||||
else:
|
||||
data = filer.read(3328)
|
||||
srlSignedHeader=SrlSignedHeader._make(unpack_from(srlSignedHeaderFormat, data))
|
||||
caddr=0x300+3328
|
||||
filer.read(0x4000-caddr)
|
||||
caddr=0x4000
|
||||
|
||||
#pprint(dict(srlSignedHeader._asdict()))
|
||||
|
||||
# Fix srlSignedHeader
|
||||
if not args.read:
|
||||
srlSignedHeader=srlSignedHeader._replace(
|
||||
arm7Sha1Hmac= '\xff'*20,
|
||||
arm9WithSecAreaSha1Hmac= '\xff'*20,
|
||||
bannerSha1Hmac= '\xff'*20,
|
||||
signature= '\xff'*128
|
||||
)
|
||||
if "dsi" in args.mode :
|
||||
srlSignedHeader=srlSignedHeader._replace(
|
||||
arm7Sha1Hmac= '\xff'*20,
|
||||
arm7iSha1Hmac= '\xff'*20,
|
||||
arm9Sha1Hmac= '\xff'*20,
|
||||
arm9WithSecAreaSha1Hmac= '\xff'*20,
|
||||
arm9iSha1Hmac= '\xff'*20,
|
||||
bannerSha1Hmac= '\xff'*20,
|
||||
digestMasterSha1Hmac= '\xff'*20,
|
||||
signature= '\xff'*128
|
||||
)
|
||||
if args.verbose or args.read:
|
||||
pprint(dict(srlSignedHeader._asdict()))
|
||||
|
||||
data3=pack(*[srlSignedHeaderFormat]+srlSignedHeader._asdict().values())
|
||||
|
||||
# ARM9 footer
|
||||
# from https://github.com/devkitPro/ndstool/ ... source/header.cpp
|
||||
# ARM9 footer size = 3*4
|
||||
ARM9Footer = namedtuple('ARM9Footer',
|
||||
"nitrocode " #0xDEC00621
|
||||
"versionInfo "
|
||||
"reserved "
|
||||
)
|
||||
ARM9FooterFormat="<III"
|
||||
file = open(fname, 'rb')
|
||||
arm9FooterAddr=srlHeader.arm9RomOffset + srlHeader.arm9Size
|
||||
file.read(arm9FooterAddr)
|
||||
data=file.read(12)
|
||||
arm9Footer=ARM9Footer._make(unpack_from(ARM9FooterFormat, data))
|
||||
if args.verbose:
|
||||
print "footer addr "+hex(arm9FooterAddr)
|
||||
if arm9Footer.nitrocode == 0xDEC00621:
|
||||
if args.verbose or args.read:
|
||||
print "ARM9 footer found."
|
||||
print "no patch needed"
|
||||
print "nitrocode "+hex(arm9Footer.nitrocode)
|
||||
print "versionInfo "+hex(arm9Footer.versionInfo)
|
||||
print "reserved "+hex(arm9Footer.reserved)
|
||||
print "\n"
|
||||
else:
|
||||
if args.verbose or args.read:
|
||||
print "ARM9 footer not found.\n"
|
||||
arm9FooterPatched=arm9Footer._replace(
|
||||
nitrocode= 0xDEC00621,
|
||||
versionInfo= 0xad8,
|
||||
reserved= 0
|
||||
)
|
||||
data4=pack(*[ARM9FooterFormat]+arm9FooterPatched._asdict().values())
|
||||
file.close()
|
||||
|
||||
if not args.read:
|
||||
# write the file
|
||||
if args.out is not None:
|
||||
filew = open(args.out, "wb")
|
||||
else:
|
||||
filew = open(fname+".tmp", "wb")
|
||||
|
||||
filew.write(data1)
|
||||
filew.write(data2)
|
||||
filew.write(data3[0:0xC80])
|
||||
filew.write('\xff'*16*8)
|
||||
writeBlankuntilAddress(filew,0x1000,0x4000)
|
||||
|
||||
if arm9Footer.nitrocode != 0xDEC00621:
|
||||
# patch ARM9 footer
|
||||
skipUntilAddress(filer,filew,caddr,arm9FooterAddr)
|
||||
filew.write(data4)
|
||||
filer.read(12)
|
||||
caddr=arm9FooterAddr+12
|
||||
|
||||
skipUntilAddress(filer,filew,caddr,srlTwlExtHeader.twlRomSize)
|
||||
|
||||
filew.close()
|
||||
filer.close()
|
||||
|
||||
if args.out is None:
|
||||
if os.path.exists(fname+".orig.nds"):
|
||||
os.remove(fname+".orig.nds")
|
||||
os.rename(fname,fname+".orig.nds")
|
||||
os.rename(fname+".tmp",fname)
|
||||
print "file patched"
|