Add files via upload

This commit is contained in:
JeffRuLz 2018-09-12 14:49:35 -05:00 committed by GitHub
parent e6291362f3
commit e0f2505016
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 2300 additions and 0 deletions

140
Makefile Normal file
View File

@ -0,0 +1,140 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/ds_rules
#---------------------------------------------------------------------------------
# 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
GAME_TITLE := TMFH
GAME_SUBTITLE1 := Title Manager for HiyaCFW
GAME_SUBTITLE2 := JeffRuLz
#---------------------------------------------------------------------------------
# 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)
icons := $(wildcard *.bmp)
ifneq (,$(findstring $(TARGET).bmp,$(icons)))
export GAME_ICON := $(CURDIR)/$(TARGET).bmp
else
ifneq (,$(findstring icon.bmp,$(icons)))
export GAME_ICON := $(CURDIR)/icon.bmp
endif
endif
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).elf $(TARGET).nds
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).nds : $(OUTPUT).elf
$(OUTPUT).elf : $(OFILES)
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
$(bin2o)
-include $(DEPSDIR)/*.d
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

642
src/installmenu.c Normal file
View File

@ -0,0 +1,642 @@
#include "menus.h"
#include "storage.h"
#include "maketmd.h"
#include <dirent.h>
static int cursor = 0;
static int scrolly = 0;
static int numberOfTitles = 0;
static void moveCursor(int dir);
static void printList();
static void printFileInfoNum(int num);
static int getNumberOfTitles();
//static void getGameTitle(char* title, FILE* f);
static int getFile(char* dest, int num, int fullpath);
static void subMenu();
static void install(char* fpath);
void installMenu()
{
cursor = 0;
scrolly = 0;
numberOfTitles = getNumberOfTitles();
consoleSelect(&topScreen);
consoleClear();
consoleSelect(&bottomScreen);
consoleClear();
//No titles error
if (numberOfTitles == 0)
{
iprintf("No files found.\n");
iprintf("Place .nds(dsi) or .app files in %s\n", ROM_PATH);
iprintf("\nBack - B\n");
keyWait(KEY_B | KEY_A | KEY_START);
return;
}
//Print data
consoleSelect(&topScreen);
printFileInfoNum(cursor);
consoleSelect(&bottomScreen);
printList();
while (1)
{
swiWaitForVBlank();
scanKeys();
int thisCursor = cursor;
int thisscrolly = scrolly;
//Clear cursor
consoleSelect(&bottomScreen);
iprintf("\x1b[%d;0H ", cursor - scrolly);
//Move cursor
if (keysDown() & KEY_DOWN)
moveCursor(1);
if (keysDown() & KEY_UP)
moveCursor(-1);
if (keysDown() & KEY_RIGHT)
{
repeat (10)
moveCursor(1);
}
if (keysDown() & KEY_LEFT)
{
repeat (10)
moveCursor(-1);
}
//Refresh screens
if (thisCursor != cursor)
{
consoleSelect(&topScreen);
consoleClear();
printFileInfoNum(cursor);
}
if (thisscrolly != scrolly)
{
consoleSelect(&bottomScreen);
consoleClear();
printList();
}
//Print cursor
consoleSelect(&bottomScreen);
iprintf("\x1b[%d;0H>", cursor - scrolly);
//
if (keysDown() & KEY_B)
break;
else if (keysDown() & KEY_A)
{
subMenu();
consoleSelect(&topScreen);
printFileInfoNum(cursor);
consoleSelect(&bottomScreen);
printList();
}
}
}
void moveCursor(int dir)
{
cursor += sign(dir);
if (cursor < 0)
cursor = 0;
if (cursor >= numberOfTitles - 1)
cursor = numberOfTitles - 1;
if (cursor - scrolly >= 23)
scrolly += 1;
if (cursor - scrolly < 0)
scrolly -= 1;
}
void printList()
{
consoleClear();
for (int i = scrolly; i < scrolly + 23; i++)
{
char str[256];
if (getFile(str, i, 0) == 1)
iprintf(" %.30s\n", str);
}
//Scroll arrows
if (scrolly > 0)
iprintf("\x1b[0;31H^");
if (scrolly < numberOfTitles - 23)
iprintf("\x1b[22;31Hv");
}
void printFileInfoNum(int num)
{
consoleClear();
char path[256];
if (getFile(path, num, 1) == 1)
printFileInfo(path);
}
int getNumberOfTitles()
{
DIR* dir;
struct dirent* ent;
int count = 0;
dir = opendir(ROM_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)
{
if (strstr(ent->d_name, ".nds") != NULL || strstr(ent->d_name, ".app") != NULL)
count++;
}
}
}
closedir(dir);
return count;
}
/*
void getGameTitle(char* title, FILE* f)
{
tDSiHeader header;
tNDSBanner banner;
fseek(f, 0, SEEK_SET);
fread(&header, sizeof(tDSiHeader), 1, f);
fseek(f, header.ndshdr.bannerOffset, SEEK_SET);
fread(&banner, sizeof(tNDSBanner), 1, f);
// iprintf("\t%s\n", header.ndshdr.gameTitle);
// iprintf("\t%s\n", (char*)banner.titles[0]);
int line = 0;
for (int i = 0; i < 64; i++)
{
char c = banner.titles[0][i];
if (c == '\n')
{
if (line == 0)
{
title[i] = ' ';
line = 1;
}
else
{
title[i] = '\0';
break;
}
}
else
title[i] = c;
}
title[64] = '\0';
}
*/
int getFile(char* dest, int num, int fullpath)
{
DIR* dir;
struct dirent* ent;
int result = 0;
dir = opendir(ROM_PATH);
if (dir)
{
int count = 0;
while ( (ent = readdir(dir)) != NULL )
{
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)
{
if (count < num)
{
count++;
continue;
}
else
{
if (fullpath == 0)
sprintf(dest, "%s", ent->d_name);
else
sprintf(dest, "%s%s", ROM_PATH, ent->d_name);
result = 1;
break;
}
}
}
}
}
closedir(dir);
return result;
}
//
static int subCursor = 0;
enum {
INSTALL_MENU_INSTALL,
INSTALL_MENU_DELETE,
INSTALL_MENU_BACK
};
void subMenu()
{
bool printMenu = true;
subCursor = 0;
while (1)
{
swiWaitForVBlank();
scanKeys();
if (printMenu == true)
{
printMenu = false;
consoleSelect(&bottomScreen);
consoleClear();
iprintf("\tInstall\n");
iprintf("\tDelete\n");
iprintf("\tBack - B\n");
}
//Clear cursor
iprintf("\x1b[%d;0H ", subCursor);
//Move cursor
if (keysDown() & KEY_DOWN)
{
if (subCursor < INSTALL_MENU_BACK)
subCursor++;
}
if (keysDown() & KEY_UP)
{
if (subCursor > 0)
subCursor--;
}
//Reprint cursor
iprintf("\x1b[%d;0H>", subCursor);
//
if (keysDown() & KEY_B)
break;
else if (keysDown() & KEY_A)
{
if (subCursor == INSTALL_MENU_INSTALL)
{
char fpath[256];
getFile(fpath, cursor, 1);
char msg[512+1];
msg[512] = '\0';
sprintf(msg, "Are you sure you want to install\n%s\n", fpath);
if (choiceBox(msg) == YES)
install(fpath);
break;
}
else if (subCursor == INSTALL_MENU_DELETE)
{
char fpath[256];
getFile(fpath, cursor, 1);
char msg[512+1];
msg[512] = '\0';
sprintf(msg, "Are you sure you want to delete\n%s\n", fpath);
if (choiceBox(msg) == YES)
{
if (remove(fpath) != 0)
messageBox("File could not be deleted.");
else
messageBox("File deleted.");
//Reset
cursor = 0;
scrolly = 0;
numberOfTitles = getNumberOfTitles();
break;
}
else
{
printMenu = true;
}
}
else if (subCursor == INSTALL_MENU_BACK)
{
break;
}
}
}
}
void install(char* fpath)
{
consoleSelect(&bottomScreen);
consoleClear();
iprintf("Installing %s\n", fpath); swiWaitForVBlank();
FILE* f = fopen(fpath, "rb");
if (!f)
{
iprintf("Error: could not open file.\nPress B to exit.\n");
keyWait(KEY_A | KEY_B);
}
else
{
//Load header
tDSiHeader header;
tNDSBanner banner;
{
fseek(f, 0, SEEK_SET);
fread(&header, sizeof(tDSiHeader), 1, f);
fseek(f, header.ndshdr.bannerOffset, SEEK_SET);
fread(&banner, sizeof(tNDSBanner), 1, f);
}
//Print file size
int fileSize = -1;
{
iprintf("File Size: "); swiWaitForVBlank();
fileSize = getFileSize(f);
printBytes(fileSize);
printf("\n");
}
//Do not want file opened anymore
fclose(f);
//SD card check
{
iprintf("Enough room on SD card?..."); swiWaitForVBlank();
if (getSDCardFree() < fileSize)
{
iprintf("No\n");
goto error;
}
iprintf("Yes\n");
}
//DSi storage check
{
iprintf("Enough room on DSi?..."); swiWaitForVBlank();
if (getDsiFree() < fileSize)
{
iprintf("No\n"); swiWaitForVBlank();
goto error;
}
iprintf("Yes\n"); swiWaitForVBlank();
}
//Menu slot check
{
iprintf("Open DSi menu slot?..."); swiWaitForVBlank();
if (getMenuSlotsFree() <= 0)
{
iprintf("No\n"); swiWaitForVBlank();
goto error;
}
iprintf("Yes\n"); swiWaitForVBlank();
}
//Create title directory
char titleID[8+1];
char dirPath[256];
{
sprintf(titleID, "%08x", (unsigned int)header.tid_low);
sprintf(dirPath, "/title/%08x/%s", (unsigned int)header.tid_high, titleID);
//iprintf("Creating dir\n%s\n", dirPath); swiWaitForVBlank();
}
//Check if title is already installed
{
DIR* dir = opendir(dirPath);
if (dir)
{
closedir(dir);
iprintf("Title %s is already used.\nInstall anyway?\n", titleID);
iprintf("Yes - A\nNo - B\n");
while (1)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & KEY_A)
break;
if (keysDown() & KEY_B)
goto complete;
}
}
}
//
mkdir(dirPath, 0777);
//Content folder
{
char contentPath[256];
sprintf(contentPath, "%s/content", dirPath);
//iprintf("Creating dir\n%s\n", contentPath); swiWaitForVBlank();
mkdir(contentPath, 0777);
//Create 0000000.app
//Does 00000000 always work?
{
char appPath[256];
sprintf(appPath, "%s/00000000.app", contentPath);
iprintf("Creating 00000000.app..."); swiWaitForVBlank();
if (copyFile(fpath, appPath) == 0)
{
iprintf("Failed\n");
goto error;
}
iprintf("Done\n");
//Make TMD
{
char tmdPath[256];
sprintf(tmdPath, "%s/title.tmd", contentPath);
if (maketmd(appPath, tmdPath) != 0)
goto error;
}
}
}
//Data folder
{
char dataPath[256];
sprintf(dataPath, "%s/data", dirPath);
//iprintf("Creating dir\n%s\n", dataPath); swiWaitForVBlank();
mkdir(dataPath, 0777);
//If needed, create public.sav
if (header.public_sav_size > 0)
{
char publicPath[512];
sprintf(publicPath, "%s/public.sav", dataPath);
iprintf("Creating public.sav..."); swiWaitForVBlank();
FILE* file = fopen(publicPath, "wb");
if (!file)
iprintf("Failed\n");
else
{
char num = 0;
repeat (header.public_sav_size)
fwrite(&num, 1, 1, f);
iprintf("Done\n");
}
fclose(file);
}
//If needed, create private.sav
if (header.private_sav_size > 0)
{
char privatePath[512];
sprintf(privatePath, "%s/private.sav", dataPath);
iprintf("Creating private.sav..."); swiWaitForVBlank();
FILE* file = fopen(privatePath, "wb");
if (!file)
iprintf("Failed\n");
else
{
char num = 0;
repeat (header.private_sav_size)
fwrite(&num, 1, 1, f);
iprintf("Done\n");
}
fclose(file);
}
//If needed, create banner.sav
if (header.appflags & 0x4)
{
char bannerPath[512];
sprintf(bannerPath, "%s/banner.sav", dataPath);
iprintf("Creating banner.sav..."); swiWaitForVBlank();
FILE* file = fopen(bannerPath, "wb");
if (!file)
iprintf("Failed\n");
else
{
char num = 0;
repeat (1024*16) //Is banner.sav always 16kb?
fwrite(&num, 1, 1, f);
iprintf("Done\n");
}
fclose(file);
}
}
iprintf("\nInstallation complete.\nPress B to exit.\n");
keyWait(KEY_A | KEY_B);
}
complete:
fclose(f);
return;
error:
fclose(f);
iprintf("\nInstallation failed.\nPress B to exit.\n");
keyWait(KEY_A | KEY_B);
return;
}

133
src/main.c Normal file
View File

@ -0,0 +1,133 @@
#include "main.h"
#include "menus.h"
#define VERSION "0.4"
enum {
MAIN_MENU_INSTALL,
MAIN_MENU_TITLES,
// MAIN_MENU_RESTORE,
MAIN_MENU_TEST,
MAIN_MENU_EXIT
};
static int mainMenu();
int main(int argc, char **argv)
{
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);
if (!fatInitDefault())
{
consoleSelect(&bottomScreen);
consoleClear();
iprintf("fatInitDefault...Failed\n");
iprintf("\nPress B to exit.\n");
keyWait(KEY_B | KEY_A | KEY_START);
}
else
{
bool programEnd = false;
while (!programEnd)
{
switch (mainMenu())
{
case MAIN_MENU_INSTALL:
installMenu();
break;
case MAIN_MENU_TITLES:
titleMenu();
break;
case MAIN_MENU_TEST:
testMenu();
break;
case MAIN_MENU_EXIT:
programEnd = true;
break;
}
}
}
return 0;
}
static int cursor = 0;
int mainMenu()
{
consoleSelect(&topScreen);
consoleClear();
iprintf("\tTitle Manager for HiyaCFW\n");
iprintf("\nversion %s\n", VERSION);
iprintf("\x1b[23;0HJeff - 2018");
consoleSelect(&bottomScreen);
consoleClear();
iprintf("\tInstall\n");
iprintf("\tTitles\n");
// iprintf("\tRestore\n");
iprintf("\tTest\n");
iprintf("\tExit");
while (1)
{
swiWaitForVBlank();
scanKeys();
//Clear cursor
iprintf("\x1b[%d;0H ", cursor);
if (keysDown() & KEY_DOWN)
{
if ( (cursor += 1) > MAIN_MENU_EXIT )
cursor = 0;
}
if (keysDown() & KEY_RIGHT)
{
repeat (10)
{
if ( (cursor += 1) > MAIN_MENU_EXIT )
cursor = 0;
}
}
if (keysDown() & KEY_UP)
{
if ( (cursor -= 1) < 0 )
cursor = MAIN_MENU_EXIT;
}
if (keysDown() & KEY_LEFT)
{
repeat (10)
{
if ( (cursor -= 1) < 0 )
cursor = MAIN_MENU_EXIT;
}
}
//print cursor
iprintf("\x1b[%d;0H>", cursor);
if (keysDown() & KEY_A)
break;
}
return cursor;
}

15
src/main.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef MAIN_H
#define MAIN_H
#include <nds.h>
#include <fat.h>
#include <stdio.h>
PrintConsole topScreen;
PrintConsole bottomScreen;
#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

173
src/maketmd.c Normal file
View File

@ -0,0 +1,173 @@
/*---------------------------------------------------------------------------------
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)
{
fseek(app, 0, SEEK_END);
uint32_t size = ftell(app);
size = __bswap32(size);
// 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);
swiSHA1Update(&ctx, buffer, buffer_read);
} while(buffer_read == SHA_BUFFER_SIZE);
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;
}

6
src/maketmd.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef MAKETMD_H
#define MAKETMD_H
int maketmd(char* input, char* tmdPath);
#endif

72
src/menus.c Normal file
View File

@ -0,0 +1,72 @@
#include "menus.h"
void keyWait(u32 key)
{
while (1)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & key)
break;
}
}
int choiceBox(char* message)
{
const int choiceRow = 10;
int cursor = 0;
consoleSelect(&bottomScreen);
consoleClear();
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;
}
void messageBox(char* message)
{
consoleSelect(&bottomScreen);
consoleClear();
iprintf("%s\n", message);
iprintf("\nOkay - A\n");
while (1)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & (KEY_A | KEY_START))
break;
}
scanKeys();
}

19
src/menus.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef MENUS_H
#define MENUS_H
#include "main.h"
//Text box choices
#define YES 1
#define NO 0
void titleMenu();
void installMenu();
void testMenu();
void keyWait(u32 key);
int choiceBox(char* message);
void messageBox(char* message);
#endif

493
src/storage.c Normal file
View File

@ -0,0 +1,493 @@
#include "storage.h"
#include "main.h"
#include <nds.h>
#include <string.h>
#include <dirent.h>
//#include <nds/arm7/sdmmc.h>
#define TITLE_LIMIT 39
//Printing
void printBytes(int bytes)
{
if (abs(bytes) < 1024)
iprintf("%dB", bytes);
else if (abs(bytes) < 1024 * 1024)
printf("%.2fKB", (float)bytes / 1024);
else if (abs(bytes) < 1024 * 1024 * 1024)
printf("%.2fMB", (float)bytes / 1024 / 1024);
else
printf("%.2fGB", (float)bytes / 1024 / 1024 / 1024);
}
void printFileInfo(const char* path)
{
tDSiHeader header;
tNDSBanner banner;
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];
gameTitle[128] = '\0';
//Convert 2 byte characters to 1 byte
for (int i = 0; i < 128; i++)
gameTitle[i] = (char)banner.titles[1][i];
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");
}
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);
}
//Progress bar
void printProgressBar(int progress, int total)
{
iprintf("\x1b[23;0H[");
iprintf("\x1b[23;31H]");
float bar = ((float)progress / (float)total) * 30.f;
for (int i = 0; i < bar; i++)
iprintf("\x1b[23;%dH|", 1 + i);
}
void clearProgressBar()
{
iprintf("\x1b[23;0H ");
}
//Files
int copyFile(const char* in, char* out)
{
int result = 0;
if (in == NULL || out == NULL)
return 0;
FILE* fin = fopen(in, "rb");
FILE* fout = fopen(out, "wb");
if (fin == NULL || fout == NULL)
{
fclose(fin);
fclose(fout);
return 0;
}
else
{
consoleSelect(&topScreen);
int fileSize = getFileSize(fin);
int totalBytesRead = 0;
int progressTimer = 100;
const int buffSize = 1024*32; //Arbitrary. A value too large freezes the system.
unsigned char* buffer = (unsigned char*)malloc(sizeof(unsigned char) * buffSize);
while (1)
{
int bytesRead = fread(buffer, 1, buffSize, fin);
fwrite(buffer, 1, bytesRead, fout);
totalBytesRead += bytesRead;
//Re-print progress bar every so often, but not every time
if ((progressTimer += 1) >= 25)
{
progressTimer = 0;
printProgressBar(totalBytesRead, fileSize);
}
if (feof(fin))
{
result = 1;
break;
}
if (ferror(fin))
{
result = 0;
break;
}
}
free(buffer);
clearProgressBar();
consoleSelect(&bottomScreen);
}
fclose(fin);
fclose(fout);
return result;
}
int getFileSize(FILE* f)
{
if (!f)
return 0;
fseek(f, 0, SEEK_END);
int size = ftell(f);
fseek(f, 0, SEEK_SET);
return size;
}
int getFileSizePath(const char* path)
{
if (path == NULL)
return -1;
FILE* f = fopen(path, "rb");
int size = getFileSize(f);
fclose(f);
return size;
}
//Directories
int dirExists(const char* path)
{
if (path == NULL)
return 0;
DIR* dir = opendir(path);
if (dir)
{
closedir(dir);
return 1;
}
closedir(dir);
return 0;
}
/* Incomplete
int copyDir(char* in, char* out)
{
if (in == NULL || out == NULL)
return 0;
DIR* dir;
struct dirent *ent;
dir = opendir(in);
if (!dir)
{
closedir(dir);
return 0;
}
else
{
mkdir(out, 0777);
while ( (ent = readdir(dir)) != NULL )
{
if(strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
char inPath[512];
char outPath[512];
sprintf(inPath, "%s%s/", in, ent->d_name);
sprintf(outPath, "%s%s/", out, ent->d_name);
copyDir(inPath, outPath);
}
else
{
char inPath[512];
char outPath[512];
sprintf(inPath, "%s%s", in, ent->d_name);
sprintf(outPath, "%s%s", out, ent->d_name);
copyFile(inPath, outPath);
}
}
}
closedir(dir);
return 1;
}
*/
int deleteDir(const char* path)
{
if (strcmp("/", path) == 0)
{
//Oh fuck no
return 0;
}
DIR* dir;
struct dirent *ent;
dir = opendir(path);
if (!dir)
{
closedir(dir);
return 0;
}
else
{
while ( (ent = readdir(dir)) != NULL )
{
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);
deleteDir(subpath);
}
else
{
//Delete file
char fpath[512];
sprintf(fpath, "%s%s", path, ent->d_name);
remove(fpath);
}
}
}
closedir(dir);
rmdir(path);
return 1;
}
int getDirSize(const char* path)
{
if (path == NULL)
return 0;
int size = 0;
DIR* dir;
struct dirent *ent;
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)
{
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;
}
#define NUM_OF_DIRECTORIES 3
static const char* directories[] = {
"00030004",
"00030005",
"00030015"
};
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
int freeSlots = TITLE_LIMIT;
DIR* dir;
struct dirent* ent;
for (int i = 0; i < NUM_OF_DIRECTORIES; i++)
{
char path[256];
sprintf(path, "/title/%s", directories[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
int sdIsInserted()
{
// Undefined reference
// return sdmmc_cardinserted();
return 1;
}
int getSDCardSize()
{
if (sdIsInserted())
{
struct statvfs st;
if (statvfs("/", &st) == 0)
return st.f_bsize * st.f_blocks;
}
return 0;
}
int 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;
return 248 * 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/");
return size;
}

47
src/storage.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef STORAGE_H
#define STORAGE_H
#include <stdio.h>
#define BACKUP_PATH "/titlebackup/"
#define ROM_PATH "/dsi/"
#define BYTES_PER_BLOCK (1024*128)
//Printing
void printBytes(int bytes);
void printFileInfo(const char* path);
//Progress bar
void printProgressBar(int progress, int total);
void clearProgressBar();
//Files
int copyFile(const char* in, char* out);
int getFileSize(FILE* f);
int getFileSizePath(const char* path);
//Directories
int dirExists(const char* path);
//int copyDir(const char* in, char* out);
int deleteDir(const char* path);
int getDirSize(const char* path);
//Home menu
int getMenuSlots();
int getMenuSlotsFree();
#define getMenuSlotsUsed() (getMenuSlots() - getMenuSlotsFree())
//SD Card
int sdIsInserted();
int getSDCardSize();
int getSDCardFree();
#define getSDCardUsedSpace() (getSDCardSize() - getSDCardFree())
//Internal storage
int getDsiSize();
int getDsiFree();
#define getDsiUsed() (getDSIStorageSize() - getDSIStorageFree())
#endif

63
src/testmenu.c Normal file
View File

@ -0,0 +1,63 @@
#include "menus.h"
#include "storage.h"
void testMenu()
{
consoleSelect(&topScreen);
consoleClear();
iprintf("Storage Check Test\n\n");
iprintf("Verify these are accurate and\nreport if they're not.\n");
consoleSelect(&bottomScreen);
consoleClear();
int free = -1;
int size = -1;
//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();
free = getSDCardFree();
printBytes(free);
iprintf(" / "); swiWaitForVBlank();
size = getSDCardSize();
printBytes(size);
iprintf("\n"); swiWaitForVBlank();
printf("\t%.0f / %.0f blocks\n", (float)free / BYTES_PER_BLOCK, (float)size / 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);
}
//
iprintf("\nBack - B\n");
keyWait(KEY_B);
}

497
src/titlemenu.c Normal file
View File

@ -0,0 +1,497 @@
#include "menus.h"
#include "storage.h"
#include <dirent.h>
#define NUM_OF_DIRECTORIES 3
static const char* directories[] = {
"00030004",
"00030005",
"00030015"
};
static int cursor = 0;
static int scrolly = 0;
static int numberOfTitles = 0;
static void moveCursor(int dir);
static void printList();
static void printTitleInfo(int num);
static int getNumberOfTitles();
static int getTitle(int num, char* title, char* path);
static void subMenu();
void titleMenu()
{
cursor = 0;
scrolly = 0;
numberOfTitles = getNumberOfTitles();
consoleSelect(&topScreen);
consoleClear();
consoleSelect(&bottomScreen);
consoleClear();
//No titles error
if (numberOfTitles <= 0)
{
iprintf("No titles found.\n");
iprintf("Back - B\n");
keyWait(KEY_B | KEY_A | KEY_START);
return;
}
//Print data
consoleSelect(&topScreen);
printTitleInfo(cursor);
consoleSelect(&bottomScreen);
printList();
while (1)
{
swiWaitForVBlank();
scanKeys();
int thisScrolly = scrolly;
int thisCursor = cursor;
//Clear cursor
consoleSelect(&bottomScreen);
iprintf("\x1b[%d;0H ", cursor - scrolly);
//Move cursor
if (keysDown() & KEY_DOWN)
moveCursor(1);
if (keysDown() & KEY_UP)
moveCursor(-1);
if (keysDown() & KEY_RIGHT)
{
repeat (10)
moveCursor(1);
}
if (keysDown() & KEY_LEFT)
{
repeat (10)
moveCursor(-1);
}
//Re-print list
if (thisCursor != cursor)
{
consoleSelect(&topScreen);
printTitleInfo(cursor);
}
if (thisScrolly != scrolly)
{
consoleSelect(&bottomScreen);
printList();
}
//Print cursor
consoleSelect(&bottomScreen);
iprintf("\x1b[%d;0H>", cursor - scrolly);
//
if (keysDown() & KEY_B)
break;
else if (keysDown() & KEY_A)
{
subMenu();
consoleSelect(&topScreen);
printTitleInfo(cursor);
consoleSelect(&bottomScreen);
printList();
}
}
}
void moveCursor(int dir)
{
cursor += sign(dir);
if (cursor < 0)
cursor = 0;
if (cursor >= numberOfTitles - 1)
cursor = numberOfTitles - 1;
if (cursor - scrolly >= 23)
scrolly += 1;
if (cursor - scrolly < 0)
scrolly -= 1;
}
void printList()
{
consoleClear();
for (int i = scrolly; i < scrolly + 23; i++)
{
char title[256];
if (getTitle(i, title, NULL) == 1)
iprintf(" %.30s\n", title);
}
//Scroll arrows
if (scrolly > 0)
iprintf("\x1b[0;31H^");
if (scrolly < numberOfTitles - 23)
iprintf("\x1b[22;31Hv");
}
void printTitleInfo(int num)
{
consoleClear();
char path[256];
if (getTitle(num, NULL, path) == 1)
printFileInfo(path);
}
int getNumberOfTitles()
{
int count = 0;
//Scan choice title directories
for (int i = 0; i < NUM_OF_DIRECTORIES; i++)
{
DIR* dir;
struct dirent* ent;
char dirPath[256];
sprintf(dirPath, "/title/%s", directories[i]);
dir = opendir(dirPath);
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)
{
//Search for an .app file
char contentPath[384];
sprintf(contentPath, "%s/%s/content", dirPath, ent->d_name);
DIR* subdir;
struct dirent* subent;
subdir = opendir(contentPath);
if (subdir)
{
while ( (subent = readdir(subdir)) != NULL )
{
if (strcmp(".", subent->d_name) == 0 || strcmp("..", subent->d_name) == 0)
continue;
//Found a title
if (strstr(subent->d_name, ".app") != NULL)
count++;
}
}
closedir(subdir);
}
}
}
closedir(dir);
}
return count;
}
int getTitle(int num, char* title, char* path)
{
int result = 0;
int count = 0;
//Scan choice title directories
for (int i = 0; i < NUM_OF_DIRECTORIES && result == 0; i++)
{
DIR* dir;
struct dirent* ent;
char dirPath[256];
sprintf(dirPath, "/title/%s", directories[i]);
dir = opendir(dirPath);
if (dir)
{
while ( (ent = readdir(dir)) != NULL && result == 0)
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
//Scan content folder
char contentPath[384];
sprintf(contentPath, "%s/%s/content", dirPath, ent->d_name);
DIR* subdir;
struct dirent* subent;
subdir = opendir(contentPath);
if (subdir)
{
while ( (subent = readdir(subdir)) != NULL && result == 0)
{
if (strcmp(".", subent->d_name) == 0 || strcmp("..", subent->d_name) == 0)
continue;
if (subent->d_type != DT_DIR)
{
if (strstr(subent->d_name, ".app") != NULL)
{
if (count < num)
count++;
else
{
//Found requested title
char filepath[384];
sprintf(filepath, "%s/%s", contentPath, subent->d_name);
//Output title
if (title != NULL)
{
FILE* f = fopen(filepath, "rb");
if (!f)
{
sprintf(title, " ");
}
else
{
tNDSHeader header;
tNDSBanner banner;
fread(&header, sizeof(tNDSHeader), 1, f);
fseek(f, header.bannerOffset, SEEK_SET);
fread(&banner, sizeof(tNDSBanner), 1, f);
char tstr[128+1];
tstr[128] = '\0';
for (int i = 0; i < 128; i++)
{
char c = banner.titles[1][i];
if (c == '\n')
c = ' ';
tstr[i] = c;
}
sprintf(title, "%s", tstr);
}
fclose(f);
}
//Output path
if (path != NULL)
{
sprintf(path, "%s", filepath);
}
//Exit this mess
result = 1;
break;
}
}
}
}
}
closedir(subdir);
}
}
}
closedir(dir);
}
return result;
}
//
static int subCursor = 0;
enum {
// TITLE_MENU_BACKUP,
TITLE_MENU_DUMP,
TITLE_MENU_DELETE,
TITLE_MENU_BACK
};
void subMenu()
{
subCursor = 0;
consoleSelect(&bottomScreen);
consoleClear();
// iprintf("\tBackup\n");
iprintf("\tDump\n");
iprintf("\tDelete\n");
iprintf("\tBack\n");
while (1)
{
swiWaitForVBlank();
scanKeys();
//Clear cursor
iprintf("\x1b[%d;0H ", subCursor);
//Move cursor
if (keysDown() & KEY_DOWN)
{
if (subCursor < TITLE_MENU_BACK)
subCursor += 1;
}
if (keysDown() & KEY_UP)
{
if (subCursor > 0)
subCursor -= 1;
}
//Print cursor
iprintf("\x1b[%d;0H>", subCursor);
if (keysDown() & KEY_A)
{
char title[256];
char path[256];
getTitle(cursor, title, path);
//Only get first line of title
for (int i = 0; i < 256; i++)
{
if (title[i] == '\n')
{
title[i] = '\0';
break;
}
}
/* //
if (subCursor == TITLE_MENU_BACKUP)
{
char dir[256];
sprintf(dir, "%.24s", path);
char msg[512];
sprintf(msg, "Are you sure you want to backup\n%s", dir);
if (choiceBox(msg) == YES)
{
if (getDirSize(dir) > getSDCardFree())
messageBox("Error, not enough space on SD card.\nTitle backup failed.");
else
{
if (copyDir(dir, BACKUP_PATH) == 1)
messageBox("Title was backed up.");
else
messageBox("Title backup failed.");
}
}
break;
}
*/
if (subCursor == TITLE_MENU_DUMP)
{
char fpath[256];
if (getTitle(cursor, NULL, fpath) == 1)
{
FILE* f = fopen(fpath, "rb");
if (!f)
messageBox("Can not dump title.\n");
else
{
tNDSHeader header;
tNDSBanner banner;
fread(&header, sizeof(tNDSHeader), 1, f);
fseek(f, header.bannerOffset, SEEK_SET);
fread(&banner, sizeof(tNDSBanner), 1, f);
fclose(f);
char outpath[256];
sprintf(outpath, "%s%.12s - %.4s.nds", ROM_PATH, header.gameTitle, header.gameCode);
char msg[512];
sprintf(msg, "Dump title to\n%s\n", outpath);
if (choiceBox(msg) == YES)
{
if (copyFile(fpath, outpath) == 1)
messageBox("Title saved.\n");
else
messageBox("Title dump failed.\n");
}
}
}
break;
}
else if (subCursor == TITLE_MENU_DELETE)
{
char msg[512];
sprintf(msg, "Are you sure you want to delete\n%s", title);
if (choiceBox(msg) == YES)
{
char dirPath[256];
sprintf(dirPath, "%.25s", path);
if (deleteDir(dirPath) == 1)
messageBox("Title deleted.\n");
else
messageBox("Title could not be deleted.\n");
//Reset main menu
cursor = 0;
scrolly = 0;
numberOfTitles = getNumberOfTitles();
}
break;
}
else if (subCursor == TITLE_MENU_BACK)
{
break;
}
}
else if (keysDown() & KEY_B)
break;
}
}