WIP: Use bitmap mode and clean up some things

This commit is contained in:
Pk11 2021-08-08 07:31:00 -05:00
parent 2e83467ab5
commit 2ca30a9bd2
19 changed files with 1443 additions and 1448 deletions

2
.gitignore vendored
View File

@ -3,7 +3,7 @@
*.nds *.nds
*.cia *.cia
*.elf *.elf
data/* data/*.bin
.vscode .vscode
*.DS_Store *.DS_Store

163
Makefile
View File

@ -9,149 +9,32 @@ endif
include $(DEVKITARM)/ds_rules include $(DEVKITARM)/ds_rules
export VERSION_MAJOR := 1 export TARGET := GodMode9i
export VERSION_MINOR := 1
export VERSION_PATCH := 0
VERSION := $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)
#---------------------------------------------------------------------------------
# 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
# DATA is a list of directories containing binary files embedded using bin2o
# GRAPHICS is a list of directories containing image files to be converted with grit
#---------------------------------------------------------------------------------
TARGET := GodMode9i
BUILD := build
SOURCES := source
INCLUDES := include source
DATA := data
GRAPHICS := gfx
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -mthumb -mthumb-interwork
CFLAGS := -g -Wall -O2 \
-ffunction-sections -fdata-sections \
-march=armv5te -mtune=arm946e-s -fomit-frame-pointer\
-ffast-math \
$(ARCH)
CFLAGS += $(INCLUDE) -DARM9
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=c++11
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=ds_arm9.specs -g -Wl,--gc-sections $(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 TOPDIR := $(CURDIR)
export OUTPUT := $(CURDIR)/$(TARGET)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
$(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
BMPFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.bmp)))
PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := load.bin bootstub.bin
#---------------------------------------------------------------------------------
# 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),-iquote $(CURDIR)/$(dir)) \
$(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
export GAME_TITLE := $(TARGET) export GAME_TITLE := $(TARGET)
.PHONY: bootloader bootstub clean arm7/$(TARGET).elf arm9/$(TARGET).elf .PHONY: all bootloader bootstub clean dsi arm7/$(TARGET).elf arm9/$(TARGET).elf
all: bootloader bootstub $(TARGET).nds all: bootloader bootstub $(TARGET).nds
dsi: $(TARGET).dsi dsi: $(TARGET).dsi
dist: all $(TARGET).nds: arm7/$(TARGET).elf arm9/$(TARGET).elf
@rm -fr hbmenu ndstool -c $(TARGET).nds -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \
@mkdir hbmenu
@cp $(TARGET).nds hbmenu/BOOT.NDS
@cp BootStrap/_BOOT_MP.NDS BootStrap/TTMENU.DAT BootStrap/_DS_MENU.DAT BootStrap/ez5sys.bin BootStrap/akmenu4.nds hbmenu
@tar -cvjf $(TARGET)-$(VERSION).tar.bz2 hbmenu testfiles README.html COPYING hbmenu -X exclude.lst
$(TARGET).nds: $(TARGET).arm7 $(TARGET).arm9
ndstool -c $(TARGET).nds -7 $(TARGET).arm7.elf -9 $(TARGET).arm9.elf \
-b icon.bmp "GodMode9i;RocketRobz" \ -b icon.bmp "GodMode9i;RocketRobz" \
-z 80040000 -u 00030004 -z 80040000 -u 00030004
python fix_ndsheader.py $(CURDIR)/$(TARGET).nds python fix_ndsheader.py $(CURDIR)/$(TARGET).nds
$(TARGET).dsi: $(TARGET).arm7 $(TARGET).arm9 $(TARGET).dsi: arm7/$(TARGET).elf arm9/$(TARGET).elf
ndstool -c $(TARGET).dsi -7 $(TARGET).arm7.elf -9 $(TARGET).arm9.elf \ ndstool -c $(TARGET).dsi -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \
-b icon.bmp "GodMode9i;RocketRobz" \ -b icon.bmp "GodMode9i;RocketRobz" \
-g HGMA 00 "GODMODE9I" -z 80040000 -u 00030004 -g HGMA 00 "GODMODE9I" -z 80040000 -u 00030004
python fix_ndsheader.py $(CURDIR)/$(TARGET).dsi python fix_ndsheader.py $(CURDIR)/$(TARGET).dsi
$(TARGET).arm7: arm7/$(TARGET).elf
cp arm7/$(TARGET).elf $(TARGET).arm7.elf
$(TARGET).arm9: arm9/$(TARGET).elf
cp arm9/$(TARGET).elf $(TARGET).arm9.elf
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
arm7/$(TARGET).elf: arm7/$(TARGET).elf:
@$(MAKE) -C arm7 @$(MAKE) -C arm7
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
arm9/$(TARGET).elf: arm9/$(TARGET).elf:
@$(MAKE) -C arm9 @$(MAKE) -C arm9
@ -163,7 +46,7 @@ arm9/$(TARGET).elf:
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
clean: clean:
@echo clean ... @echo clean ...
@rm -fr data @rm -fr data/*.bin
@rm -fr $(BUILD) $(TARGET).elf $(TARGET).nds @rm -fr $(BUILD) $(TARGET).elf $(TARGET).nds
@rm -fr $(TARGET).arm7.elf @rm -fr $(TARGET).arm7.elf
@rm -fr $(TARGET).arm9.elf @rm -fr $(TARGET).arm9.elf
@ -172,32 +55,8 @@ clean:
@$(MAKE) -C arm9 clean @$(MAKE) -C arm9 clean
@$(MAKE) -C arm7 clean @$(MAKE) -C arm7 clean
data:
@mkdir -p data
bootloader: data bootloader: data
@$(MAKE) -C bootloader LOADBIN=$(CURDIR)/data/load.bin @$(MAKE) -C bootloader LOADBIN=$(CURDIR)/data/load.bin
bootstub: data bootstub: data
@$(MAKE) -C bootstub @$(MAKE) -C bootstub
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
#$(OUTPUT).nds : $(OUTPUT).elf
#$(OUTPUT).elf : $(OFILES)
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
$(bin2o)
-include $(DEPSDIR)/*.d
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

View File

@ -37,8 +37,8 @@ endif
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
TARGET := GodMode9i TARGET := GodMode9i
BUILD := build BUILD := build
SOURCES := source dldi-include ramdrive-include mbedtls SOURCES := source source/graphics dldi-include ramdrive-include mbedtls
INCLUDES := include dldi-include ramdrive-include source INCLUDES := include dldi-include ramdrive-include source source/graphics
DATA := ../data DATA := ../data
GRAPHICS := ../gfx GRAPHICS := ../gfx
@ -53,7 +53,7 @@ CFLAGS := -g -Wall -O2\
$(ARCH) $(ARCH)
CFLAGS += $(INCLUDE) -DARM9 -D_NO_BOOTSTUB_ CFLAGS += $(INCLUDE) -DARM9 -D_NO_BOOTSTUB_
CXXFLAGS := $(CFLAGS) -fno-exceptions -std=gnu++11 CXXFLAGS := $(CFLAGS) -fno-exceptions -std=gnu++17
ASFLAGS := -g $(ARCH) ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=../ds_arm9_hi.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) LDFLAGS = -specs=../ds_arm9_hi.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
@ -62,14 +62,13 @@ LDFLAGS = -specs=../ds_arm9_hi.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
# any extra libraries we wish to link with the project (order is important) # any extra libraries we wish to link with the project (order is important)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
LIBS := -lfat -lnds9 LIBS := -lfat -lnds9
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing # list of directories containing libraries, this must be the top level containing
# include and lib # include and lib
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
LIBDIRS := $(CURDIR) ../ $(LIBNDS) LIBDIRS := $(CURDIR) ../ $(LIBNDS)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional # no real need to edit anything past this point unless you need to add additional
# rules for different file extensions # rules for different file extensions
@ -91,7 +90,7 @@ BMPFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.bmp)))
PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png))) PNGFILES := $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.png)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C # use CXX for linking C++ projects, CC for standard C
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@ -110,20 +109,20 @@ export OFILES := $(addsuffix .o,$(BINFILES)) \
$(BMPFILES:.bmp=.o) \ $(BMPFILES:.bmp=.o) \
$(PNGFILES:.png=.o) \ $(PNGFILES:.png=.o) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \ export INCLUDE := $(foreach dir,$(INCLUDES),-iquote $(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \ $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD) -I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export OUTPUT := $(CURDIR)/$(TARGET) export OUTPUT := $(CURDIR)/$(TARGET)
.PHONY: $(BUILD) clean .PHONY: $(BUILD) clean
all : $(BUILD) all : $(BUILD)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
$(BUILD): $(BUILD):
@[ -d $@ ] || mkdir -p $@ @[ -d $@ ] || mkdir -p $@
@ -135,38 +134,43 @@ clean:
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
else else
DEPENDS := $(OFILES:.o=.d) DEPENDS := $(OFILES:.o=.d)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# main targets # main targets
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
$(OUTPUT).elf : $(OFILES) $(OUTPUT).elf : $(OFILES)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
%.bin.o : %.bin %.bin.o : %.bin
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
@echo $(notdir $<) @echo $(notdir $<)
$(bin2o) $(bin2o)
#---------------------------------------------------------------------------------
%.frf.o : %.frf
#---------------------------------------------------------------------------------
@echo $(notdir $<)
$(bin2o)
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
# This rule creates assembly source files using grit # This rule creates assembly source files using grit
# grit takes an image file and a .grit describing how the file is to be processed # grit takes an image file and a .grit describing how the file is to be processed
# add additional rules like this for each image extension # add additional rules like this for each image extension
# you use in the graphics folders # you use in the graphics folders
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
%.s %.h : %.bmp %.grit %.s %.h : %.bmp %.grit
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
grit $< -fts -o$* grit $< -fts -o$*
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
%.s %.h : %.png %.grit %.s %.h : %.png %.grit
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
grit $< -fts -o$* grit $< -fts -o$*
-include $(DEPSDIR)/*.d -include $(DEPSDIR)/*.d
#--------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------
endif endif
#--------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------

View File

@ -1,23 +1,29 @@
#ifndef _bmp_h_ #ifndef _bmp_h_
#define _bmp_h_ #define _bmp_h_
#include <nds/ndstypes.h>
typedef struct { typedef struct {
u16 type; /* Magic identifier */ u16 type; /* Magic identifier */
u32 size; /* File size in bytes */ u32 size; /* File size in bytes */
u16 reserved1, reserved2; u16 reserved1, reserved2;
u32 offset; /* Offset to image data, bytes */ u32 offset; /* Offset to image data, bytes */
} PACKED HEADER; } PACKED HEADER;
typedef struct { typedef struct {
u32 size; /* Header size in bytes */ u32 size; /* Header size in bytes */
u32 width,height; /* Width and height of image */ u32 width, height; /* Width and height of image */
u16 planes; /* Number of colour planes */ u16 planes; /* Number of colour planes */
u16 bits; /* Bits per pixel */ u16 bits; /* Bits per pixel */
u32 compression; /* Compression type */ u32 compression; /* Compression type */
u32 imagesize; /* Image size in bytes */ u32 imagesize; /* Image size in bytes */
u32 xresolution,yresolution; /* Pixels per meter */ u32 xresolution, yresolution; /* Pixels per meter */
u32 ncolours; /* Number of colours */ u32 ncolours; /* Number of colours */
u32 importantcolours; /* Important colours */ u32 importantcolours; /* Important colours */
u32 redBitmask; /* Red bitmask */
u32 greenBitmask; /* Green bitmask */
u32 blueBitmask; /* Blue bitmask */
u32 reserved;
} PACKED INFOHEADER; } PACKED INFOHEADER;
#endif //_bmp_h_ #endif //_bmp_h_

View File

@ -1,2 +1,6 @@
void screenshot(const char* filename); #ifndef SCREENSHOT_H
void screenshotbmp(const char* filename); #define SCREENSHOT_H
bool screenshot(void);
#endif // SCREENSHOT_H

View File

@ -33,214 +33,178 @@
#include "dumpOperations.h" #include "dumpOperations.h"
#include "driveOperations.h" #include "driveOperations.h"
#include "fileOperations.h" #include "fileOperations.h"
#include "font.h"
#define SCREEN_COLS 32
#define ENTRIES_PER_SCREEN 22
#define ENTRIES_START_ROW 1 #define ENTRIES_START_ROW 1
#define ENTRY_PAGE_LENGTH 10 #define ENTRY_PAGE_LENGTH 10
#define sizeOfdmAssignedOp 8 enum class DriveMenuOperation {
none,
sdCard,
flashcard,
ramDrive1,
ramDrive2,
sysNand,
nitroFs,
fatImage,
gbaCart,
ndsCard,
};
//static bool ramDumped = false; //static bool ramDumped = false;
bool flashcardMountSkipped = true; bool flashcardMountSkipped = true;
static bool flashcardMountRan = true; static bool flashcardMountRan = true;
static bool dmTextPrinted = false;
static int dmCursorPosition = 0; static int dmCursorPosition = 0;
static int dmAssignedOp[sizeOfdmAssignedOp] = {-1}; static std::vector<DriveMenuOperation> dmOperations;
static int dmMaxCursors = -1;
static u8 gbaFixedValue = 0; static u8 gbaFixedValue = 0;
extern bool arm7SCFGLocked; extern bool arm7SCFGLocked;
extern bool expansionPakFound; extern bool expansionPakFound;
extern PrintConsole topConsole, bottomConsole;
extern void printBorderTop(void);
extern void printBorderBottom(void);
extern void clearBorderTop(void);
extern void clearBorderBottom(void);
void dm_drawTopScreen(void) { void dm_drawTopScreen(void) {
/*if (!ramDumped) { font->clear(true);
printf ("Dumping RAM...");
FILE* destinationFile = fopen("sd:/ramdump.bin", "wb");
fwrite((void*)0x02000000, 1, 0x400000, destinationFile);
fclose(destinationFile);
consoleClear();
ramDumped = true;
}*/
consoleClear(); // Top bar
font->printf(0, 0, true, Alignment::left, Palette::blackGreen, "%*c", 256 / font->width(), ' ');
font->print(0, 0, true, "[root]", Alignment::left, Palette::blackGreen);
printf ("\x1B[30m"); // Print background black color
// Print time // Print time
printf ("\x1b[0;27H"); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
printf (RetTime().c_str());
printf ("\x1b[0;0H"); if (dmOperations.size() == 0) {
printf ("[root]"); font->print(0, 1, true, "No drives found!", Alignment::left, Palette::blackGreen);
printf ("\x1B[47m"); // Print foreground white color
// Move to 2nd row
printf ("\x1b[1;0H");
if (dmMaxCursors == -1) {
printf ("No drives found!");
} else } else
for (int i = 0; i <= dmMaxCursors; i++) { for (int i = 0; i < (int)dmOperations.size(); i++) {
iprintf ("\x1b[%d;0H", i + ENTRIES_START_ROW); Palette pal = dmCursorPosition == i ? Palette::white : Palette::gray;
if (dmCursorPosition == i) { switch(dmOperations[i]) {
printf ("\x1B[47m"); // Print foreground white color case DriveMenuOperation::sdCard:
} else { font->printf(0, i + 1, true, Alignment::left, pal, "[sd:] SDCARD (%s)", sdLabel[0] == 0 ? "UNTITLED" : sdLabel);
printf ("\x1B[40m"); // Print foreground black color break;
} case DriveMenuOperation::flashcard:
if (dmAssignedOp[i] == 0) { font->printf(0, i + 1, true, Alignment::left, pal, "[fat:] FLASHCARD (%s)", fatLabel[0] == 0 ? "UNTITLED" : fatLabel);
printf ("[sd:] SDCARD"); break;
if (sdLabel[0] != '\0') { case DriveMenuOperation::ramDrive1:
iprintf (" (%s)", sdLabel); font->print(0, i + 1, true, "[ram1:] RAMDRIVE", Alignment::left, pal);
} break;
} else if (dmAssignedOp[i] == 1) { case DriveMenuOperation::ramDrive2:
printf ("[fat:] FLASHCART"); font->print(0, i + 1, true, "[ram2:] RAMDRIVE", Alignment::left, pal);
if (fatLabel[0] != '\0') { break;
iprintf (" (%s)", fatLabel); case DriveMenuOperation::sysNand:
} font->print(0, i + 1, true, "[nand:] SYSNAND", Alignment::left, pal);
} else if (dmAssignedOp[i] == 2) { break;
printf ("GBA GAMECART"); case DriveMenuOperation::nitroFs:
if (gbaFixedValue != 0x96) { font->print(0, i + 1, true, "[nitro:] NDS GAME IMAGE", Alignment::left, pal);
iprintf ("\x1b[%d;29H", i + ENTRIES_START_ROW); if (!((sdMounted && nitroCurrentDrive==0)
printf ("[x]"); || (flashcardMounted && nitroCurrentDrive==1)
} || (ramdrive1Mounted && nitroCurrentDrive==2)
} else if (dmAssignedOp[i] == 3) { || (ramdrive2Mounted && nitroCurrentDrive==3)
printf ("[nitro:] NDS GAME IMAGE"); || (nandMounted && nitroCurrentDrive==4)
if ((sdMounted && nitroCurrentDrive==0) || (imgMounted && nitroCurrentDrive==6)))
|| (flashcardMounted && nitroCurrentDrive==1) font->print(256 - font->width(), i + 1, true, "[x]", Alignment::right, pal);
|| (ramdrive1Mounted && nitroCurrentDrive==2) break;
|| (ramdrive2Mounted && nitroCurrentDrive==3) case DriveMenuOperation::fatImage:
|| (nandMounted && nitroCurrentDrive==4) if ((sdMounted && imgCurrentDrive==0)
|| (imgMounted && nitroCurrentDrive==6)) || (flashcardMounted && imgCurrentDrive==1)
{ || (ramdrive1Mounted && imgCurrentDrive==2)
// Do nothing || (ramdrive2Mounted && imgCurrentDrive==3)
} || (nandMounted && imgCurrentDrive==4)) {
else font->printf(0, i + 1, true, Alignment::left, pal, "[nitro:] FAT IMAGE (%s)", imgLabel[0] == 0 ? "UNTITLED" : imgLabel);
{ } else {
iprintf ("\x1b[%d;29H", i + ENTRIES_START_ROW); font->print(0, i + 1, true, "[nitro:] FAT IMAGE", Alignment::left, pal);
printf ("[x]"); font->print(256 - font->width(), i + 1, true, "[x]", Alignment::right, pal);
}
} else if (dmAssignedOp[i] == 4) {
printf ("NDS GAMECARD");
} else if (dmAssignedOp[i] == 5) {
printf ("[ram1:] RAMDRIVE");
} else if (dmAssignedOp[i] == 6) {
printf ("[ram2:] RAMDRIVE");
} else if (dmAssignedOp[i] == 7) {
printf ("[nand:] SYSNAND");
} else if (dmAssignedOp[i] == 8) {
printf ("[img:] FAT IMAGE");
if ((sdMounted && imgCurrentDrive==0)
|| (flashcardMounted && imgCurrentDrive==1)
|| (ramdrive1Mounted && imgCurrentDrive==2)
|| (ramdrive2Mounted && imgCurrentDrive==3)
|| (nandMounted && imgCurrentDrive==4))
{
if (imgLabel[0] != '\0') {
iprintf (" (%s)", imgLabel);
} }
} break;
else case DriveMenuOperation::gbaCart:
{ font->print(0, i + 1, true, "GBA GAMECART", Alignment::left, pal);
iprintf ("\x1b[%d;29H", i + ENTRIES_START_ROW); if (gbaFixedValue != 0x96)
printf ("[x]"); font->print(256 - font->width(), i + 1, true, "[x]", Alignment::right, pal);
} break;
case DriveMenuOperation::ndsCard:
font->print(0, i + 1, true, "NDS GAMECARD", Alignment::left, pal);
break;
case DriveMenuOperation::none:
break;
} }
} }
font->update(true);
} }
void dm_drawBottomScreen(void) { void dm_drawBottomScreen(void) {
consoleClear(); font->clear(false);
printf ("\x1B[47m"); // Print foreground white color int row = -1;
printf ("\x1b[23;0H");
printf (titleName); if (!isDSiMode() && isRegularDS) {
if (nitroMounted || imgMounted) { font->print(0, row--, false, POWERTEXT_DS);
printf ("\n"); } else if (is3DS) {
printf (IMAGETEXT); font->print(0, row--, false, HOMETEXT);
font->print(0, row--, false, POWERTEXT_3DS);
} else {
font->print(0, row--, false, POWERTEXT);
} }
if (sdMountedDone) { if (sdMountedDone) {
if (isRegularDS || sdMounted) { if (isRegularDS || sdMounted) {
printf ("\n"); font->print(0, row--, false, sdMounted ? "R+B - Unmount SD card" : "R+B - Remount SD card");
printf (sdMounted ? "R+B - Unmount SD card" : "R+B - Remount SD card");
} }
} else { } else {
printf ("\n"); font->print(0, row--, false, flashcardMounted ? "R+B - Unmount Flashcard" : "R+B - Remount Flashcard");
printf (flashcardMounted ? "R+B - Unmount Flashcard" : "R+B - Remount Flashcard");
} }
if (sdMounted || flashcardMounted) { if (sdMounted || flashcardMounted) {
printf ("\n"); font->print(0, row--, false, SCREENSHOTTEXT);
printf (SCREENSHOTTEXT);
}
printf ("\n");
if (!isDSiMode() && isRegularDS) {
printf (POWERTEXT_DS);
} else if (is3DS) {
printf (POWERTEXT_3DS);
printf ("\n");
printf (HOMETEXT);
} else {
printf (POWERTEXT);
} }
printf ("\x1B[40m"); // Print foreground black color font->print(0, row--, false, IMAGETEXT);
printf ("\x1b[0;0H"); font->print(0, row--, false, titleName);
if (dmAssignedOp[dmCursorPosition] == 0) {
printf ("[sd:] SDCARD"); switch(dmOperations[dmCursorPosition]) {
if (sdLabel[0] != '\0') { case DriveMenuOperation::sdCard:
iprintf (" (%s)", sdLabel); font->printf(0, 0, false, Alignment::left, Palette::white, "[sd:] SDCARD (%s)", sdLabel[0] == 0 ? "UNTITLED" : sdLabel);
} font->printf(0, 1, false, Alignment::left, Palette::white, "(SD FAT, %s)", getDriveBytes(sdSize).c_str());
printf ("\n(SD FAT, "); font->printf(0, 2, false, Alignment::left, Palette::white, "%s free", getDriveBytes(getBytesFree("sd:/")).c_str());
printDriveBytes(sdSize); break;
printf(")\n"); case DriveMenuOperation::flashcard:
printDriveBytes(getBytesFree("sd:/")); font->printf(0, 0, false, Alignment::left, Palette::white, "[fat:] FLASHCARD (%s)", fatLabel[0] == 0 ? "UNTITLED" : fatLabel);
printf(" space free"); font->printf(0, 1, false, Alignment::left, Palette::white, "(Slot-1 SD FAT, %s)", getDriveBytes(fatSize).c_str());
} else if (dmAssignedOp[dmCursorPosition] == 1) { font->printf(0, 2, false, Alignment::left, Palette::white, "%s free", getDriveBytes(getBytesFree("fat:/")).c_str());
printf ("[fat:] FLASHCART"); break;
if (fatLabel[0] != '\0') { case DriveMenuOperation::gbaCart:
iprintf (" (%s)", fatLabel); font->print(0, 0, false, "GBA GAMECART");
} font->print(0, 1, false, "(GBA Game)");
printf ("\n(Slot-1 SD FAT, "); break;
printDriveBytes(fatSize); case DriveMenuOperation::nitroFs:
printf(")\n"); font->print(0, 0, false, "[nitro:] NDS GAME IMAGE\n");
printDriveBytes(getBytesFree("fat:/")); font->print(0, 1, false, "(Game Virtual)");
printf(" space free"); break;
} else if (dmAssignedOp[dmCursorPosition] == 2) { case DriveMenuOperation::ndsCard:
printf ("GBA GAMECART\n"); font->print(0, 0, false, "NDS GAMECARD\n");
printf ("(GBA Game)"); font->print(0, 1, false, "(NDS Game)");
} else if (dmAssignedOp[dmCursorPosition] == 3) { break;
printf ("[nitro:] NDS GAME IMAGE\n"); case DriveMenuOperation::ramDrive1:
printf ("(Game Virtual)"); font->print(0, 0, false, "[ram1:] RAMDRIVE\n");
} else if (dmAssignedOp[dmCursorPosition] == 4) { font->print(0, 1, false, "(RAMdrive FAT, 9 MB)");
printf ("NDS GAMECARD\n"); break;
printf ("(NDS Game)"); case DriveMenuOperation::ramDrive2:
} else if (dmAssignedOp[dmCursorPosition] == 5) { font->print(0, 0, false, "[ram2:] RAMDRIVE\n");
printf ("[ram1:] RAMDRIVE\n"); font->print(0, 1, false, "(RAMdrive FAT, 16 MB)");
printf ("(RAMdrive FAT, 9 MB)"); break;
} else if (dmAssignedOp[dmCursorPosition] == 6) { case DriveMenuOperation::sysNand:
printf ("[ram2:] RAMDRIVE\n"); font->print(0, 0, false, "[nand:] SYSNAND");
printf ("(RAMdrive FAT, 16 MB)"); font->printf(0, 1, false, Alignment::left, Palette::white, "(SysNAND FAT, %s)", getDriveBytes(fatSize).c_str());
} else if (dmAssignedOp[dmCursorPosition] == 7) { font->printf(0, 2, false, Alignment::left, Palette::white, "%s free", getDriveBytes(getBytesFree("nand:/")).c_str());
printf ("[nand:] SYSNAND"); break;
printf ("\n(SysNAND FAT, "); case DriveMenuOperation::fatImage:
printDriveBytes(nandSize); font->print(0, 0, false, "[img:] FAT IMAGE");
printf(")\n"); font->printf(0, 1, false, Alignment::left, Palette::white, "(Image FAT, %s)", getDriveBytes(imgSize).c_str());
printDriveBytes(getBytesFree("nand:/")); break;
printf(" space free"); case DriveMenuOperation::none:
} else if (dmAssignedOp[dmCursorPosition] == 8) { break;
printf ("[img:] FAT IMAGE");
printf ("\n(Image FAT, ");
printDriveBytes(imgSize);
printf(")");
} }
font->update(false);
} }
void driveMenu (void) { void driveMenu (void) {
@ -252,71 +216,38 @@ void driveMenu (void) {
gbaFixedValue = *(u8*)(0x080000B2); gbaFixedValue = *(u8*)(0x080000B2);
} }
for (int i = 0; i < sizeOfdmAssignedOp; i++) { dmOperations.clear();
dmAssignedOp[i] = -1; if (sdMounted)
} dmOperations.push_back(DriveMenuOperation::sdCard);
dmMaxCursors = -1; if (nandMounted)
if (sdMounted){ dmOperations.push_back(DriveMenuOperation::sysNand);
dmMaxCursors++; if (flashcardMounted)
dmAssignedOp[dmMaxCursors] = 0; dmOperations.push_back(DriveMenuOperation::flashcard);
} if (ramdrive1Mounted)
if (nandMounted) { dmOperations.push_back(DriveMenuOperation::ramDrive1);
dmMaxCursors++; if (ramdrive2Mounted)
dmAssignedOp[dmMaxCursors] = 7; dmOperations.push_back(DriveMenuOperation::ramDrive2);
} if (imgMounted)
if (flashcardMounted) { dmOperations.push_back(DriveMenuOperation::fatImage);
dmMaxCursors++;
dmAssignedOp[dmMaxCursors] = 1;
}
if (ramdrive1Mounted) {
dmMaxCursors++;
dmAssignedOp[dmMaxCursors] = 5;
}
if (ramdrive2Mounted) {
dmMaxCursors++;
dmAssignedOp[dmMaxCursors] = 6;
}
if (imgMounted) {
dmMaxCursors++;
dmAssignedOp[dmMaxCursors] = 8;
}
if (expansionPakFound if (expansionPakFound
|| (io_dldi_data->ioInterface.features & FEATURE_SLOT_GBA) || (io_dldi_data->ioInterface.features & FEATURE_SLOT_GBA)
|| (isDSiMode() && !arm7SCFGLocked && !(REG_SCFG_MC & BIT(0)))) { || (isDSiMode() && !arm7SCFGLocked && !(REG_SCFG_MC & BIT(0))))
dmMaxCursors++; dmOperations.push_back(DriveMenuOperation::ndsCard);
dmAssignedOp[dmMaxCursors] = 4; if (!isDSiMode() && isRegularDS)
} dmOperations.push_back(DriveMenuOperation::gbaCart);
if (!isDSiMode() && isRegularDS) { if (nitroMounted)
dmMaxCursors++; dmOperations.push_back(DriveMenuOperation::nitroFs);
dmAssignedOp[dmMaxCursors] = 2;
}
if (nitroMounted) {
dmMaxCursors++;
dmAssignedOp[dmMaxCursors] = 3;
}
if (dmCursorPosition < 0) dmCursorPosition = dmMaxCursors; // Wrap around to bottom of list dm_drawBottomScreen();
if (dmCursorPosition > dmMaxCursors) dmCursorPosition = 0; // Wrap around to top of list dm_drawTopScreen();
if (!dmTextPrinted) {
consoleSelect(&bottomConsole);
dm_drawBottomScreen();
consoleSelect(&topConsole);
dm_drawTopScreen();
dmTextPrinted = true;
}
stored_SCFG_MC = REG_SCFG_MC; stored_SCFG_MC = REG_SCFG_MC;
printf ("\x1B[30m"); // Print black color for time text
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
do { do {
// Move to right side of screen
printf ("\x1b[0;27H");
// Print time // Print time
printf (RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDownRepeat(); pressed = keysDownRepeat();
@ -325,52 +256,53 @@ void driveMenu (void) {
if (!isDSiMode() && isRegularDS) { if (!isDSiMode() && isRegularDS) {
if (*(u8*)(0x080000B2) != gbaFixedValue) { if (*(u8*)(0x080000B2) != gbaFixedValue) {
dmTextPrinted = false;
break; break;
} }
} else if (isDSiMode()) { } else if (isDSiMode()) {
if (REG_SCFG_MC != stored_SCFG_MC) { if (REG_SCFG_MC != stored_SCFG_MC) {
dmTextPrinted = false;
break; break;
} }
} }
} while (!(pressed & KEY_UP) && !(pressed & KEY_DOWN) && !(pressed & KEY_A) && !(held & KEY_R) } while (!(pressed & (KEY_UP | KEY_DOWN | KEY_LEFT | KEY_RIGHT | KEY_A | KEY_R
#ifdef SCREENSWAP #ifdef SCREENSWAP
&& !(pressed & KEY_TOUCH) | KEY_TOUCH
#endif #endif
); )));
printf ("\x1B[47m"); // Print foreground white color
if ((pressed & KEY_UP) && dmMaxCursors != -1) { if(dmOperations.size() != 0) {
dmCursorPosition -= 1; if (pressed & KEY_UP) {
dmTextPrinted = false; dmCursorPosition -= 1;
if(dmCursorPosition < 0)
dmCursorPosition = dmOperations.size() - 1;
} else if (pressed & KEY_DOWN) {
dmCursorPosition += 1;
if(dmCursorPosition >= (int)dmOperations.size())
dmCursorPosition = 0;
} else if(pressed & KEY_LEFT) {
dmCursorPosition -= ENTRY_PAGE_LENGTH;
if(dmCursorPosition < 0)
dmCursorPosition = 0;
} else if(pressed & KEY_RIGHT) {
dmCursorPosition += ENTRY_PAGE_LENGTH;
if(dmCursorPosition >= (int)dmOperations.size())
dmCursorPosition = dmOperations.size() - 1;
}
} }
if ((pressed & KEY_DOWN) && dmMaxCursors != -1) {
dmCursorPosition += 1;
dmTextPrinted = false;
}
if (dmCursorPosition < 0) dmCursorPosition = dmMaxCursors; // Wrap around to bottom of list
if (dmCursorPosition > dmMaxCursors) dmCursorPosition = 0; // Wrap around to top of list
if (pressed & KEY_A) { if (pressed & KEY_A) {
if (dmAssignedOp[dmCursorPosition] == 0 && sdMounted) { if (dmOperations[dmCursorPosition] == DriveMenuOperation::sdCard && sdMounted) {
dmTextPrinted = false;
currentDrive = 0; currentDrive = 0;
chdir("sd:/"); chdir("sd:/");
screenMode = 1; screenMode = 1;
break; break;
} else if (dmAssignedOp[dmCursorPosition] == 1 && flashcardMounted) { } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::flashcard && flashcardMounted) {
dmTextPrinted = false;
currentDrive = 1; currentDrive = 1;
chdir("fat:/"); chdir("fat:/");
screenMode = 1; screenMode = 1;
break; break;
} else if (dmAssignedOp[dmCursorPosition] == 2 && isRegularDS && flashcardMounted && gbaFixedValue == 0x96) { } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::gbaCart && isRegularDS && flashcardMounted && gbaFixedValue == 0x96) {
dmTextPrinted = false;
gbaCartDump(); gbaCartDump();
} else if (dmAssignedOp[dmCursorPosition] == 3 && nitroMounted) { } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::nitroFs && nitroMounted) {
if ((sdMounted && nitroCurrentDrive==0) if ((sdMounted && nitroCurrentDrive==0)
|| (flashcardMounted && nitroCurrentDrive==1) || (flashcardMounted && nitroCurrentDrive==1)
|| (ramdrive1Mounted && nitroCurrentDrive==2) || (ramdrive1Mounted && nitroCurrentDrive==2)
@ -378,41 +310,35 @@ void driveMenu (void) {
|| (nandMounted && nitroCurrentDrive==4) || (nandMounted && nitroCurrentDrive==4)
|| (imgMounted && nitroCurrentDrive==6)) || (imgMounted && nitroCurrentDrive==6))
{ {
dmTextPrinted = false;
currentDrive = 5; currentDrive = 5;
chdir("nitro:/"); chdir("nitro:/");
screenMode = 1; screenMode = 1;
break; break;
} }
} else if (dmAssignedOp[dmCursorPosition] == 4 && (sdMounted || flashcardMounted)) { } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::ndsCard && (sdMounted || flashcardMounted)) {
dmTextPrinted = false;
ndsCardDump(); ndsCardDump();
} else if (dmAssignedOp[dmCursorPosition] == 5 && isDSiMode() && ramdrive1Mounted) { } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::ramDrive1 && isDSiMode() && ramdrive1Mounted) {
dmTextPrinted = false;
currentDrive = 2; currentDrive = 2;
chdir("ram1:/"); chdir("ram1:/");
screenMode = 1; screenMode = 1;
break; break;
} else if (dmAssignedOp[dmCursorPosition] == 6 && isDSiMode() && ramdrive2Mounted) { } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::ramDrive2 && isDSiMode() && ramdrive2Mounted) {
dmTextPrinted = false;
currentDrive = 3; currentDrive = 3;
chdir("ram2:/"); chdir("ram2:/");
screenMode = 1; screenMode = 1;
break; break;
} else if (dmAssignedOp[dmCursorPosition] == 7 && isDSiMode() && nandMounted) { } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::sysNand && isDSiMode() && nandMounted) {
dmTextPrinted = false;
currentDrive = 4; currentDrive = 4;
chdir("nand:/"); chdir("nand:/");
screenMode = 1; screenMode = 1;
break; break;
} else if (dmAssignedOp[dmCursorPosition] == 8 && imgMounted) { } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::fatImage && imgMounted) {
if ((sdMounted && imgCurrentDrive==0) if ((sdMounted && imgCurrentDrive==0)
|| (flashcardMounted && imgCurrentDrive==1) || (flashcardMounted && imgCurrentDrive==1)
|| (ramdrive1Mounted && imgCurrentDrive==2) || (ramdrive1Mounted && imgCurrentDrive==2)
|| (ramdrive2Mounted && imgCurrentDrive==3) || (ramdrive2Mounted && imgCurrentDrive==3)
|| (nandMounted && imgCurrentDrive==4)) || (nandMounted && imgCurrentDrive==4))
{ {
dmTextPrinted = false;
currentDrive = 6; currentDrive = 6;
chdir("img:/"); chdir("img:/");
screenMode = 1; screenMode = 1;
@ -423,7 +349,6 @@ void driveMenu (void) {
// Unmount/Remount FAT image // Unmount/Remount FAT image
if ((held & KEY_R) && (pressed & KEY_X)) { if ((held & KEY_R) && (pressed & KEY_X)) {
dmTextPrinted = false;
if (nitroMounted) { if (nitroMounted) {
currentDrive = 5; currentDrive = 5;
chdir("nitro:/"); chdir("nitro:/");
@ -437,7 +362,6 @@ void driveMenu (void) {
// Unmount/Remount SD card // Unmount/Remount SD card
if ((held & KEY_R) && (pressed & KEY_B)) { if ((held & KEY_R) && (pressed & KEY_B)) {
dmTextPrinted = false;
if (isDSiMode() && sdMountedDone) { if (isDSiMode() && sdMountedDone) {
if (sdMounted) { if (sdMounted) {
currentDrive = 0; currentDrive = 0;
@ -467,40 +391,7 @@ void driveMenu (void) {
// Make a screenshot // Make a screenshot
if ((held & KEY_R) && (pressed & KEY_L)) { if ((held & KEY_R) && (pressed & KEY_L)) {
if (sdMounted || flashcardMounted) { screenshot();
if (access((sdMounted ? "sd:/gm9i" : "fat:/gm9i"), F_OK) != 0) {
mkdir((sdMounted ? "sd:/gm9i" : "fat:/gm9i"), 0777);
}
if (access((sdMounted ? "sd:/gm9i/out" : "fat:/gm9i/out"), F_OK) != 0) {
mkdir((sdMounted ? "sd:/gm9i/out" : "fat:/gm9i/out"), 0777);
}
char timeText[8];
snprintf(timeText, sizeof(timeText), "%s", RetTime().c_str());
char fileTimeText[8];
snprintf(fileTimeText, sizeof(fileTimeText), "%s", RetTimeForFilename().c_str());
char snapPath[40];
// Take top screenshot
snprintf(snapPath, sizeof(snapPath), "%s:/gm9i/out/snap_%s_top.bmp", (sdMounted ? "sd" : "fat"), fileTimeText);
screenshotbmp(snapPath);
// Seamlessly swap top and bottom screens
lcdMainOnBottom();
printBorderBottom();
consoleSelect(&bottomConsole);
dm_drawTopScreen();
printf("\x1B[30m"); // Print black color for time text
printf("\x1b[0;27H");
printf(timeText);
clearBorderTop();
consoleSelect(&topConsole);
dm_drawBottomScreen();
// Take bottom screenshot
snprintf(snapPath, sizeof(snapPath), "%s:/gm9i/out/snap_%s_bot.bmp", (sdMounted ? "sd" : "fat"), fileTimeText);
screenshotbmp(snapPath);
dmTextPrinted = false;
lcdMainOnTop();
printBorderTop();
clearBorderBottom();
}
} }
if (isDSiMode() && !flashcardMountSkipped && !pressed && !held) { if (isDSiMode() && !flashcardMountSkipped && !pressed && !held) {

View File

@ -1,9 +1,10 @@
#include <nds.h> #include <nds.h>
#include <nds/arm9/dldi.h> #include <nds/arm9/dldi.h>
#include <fat.h> #include <fat.h>
#include <stdio.h>
#include <string>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/statvfs.h> #include <sys/statvfs.h>
#include <stdio.h>
#include "main.h" #include "main.h"
#include "dldi-include.h" #include "dldi-include.h"
@ -58,19 +59,22 @@ static float getTbNumber(u64 bytes) {
return tbNumber; return tbNumber;
} }
void printDriveBytes(u64 bytes) std::string getDriveBytes(u64 bytes)
{ {
char buffer[12];
if (bytes < (1024 * 1024)) if (bytes < (1024 * 1024))
printf("%d KB", (int)bytes / 1024); sniprintf(buffer, sizeof(buffer), "%d KB", (int)bytes / 1024);
else if (bytes >= (1024 * 1024) && bytes < (1024 * 1024 * 1024)) else if (bytes >= (1024 * 1024) && bytes < (1024 * 1024 * 1024))
printf("%d MB", (int)bytes / 1024 / 1024); sniprintf(buffer, sizeof(buffer), "%d MB", (int)bytes / 1024 / 1024);
else if (bytes >= 0x40000000 && bytes < 0x10000000000) else if (bytes >= 0x40000000 && bytes < 0x10000000000)
printf("%.1f GB", getGbNumber(bytes)); snprintf(buffer, sizeof(buffer), "%.1f GB", getGbNumber(bytes));
else else
printf("%.1f TB", getTbNumber(bytes)); snprintf(buffer, sizeof(buffer), "%.1f TB", getTbNumber(bytes));
return buffer;
} }
const char* getDrivePath(void) { const char* getDrivePath(void) {

View File

@ -1,6 +1,8 @@
#ifndef FLASHCARD_H #ifndef FLASHCARD_H
#define FLASHCARD_H #define FLASHCARD_H
#include <string>
extern u8 stored_SCFG_MC; extern u8 stored_SCFG_MC;
extern bool nandMounted; extern bool nandMounted;
@ -24,7 +26,7 @@ extern u32 nandSize;
extern u64 sdSize; extern u64 sdSize;
extern u64 fatSize; extern u64 fatSize;
extern u64 imgSize; extern u64 imgSize;
extern void printDriveBytes(u64 bytes); extern std::string getDriveBytes(u64 bytes);
extern const char* getDrivePath(void); extern const char* getDrivePath(void);

View File

@ -1,23 +1,23 @@
#include <nds.h> #include "dumpOperations.h"
#include <nds/arm9/dldi.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <dirent.h>
#include "auxspi.h" #include "auxspi.h"
#include "date.h" #include "date.h"
#include "driveOperations.h" #include "driveOperations.h"
#include "font.h"
#include "ndsheaderbanner.h" #include "ndsheaderbanner.h"
#include "read_card.h" #include "read_card.h"
#include "tonccpy.h" #include "tonccpy.h"
#include <dirent.h>
#include <nds.h>
#include <nds/arm9/dldi.h>
#include <unistd.h>
#include <stdio.h>
extern u8 copyBuf[]; extern u8 copyBuf[];
extern bool expansionPakFound; extern bool expansionPakFound;
extern PrintConsole topConsole, bottomConsole;
static sNDSHeaderExt ndsCardHeader; static sNDSHeaderExt ndsCardHeader;
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
@ -138,9 +138,11 @@ uint32 cardEepromGetSizeFixed() {
void ndsCardSaveDump(const char* filename) { void ndsCardSaveDump(const char* filename) {
FILE *out = fopen(filename, "wb"); FILE *out = fopen(filename, "wb");
if(out) { if(out) {
consoleClear(); font->clear(false);
iprintf("Dumping save...\n"); font->print(0, 0, false, "Dumping save...");
iprintf("Do not remove the NDS card.\n"); font->print(0, 1, false, "Do not remove the NDS card.");
font->update(false);
unsigned char *buffer; unsigned char *buffer;
auxspi_extra card_type = auxspi_has_extra(); auxspi_extra card_type = auxspi_has_extra();
if(card_type == AUXSPI_INFRARED) { if(card_type == AUXSPI_INFRARED) {
@ -168,22 +170,17 @@ void ndsCardSaveDump(const char* filename) {
} }
void ndsCardSaveRestore(const char *filename) { void ndsCardSaveRestore(const char *filename) {
consoleSelect(&bottomConsole); font->clear(false);
consoleClear(); font->print(0, 0, false, "Restore the selected save to the inserted game card?");
iprintf("\x1B[47m"); // Print foreground white color font->print(0, 2, false, "(<A> yes, <B> no)\n");
iprintf("Restore the selected save to the"); // Line is 32 chars font->update(false);
iprintf("inserted game card?\n"); // Line is 32 chars
iprintf("(<A> yes, <B> no)\n");
consoleSelect(&topConsole);
iprintf ("\x1B[30m"); // Print black color
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
u16 pressed; u16 pressed;
do { do {
// Move to right side of screen
iprintf ("\x1b[0;26H");
// Print time // Print time
iprintf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDownRepeat(); pressed = keysDownRepeat();
@ -191,9 +188,6 @@ void ndsCardSaveRestore(const char *filename) {
} while (!(pressed & (KEY_A | KEY_B))); } while (!(pressed & (KEY_A | KEY_B)));
if(pressed & KEY_A) { if(pressed & KEY_A) {
consoleSelect(&bottomConsole);
consoleClear();
auxspi_extra card_type = auxspi_has_extra(); auxspi_extra card_type = auxspi_has_extra();
bool auxspi = card_type == AUXSPI_INFRARED; bool auxspi = card_type == AUXSPI_INFRARED;
FILE *in = fopen(filename, "rb"); FILE *in = fopen(filename, "rb");
@ -228,23 +222,20 @@ void ndsCardSaveRestore(const char *filename) {
fseek(in, 0, SEEK_END); fseek(in, 0, SEEK_END);
length = ftell(in); length = ftell(in);
fseek(in, 0, SEEK_SET); fseek(in, 0, SEEK_SET);
if(length != (auxspi ? (int)(LEN*num_blocks) : size)) { if(length != (auxspi ? (int)(LEN * num_blocks) : size)) {
fclose(in); fclose(in);
iprintf("\x1B[41m"); // Print foreground red color
iprintf("The size of this save doesn't\n");
iprintf("match the size of the size of\n");
iprintf("the inserted game card.\n\n");
iprintf("Write cancelled!\n");
iprintf("\x1B[47m"); // Print foreground white color
iprintf("(<A> OK)\n");
consoleSelect(&topConsole); const std::string_view sizeError = "The size of this save doesn't match the size of the inserted game card.\n\nWrite cancelled!";
iprintf ("\x1B[30m"); // Print black color
font->clear(false);
font->print(0, 0, false, sizeError, Alignment::left, Palette::red);
font->print(0, font->calcHeight(sizeError) + 1, false, "(<A> OK)");
font->update(false);
do { do {
// Move to right side of screen
iprintf ("\x1b[0;26H");
// Print time // Print time
iprintf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDownRepeat(); pressed = keysDownRepeat();
@ -252,7 +243,13 @@ void ndsCardSaveRestore(const char *filename) {
} while (!(pressed & KEY_A)); } while (!(pressed & KEY_A));
return; return;
} }
iprintf("Restoring save...\nDo not remove the NDS card.\n\n\n\n\n\n\nProgress:");
font->clear(false);
font->print(0, 0, false, "Restoring save...");
font->print(0, 1, false, "Do not remove the NDS card.");
font->print(0, 4, false, "Progress:");
font->update(false);
if(type == 3) { if(type == 3) {
if(auxspi) if(auxspi)
auxspi_erase(card_type); auxspi_erase(card_type);
@ -261,9 +258,12 @@ void ndsCardSaveRestore(const char *filename) {
} }
if(auxspi){ if(auxspi){
buffer = new unsigned char[LEN]; buffer = new unsigned char[LEN];
font->print(0, 5, false, "[");
font->print(-1, 5, false, "]");
for(unsigned int i = 0; i < num_blocks; i++) { for(unsigned int i = 0; i < num_blocks; i++) {
iprintf ("\x1b[9;0H"); font->print((i * (SCREEN_COLS - 2) / num_blocks) + 1, 5, false, "=");
iprintf ("%d/%d Bytes", i * LEN, length); font->printf(0, 6, false, Alignment::left, Palette::white, "%d/%d Bytes", i * LEN, length);
font->update(false);
fread(buffer, 1, LEN, in); fread(buffer, 1, LEN, in);
auxspi_write_data(i << shift, buffer, LEN, type, card_type); auxspi_write_data(i << shift, buffer, LEN, type, card_type);
@ -272,9 +272,13 @@ void ndsCardSaveRestore(const char *filename) {
int blocks = size / 32; int blocks = size / 32;
int written = 0; int written = 0;
buffer = new unsigned char[blocks]; buffer = new unsigned char[blocks];
font->print(0, 5, false, "[");
font->print(-1, 5, false, "]");
for(unsigned int i = 0; i < 32; i++) { for(unsigned int i = 0; i < 32; i++) {
iprintf ("\x1b[9;0H"); font->print((i * (SCREEN_COLS - 2) / 32) + 1, 5, false, "=");
iprintf ("%d/%d Bytes", i * blocks, length); font->printf(0, 6, false, Alignment::left, Palette::white, "%d/%d Bytes", i * LEN, length);
font->update(false);
fread(buffer, 1, blocks, in); fread(buffer, 1, blocks, in);
cardWriteEeprom(written, buffer, blocks, type); cardWriteEeprom(written, buffer, blocks, type);
written += blocks; written += blocks;
@ -287,8 +291,10 @@ void ndsCardSaveRestore(const char *filename) {
} }
void dumpFailMsg(void) { void dumpFailMsg(void) {
consoleClear(); font->clear(false);
iprintf("Failed to dump the ROM.\n"); font->print(0, 0, false, "Failed to dump the ROM.");
font->update(false);
for (int i = 0; i < 60*2; i++) { for (int i = 0; i < 60*2; i++) {
swiWaitForVBlank(); swiWaitForVBlank();
} }
@ -298,49 +304,44 @@ void ndsCardDump(void) {
int pressed = 0; int pressed = 0;
//bool showGameCardMsgAgain = false; //bool showGameCardMsgAgain = false;
consoleSelect(&bottomConsole); font->clear(false);
consoleClear(); font->printf(0, 0, false, Alignment::left, Palette::white, "Dump NDS card ROM to\n\"%s:/gm9i/out\"?", sdMounted ? "sd:" : "fat:");
iprintf("\x1B[47m"); // Print foreground white color font->print(0, 2, false, "(<A> yes, <Y> trim, <B> no, <X> save only)");
iprintf("Dump NDS card ROM to\n"); font->update(false);
iprintf("\"%s:/gm9i/out\"?\n", (sdMounted ? "sd" : "fat"));
iprintf("(<A> yes, <Y> trim, <B> no,\n");
iprintf(" <X> save only)");
consoleSelect(&topConsole);
iprintf ("\x1B[30m"); // Print black color
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
do { do {
// Move to right side of screen
iprintf ("\x1b[0;26H");
// Print time // Print time
iprintf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDownRepeat(); pressed = keysDownRepeat();
swiWaitForVBlank(); swiWaitForVBlank();
} while (!(pressed & (KEY_A | KEY_Y | KEY_B | KEY_X))); } while (!(pressed & (KEY_A | KEY_Y | KEY_B | KEY_X)));
consoleSelect(&bottomConsole);
iprintf ("\x1B[47m"); // Print foreground white color
if (pressed & KEY_X) { if (pressed & KEY_X) {
consoleClear();
char folderPath[2][256]; char folderPath[2][256];
sprintf(folderPath[0], "%s:/gm9i", (sdMounted ? "sd" : "fat")); sprintf(folderPath[0], "%s:/gm9i", (sdMounted ? "sd" : "fat"));
sprintf(folderPath[1], "%s:/gm9i/out", (sdMounted ? "sd" : "fat")); sprintf(folderPath[1], "%s:/gm9i/out", (sdMounted ? "sd" : "fat"));
if (access(folderPath[0], F_OK) != 0) { if (access(folderPath[0], F_OK) != 0) {
iprintf("Creating directory..."); font->clear(false);
font->print(0, 0, false, "Creating directory...");
font->update(false);
mkdir(folderPath[0], 0777); mkdir(folderPath[0], 0777);
} }
if (access(folderPath[1], F_OK) != 0) { if (access(folderPath[1], F_OK) != 0) {
iprintf("\x1b[0;0H"); font->clear(false);
iprintf("Creating directory..."); font->print(0, 0, false, "Creating directory...");
font->update(false);
mkdir(folderPath[1], 0777); mkdir(folderPath[1], 0777);
} }
consoleClear();
if (cardInit(&ndsCardHeader) != 0) { if (cardInit(&ndsCardHeader) != 0) {
iprintf("Unable to dump the save.\n"); font->clear(false);
for (int i = 0; i < 60*2; i++) { font->print(0, 0, false, "Unable to dump the save.");
font->update(false);
for (int i = 0; i < 60 * 2; i++) {
swiWaitForVBlank(); swiWaitForVBlank();
} }
return; return;
@ -354,44 +355,41 @@ void ndsCardDump(void) {
ndsCardSaveDump(destSavPath); ndsCardSaveDump(destSavPath);
} else } else
if ((pressed & KEY_A) || (pressed & KEY_Y)) { if ((pressed & KEY_A) || (pressed & KEY_Y)) {
consoleClear();
bool trimRom = (pressed & KEY_Y); bool trimRom = (pressed & KEY_Y);
char folderPath[2][256]; char folderPath[2][256];
sprintf(folderPath[0], "%s:/gm9i", (sdMounted ? "sd" : "fat")); sprintf(folderPath[0], "%s:/gm9i", (sdMounted ? "sd" : "fat"));
sprintf(folderPath[1], "%s:/gm9i/out", (sdMounted ? "sd" : "fat")); sprintf(folderPath[1], "%s:/gm9i/out", (sdMounted ? "sd" : "fat"));
if (access(folderPath[0], F_OK) != 0) { if (access(folderPath[0], F_OK) != 0) {
iprintf("Creating directory..."); font->clear(false);
font->print(0, 0, false, "Creating directory...");
font->update(false);
mkdir(folderPath[0], 0777); mkdir(folderPath[0], 0777);
} }
if (access(folderPath[1], F_OK) != 0) { if (access(folderPath[1], F_OK) != 0) {
iprintf("\x1b[0;0H"); font->clear(false);
iprintf("Creating directory..."); font->print(0, 0, false, "Creating directory...");
font->update(false);
mkdir(folderPath[1], 0777); mkdir(folderPath[1], 0777);
} }
/*if (expansionPakFound) { /*if (expansionPakFound) {
consoleClear(); font->clear(false)
printf("Please switch to the\ngame card, then press A.\n"); font->print(0, 0, false, "Please switch to the game card, then press A.");
font->update(false);
//flashcardUnmount(); //flashcardUnmount();
io_dldi_data->ioInterface.shutdown(); io_dldi_data->ioInterface.shutdown();
consoleSelect(&topConsole);
printf ("\x1B[30m"); // Print black color
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
do { do {
// Move to right side of screen
printf ("\x1b[0;26H");
// Print time // Print time
printf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDownRepeat(); pressed = keysDownRepeat();
swiWaitForVBlank(); swiWaitForVBlank();
} while (!(pressed & KEY_A)); } while (!(pressed & KEY_A));
consoleSelect(&bottomConsole);
printf ("\x1B[47m"); // Print foreground white color
}*/ }*/
consoleClear();
int cardInited = cardInit(&ndsCardHeader); int cardInited = cardInit(&ndsCardHeader);
char gameTitle[13] = {0}; char gameTitle[13] = {0};
char gameCode[7] = {0}; char gameCode[7] = {0};
@ -426,10 +424,14 @@ void ndsCardDump(void) {
sprintf(destSavPath, "%s:/gm9i/out/%s.sav", (sdMounted ? "sd" : "fat"), fileName); sprintf(destSavPath, "%s:/gm9i/out/%s.sav", (sdMounted ? "sd" : "fat"), fileName);
if (cardInited == 0) { if (cardInited == 0) {
iprintf("%s.nds\nis dumping...\n", fileName); font->clear(false);
iprintf("Do not remove the NDS card.\n"); font->printf(0, 0, false, Alignment::left, Palette::white, "%s.nds\nis dumping...", fileName);
font->print(0, 2, false, "Do not remove the NDS card.");
font->update(false);
} else { } else {
iprintf("Unable to dump the ROM.\n"); font->clear(false);
font->print(0, 0, false, "Unable to dump the ROM.");
font->update(false);
for (int i = 0; i < 60*2; i++) { for (int i = 0; i < 60*2; i++) {
swiWaitForVBlank(); swiWaitForVBlank();
} }
@ -498,14 +500,11 @@ void ndsCardDump(void) {
//flashcardUnmount(); //flashcardUnmount();
io_dldi_data->ioInterface.shutdown(); io_dldi_data->ioInterface.shutdown();
consoleSelect(&topConsole);
iprintf ("\x1B[30m"); // Print black color
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
do { do {
// Move to right side of screen
iprintf ("\x1b[0;26H");
// Print time // Print time
iprintf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDownRepeat(); pressed = keysDownRepeat();
@ -521,12 +520,9 @@ void ndsCardDump(void) {
// Read from game card // Read from game card
for (src = src; src < currentSize; src += 0x200) { for (src = src; src < currentSize; src += 0x200) {
consoleSelect(&topConsole);
iprintf ("\x1B[30m"); // Print black color
// Move to right side of screen
iprintf ("\x1b[0;26H");
// Print time // Print time
iprintf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
consoleSelect(&bottomConsole); consoleSelect(&bottomConsole);
iprintf ("\x1B[47m"); // Print foreground white color iprintf ("\x1B[47m"); // Print foreground white color
@ -537,14 +533,11 @@ void ndsCardDump(void) {
} }
iprintf("\x1b[15;0H"); iprintf("\x1b[15;0H");
iprintf("Please switch to the\nflashcard, then press A.\n"); iprintf("Please switch to the\nflashcard, then press A.\n");
consoleSelect(&topConsole);
iprintf ("\x1B[30m"); // Print black color
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
do { do {
// Move to right side of screen
printf ("\x1b[0;26H");
// Print time // Print time
printf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDownRepeat(); pressed = keysDownRepeat();
@ -568,12 +561,9 @@ void ndsCardDump(void) {
destinationFileOpened = true; destinationFileOpened = true;
} }
for (writeSrc = writeSrc; writeSrc < currentSize; writeSrc += 0x200) { for (writeSrc = writeSrc; writeSrc < currentSize; writeSrc += 0x200) {
consoleSelect(&topConsole);
iprintf ("\x1B[30m"); // Print black color
// Move to right side of screen
iprintf ("\x1b[0;26H");
// Print time // Print time
iprintf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
consoleSelect(&bottomConsole); consoleSelect(&bottomConsole);
printf ("\x1B[47m"); // Print foreground white color printf ("\x1B[47m"); // Print foreground white color
@ -590,19 +580,19 @@ void ndsCardDump(void) {
u32 currentSize = romSize; u32 currentSize = romSize;
FILE* destinationFile = fopen(destPath, "wb"); FILE* destinationFile = fopen(destPath, "wb");
if (destinationFile) { if (destinationFile) {
for (u32 src = 0; src < romSize; src += 0x8000) {
consoleSelect(&topConsole);
iprintf ("\x1B[30m"); // Print black color
// Move to right side of screen
iprintf ("\x1b[0;26H");
// Print time
iprintf (" %s" ,RetTime().c_str());
consoleSelect(&bottomConsole); font->print(0, 4, false, "Progress:");
iprintf ("\x1B[47m"); // Print foreground white color font->print(0, 5, false, "[");
iprintf ("\x1b[8;0H"); font->print(-1, 5, false, "]");
iprintf ("Progress:\n"); for (u32 src = 0; src < romSize; src += 0x8000) {
iprintf ("%i/%i Bytes", (int)src, (int)romSize); // Print time
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
font->print((src / (romSize / (SCREEN_COLS - 2))) + 1, 5, false, "=");
font->printf(0, 6, false, Alignment::left, Palette::white, "%d/%d Bytes", src, romSize);
font->update(false);
for (u32 i = 0; i < 0x8000; i += 0x200) { for (u32 i = 0; i < 0x8000; i += 0x200) {
cardRead (src+i, copyBuf+i); cardRead (src+i, copyBuf+i);
} }
@ -639,21 +629,16 @@ void readChange(void) {
void gbaCartDump(void) { void gbaCartDump(void) {
int pressed = 0; int pressed = 0;
consoleSelect(&bottomConsole); font->clear(false);
consoleClear(); font->printf(0, 0, false, Alignment::left, Palette::white, "Dump GBA cart ROM to\n\"%s:/gm9i/out\"?", sdMounted ? "sd:" : "fat:");
iprintf("\x1B[47m"); // Print foreground white color font->print(0, 2, false, "(<A> yes, <B> no)");
iprintf("Dump GBA cart ROM to\n"); font->update(false);
iprintf("\"fat:/gm9i/out\"?\n");
iprintf("(<A> yes, <B> no)");
consoleSelect(&topConsole);
iprintf ("\x1B[30m"); // Print black color
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
do { do {
// Move to right side of screen
iprintf ("\x1b[0;26H");
// Print time // Print time
iprintf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDownRepeat(); pressed = keysDownRepeat();
@ -661,22 +646,23 @@ void gbaCartDump(void) {
} while (!(pressed & KEY_A) && !(pressed & KEY_B)); } while (!(pressed & KEY_A) && !(pressed & KEY_B));
if (pressed & KEY_A) { if (pressed & KEY_A) {
iprintf ("\x1b[0;27H"); // Clear time
iprintf (" "); // Clear time font->print(-1, 0, true, " ", Alignment::right, Palette::blackGreen);
font->update(true);
} }
consoleSelect(&bottomConsole);
printf ("\x1B[47m"); // Print foreground white color
if (pressed & KEY_A) { if (pressed & KEY_A) {
consoleClear(); consoleClear();
if (access("fat:/gm9i", F_OK) != 0) { if (access("fat:/gm9i", F_OK) != 0) {
iprintf("Creating directory..."); font->clear(false);
font->print(0, 0, false, "Creating directory...");
font->update(false);
mkdir("fat:/gm9i", 0777); mkdir("fat:/gm9i", 0777);
} }
if (access("fat:/gm9i/out", F_OK) != 0) { if (access("fat:/gm9i/out", F_OK) != 0) {
iprintf ("\x1b[0;0H"); font->clear(false);
iprintf("Creating directory..."); font->print(0, 0, false, "Creating directory...");
font->update(false);
mkdir("fat:/gm9i/out", 0777); mkdir("fat:/gm9i/out", 0777);
} }
char gbaHeaderGameTitle[13] = "\0"; char gbaHeaderGameTitle[13] = "\0";
@ -716,9 +702,12 @@ void gbaCartDump(void) {
char destSavPath[256] = {0}; char destSavPath[256] = {0};
sprintf(destPath, "fat:/gm9i/out/%s.gba", fileName); sprintf(destPath, "fat:/gm9i/out/%s.gba", fileName);
sprintf(destSavPath, "fat:/gm9i/out/%s.sav", fileName); sprintf(destSavPath, "fat:/gm9i/out/%s.sav", fileName);
consoleClear();
iprintf("%s.gba\nis dumping...\n", fileName); font->clear(false);
iprintf("Do not remove the GBA cart.\n"); font->printf(0, 0, false, Alignment::left, Palette::white, "%s.gba\nis dumping...", fileName);
font->print(0, 2, false, "Do not remove the GBA cart.");
font->update(false);
// Determine ROM size // Determine ROM size
u32 romSize = 0x02000000; u32 romSize = 0x02000000;
for (u32 i = 0x09FE0000; i > 0x08000000; i -= 0x20000) { for (u32 i = 0x09FE0000; i > 0x08000000; i -= 0x20000) {
@ -772,6 +761,7 @@ void gbaCartDump(void) {
} else { } else {
dumpFailMsg(); dumpFailMsg();
} }
// Save file // Save file
remove(destSavPath); remove(destSavPath);
destinationFile = fopen(destSavPath, "wb"); destinationFile = fopen(destSavPath, "wb");

View File

@ -7,56 +7,38 @@
#include "date.h" #include "date.h"
#include "file_browse.h" #include "file_browse.h"
#include "font.h"
#define copyBufSize 0x8000 #define copyBufSize 0x8000
#define shaChunkSize 0x10000 #define shaChunkSize 0x10000
u32 copyBuf[copyBufSize]; u32 copyBuf[copyBufSize];
extern PrintConsole topConsole, bottomConsole;
std::vector<ClipboardFile> clipboard; std::vector<ClipboardFile> clipboard;
bool clipboardOn = false; bool clipboardOn = false;
bool clipboardUsed = false; bool clipboardUsed = false;
void printBytes(int bytes) std::string getBytes(int bytes) {
{ char buffer[11];
if (bytes == 1) if (bytes == 1)
iprintf("%d Byte", bytes); sniprintf(buffer, sizeof(buffer), "%d Byte", bytes);
else if (bytes < 1024) else if (bytes < 1024)
iprintf("%d Bytes", bytes); sniprintf(buffer, sizeof(buffer), "%d Bytes", bytes);
else if (bytes < (1024 * 1024)) else if (bytes < (1024 * 1024))
printf("%d KB", bytes / 1024); sniprintf(buffer, sizeof(buffer), "%d KB", bytes / 1024);
else if (bytes < (1024 * 1024 * 1024)) else if (bytes < (1024 * 1024 * 1024))
printf("%d MB", bytes / 1024 / 1024); sniprintf(buffer, sizeof(buffer), "%d MB", bytes / 1024 / 1024);
else else
printf("%d GB", bytes / 1024 / 1024 / 1024); sniprintf(buffer, sizeof(buffer), "%d GB", bytes / 1024 / 1024 / 1024);
return buffer;
} }
void printBytesAlign(int bytes) off_t getFileSize(const char *fileName) {
{
if (bytes == 1)
iprintf("%4d Byte", bytes);
else if (bytes < 1024)
iprintf("%3d Bytes", bytes);
else if (bytes < (1024 * 1024))
printf("%6d KB", bytes / 1024);
else if (bytes < (1024 * 1024 * 1024))
printf("%6d MB", bytes / 1024 / 1024);
else
printf("%6d GB", bytes / 1024 / 1024 / 1024);
}
off_t getFileSize(const char *fileName)
{
FILE* fp = fopen(fileName, "rb"); FILE* fp = fopen(fileName, "rb");
off_t fsize = 0; off_t fsize = 0;
if (fp) { if (fp) {
@ -69,17 +51,24 @@ off_t getFileSize(const char *fileName)
return fsize; return fsize;
} }
bool calculateSHA1(const char *fileName, u8 *sha1) bool calculateSHA1(const char *fileName, u8 *sha1) {
{
off_t fsize = getFileSize(fileName); off_t fsize = getFileSize(fileName);
u8 *buf = (u8*) malloc(shaChunkSize); u8 *buf = (u8*) malloc(shaChunkSize);
if (!buf) { if (!buf) {
iprintf("Could not allocate buffer\n"); font->clear(false);
font->print(0, 0, false, "Could not allocate buffer");
font->update(false);
for(int i = 0; i < 60 * 2; i++)
swiWaitForVBlank();
return false; return false;
} }
FILE* fp = fopen(fileName, "rb"); FILE* fp = fopen(fileName, "rb");
if (!fp) { if (!fp) {
iprintf("Could not open file for reading\n"); font->clear(false);
font->print(0, 0, false, "Could not open file for reading");
font->update(false);
for(int i = 0; i < 60 * 2; i++)
swiWaitForVBlank();
free(buf); free(buf);
return false; return false;
} }
@ -87,6 +76,18 @@ bool calculateSHA1(const char *fileName, u8 *sha1)
swiSHA1context_t ctx; swiSHA1context_t ctx;
ctx.sha_block=0; //this is weird but it has to be done ctx.sha_block=0; //this is weird but it has to be done
swiSHA1Init(&ctx); swiSHA1Init(&ctx);
font->clear(false);
font->print(0, 0, false, "Calculating SHA1 hash of:");
font->print(0, 1, false, fileName);
int nameHeight = font->calcHeight(fileName);
font->print(0, nameHeight + 2, false, "(<START> to cancel)");
font->print(0, nameHeight + 4, false, "Progress:");
font->print(0, nameHeight + 5, false, "[");
font->print(-1, nameHeight + 5, false, "]");
while (true) { while (true) {
size_t ret = fread(buf, 1, shaChunkSize, fp); size_t ret = fread(buf, 1, shaChunkSize, fp);
if (!ret) break; if (!ret) break;
@ -94,26 +95,27 @@ bool calculateSHA1(const char *fileName, u8 *sha1)
scanKeys(); scanKeys();
int keys = keysHeld(); int keys = keysHeld();
if (keys & KEY_START) return false; if (keys & KEY_START) return false;
iprintf("\x1b[1;A");
iprintf("%ld/%lld bytes processed\n", ftell(fp), fsize); font->print((ftell(fp) / (fsize / (SCREEN_COLS - 2))) + 1, nameHeight + 5, false, "=");
font->printf(0, nameHeight + 6, false, Alignment::left, Palette::white, "%d/%d bytes processed", ftell(fp), fsize);
font->update(false);
} }
swiSHA1Final(sha1, &ctx); swiSHA1Final(sha1, &ctx);
free(buf); free(buf);
return true; return true;
} }
void dirCopy(DirEntry* entry, int i, const char *destinationPath, const char *sourcePath) { void dirCopy(const DirEntry &entry, int i, const char *destinationPath, const char *sourcePath) {
std::vector<DirEntry> dirContents; std::vector<DirEntry> dirContents;
dirContents.clear(); dirContents.clear();
if (entry->isDirectory) chdir((sourcePath + ("/" + entry->name)).c_str()); if (entry.isDirectory) chdir((sourcePath + ("/" + entry.name)).c_str());
getDirectoryContents(dirContents); getDirectoryContents(dirContents);
if (((int)dirContents.size()) == 1) mkdir((destinationPath + ("/" + entry->name)).c_str(), 0777); if (((int)dirContents.size()) == 1) mkdir((destinationPath + ("/" + entry.name)).c_str(), 0777);
if (((int)dirContents.size()) != 1) fcopy((sourcePath + ("/" + entry->name)).c_str(), (destinationPath + ("/" + entry->name)).c_str()); if (((int)dirContents.size()) != 1) fcopy((sourcePath + ("/" + entry.name)).c_str(), (destinationPath + ("/" + entry.name)).c_str());
} }
int fcopy(const char *sourcePath, const char *destinationPath) int fcopy(const char *sourcePath, const char *destinationPath) {
{ DIR *isDir = opendir(sourcePath);
DIR *isDir = opendir (sourcePath);
if (isDir != NULL) { if (isDir != NULL) {
closedir(isDir); closedir(isDir);
@ -122,17 +124,15 @@ int fcopy(const char *sourcePath, const char *destinationPath)
chdir(sourcePath); chdir(sourcePath);
std::vector<DirEntry> dirContents; std::vector<DirEntry> dirContents;
getDirectoryContents(dirContents); getDirectoryContents(dirContents);
DirEntry* entry = NULL;
mkdir(destinationPath, 0777); mkdir(destinationPath, 0777);
for (int i = 1; i < ((int)dirContents.size()); i++) { for (int i = 1; i < ((int)dirContents.size()); i++) {
chdir(sourcePath); chdir(sourcePath);
entry = &dirContents.at(i); dirCopy(dirContents[i], i, destinationPath, sourcePath);
dirCopy(entry, i, destinationPath, sourcePath);
} }
chdir (destinationPath); chdir(destinationPath);
chdir (".."); chdir("..");
return 1; return 1;
} else { } else {
closedir(isDir); closedir(isDir);
@ -142,26 +142,26 @@ int fcopy(const char *sourcePath, const char *destinationPath)
off_t fsize = 0; off_t fsize = 0;
if (sourceFile) { if (sourceFile) {
fseek(sourceFile, 0, SEEK_END); fseek(sourceFile, 0, SEEK_END);
fsize = ftell(sourceFile); // Get source file's size fsize = ftell(sourceFile); // Get source file's size
fseek(sourceFile, 0, SEEK_SET); fseek(sourceFile, 0, SEEK_SET);
} else { } else {
fclose(sourceFile);
return -1; return -1;
} }
FILE* destinationFile = fopen(destinationPath, "wb"); FILE* destinationFile = fopen(destinationPath, "wb");
//if (destinationFile) { if (!destinationFile) {
fseek(destinationFile, 0, SEEK_SET);
/*} else {
fclose(sourceFile); fclose(sourceFile);
fclose(destinationFile);
return -1; return -1;
}*/ }
font->clear(false);
font->print(0, 0, false, "Progress:");
font->print(0, 1, false, "[");
font->print(-1, 1, false, "]");
off_t offset = 0; off_t offset = 0;
int numr; int numr;
while (1) while (1) {
{
scanKeys(); scanKeys();
if (keysHeld() & KEY_B) { if (keysHeld() & KEY_B) {
// Cancel copying // Cancel copying
@ -170,18 +170,14 @@ int fcopy(const char *sourcePath, const char *destinationPath)
return -1; return -1;
break; break;
} }
consoleSelect(&topConsole);
printf ("\x1B[30m"); // Print black color
// Move to right side of screen
printf ("\x1b[0;26H");
// Print time
printf (" %s" ,RetTime().c_str());
consoleSelect(&bottomConsole); // Print time
printf ("\x1B[47m"); // Print foreground white color font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
printf ("\x1b[16;0H"); font->update(true);
printf ("Progress:\n");
printf ("%i/%i Bytes ", (int)offset, (int)fsize); font->print((offset / (fsize / (SCREEN_COLS - 2))) + 1, 1, false, "=");
font->printf(0, 2, false, Alignment::left, Palette::white, "%lld/%lld Bytes", offset, fsize);
font->update(false);
// Copy file to destination path // Copy file to destination path
numr = fread(copyBuf, 1, copyBufSize, sourceFile); numr = fread(copyBuf, 1, copyBufSize, sourceFile);
@ -192,10 +188,6 @@ int fcopy(const char *sourcePath, const char *destinationPath)
fclose(sourceFile); fclose(sourceFile);
fclose(destinationFile); fclose(destinationFile);
printf ("\x1b[17;0H");
printf ("%i/%i Bytes ", (int)fsize, (int)fsize);
for (int i = 0; i < 30; i++) swiWaitForVBlank();
return 1; return 1;
break; break;
} }
@ -205,62 +197,29 @@ int fcopy(const char *sourcePath, const char *destinationPath)
} }
} }
void changeFileAttribs(DirEntry* entry) { void changeFileAttribs(const DirEntry *entry) {
consoleClear();
int pressed = 0; int pressed = 0;
int cursorScreenPos = 0; int cursorScreenPos = font->calcHeight(entry->name);
uint8_t currentAttribs = FAT_getAttr(entry->name.c_str()); uint8_t currentAttribs = FAT_getAttr(entry->name.c_str());
uint8_t newAttribs = currentAttribs; uint8_t newAttribs = currentAttribs;
// Position cursor, depending on how long the file name is
for (int i = 0; i < 256; i++) {
if (i == 33 || i == 65 || i == 97 || i == 129 || i == 161 || i == 193 || i == 225) {
cursorScreenPos++;
}
if (entry->name.c_str()[i] == '\0') {
break;
}
}
printf ("\x1b[0;0H");
printf (entry->name.c_str());
if (!entry->isDirectory) {
printf ("\x1b[%i;0H", 3+cursorScreenPos);
printf ("filesize: ");
printBytes(entry->size);
}
printf ("\x1b[%i;0H", 5+cursorScreenPos);
printf ("[ ] U read-only [ ] D hidden");
printf ("\x1b[%i;0H", 6+cursorScreenPos);
printf ("[ ] R system [ ] L archive");
printf ("\x1b[%i;0H", 7+cursorScreenPos);
printf ("[ ] virtual");
printf ("\x1b[%i;1H", 7+cursorScreenPos);
printf ((newAttribs & ATTR_VOLUME) ? "X" : " ");
printf ("\x1b[%i;0H", 9+cursorScreenPos);
printf ("(UDRL to change attributes)");
while (1) { while (1) {
consoleSelect(&bottomConsole); font->clear(false);
printf ("\x1B[47m"); // Print foreground white color font->print(0, 0, false, entry->name);
printf ("\x1b[%i;1H", 5+cursorScreenPos); if (!entry->isDirectory)
printf ((newAttribs & ATTR_READONLY) ? "X" : " "); font->printf(0, cursorScreenPos + 1, false, Alignment::left, Palette::white, "filesize: %s", getBytes(entry->size).c_str());
printf ("\x1b[%i;18H", 5+cursorScreenPos); font->printf(0, cursorScreenPos + 3, false, Alignment::left, Palette::white, "[%c] ↑ read-only [%c] ↓ hidden", (newAttribs & ATTR_READONLY) ? 'X' : ' ', (newAttribs & ATTR_HIDDEN) ? 'X' : ' ');
printf ((newAttribs & ATTR_HIDDEN) ? "X" : " "); font->printf(0, cursorScreenPos + 4, false, Alignment::left, Palette::white, "[%c] → system [%c] ← archive", (newAttribs & ATTR_SYSTEM) ? 'X' : ' ', (newAttribs & ATTR_ARCHIVE) ? 'X' : ' ');
printf ("\x1b[%i;1H", 6+cursorScreenPos); font->printf(0, cursorScreenPos + 5, false, Alignment::left, Palette::white, "[%c] virtual", (newAttribs & ATTR_VOLUME) ? 'X' : ' ');
printf ((newAttribs & ATTR_SYSTEM) ? "X" : " "); font->printf(0, cursorScreenPos + 6, false, Alignment::left, Palette::white, "(↑↓→← to change attributes)");
printf ("\x1b[%i;18H", 6+cursorScreenPos); font->print(0, cursorScreenPos + 8, false, (currentAttribs == newAttribs) ? "(<A> to continue)" : "(<A> to apply, <B> to cancel)");
printf ((newAttribs & ATTR_ARCHIVE) ? "X" : " "); font->update(false);
printf ("\x1b[%i;0H", 11+cursorScreenPos);
printf ((currentAttribs==newAttribs) ? "(<A> to continue) " : "(<A> to apply, <B> to cancel)");
consoleSelect(&topConsole);
printf ("\x1B[30m"); // Print black color
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
do { do {
// Move to right side of screen
printf ("\x1b[0;26H");
// Print time // Print time
printf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDown(); pressed = keysDown();
@ -269,43 +228,17 @@ void changeFileAttribs(DirEntry* entry) {
&& !(pressed & KEY_A) && !(pressed & KEY_B)); && !(pressed & KEY_A) && !(pressed & KEY_B));
if (pressed & KEY_UP) { if (pressed & KEY_UP) {
if (newAttribs & ATTR_READONLY) { newAttribs ^= ATTR_READONLY;
newAttribs -= ATTR_READONLY; } else if (pressed & KEY_DOWN) {
} else { newAttribs ^= ATTR_HIDDEN;
newAttribs += ATTR_READONLY; } else if (pressed & KEY_RIGHT) {
} newAttribs ^= ATTR_SYSTEM;
} } else if (pressed & KEY_LEFT) {
newAttribs ^= ATTR_ARCHIVE;
if (pressed & KEY_DOWN) { } else if ((pressed & KEY_A) && (currentAttribs != newAttribs)) {
if (newAttribs & ATTR_HIDDEN) {
newAttribs -= ATTR_HIDDEN;
} else {
newAttribs += ATTR_HIDDEN;
}
}
if (pressed & KEY_RIGHT) {
if (newAttribs & ATTR_SYSTEM) {
newAttribs -= ATTR_SYSTEM;
} else {
newAttribs += ATTR_SYSTEM;
}
}
if (pressed & KEY_LEFT) {
if (newAttribs & ATTR_ARCHIVE) {
newAttribs -= ATTR_ARCHIVE;
} else {
newAttribs += ATTR_ARCHIVE;
}
}
if ((pressed & KEY_A) && (currentAttribs!=newAttribs)) {
FAT_setAttr(entry->name.c_str(), newAttribs); FAT_setAttr(entry->name.c_str(), newAttribs);
break; break;
} } else if (pressed & (KEY_A | KEY_B)) {
if ((pressed & KEY_A) || (pressed & KEY_B)) {
break; break;
} }
} }

View File

@ -19,12 +19,11 @@ extern std::vector<ClipboardFile> clipboard;
extern bool clipboardOn; extern bool clipboardOn;
extern bool clipboardUsed; extern bool clipboardUsed;
extern void printBytes(int bytes); extern std::string getBytes(int bytes);
extern void printBytesAlign(int bytes);
extern off_t getFileSize(const char *fileName); extern off_t getFileSize(const char *fileName);
extern bool calculateSHA1(const char *fileName, u8 *sha1); extern bool calculateSHA1(const char *fileName, u8 *sha1);
extern int fcopy(const char *sourcePath, const char *destinationPath); extern int fcopy(const char *sourcePath, const char *destinationPath);
void changeFileAttribs(DirEntry* entry); void changeFileAttribs(const DirEntry *entry);
#endif // FILE_COPY #endif // FILE_COPY

View File

@ -38,24 +38,16 @@
#include "driveMenu.h" #include "driveMenu.h"
#include "driveOperations.h" #include "driveOperations.h"
#include "dumpOperations.h" #include "dumpOperations.h"
#include "font.h"
#include "hexEditor.h" #include "hexEditor.h"
#include "ndsInfo.h" #include "ndsInfo.h"
#include "nitrofs.h" #include "nitrofs.h"
#include "inifile.h" #include "inifile.h"
#include "nds_loader_arm9.h" #include "nds_loader_arm9.h"
#define SCREEN_COLS 22
#define ENTRIES_PER_SCREEN 23
#define ENTRIES_START_ROW 1 #define ENTRIES_START_ROW 1
#define OPTIONS_ENTRIES_START_ROW 2 #define OPTIONS_ENTRIES_START_ROW 2
#define ENTRY_PAGE_LENGTH 10 #define ENTRY_PAGE_LENGTH 10
extern PrintConsole topConsole, bottomConsole;
extern void printBorderTop(void);
extern void printBorderBottom(void);
extern void clearBorderTop(void);
extern void clearBorderBottom(void);
extern void reinitConsoles(void);
static char path[PATH_MAX]; static char path[PATH_MAX];
@ -84,7 +76,7 @@ bool dirEntryPredicate (const DirEntry& lhs, const DirEntry& rhs) {
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0; return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0;
} }
void getDirectoryContents (std::vector<DirEntry>& dirContents) { void getDirectoryContents(std::vector<DirEntry>& dirContents) {
struct stat st; struct stat st;
dirContents.clear(); dirContents.clear();
@ -92,7 +84,8 @@ void getDirectoryContents (std::vector<DirEntry>& dirContents) {
DIR *pdir = opendir ("."); DIR *pdir = opendir (".");
if (pdir == NULL) { if (pdir == NULL) {
iprintf ("Unable to open the directory.\n"); font->print(0, 0, true, "Unable to open the directory.");
font->update(true);
} else { } else {
while(true) { while(true) {
@ -138,145 +131,150 @@ void getDirectoryContents (std::vector<DirEntry>& dirContents) {
void showDirectoryContents (const std::vector<DirEntry>& dirContents, int fileOffset, int startRow) { void showDirectoryContents (const std::vector<DirEntry>& dirContents, int fileOffset, int startRow) {
getcwd(path, PATH_MAX); getcwd(path, PATH_MAX);
consoleClear(); font->clear(true);
// Top bar
font->printf(0, 0, true, Alignment::left, Palette::blackGreen, "%*c", 256 / font->width(), ' ');
// Print time
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
// Print the path // Print the path
printf ("\x1B[30m"); // Print black color if(font->calcWidth(path) > SCREEN_COLS - 6)
// Print time font->print(-6 - 1, 0, true, path, Alignment::right, Palette::blackGreen);
printf ("\x1b[0;27H"); else
printf (RetTime().c_str()); font->print(0, 0, true, path, Alignment::left, Palette::blackGreen);
printf ("\x1b[0;0H");
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 directory listing // Print directory listing
for (int i = 0; i < ((int)dirContents.size() - startRow) && i < ENTRIES_PER_SCREEN; i++) { for (int i = 0; i < ((int)dirContents.size() - startRow) && i < ENTRIES_PER_SCREEN; i++) {
const DirEntry* entry = &dirContents.at(i + startRow); const DirEntry *entry = &dirContents[i + startRow];
// Set row Palette pal;
iprintf ("\x1b[%d;0H", i + ENTRIES_START_ROW);
if ((fileOffset - startRow) == i) { if ((fileOffset - startRow) == i) {
printf ("\x1B[47m"); // Print foreground white color pal = Palette::white;
} else if (entry->selected) { } else if (entry->selected) {
printf ("\x1B[33m"); // Print custom yellow color pal = Palette::yellow;
} else if (entry->isDirectory) { } else if (entry->isDirectory) {
printf ("\x1B[37m"); // Print custom blue color pal = Palette::blue;
} else { } else {
printf ("\x1B[40m"); // Print foreground black color pal = Palette::gray;
} }
printf ("%.*s", SCREEN_COLS, entry->name.c_str()); font->print(0, i + 1, true, entry->name, Alignment::left, pal);
if (entry->name == "..") { if (entry->name == "..") {
printf ("\x1b[%d;28H", i + ENTRIES_START_ROW); font->print(-1, i + 1, true, "(..)", Alignment::right, pal);
printf ("(..)");
} else if (entry->isDirectory) { } else if (entry->isDirectory) {
printf ("\x1b[%d;27H", i + ENTRIES_START_ROW); font->print(-1, i + 1, true, "(dir)", Alignment::right, pal);
printf ("(dir)");
} else { } else {
printf ("\x1b[%d;23H", i + ENTRIES_START_ROW); font->printf(-1, i + 1, true, Alignment::right, pal, "(%s)", getBytes(entry->size).c_str());
printBytesAlign((int)entry->size);
} }
} }
printf ("\x1B[47m"); // Print foreground white color font->update(true);
} }
FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) {
int pressed = 0; int pressed = 0;
FileOperation assignedOp[4] = {FileOperation::none}; std::vector<FileOperation> operations;
int optionOffset = 0; int optionOffset = 0;
int cursorScreenPos = 0; std::string fullPath = path + entry->name;
int maxCursors = -1; int y = font->calcHeight(fullPath) + 1;
consoleSelect(&bottomConsole);
consoleClear();
printf ("\x1B[47m"); // Print foreground white color
char fullPath[256];
snprintf(fullPath, sizeof(fullPath), "%s%s", path, entry->name.c_str());
printf(fullPath);
// Position cursor, depending on how long the full file path is
for (int i = 0; i < 256; i++) {
if (i == 33 || i == 65 || i == 97 || i == 129 || i == 161 || i == 193 || i == 225) {
cursorScreenPos++;
}
if (fullPath[i] == '\0') {
break;
}
}
iprintf ("\x1b[%d;0H", cursorScreenPos + OPTIONS_ENTRIES_START_ROW);
if (!entry->isDirectory) { if (!entry->isDirectory) {
if (entry->isApp) { if (entry->isApp) {
assignedOp[++maxCursors] = FileOperation::bootFile; operations.push_back(FileOperation::bootFile);
if (extension(entry->name, {"firm"})) { if (!extension(entry->name, {"firm"})) {
printf(" Boot file\n"); operations.push_back(FileOperation::bootstrapFile);
} else {
printf(" Boot file (Direct)\n");
assignedOp[++maxCursors] = FileOperation::bootstrapFile;
printf(" Bootstrap file\n");
} }
} }
if(extension(entry->name, {"nds", "dsi", "ids", "app"}))
{
assignedOp[++maxCursors] = FileOperation::mountNitroFS;
printf(" Mount NitroFS\n");
assignedOp[++maxCursors] = FileOperation::ndsInfo;
printf(" Show NDS file info\n");
}
else if(extension(entry->name, {"sav", "sav1", "sav2", "sav3", "sav4", "sav5", "sav6", "sav7", "sav8", "sav9"}))
{
assignedOp[++maxCursors] = FileOperation::restoreSave;
printf(" Restore save\n");
}
else if(extension(entry->name, {"img", "sd"}))
{
assignedOp[++maxCursors] = FileOperation::mountImg;
printf(" Mount as FAT image\n");
}
assignedOp[++maxCursors] = FileOperation::hexEdit;
printf(" Open in hex editor\n");
}
assignedOp[++maxCursors] = FileOperation::showInfo;
printf(entry->isDirectory ? " Show directory info\n" : " Show file info\n");
if (sdMounted && (strcmp(path, "sd:/gm9i/out/") != 0)) {
assignedOp[++maxCursors] = FileOperation::copySdOut;
printf(" Copy to sd:/gm9i/out\n");
}
if (flashcardMounted && (strcmp(path, "fat:/gm9i/out/") != 0)) {
assignedOp[++maxCursors] = FileOperation::copyFatOut;
printf(" Copy to fat:/gm9i/out\n");
}
// The bios SHA1 functions are only available on the DSi
// https://problemkaputt.de/gbatek.htm#biossha1functionsdsionly
if (isDSiMode()) {
assignedOp[++maxCursors] = FileOperation::calculateSHA1;
printf(" Calculate SHA1 hash\n");
}
printf("\n(<A> select, <B> cancel)");
consoleSelect(&bottomConsole);
printf ("\x1B[47m"); // Print foreground white color
while (true) {
// Clear old cursors
for (int i = OPTIONS_ENTRIES_START_ROW+cursorScreenPos; i < (maxCursors+1) + OPTIONS_ENTRIES_START_ROW+cursorScreenPos; i++) {
iprintf ("\x1b[%d;0H ", i);
}
// Show cursor
iprintf ("\x1b[%d;0H->", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos);
consoleSelect(&topConsole); if(extension(entry->name, {"nds", "dsi", "ids", "app"})) {
printf ("\x1B[30m"); // Print black color for time text operations.push_back(FileOperation::mountNitroFS);
operations.push_back(FileOperation::ndsInfo);
} else if(extension(entry->name, {"sav", "sav1", "sav2", "sav3", "sav4", "sav5", "sav6", "sav7", "sav8", "sav9"})) {
operations.push_back(FileOperation::restoreSave);
} else if(extension(entry->name, {"img", "sd"})) {
operations.push_back(FileOperation::mountImg);
}
operations.push_back(FileOperation::hexEdit);
// The bios SHA1 functions are only available on the DSi
// https://problemkaputt.de/gbatek.htm#biossha1functionsdsionly
if (isDSiMode()) {
operations.push_back(FileOperation::calculateSHA1);
}
}
operations.push_back(FileOperation::showInfo);
if (sdMounted && (strcmp(path, "sd:/gm9i/out/") != 0)) {
operations.push_back(FileOperation::copySdOut);
}
if (flashcardMounted && (strcmp(path, "fat:/gm9i/out/") != 0)) {
operations.push_back(FileOperation::copyFatOut);
}
while (true) {
font->clear(false);
font->print(0, 0, false, fullPath);
int row = y;
for(FileOperation operation : operations) {
switch(operation) {
case FileOperation::bootFile:
font->print(3, row++, false, extension(entry->name, {"firm"}) ? "Boot file" : "Boot file (Direct)");
break;
case FileOperation::bootstrapFile:
font->print(3, row++, false, "Bootstrap file");
break;
case FileOperation::mountNitroFS:
font->print(3, row++, false, "Mount NitroFS");
break;
case FileOperation::ndsInfo:
font->print(3, row++, false, "Show NDS file info");
break;
case FileOperation::restoreSave:
font->print(3, row++, false, "Restore save");
break;
case FileOperation::mountImg:
font->print(3, row++, false, "Mount as FAT image");
break;
case FileOperation::hexEdit:
font->print(3, row++, false, "Open in hex editor");
break;
case FileOperation::showInfo:
font->print(3, row++, false, entry->isDirectory ? "Show directory info" : "Show file info");
break;
case FileOperation::copySdOut:
font->print(3, row++, false, "Copy to sd:/gm9i/out");
break;
case FileOperation::copyFatOut:
font->print(3, row++, false, "Copy to fat:/gm9i/out");
break;
case FileOperation::calculateSHA1:
font->print(3, row++, false, "Calculate SHA1 hash");
break;
case FileOperation::none:
row++;
break;
}
}
font->print(3, ++row, false, "(<A> select, <B> cancel)");
// Show cursor
font->print(0, y + optionOffset, false, "->");
font->update(false);
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
do { do {
// Move to right side of screen
printf ("\x1b[0;26H");
// Print time // Print time
printf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDownRepeat(); pressed = keysDownRepeat();
@ -288,26 +286,26 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) {
#endif #endif
); );
consoleSelect(&bottomConsole);
printf ("\x1B[47m"); // Print foreground white color
if (pressed & KEY_UP) optionOffset -= 1; if (pressed & KEY_UP) optionOffset -= 1;
if (pressed & KEY_DOWN) optionOffset += 1; if (pressed & KEY_DOWN) optionOffset += 1;
if (optionOffset < 0) optionOffset = maxCursors; // Wrap around to bottom of list if (optionOffset < 0) // Wrap around to bottom of list
if (optionOffset > maxCursors) optionOffset = 0; // Wrap around to top of list optionOffset = operations.size() - 1;
if (optionOffset >= (int)operations.size()) // Wrap around to top of list
optionOffset = 0;
if (pressed & KEY_A) { if (pressed & KEY_A) {
switch(assignedOp[optionOffset]) { switch(operations[optionOffset]) {
case FileOperation::bootFile: { case FileOperation::bootFile: {
applaunch = true; applaunch = true;
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos); font->print(3, optionOffset + y, false, "Now loading...");
printf("Now loading..."); font->update(false);
break; break;
} case FileOperation::bootstrapFile: { } case FileOperation::bootstrapFile: {
char baseFile[256], savePath[PATH_MAX]; //, bootstrapConfigPath[32]; char baseFile[256], savePath[PATH_MAX]; //, bootstrapConfigPath[32];
//snprintf(bootstrapConfigPath, 32, "%s:/_nds/nds-bootstrap.ini", isDSiMode() ? "sd" : "fat"); //snprintf(bootstrapConfigPath, 32, "%s:/_nds/nds-bootstrap.ini", isDSiMode() ? "sd" : "fat");
strncpy(baseFile, entry->name.c_str(), 256); strncpy(baseFile, entry->name.c_str(), 255);
*strrchr(baseFile, '.') = 0; *strrchr(baseFile, '.') = 0;
snprintf(savePath, PATH_MAX, "%s%s%s.sav", path, !access("saves", F_OK) ? "saves/" : "", baseFile); snprintf(savePath, PATH_MAX, "%s%s%s.sav", path, !access("saves", F_OK) ? "saves/" : "", baseFile);
CIniFile bootstrapConfig("/_nds/nds-bootstrap.ini"); CIniFile bootstrapConfig("/_nds/nds-bootstrap.ini");
@ -327,19 +325,19 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) {
break; break;
} case FileOperation::copySdOut: { } case FileOperation::copySdOut: {
if (access("sd:/gm9i", F_OK) != 0) { if (access("sd:/gm9i", F_OK) != 0) {
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos); font->print(3, optionOffset + y, false, "Creating directory...");
printf("Creating directory..."); font->update(false);
mkdir("sd:/gm9i", 0777); mkdir("sd:/gm9i", 0777);
} }
if (access("sd:/gm9i/out", F_OK) != 0) { if (access("sd:/gm9i/out", F_OK) != 0) {
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos); font->print(3, optionOffset + y, false, "Creating directory...");
printf("Creating directory..."); font->update(false);
mkdir("sd:/gm9i/out", 0777); mkdir("sd:/gm9i/out", 0777);
} }
char destPath[256]; char destPath[256];
snprintf(destPath, sizeof(destPath), "sd:/gm9i/out/%s", entry->name.c_str()); snprintf(destPath, sizeof(destPath), "sd:/gm9i/out/%s", entry->name.c_str());
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos); font->print(3, optionOffset + y, false, "Copying...");
printf("Copying... "); font->update(false);
remove(destPath); remove(destPath);
char sourceFolder[PATH_MAX]; char sourceFolder[PATH_MAX];
getcwd(sourceFolder, PATH_MAX); getcwd(sourceFolder, PATH_MAX);
@ -350,19 +348,19 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) {
break; break;
} case FileOperation::copyFatOut: { } case FileOperation::copyFatOut: {
if (access("fat:/gm9i", F_OK) != 0) { if (access("fat:/gm9i", F_OK) != 0) {
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos); font->print(3, optionOffset + y, false, "Creating directory...");
printf("Creating directory..."); font->update(false);
mkdir("fat:/gm9i", 0777); mkdir("fat:/gm9i", 0777);
} }
if (access("fat:/gm9i/out", F_OK) != 0) { if (access("fat:/gm9i/out", F_OK) != 0) {
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos); font->print(3, optionOffset + y, false, "Creating directory...");
printf("Creating directory..."); font->update(false);
mkdir("fat:/gm9i/out", 0777); mkdir("fat:/gm9i/out", 0777);
} }
char destPath[256]; char destPath[256];
snprintf(destPath, sizeof(destPath), "fat:/gm9i/out/%s", entry->name.c_str()); snprintf(destPath, sizeof(destPath), "fat:/gm9i/out/%s", entry->name.c_str());
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos); font->print(3, (optionOffset + y), false, "Copying...");
printf("Copying... "); font->update(false);
remove(destPath); remove(destPath);
char sourceFolder[PATH_MAX]; char sourceFolder[PATH_MAX];
getcwd(sourceFolder, PATH_MAX); getcwd(sourceFolder, PATH_MAX);
@ -397,23 +395,27 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) {
hexEditor(entry->name.c_str(), currentDrive); hexEditor(entry->name.c_str(), currentDrive);
break; break;
} case FileOperation::calculateSHA1: { } case FileOperation::calculateSHA1: {
iprintf("\x1b[2J");
iprintf("Calculating SHA1 hash of:\n%s\n", entry->name.c_str());
iprintf("Press <START> to cancel\n\n");
u8 sha1[20] = {0}; u8 sha1[20] = {0};
bool ret = calculateSHA1(strcat(getcwd(path, PATH_MAX), entry->name.c_str()), sha1); bool ret = calculateSHA1(strcat(getcwd(path, PATH_MAX), entry->name.c_str()), sha1);
if (!ret) break; if (!ret)
iprintf("SHA1 hash is: \n"); break;
for (int i = 0; i < 20; ++i) iprintf("%02X", sha1[i]);
consoleSelect(&topConsole); font->clear(false);
iprintf ("\x1B[30m"); // Print black color font->print(0, 0, false, "SHA1 hash is:");
char sha1Str[41];
for (int i = 0; i < 20; ++i)
sniprintf(sha1Str + i * 2, 3, "%02X", sha1[i]);
font->print(0, 1, false, sha1Str);
font->print(0, font->calcHeight(sha1Str) + 2, false, "(<A> to continue)");
font->update(false);
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
int pressed; int pressed;
do { do {
// Move to right side of screen
iprintf ("\x1b[0;26H");
// Print time // Print time
iprintf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDownRepeat(); pressed = keysDownRepeat();
swiWaitForVBlank(); swiWaitForVBlank();
@ -423,7 +425,7 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) {
break; break;
} }
} }
return assignedOp[optionOffset]; return operations[optionOffset];
} }
if (pressed & KEY_B) { if (pressed & KEY_B) {
return FileOperation::none; return FileOperation::none;
@ -441,43 +443,33 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) {
bool fileBrowse_paste(char dest[256]) { bool fileBrowse_paste(char dest[256]) {
int pressed = 0; int pressed = 0;
int optionOffset = 0; int optionOffset = 0;
int maxCursors = -1;
consoleSelect(&bottomConsole);
consoleClear();
printf ("\x1B[47m"); // Print foreground white color
printf("Paste clipboard here?");
printf("\n\n");
iprintf ("\x1b[%d;0H", OPTIONS_ENTRIES_START_ROW);
maxCursors++;
printf(" Copy files\n");
for (auto &file : clipboard) {
if (file.nitro)
continue;
maxCursors++;
printf(" Move files\n");
break;
}
printf("\n");
printf("(<A> select, <B> cancel)");
consoleSelect(&bottomConsole);
printf ("\x1B[47m"); // Print foreground white color
while (true) { while (true) {
// Clear old cursors font->clear(false);
for (int i = OPTIONS_ENTRIES_START_ROW; i < (maxCursors+1) + OPTIONS_ENTRIES_START_ROW; i++) {
iprintf ("\x1b[%d;0H ", i); font->print(0, 0, false, "Paste clipboard here?");
}
// Show cursor int row = OPTIONS_ENTRIES_START_ROW, maxCursors = 0;
iprintf ("\x1b[%d;0H->", optionOffset + OPTIONS_ENTRIES_START_ROW); font->print(3, row++, false, "Copy files");
for (auto &file : clipboard) {
if (file.nitro)
continue;
maxCursors++;
font->print(3, row++, false, "Move files");
break;
}
font->print(3, ++row, false, "(<A> select, <B> cancel)");
// Show cursor
font->print(0, optionOffset + OPTIONS_ENTRIES_START_ROW, false, "->");
font->update(false);
consoleSelect(&topConsole);
printf ("\x1B[30m"); // Print black color for time text
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
do { do {
// Move to right side of screen
printf ("\x1b[0;26H");
// Print time // Print time
printf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDownRepeat(); pressed = keysDownRepeat();
@ -489,10 +481,6 @@ bool fileBrowse_paste(char dest[256]) {
#endif #endif
); );
consoleSelect(&bottomConsole);
printf ("\x1B[47m"); // Print foreground white color
if (pressed & KEY_UP) optionOffset -= 1; if (pressed & KEY_UP) optionOffset -= 1;
if (pressed & KEY_DOWN) optionOffset += 1; if (pressed & KEY_DOWN) optionOffset += 1;
@ -500,8 +488,7 @@ bool fileBrowse_paste(char dest[256]) {
if (optionOffset > maxCursors) optionOffset = 0; // Wrap around to top of list if (optionOffset > maxCursors) optionOffset = 0; // Wrap around to top of list
if (pressed & KEY_A) { if (pressed & KEY_A) {
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW); font->print(3, optionOffset + OPTIONS_ENTRIES_START_ROW, false, optionOffset ? "Moving... " : "Copying...");
printf(optionOffset ? "Moving... " : "Copying...");
for (auto &file : clipboard) { for (auto &file : clipboard) {
std::string destPath = dest + file.name; std::string destPath = dest + file.name;
if (file.path == destPath) if (file.path == destPath)
@ -537,14 +524,14 @@ bool fileBrowse_paste(char dest[256]) {
} }
void recRemove(const char *path, std::vector<DirEntry> dirContents) { void recRemove(const char *path, std::vector<DirEntry> dirContents) {
DirEntry *entry = NULL;
chdir (path); chdir (path);
getDirectoryContents(dirContents); getDirectoryContents(dirContents);
for (int i = 1; i < ((int)dirContents.size()); i++) { for (int i = 1; i < ((int)dirContents.size()); i++) {
entry = &dirContents.at(i); DirEntry &entry = dirContents[i];
if (entry->isDirectory) recRemove(entry->name.c_str(), dirContents); if (entry.isDirectory)
if (!(FAT_getAttr(entry->name.c_str()) & ATTR_READONLY)) { recRemove(entry.name.c_str(), dirContents);
remove(entry->name.c_str()); if (!(FAT_getAttr(entry.name.c_str()) & ATTR_READONLY)) {
remove(entry.name.c_str());
} }
} }
chdir (".."); chdir ("..");
@ -552,51 +539,52 @@ void recRemove(const char *path, std::vector<DirEntry> dirContents) {
} }
void fileBrowse_drawBottomScreen(DirEntry* entry) { void fileBrowse_drawBottomScreen(DirEntry* entry) {
consoleClear(); font->clear(false);
printf ("\x1B[47m"); // Print foreground white color
printf ("\x1b[22;0H"); int row = -1;
printf ("%s\n", titleName);
printf ("X - DELETE/[+R] RENAME file\n");
printf ("L - %s files (with \x18\x19\x1A\x1B)\n", entry->selected ? "DESELECT" : "SELECT");
printf ("Y - %s file/[+R] CREATE entry%s", clipboardOn ? "PASTE" : "COPY", clipboardOn ? "" : "\n");
printf ("R+A - Directory options\n");
if (sdMounted || flashcardMounted) {
printf ("%s\n", SCREENSHOTTEXT);
}
printf ("%s\n", clipboardOn ? "SELECT - Clear Clipboard" : "SELECT - Restore Clipboard");
if (!isDSiMode() && isRegularDS) { if (!isDSiMode() && isRegularDS) {
printf (POWERTEXT_DS); font->print(0, row--, false, POWERTEXT_DS);
} else if (is3DS) { } else if (is3DS) {
printf ("%s\n%s", POWERTEXT_3DS, HOMETEXT); font->print(0, row--, false, HOMETEXT);
font->print(0, row--, false, POWERTEXT_3DS);
} else { } else {
printf (POWERTEXT); font->print(0, row--, false, POWERTEXT);
} }
printf (entry->selected ? "\x1B[33m" : (entry->isDirectory ? "\x1B[37m" : "\x1B[40m")); // Print custom blue color or foreground black color font->print(0, row--, false, clipboardOn ? "SELECT - Clear Clipboard" : "SELECT - Restore Clipboard");
printf ("\x1b[0;0H"); if (sdMounted || flashcardMounted) {
printf ("%s\n", entry->name.c_str()); font->print(0, row--, false, SCREENSHOTTEXT);
}
font->print(0, row--, false, "R+A - Directory options\n");
font->printf(0, row--, false, Alignment::left, Palette::white, "Y - %s file/[+R] CREATE entry", clipboardOn ? "PASTE" : "COPY");
font->printf(0, row--, false, Alignment::left, Palette::white, "L - %s files (with ↑↓→←)\n", entry->selected ? "DESELECT" : "SELECT");
font->print(0, row--, false, "X - DELETE/[+R] RENAME file\n");
font->print(0, row--, false, titleName);
Palette pal = entry->selected ? Palette::yellow : (entry->isDirectory ? Palette::blue : Palette::gray);
font->print(0, 0, false, entry->name, Alignment::left, pal);
if (entry->name != "..") { if (entry->name != "..") {
if (entry->isDirectory) { if (entry->isDirectory) {
printf ("(dir)"); font->print(0, font->calcHeight(entry->name), false, "(dir)", Alignment::left, pal);
} else if (entry->size == 1) { } else if (entry->size == 1) {
printf ("%i Byte", (int)entry->size); font->printf(0, font->calcHeight(entry->name), false, Alignment::left, pal, "%i Byte", entry->size);
} else { } else {
printf ("%i Bytes", (int)entry->size); font->printf(0, font->calcHeight(entry->name), false, Alignment::left, pal, "%i Bytes", entry->size);
} }
} }
if (clipboardOn) { if (clipboardOn) {
printf ("\x1b[9;0H"); font->print(0, 6, false, "[CLIPBOARD]");
printf ("\x1B[47m"); // Print foreground white color
printf ("[CLIPBOARD]\n");
for (size_t i = 0; i < clipboard.size(); ++i) { for (size_t i = 0; i < clipboard.size(); ++i) {
printf (clipboard[i].folder ? "\x1B[37m" : "\x1B[40m"); // Print custom blue color or foreground black color
if (i < 4) { if (i < 4) {
printf ("%s\n", clipboard[i].name.c_str()); font->print(0, 7 + i, false, clipboard[i].name, Alignment::left, clipboard[i].folder ? Palette::blue : Palette::gray);
} else { } else {
printf ("%d more files...\n", clipboard.size() - 4); font->printf(0, 7 + i, false, Alignment::left, Palette::gray, "%d more files...", clipboard.size() - 4);
break; break;
} }
} }
} }
font->update(false);
} }
std::string browseForFile (void) { std::string browseForFile (void) {
@ -609,23 +597,18 @@ std::string browseForFile (void) {
getDirectoryContents (dirContents); getDirectoryContents (dirContents);
while (true) { while (true) {
DirEntry* entry = &dirContents.at(fileOffset); DirEntry* entry = &dirContents[fileOffset];
consoleSelect(&bottomConsole);
fileBrowse_drawBottomScreen(entry); fileBrowse_drawBottomScreen(entry);
consoleSelect(&topConsole); showDirectoryContents(dirContents, fileOffset, screenOffset);
showDirectoryContents (dirContents, fileOffset, screenOffset);
stored_SCFG_MC = REG_SCFG_MC; stored_SCFG_MC = REG_SCFG_MC;
printf ("\x1B[30m"); // Print black color for time text
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do // Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
do { do {
// Move to right side of screen
printf ("\x1b[0;26H");
// Print time // Print time
printf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDownRepeat(); pressed = keysDownRepeat();
@ -639,16 +622,7 @@ std::string browseForFile (void) {
if ((held & KEY_R) && (pressed & KEY_L)) { if ((held & KEY_R) && (pressed & KEY_L)) {
break; break;
} }
} while (!(pressed & KEY_UP) && !(pressed & KEY_DOWN) && !(pressed & KEY_LEFT) && !(pressed & KEY_RIGHT) } while (!pressed);
&& !(pressed & KEY_A) && !(pressed & KEY_B) && !(pressed & KEY_X) && !(pressed & KEY_Y)
&& !(pressed & KEY_L) && !(pressed & KEY_SELECT)
#ifdef SCREENSWAP
&& !(pressed & KEY_TOUCH)
#endif
);
printf ("\x1B[47m"); // Print foreground white color
iprintf ("\x1b[%d;0H", fileOffset - screenOffset + ENTRIES_START_ROW);
if (isDSiMode() && !pressed && currentDrive == 1 && REG_SCFG_MC == 0x11 && flashcardMounted) { if (isDSiMode() && !pressed && currentDrive == 1 && REG_SCFG_MC == 0x11 && flashcardMounted) {
flashcardUnmount(); flashcardUnmount();
@ -692,10 +666,11 @@ std::string browseForFile (void) {
screenMode = 0; screenMode = 0;
return "null"; return "null";
} else if (entry->isDirectory) { } else if (entry->isDirectory) {
iprintf("Entering directory "); font->printf(0, fileOffset - screenOffset + ENTRIES_START_ROW, true, Alignment::left, Palette::white, "%-*s", SCREEN_COLS - 5, "Entering directory");
font->update(true);
// Enter selected directory // Enter selected directory
chdir (entry->name.c_str()); chdir (entry->name.c_str());
getDirectoryContents (dirContents); getDirectoryContents(dirContents);
screenOffset = 0; screenOffset = 0;
fileOffset = 0; fileOffset = 0;
} else { } else {
@ -748,8 +723,10 @@ std::string browseForFile (void) {
// Rename file/folder // Rename file/folder
if ((held & KEY_R) && (pressed & KEY_X) && (entry->name != ".." && strncmp(path, "nitro:/", 7) != 0)) { if ((held & KEY_R) && (pressed & KEY_X) && (entry->name != ".." && strncmp(path, "nitro:/", 7) != 0)) {
printf ("\x1b[0;27H"); // Clear time
printf (" "); // Clear time font->print(-1, 0, true, " ", Alignment::right, Palette::blackGreen);
font->update(true);
pressed = 0; pressed = 0;
consoleDemoInit(); consoleDemoInit();
Keyboard *kbd = keyboardDemoInit(); Keyboard *kbd = keyboardDemoInit();
@ -757,13 +734,13 @@ std::string browseForFile (void) {
kbd->OnKeyPressed = OnKeyPressed; kbd->OnKeyPressed = OnKeyPressed;
keyboardShow(); keyboardShow();
printf("Rename to: \n"); iprintf("Rename to:\n");
fgets(newName, 256, stdin); fgets(newName, 256, stdin);
newName[strlen(newName)-1] = 0; newName[strlen(newName)-1] = 0;
keyboardHide(); keyboardHide();
consoleClear();
reinitConsoles(); videoSetModeSub(MODE_5_2D);
bgShow(bgInitSub(2, BgType_Bmp8, BgSize_B8_256x256, 3, 0));
if (newName[0] != '\0') { if (newName[0] != '\0') {
// Check for unsupported characters // Check for unsupported characters
@ -782,49 +759,44 @@ std::string browseForFile (void) {
} }
} }
if (rename(entry->name.c_str(), newName) == 0) { if (rename(entry->name.c_str(), newName) == 0) {
getDirectoryContents (dirContents); getDirectoryContents(dirContents);
} }
} }
} }
// Delete action // Delete action
if ((pressed & KEY_X) && (entry->name != ".." && strncmp(path, "nitro:/", 7) != 0)) { if ((pressed & KEY_X) && (entry->name != ".." && strncmp(path, "nitro:/", 7) != 0)) {
consoleSelect(&bottomConsole); font->clear(false);
consoleClear();
printf ("\x1B[47m"); // Print foreground white color
int selections = std::count_if(dirContents.begin(), dirContents.end(), [](const DirEntry &x){ return x.selected; }); int selections = std::count_if(dirContents.begin(), dirContents.end(), [](const DirEntry &x){ return x.selected; });
if (entry->selected && selections > 1) { if (entry->selected && selections > 1) {
iprintf("Delete %d paths?\n", selections); font->printf(0, 0, false, Alignment::left, Palette::white, "Delete %d paths?", selections);
for (uint i = 0, printed = 0; i < dirContents.size() && printed < 5; i++) { for (uint i = 0, printed = 0; i < dirContents.size() && printed < 5; i++) {
if (dirContents[i].selected) { if (dirContents[i].selected) {
iprintf("\x1B[41m- %s\n", dirContents[i].name.c_str()); font->printf(0, printed + 2, false, Alignment::left, Palette::red, "- %s", dirContents[i].name.c_str());
printed++; printed++;
} }
} }
if(selections > 5) if(selections > 5)
iprintf("\x1B[41m- and %d more...\n", selections - 5); font->printf(0, 7, false, Alignment::left, Palette::red, "- and %d more...", selections - 5);
iprintf("\x1B[47m");
} else { } else {
iprintf("Delete \"%s\"?\n", entry->name.c_str()); font->printf(0, 0, false, Alignment::left, Palette::white, "Delete \"%s\"?", entry->name.c_str());
} }
printf ("(<A> yes, <B> no)"); font->print(0, (!entry->selected || selections == 1) ? 2 : (selections > 5 ? 9 : selections + 3), false, "(<A> yes, <B> no)");
consoleSelect(&topConsole); font->update(false);
printf ("\x1B[30m"); // Print black color for time text
while (true) { while (true) {
// Move to right side of screen
printf ("\x1b[0;26H");
// Print time // Print time
printf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDownRepeat(); pressed = keysDownRepeat();
swiWaitForVBlank(); swiWaitForVBlank();
if (pressed & KEY_A) { if (pressed & KEY_A) {
consoleSelect(&bottomConsole);
consoleClear();
printf ("\x1B[47m"); // Print foreground white color
if (entry->selected) { if (entry->selected) {
printf ("Deleting files, please wait..."); font->clear(false);
font->print(0, 0, false, "Deleting files, please wait...");
font->update(false);
struct stat st; struct stat st;
for (auto &item : dirContents) { for (auto &item : dirContents) {
if(item.selected) { if(item.selected) {
@ -839,19 +811,16 @@ std::string browseForFile (void) {
} }
fileOffset = 0; fileOffset = 0;
} else if (FAT_getAttr(entry->name.c_str()) & ATTR_READONLY) { } else if (FAT_getAttr(entry->name.c_str()) & ATTR_READONLY) {
printf ("Failed deleting:\n"); font->clear(false);
printf (entry->name.c_str()); font->print(0, 0, false, "Failed deleting:");
printf ("\n"); font->print(0, 1, false, entry->name);
printf ("\n"); font->print(0, 3, false, "(<A> to continue)");
printf ("(<A> to continue)");
pressed = 0; pressed = 0;
consoleSelect(&topConsole);
printf ("\x1B[30m"); // Print black color for time text
while (!(pressed & KEY_A)) { while (!(pressed & KEY_A)) {
// Move to right side of screen
printf ("\x1b[0;26H");
// Print time // Print time
printf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDown(); pressed = keysDown();
@ -860,10 +829,14 @@ std::string browseForFile (void) {
for (int i = 0; i < 15; i++) swiWaitForVBlank(); for (int i = 0; i < 15; i++) swiWaitForVBlank();
} else { } else {
if (entry->isDirectory) { if (entry->isDirectory) {
printf ("Deleting folder, please wait..."); font->clear(false);
font->print(0, 0, false, "Deleting folder, please wait...");
font->update(false);
recRemove(entry->name.c_str(), dirContents); recRemove(entry->name.c_str(), dirContents);
} else { } else {
printf ("Deleting file, please wait..."); font->clear(false);
font->print(0, 0, false, "Deleting folder, please wait...");
font->update(false);
remove(entry->name.c_str()); remove(entry->name.c_str());
} }
fileOffset--; fileOffset--;
@ -881,8 +854,10 @@ std::string browseForFile (void) {
// Create new folder // Create new folder
if ((held & KEY_R) && (pressed & KEY_Y) && (strncmp(path, "nitro:/", 7) != 0)) { if ((held & KEY_R) && (pressed & KEY_Y) && (strncmp(path, "nitro:/", 7) != 0)) {
printf ("\x1b[0;27H"); // Clear time
printf (" "); // Clear time font->print(-1, 0, true, " ", Alignment::right, Palette::blackGreen);
font->update(true);
pressed = 0; pressed = 0;
consoleDemoInit(); consoleDemoInit();
Keyboard *kbd = keyboardDemoInit(); Keyboard *kbd = keyboardDemoInit();
@ -890,13 +865,13 @@ std::string browseForFile (void) {
kbd->OnKeyPressed = OnKeyPressed; kbd->OnKeyPressed = OnKeyPressed;
keyboardShow(); keyboardShow();
printf("Name for new folder: \n"); iprintf("Name for new folder:\n");
fgets(newName, 256, stdin); fgets(newName, 256, stdin);
newName[strlen(newName)-1] = 0; newName[strlen(newName)-1] = 0;
keyboardHide(); keyboardHide();
consoleClear();
reinitConsoles(); videoSetModeSub(MODE_5_2D);
bgShow(bgInitSub(2, BgType_Bmp8, BgSize_B8_256x256, 3, 0));
if (newName[0] != '\0') { if (newName[0] != '\0') {
// Check for unsupported characters // Check for unsupported characters
@ -921,17 +896,14 @@ std::string browseForFile (void) {
} }
// Add to selection // Add to selection
if (pressed & KEY_L && entry->name != "..") { if ((pressed & KEY_L && !(held & KEY_R)) && entry->name != "..") {
bool select = !entry->selected; bool select = !entry->selected;
entry->selected = select; entry->selected = select;
while(held & KEY_L) { while(held & KEY_L) {
do { do {
// Move to right side of screen
printf ("\x1b[0;26H");
// Print black color for time text
printf ("\x1B[30m");
// Print time // Print time
printf (" %s" ,RetTime().c_str()); font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
scanKeys(); scanKeys();
pressed = keysDownRepeat(); pressed = keysDownRepeat();
@ -980,10 +952,8 @@ std::string browseForFile (void) {
} }
} }
consoleSelect(&bottomConsole);
fileBrowse_drawBottomScreen(entry); fileBrowse_drawBottomScreen(entry);
consoleSelect(&topConsole); showDirectoryContents(dirContents, fileOffset, screenOffset);
showDirectoryContents (dirContents, fileOffset, screenOffset);
} }
} }
@ -1025,48 +995,8 @@ std::string browseForFile (void) {
// Make a screenshot // Make a screenshot
if ((held & KEY_R) && (pressed & KEY_L)) { if ((held & KEY_R) && (pressed & KEY_L)) {
if (sdMounted || flashcardMounted) { if(screenshot())
if (access((sdMounted ? "sd:/gm9i" : "fat:/gm9i"), F_OK) != 0) { getDirectoryContents(dirContents);
mkdir((sdMounted ? "sd:/gm9i" : "fat:/gm9i"), 0777);
if (strcmp(path, (sdMounted ? "sd:/" : "fat:/")) == 0) {
getDirectoryContents (dirContents);
}
}
if (access((sdMounted ? "sd:/gm9i/out" : "fat:/gm9i/out"), F_OK) != 0) {
mkdir((sdMounted ? "sd:/gm9i/out" : "fat:/gm9i/out"), 0777);
if (strcmp(path, (sdMounted ? "sd:/gm9i/" : "fat:/gm9i/")) == 0) {
getDirectoryContents (dirContents);
}
}
char timeText[8];
snprintf(timeText, sizeof(timeText), "%s", RetTime().c_str());
char fileTimeText[8];
snprintf(fileTimeText, sizeof(fileTimeText), "%s", RetTimeForFilename().c_str());
char snapPath[40];
// Take top screenshot
snprintf(snapPath, sizeof(snapPath), "%s:/gm9i/out/snap_%s_top.bmp", (sdMounted ? "sd" : "fat"), fileTimeText);
screenshotbmp(snapPath);
// Seamlessly swap top and bottom screens
lcdMainOnBottom();
printBorderBottom();
consoleSelect(&bottomConsole);
showDirectoryContents (dirContents, fileOffset, screenOffset);
printf("\x1B[30m"); // Print black color for time text
printf ("\x1b[0;26H");
printf (" %s" ,timeText);
clearBorderTop();
consoleSelect(&topConsole);
fileBrowse_drawBottomScreen(entry);
// Take bottom screenshot
snprintf(snapPath, sizeof(snapPath), "%s:/gm9i/out/snap_%s_bot.bmp", (sdMounted ? "sd" : "fat"), fileTimeText);
screenshotbmp(snapPath);
if (strcmp(path, (sdMounted ? "sd:/gm9i/out/" : "fat:/gm9i/out/")) == 0) {
getDirectoryContents (dirContents);
}
lcdMainOnTop();
printBorderTop();
clearBorderBottom();
}
} }
} }
} }

369
arm9/source/font.cpp Normal file
View File

@ -0,0 +1,369 @@
#include "font.h"
#include "font_default_frf.h"
#include "tonccpy.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include<nds.h>
u8 Font::textBuf[2][256 * 192];
bool Font::mainScreen = false;
Font *font = nullptr;
bool Font::isStrongRTL(char16_t c) {
return (c >= 0x0590 && c <= 0x05FF) || c == 0x200F;
}
bool Font::isWeak(char16_t c) {
return c < 'A' || (c > 'Z' && c < 'a') || (c > 'z' && c < 127);
}
bool Font::isNumber(char16_t c) {
return c >= '0' && c <= '9';
}
Font::Font(const char *path) {
FILE *file = fopen(path, "rb");
const u8 *fileBuffer = font_default_frf;
if(file) {
fseek(file, 0, SEEK_END);
size_t size = ftell(file);
fileBuffer = new u8[size];
if(!fileBuffer) {
fclose(file);
return;
}
fseek(file, 0, SEEK_SET);
fread((void *)fileBuffer, 1, size, file);
}
const u8 *ptr = fileBuffer;
// Check header magic, then skip over
if(memcmp(ptr, "RIFF", 4) != 0)
goto cleanup;
ptr += 8;
// check for and load META section
if(memcmp(ptr, "META", 4) == 0) {
tileWidth = ptr[8];
tileHeight = ptr[9];
tonccpy(&tileCount, ptr + 10, sizeof(u16));
if(tileWidth > TILE_MAX_WIDTH || tileHeight > TILE_MAX_HEIGHT)
goto cleanup;
u32 section_size;
tonccpy(&section_size, ptr + 4, sizeof(u32));
ptr += 8 + section_size;
} else {
goto cleanup;
}
// Character data
if(memcmp(ptr, "CDAT", 4) == 0) {
fontTiles = new u8[tileHeight * tileCount];
if(!fontTiles)
goto cleanup;
tonccpy(fontTiles, ptr + 8, tileHeight * tileCount);
u32 section_size;
tonccpy(&section_size, ptr + 4, sizeof(u32));
ptr += 8 + section_size;
} else {
goto cleanup;
}
// character map
if(memcmp(ptr, "CMAP", 4) == 0) {
fontMap = new u16[tileCount];
if(!fontMap)
goto cleanup;
tonccpy(fontMap, ptr + 8, sizeof(u16) * tileCount);
u32 section_size;
tonccpy(&section_size, ptr + 4, sizeof(u32));
ptr += 8 + section_size;
} else {
goto cleanup;
}
questionMark = getCharIndex('?');
// Copy palette to VRAM
for(uint i = 0; i < sizeof(palette) / sizeof(palette[0]); i++) {
tonccpy(BG_PALETTE + i * 0x10, palette[i], 4);
tonccpy(BG_PALETTE_SUB + i * 0x10, palette[i], 4);
}
cleanup:
if(fileBuffer != font_default_frf)
delete[] fileBuffer;
}
Font::~Font(void) {
if(fontTiles)
delete[] fontTiles;
if(fontMap)
delete[] fontMap;
}
u16 Font::getCharIndex(char16_t c) {
// Try a binary search
int left = 0;
int right = tileCount;
while(left <= right) {
int mid = left + ((right - left) / 2);
if(fontMap[mid] == c) {
return mid;
}
if(fontMap[mid] < c) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return questionMark;
}
std::u16string Font::utf8to16(std::string_view text) {
std::u16string out;
for(uint i = 0; i < text.size();) {
char16_t c;
if(!(text[i] & 0x80)) {
c = text[i++];
} else if((text[i] & 0xE0) == 0xC0) {
c = (text[i++] & 0x1F) << 6;
c |= text[i++] & 0x3F;
} else if((text[i] & 0xF0) == 0xE0) {
c = (text[i++] & 0x0F) << 12;
c |= (text[i++] & 0x3F) << 6;
c |= text[i++] & 0x3F;
} else {
i++; // out of range or something (This only does up to U+FFFF since it goes to a U16 anyways)
}
out += c;
}
return out;
}
int Font::calcHeight(std::u16string_view text, int xPos) {
int lines = 1, chars = xPos + 1;
for(char16_t c : text) {
if(c == '\n' || chars > 256 / tileWidth) {
nocashMessage(c == '\n' ? "line" : "cha");
lines++;
chars = xPos + 1;
} else {
chars++;
}
}
return lines;
}
void Font::printf(int xPos, int yPos, bool top, Alignment align, Palette palette, const char *format, ...) {
char str[0x100];
va_list va;
va_start(va, format);
vsniprintf(str, 0x100, format, va);
va_end(va);
print(xPos, yPos, top, str, align, palette);
}
ITCM_CODE void Font::print(int xPos, int yPos, bool top, std::u16string_view text, Alignment align, Palette palette, bool rtl) {
int x = xPos * tileWidth, y = yPos * tileHeight;
if(x < 0 && align != Alignment::center)
x += 255;
if(y < 0)
y += 191;
// If RTL isn't forced, check for RTL text
if(!rtl) {
for(const auto c : text) {
if(isStrongRTL(c)) {
rtl = true;
break;
}
}
}
auto ltrBegin = text.end(), ltrEnd = text.end();
// Adjust x for alignment
switch(align) {
case Alignment::left: {
break;
} case Alignment::center: {
size_t newline = text.find('\n');
while(newline != text.npos) {
print(xPos, yPos, top, text.substr(0, newline), align, palette, rtl);
text = text.substr(newline + 1);
newline = text.find('\n');
yPos++;
y += tileHeight;
}
x = ((256 - (text.length() * tileWidth)) / 2) + x;
break;
} case Alignment::right: {
size_t newline = text.find('\n');
while(newline != text.npos) {
print(xPos, yPos, top, text.substr(0, newline), Alignment::left, palette, rtl);
text = text.substr(newline + 1);
newline = text.find('\n');
yPos++;
y += tileHeight;
}
break;
}
}
if(align == Alignment::right)
x -= (text.length() - 1) * tileWidth;
// Align to grid
x -= x % tileWidth;
y -= y % tileHeight;
x += (256 % tileWidth) / 2;
y += (192 % tileHeight) / 2;
const int xStart = x;
// Loop through string and print it
for(auto it = (rtl ? text.end() - 1 : text.begin()); true; it += (rtl ? -1 : 1)) {
// If we hit the end of the string in an LTR section of an RTL
// string, it may not be done, if so jump back to printing RTL
if(it == (rtl ? text.begin() - 1 : text.end())) {
if(ltrBegin == text.end() || (ltrBegin == text.begin() && ltrEnd == text.end())) {
break;
} else {
it = ltrBegin;
ltrBegin = text.end();
rtl = true;
}
}
// If at the end of an LTR section within RTL, jump back to the RTL
if(it == ltrEnd && ltrBegin != text.end()) {
if(ltrBegin == text.begin() && (!isWeak(*ltrBegin) || isNumber(*ltrBegin)))
break;
it = ltrBegin;
ltrBegin = text.end();
rtl = true;
// If in RTL and hit a non-RTL character that's not punctuation, switch to LTR
} else if(rtl && !isStrongRTL(*it) && (!isWeak(*it) || isNumber(*it))) {
// Save where we are as the end of the LTR section
ltrEnd = it + 1;
// Go back until an RTL character or the start of the string
bool allNumbers = true;
while(!isStrongRTL(*it) && it != text.begin()) {
// Check for if the LTR section is only numbers,
// if so they won't be removed from the end
if(allNumbers && !isNumber(*it) && !isWeak(*it))
allNumbers = false;
it--;
}
// Save where we are to return to after printing the LTR section
ltrBegin = it;
// If on an RTL char right now, add one
if(isStrongRTL(*it)) {
it++;
}
// Remove all punctuation and, if the section isn't only numbers,
// numbers from the end of the LTR section
if(allNumbers) {
while(isWeak(*it) && !isNumber(*it)) {
if(it != text.begin())
ltrBegin++;
it++;
}
} else {
while(isWeak(*it)) {
if(it != text.begin())
ltrBegin++;
it++;
}
}
// But then allow all numbers directly touching the strong LTR or with 1 weak between
while((it - 1 >= text.begin() && isNumber(*(it - 1))) || (it - 2 >= text.begin() && isWeak(*(it - 1)) && isNumber(*(it - 2)))) {
if(it - 1 != text.begin())
ltrBegin--;
it--;
}
rtl = false;
}
if(*it == '\n') {
x = xStart;
y += tileHeight;
continue;
}
// Brackets are flipped in RTL
u16 index;
if(rtl) {
switch(*it) {
case '(':
index = getCharIndex(')');
break;
case ')':
index = getCharIndex('(');
break;
case '[':
index = getCharIndex(']');
break;
case ']':
index = getCharIndex('[');
break;
case '<':
index = getCharIndex('>');
break;
case '>':
index = getCharIndex('<');
break;
default:
index = getCharIndex(*it);
break;
}
} else {
index = getCharIndex(*it);
}
// Wrap at right edge if not center aligning
if(x + tileWidth > 256 && align != Alignment::center) {
x = xStart;
y += tileHeight;
}
// Don't draw off screen chars
if(x >= 0 && y >= 0 && y + tileHeight <= 192) {
u8 *dst = textBuf[top] + x;
for(int i = 0; i < tileHeight; i++) {
u8 px = fontTiles[(index * tileHeight) + i];
for(int j = 0; j < tileWidth; j++) {
dst[(y + i) * 256 + j] = u8(palette) * 0x10 + ((px >> (7 - j)) & 1);
}
}
}
x += tileWidth;
}
}

94
arm9/source/font.h Normal file
View File

@ -0,0 +1,94 @@
#ifndef FONT_H
#define FONT_H
#include "tonccpy.h"
#include <nds/arm9/background.h>
#include <nds/dma.h>
#include <nds/ndstypes.h>
#include <string>
#define TILE_MAX_WIDTH 8
#define TILE_MAX_HEIGHT 10
#define SCREEN_COLS (256 / font->width())
#define ENTRIES_PER_SCREEN ((192 - font->height()) / font->height())
enum class Alignment {
left,
center,
right,
};
enum class Palette : u8 {
white = 0,
gray,
red,
green,
greenAlt,
blue,
yellow,
blackRed,
blackGreen,
blackBlue,
};
class Font {
static bool isStrongRTL(char16_t c);
static bool isWeak(char16_t c);
static bool isNumber(char16_t c);
static constexpr u16 palette[16][2] = {
{0x0000, 0x7FFF}, // White
{0x0000, 0x3DEF}, // Gray
{0x0000, 0x001F}, // Red
{0x0000, 0x03E0}, // Green
{0x0000, 0x02E0}, // Green (alt)
{0x0000, 0x656A}, // Blue
{0x0000, 0x3339}, // Yellow
{0x001F, 0x0000}, // Black on red
{0x03E0, 0x0000}, // Black on green
{0x656A, 0x0000}, // Black on blue
};
static u8 textBuf[2][256 * 192];
static bool mainScreen;
u8 tileWidth = 0, tileHeight = 0;
u16 tileCount = 0;
u16 questionMark = 0;
u8 *fontTiles = nullptr;
u16 *fontMap = nullptr;
u16 getCharIndex(char16_t c);
public:
static std::u16string utf8to16(std::string_view text);
static void update(bool top) { tonccpy(bgGetGfxPtr(top ? 2 : 6), Font::textBuf[top ^ mainScreen], 256 * 192); }
static void clear(bool top) { dmaFillWords(0, Font::textBuf[top ^ mainScreen], 256 * 192); }
static void mainOnTop(bool top) { mainScreen = !top; }
Font(const char *path);
~Font(void);
u8 width(void) { return tileWidth; }
u8 height(void) { return tileHeight; }
int calcWidth(std::string_view text) { return utf8to16(text).length(); }
int calcWidth(std::u16string_view text) { return text.length(); };
int calcHeight(std::string_view text, int xPos = 0) { return calcHeight(utf8to16(text)); }
int calcHeight(std::u16string_view text, int xPos = 0);
void printf(int xPos, int yPos, bool top, Alignment align, Palette palette, const char *format, ...);
void print(int xPos, int yPos, bool top, int value, Alignment align = Alignment::left, Palette palette = Palette::white) { print(xPos, yPos, top, std::to_string(value), align, palette); }
void print(int xPos, int yPos, bool top, std::string_view text, Alignment align = Alignment::left, Palette palette = Palette::white) { print(xPos, yPos, top, utf8to16(text), align, palette); }
void print(int xPos, int yPos, bool top, std::u16string_view text, Alignment align = Alignment::left, Palette palette = Palette::white, bool rtl = false);
};
extern Font *font;
#endif // FONT_H

View File

@ -1,41 +1,37 @@
#include "hexEditor.h" #include "hexEditor.h"
#include "date.h" #include "date.h"
#include "tonccpy.h"
#include "file_browse.h" #include "file_browse.h"
#include "font.h"
#include "tonccpy.h"
#include <algorithm> #include <algorithm>
#include <nds.h> #include <nds.h>
#include <stdio.h> #include <stdio.h>
extern PrintConsole bottomConsole, bottomConsoleBG, topConsole;
extern void reinitConsoles(void);
u32 jumpToOffset(u32 offset) { u32 jumpToOffset(u32 offset) {
consoleSelect(&bottomConsoleBG);
consoleClear();
consoleSelect(&bottomConsole);
consoleClear();
u8 cursorPosition = 0; u8 cursorPosition = 0;
u16 pressed = 0, held = 0; u16 pressed = 0, held = 0;
while(1) { while(1) {
printf("\x1B[9;6H\x1B[47m-------------------"); int y = (ENTRIES_PER_SCREEN - 4) / 2;
printf("\x1B[10;8H\x1B[47mJump to Offset"); font->clear(false);
printf("\x1B[12;11H\x1B[37m%08lX", offset); font->print(0, y, false, "--------------------", Alignment::center);
printf("\x1B[12;%dH\x1B[41m%lX", 17 - cursorPosition, (offset >> ((cursorPosition + 1) * 4)) & 0xF); font->print(0, y + 1, false, "Jump to Offset", Alignment::center);
printf("\x1B[13;6H\x1B[47m-------------------"); font->printf(0, y + 3, false, Alignment::center, Palette::blue, "%08lX", offset);
font->printf(3 - cursorPosition, y + 3, false, Alignment::center, Palette::red, "%lX", (offset >> ((cursorPosition + 1) * 4)) & 0xF);
font->print(0, y + 4, false, "--------------------", Alignment::center);
font->update(false);
consoleSelect(&topConsole);
do { do {
swiWaitForVBlank(); swiWaitForVBlank();
scanKeys(); scanKeys();
pressed = keysDown(); pressed = keysDown();
held = keysDownRepeat(); held = keysDownRepeat();
printf("\x1B[30m\x1B[0;26H %s", RetTime().c_str()); // Print time
// Print time
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
} while(!held); } while(!held);
consoleSelect(&bottomConsole);
if(held & KEY_UP) { if(held & KEY_UP) {
offset = (offset & ~(0xF0 << cursorPosition * 4)) | ((offset + (0x10 << (cursorPosition * 4))) & (0xF0 << cursorPosition * 4)); offset = (offset & ~(0xF0 << cursorPosition * 4)) | ((offset + (0x10 << (cursorPosition * 4))) & (0xF0 << cursorPosition * 4));
@ -54,28 +50,27 @@ u32 jumpToOffset(u32 offset) {
} }
u32 search(u32 offset, FILE *file) { u32 search(u32 offset, FILE *file) {
consoleSelect(&bottomConsoleBG);
consoleClear();
consoleSelect(&bottomConsole);
consoleClear();
u8 cursorPosition = 0; u8 cursorPosition = 0;
u16 pressed = 0, held = 0; u16 pressed = 0, held = 0;
while(1) { while(1) {
printf("\x1B[9;4H\x1B[47m-----------------------"); int y = (ENTRIES_PER_SCREEN - 3) / 2;
printf("\x1B[10;5H%c Search for String %c", cursorPosition == 0 ? '>' : ' ', cursorPosition == 0 ? '<' : ' '); font->clear(false);
printf("\x1B[11;5H%c Search for Data %c", cursorPosition == 1 ? '>' : ' ', cursorPosition == 1 ? '<' : ' '); font->print(0, y, false, "--------------------", Alignment::center);
printf("\x1B[12;4H-----------------------"); font->printf(0, y + 1, false, Alignment::center, Palette::white, "%c Search for String %c", cursorPosition == 0 ? '>' : ' ', cursorPosition == 0 ? '<' : ' ');
font->printf(0, y + 2, false, Alignment::center, Palette::white, "%c Search for Data %c", cursorPosition == 1 ? '>' : ' ', cursorPosition == 1 ? '<' : ' ');
font->print(0, y + 3, false, "--------------------", Alignment::center);
font->update(false);
consoleSelect(&topConsole);
do { do {
swiWaitForVBlank(); swiWaitForVBlank();
scanKeys(); scanKeys();
pressed = keysDown(); pressed = keysDown();
held = keysDownRepeat(); held = keysDownRepeat();
printf("\x1B[30m\x1B[0;26H %s", RetTime().c_str()); // Print time
// Print time
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
} while(!held); } while(!held);
consoleSelect(&bottomConsole);
if(held & (KEY_UP | KEY_DOWN)) { if(held & (KEY_UP | KEY_DOWN)) {
cursorPosition ^= 1; cursorPosition ^= 1;
@ -98,10 +93,9 @@ u32 search(u32 offset, FILE *file) {
printf("Search for:\n"); printf("Search for:\n");
fgets(str, sizeof(str), stdin); fgets(str, sizeof(str), stdin);
keyboardHide(); keyboardHide();
consoleClear();
reinitConsoles(); videoSetModeSub(MODE_5_2D);
consoleSelect(&bottomConsole); bgShow(bgInitSub(2, BgType_Bmp8, BgSize_B8_256x256, 3, 0));
BG_PALETTE_SUB[0x1F] = 0x9CF7; BG_PALETTE_SUB[0x1F] = 0x9CF7;
BG_PALETTE_SUB[0x2F] = 0xB710; BG_PALETTE_SUB[0x2F] = 0xB710;
@ -114,27 +108,27 @@ u32 search(u32 offset, FILE *file) {
str[strLen] = 0; // Remove ending \n that fgets has str[strLen] = 0; // Remove ending \n that fgets has
} else { } else {
consoleClear();
cursorPosition = 0; cursorPosition = 0;
while(1) { while(1) {
printf("\x1B[9;6H\x1B[47m------------------"); int y = (ENTRIES_PER_SCREEN - 4) / 2;
printf("\x1B[10;9HEnter value:"); font->clear(false);
u8 pos = 15 - strLen; font->print(0, y, false, "--------------------", Alignment::center);
for(size_t i = 0; i < strLen * 2; i++) { font->print(0, y + 1, false, "Enter value:", Alignment::center);
printf("\x1B[12;%dH\x1B[%dm%X", pos + i, (i == cursorPosition ? 31 : ((i / 2 % 2) ? 33 : 32)), str[i / 2] >> (!(i % 2) * 4) & 0xF); for(size_t i = 0; i < strLen * 2; i++)
} font->printf(-strLen + i + 1, y + 3, false, Alignment::center, i == cursorPosition ? Palette::red : ((i / 2 % 2) ? Palette::greenAlt : Palette::green), "%X", str[i / 2] >> (!(i % 2) * 4) & 0xF);
printf("\x1B[13;6H\x1B[47m------------------"); font->print(0, y + 4, false, "--------------------", Alignment::center);
font->update(false);
consoleSelect(&topConsole);
do { do {
swiWaitForVBlank(); swiWaitForVBlank();
scanKeys(); scanKeys();
pressed = keysDown(); pressed = keysDown();
held = keysDownRepeat(); held = keysDownRepeat();
printf("\x1B[30m\x1B[0;26H %s", RetTime().c_str()); // Print time
// Print time
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
} while(!held); } while(!held);
consoleSelect(&bottomConsole);
if(held & KEY_UP) { if(held & KEY_UP) {
char val = str[cursorPosition / 2]; char val = str[cursorPosition / 2];
@ -164,17 +158,20 @@ u32 search(u32 offset, FILE *file) {
strLen--; strLen--;
if(cursorPosition > strLen * 2 - 1) if(cursorPosition > strLen * 2 - 1)
cursorPosition -= 2; cursorPosition -= 2;
consoleClear();
} }
} }
} }
} }
consoleClear(); int y = (ENTRIES_PER_SCREEN - 7) / 2;
printf("\x1B[9;6H\x1B[47m---------------------"); font->clear(false);
printf("\x1B[10;12HSearching"); font->print(0, y, false, "--------------------", Alignment::center);
printf("\x1B[14;8HPress B to cancel"); font->print(0, y + 1, false, "Searching", Alignment::center);
printf("\x1B[15;6H---------------------"); font->print(0, y + 6, false, "Press B to cancel", Alignment::center);
font->print(0, y + 7, false, "--------------------", Alignment::center);
font->update(false);
char progressBar[21] = "[ ]";
size_t len = 32 << 10, pos = offset; size_t len = 32 << 10, pos = offset;
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);
@ -187,7 +184,10 @@ u32 search(u32 offset, FILE *file) {
return offset; return offset;
} }
printf("\x1B[12;6H%10d/%d", pos, fileLen); progressBar[pos / (fileLen / 18) + 1] = '=';
font->print(0, y + 3, false, progressBar, Alignment::center);
font->printf(0, y + 4, false, Alignment::center, Palette::white, "%d/%d", pos, fileLen);
font->update(false);
if(fseek(file, pos, SEEK_SET) != 0) if(fseek(file, pos, SEEK_SET) != 0)
break; break;
@ -204,64 +204,53 @@ u32 search(u32 offset, FILE *file) {
} while(len == 32 << 10); } while(len == 32 << 10);
delete[] buf; delete[] buf;
consoleClear(); y = (ENTRIES_PER_SCREEN - 3) / 2;
printf("\x1B[9;5H\x1B[47m---------------------"); font->clear(false);
printf("\x1B[10;6HReached end of file"); font->print(0, y, false, "--------------------", Alignment::center);
printf("\x1B[11;8Hwith no results"); font->print(0, y + 1, false, "Reached end of file", Alignment::center);
printf("\x1B[12;5H---------------------"); font->print(0, y + 2, false, "with no results", Alignment::center);
font->print(0, y + 3, false, "--------------------", Alignment::center);
font->update(false);
do { do {
swiWaitForVBlank(); swiWaitForVBlank();
scanKeys(); scanKeys();
printf("\x1B[30m\x1B[0;26H %s", RetTime().c_str()); // Print time
// Print time
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
} while(!keysDown()); } while(!keysDown());
return offset; return offset;
} }
void hexEditor(const char *path, int drive) { void hexEditor(const char *path, int drive) {
// Custom palettes
BG_PALETTE_SUB[0x1F] = 0x9CF7;
BG_PALETTE_SUB[0x2F] = 0xB710;
BG_PALETTE_SUB[0x3F] = 0xAE8D;
BG_PALETTE_SUB[0x7F] = 0xEA2D;
FILE *file = fopen(path, drive < 4 ? "rb+" : "rb"); FILE *file = fopen(path, drive < 4 ? "rb+" : "rb");
if(!file) if(!file)
return; return;
consoleClear();
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);
u32 fileSize = ftell(file); u32 fileSize = ftell(file);
fseek(file, 0, SEEK_SET); fseek(file, 0, SEEK_SET);
u8 maxLines = std::min(23lu, fileSize / 8 + (fileSize % 8 != 0)); u8 maxLines = std::min((u32)ENTRIES_PER_SCREEN, fileSize / 8 + (fileSize % 8 != 0));
u32 maxSize = ((fileSize - 8 * maxLines) & ~7) + (fileSize & 7 ? 8 : 0); u32 maxSize = ((fileSize - 8 * maxLines) & ~7) + (fileSize & 7 ? 8 : 0);
u8 cursorPosition = 0, mode = 0;
u16 pressed = 0, held = 0; u16 pressed = 0, held = 0;
u32 offset = 0; u32 offset = 0, cursorPosition = 0, mode = 0;
char data[8 * maxLines]; char data[8 * maxLines];
fseek(file, offset, SEEK_SET); fseek(file, offset, SEEK_SET);
fread(data, 1, sizeof(data), file); fread(data, 1, sizeof(data), file);
while(1) { while(1) {
consoleSelect(&bottomConsoleBG); font->clear(false);
printf ("\x1B[0;0H\x1B[46m"); // Blue
for(int i = 0; i < 4; i++)
printf ("\2");
printf ("\x1B[42m"); // Green
for(int i = 0; i < 32 - 4; i++)
printf ("\2");
consoleSelect(&bottomConsole); font->printf(4, 0, false, Alignment::left, Palette::blackGreen, "%*c", SCREEN_COLS - 4, ' ');
font->print(0, 0, false, "Hex Editor", Alignment::center, Palette::blackGreen);
printf("\x1B[0;11H\x1B[30mHex Editor"); font->printf(0, 0, false, Alignment::left, Palette::blackBlue, "%04lX", offset >> 0x10);
printf("\x1B[0;0H\x1B[30m%04lX", offset >> 0x10);
if(mode < 2) { if(mode < 2) {
fseek(file, offset, SEEK_SET); fseek(file, offset, SEEK_SET);
@ -269,36 +258,38 @@ void hexEditor(const char *path, int drive) {
fread(data, 1, std::min((u32)sizeof(data), fileSize - offset), file); fread(data, 1, std::min((u32)sizeof(data), fileSize - offset), file);
} }
for(int i = 0; i < maxLines; i++) { for(u32 i = 0; i < maxLines; i++) {
printf("\x1B[%d;0H\x1B[37m%04lX", i + 1, (offset + i * 8) & 0xFFFF); font->printf(0, i + 1, false, Alignment::left, Palette::blue, "%04lX", (offset + i * 8) & 0xFFFF);
for(int j = 0; j < 4; j++) for(int j = 0; j < 4; j++)
printf("\x1B[%d;%dH\x1B[%dm%02X", i + 1, 5 + (j * 2), (mode > 0 && i * 8 + j == cursorPosition) ? (mode > 1 ? 30 : 31) : (offset + i * 8 + j >= fileSize ? 38 : 32 + (j % 2)), data[i * 8 + j]); font->printf(5 + (j * 2), i + 1, false, Alignment::left, (mode > 0 && i * 8 + j == cursorPosition) ? (mode > 1 ? Palette::blackRed : Palette::red) : (offset + i * 8 + j >= fileSize ? Palette::gray : (j % 2 ? Palette::greenAlt : Palette::green)), "%02X", data[i * 8 + j]);
for(int j = 0; j < 4; j++) for(int j = 0; j < 4; j++)
printf("\x1B[%d;%dH\x1B[%dm%02X", i + 1, 14 + (j * 2), (mode > 0 && i * 8 + 4 + j == cursorPosition) ? (mode > 1 ? 30 : 31) : (offset + i * 8 + 4 + j >= fileSize ? 38 : 32 + (j % 2)), data[i * 8 + 4 + j]); font->printf(14 + (j * 2), i + 1, false, Alignment::left, (mode > 0 && i * 8 + 4 + j == cursorPosition) ? (mode > 1 ? Palette::blackRed : Palette::red) : (offset + i * 8 + 4 + j >= fileSize ? Palette::gray : (j % 2 ? Palette::greenAlt : Palette::green)), "%02X", data[i * 8 + 4 + j]);
char line[9] = {0}; char line[9] = {0};
for(int j = 0; j < 8; j++) { for(int j = 0; j < 8; j++) {
char c = data[i * 8 + j]; char c = data[i * 8 + j];
if(c < ' ' || c > 127) if(c < ' ' || c > 127)
line[j] = ' '; line[j] = '.';
else else
line[j] = c; line[j] = c;
} }
printf("\x1B[%d;23H\x1B[47m%.8s", i + 1, line); font->print(23, i + 1, false, line);
if(mode > 0 && cursorPosition / 8 == i) { if(mode > 0 && cursorPosition / 8 == i) {
printf("\x1B[%d;%dH\x1B[%dm%c", i + 1, 23 + cursorPosition % 8, mode > 1 ? 30 : 31, line[cursorPosition % 8]); font->printf(23 + cursorPosition % 8, i + 1, false, Alignment::left, mode > 1 ? Palette::blackRed : Palette::red, "%c", line[cursorPosition % 8]);
} }
} }
font->update(false);
consoleSelect(&topConsole);
do { do {
swiWaitForVBlank(); swiWaitForVBlank();
scanKeys(); scanKeys();
pressed = keysDown(); pressed = keysDown();
held = keysDownRepeat(); held = keysDownRepeat();
printf("\x1B[30m\x1B[0;26H %s", RetTime().c_str()); // Print time
// Print time
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
} while(!held); } while(!held);
consoleSelect(&bottomConsole);
if(mode == 0) { if(mode == 0) {
if(keysHeld() & KEY_R && held & (KEY_UP | KEY_DOWN | KEY_LEFT | KEY_RIGHT)) { if(keysHeld() & KEY_R && held & (KEY_UP | KEY_DOWN | KEY_LEFT | KEY_RIGHT)) {
@ -323,49 +314,42 @@ void hexEditor(const char *path, int drive) {
offset = std::min(offset + 8 * maxLines, maxSize); offset = std::min(offset + 8 * maxLines, maxSize);
} else if(pressed & KEY_A) { } else if(pressed & KEY_A) {
mode = 1; mode = 1;
cursorPosition = std::min(cursorPosition, fileSize - offset - 1);
} else if(pressed & KEY_B) { } else if(pressed & KEY_B) {
break; break;
} else if(pressed & KEY_X) { } else if(pressed & KEY_X) {
offset = std::min(search(offset, file), maxSize); offset = std::min(search(offset, file), maxSize);
consoleClear();
} else if(pressed & KEY_Y) { } else if(pressed & KEY_Y) {
offset = std::min(jumpToOffset(offset), maxSize); offset = std::min(jumpToOffset(offset), maxSize);
consoleClear();
} }
} else if(mode == 1) { } else if(mode == 1) {
if(held & KEY_UP) { if(held & KEY_UP) {
if(cursorPosition >= 8) if(cursorPosition >= 8)
cursorPosition -= 8; cursorPosition -= 8;
else if(offset > 8) else if(offset >= 8)
offset -= 8; offset -= 8;
} else if(held & KEY_DOWN) { } else if(held & KEY_DOWN) {
if(cursorPosition < 8 * 22) if(cursorPosition < 8u * (maxLines - 1))
cursorPosition += 8; cursorPosition += 8;
else if(offset < fileSize - 8 * maxLines && fileSize > 8 * maxLines) else if(offset < fileSize - 8 * maxLines && fileSize > 8 * maxLines)
offset += 8; offset += 8;
cursorPosition = std::min(cursorPosition, (u8)(fileSize - offset - 1)); cursorPosition = std::min(cursorPosition, fileSize - offset - 1);
} else if(held & KEY_LEFT) { } else if(held & KEY_LEFT) {
if(cursorPosition > 0) if(cursorPosition > 0)
cursorPosition--; cursorPosition--;
} else if(held & KEY_RIGHT) { } else if(held & KEY_RIGHT) {
if(cursorPosition < 8 * maxLines - 1) if(cursorPosition < 8u * maxLines - 1)
cursorPosition = std::min((u8)(cursorPosition + 1), (u8)(fileSize - offset - 1)); cursorPosition = std::min(cursorPosition + 1, fileSize - offset - 1);
} else if(pressed & KEY_A) { } else if(pressed & KEY_A) {
if(drive < 4) { if(drive < 4) {
mode = 2; mode = 2;
consoleSelect(&bottomConsoleBG);
printf("\x1B[%d;%dH\x1B[%dm\2\2", 1 + cursorPosition / 8, 5 + (cursorPosition % 8 * 2) + (cursorPosition % 8 / 4), 31);
printf("\x1B[%d;%dH\x1B[%dm\2", 1 + cursorPosition / 8, 23 + cursorPosition % 8, 31);
consoleSelect(&bottomConsole);
} }
} else if(pressed & KEY_B) { } else if(pressed & KEY_B) {
mode = 0; mode = 0;
} else if(pressed & KEY_X) { } else if(pressed & KEY_X) {
offset = std::min(search(offset, file), maxSize); offset = std::min(search(offset, file), maxSize);
consoleClear();
} else if(pressed & KEY_Y) { } else if(pressed & KEY_Y) {
offset = std::min(jumpToOffset(offset), maxSize); offset = std::min(jumpToOffset(offset), maxSize);
consoleClear();
} }
} else if(mode == 2) { } else if(mode == 2) {
if(held & KEY_UP) { if(held & KEY_UP) {
@ -380,23 +364,9 @@ void hexEditor(const char *path, int drive) {
mode = 1; mode = 1;
fseek(file, offset + cursorPosition, SEEK_SET); fseek(file, offset + cursorPosition, SEEK_SET);
fwrite(data + cursorPosition, 1, 1, file); fwrite(data + cursorPosition, 1, 1, file);
consoleSelect(&bottomConsoleBG);
printf("\x1B[%d;%dH\x1B[%dm\2\2", 1 + cursorPosition / 8, 5 + (cursorPosition % 8 * 2) + (cursorPosition % 8 / 4), 30);
printf("\x1B[%d;%dH\x1B[%dm\2", 1 + cursorPosition / 8, 23 + cursorPosition % 8, 30);
consoleSelect(&bottomConsole);
} }
} }
} }
fclose(file); fclose(file);
// Restore color palette
BG_PALETTE_SUB[0x1F] = 0x000F;
BG_PALETTE_SUB[0x2F] = 0x01E0;
BG_PALETTE_SUB[0x3F] = 0x3339;
BG_PALETTE_SUB[0x7F] = 0x656A;
consoleSelect(&bottomConsoleBG);
consoleClear();
} }

View File

@ -34,6 +34,7 @@
#include "driveOperations.h" #include "driveOperations.h"
#include "file_browse.h" #include "file_browse.h"
#include "fileOperations.h" #include "fileOperations.h"
#include "font.h"
#include "tonccpy.h" #include "tonccpy.h"
#include "version.h" #include "version.h"
@ -57,8 +58,6 @@ bool applaunch = false;
static int bg3; static int bg3;
PrintConsole topConsoleBG, topConsole, bottomConsoleBG, bottomConsole;
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
void stop (void) { void stop (void) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
@ -69,60 +68,6 @@ void stop (void) {
char filePath[PATH_MAX]; char filePath[PATH_MAX];
void printBorderTop(void) {
consoleSelect(&topConsoleBG);
printf ("\x1B[42m"); // Print green color
for (int i = 0; i < 32; i++) {
printf ("\x02"); // Print top border
}
}
void printBorderBottom(void) {
consoleSelect(&bottomConsoleBG);
printf ("\x1B[42m"); // Print green color
for (int i = 0; i < 32; i++) {
printf ("\x02"); // Print top border
}
}
void clearBorderTop(void) {
consoleSelect(&topConsoleBG);
consoleClear();
}
void clearBorderBottom(void) {
consoleSelect(&bottomConsoleBG);
consoleClear();
}
void reinitConsoles(void) {
// Subscreen as a console
videoSetModeSub(MODE_0_2D);
vramSetBankH(VRAM_H_SUB_BG);
consoleInit(&bottomConsoleBG, 1, BgType_Text4bpp, BgSize_T_256x256, 7, 0, false, true);
consoleInit(&bottomConsole, 0, BgType_Text4bpp, BgSize_T_256x256, 15, 0, false, true);
// Top screen as a console
videoSetMode(MODE_0_2D);
vramSetBankG(VRAM_G_MAIN_BG);
consoleInit(&topConsoleBG, 1, BgType_Text4bpp, BgSize_T_256x256, 7, 0, true, true);
consoleInit(&topConsole, 0, BgType_Text4bpp, BgSize_T_256x256, 15, 0, true, true);
// Overwrite background white color
BG_PALETTE[15+(7*16)] = 0x656A;
BG_PALETTE_SUB[15+(7*16)] = 0x656A;
// Custom yellow color
BG_PALETTE[15+(3*16)] = 0x3339;
BG_PALETTE_SUB[15+(3*16)] = 0x3339;
// Overwrite 2nd smiley face with filled tile
dmaFillWords(0xFFFFFFFF, (void*)0x6000040, 8*8); // Top screen
dmaFillWords(0xFFFFFFFF, (void*)0x6200040, 8*8); // Bottom screen
printBorderTop();
}
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
int main(int argc, char **argv) { int main(int argc, char **argv) {
//--------------------------------------------------------------------------------- //---------------------------------------------------------------------------------
@ -141,34 +86,29 @@ int main(int argc, char **argv) {
sprintf(titleName, "GodMode9i %s", VER_NUMBER); sprintf(titleName, "GodMode9i %s", VER_NUMBER);
// initialize video mode // initialize video mode
videoSetMode(MODE_4_2D); videoSetMode(MODE_5_2D);
videoSetModeSub(MODE_5_2D);
// initialize VRAM banks // initialize VRAM banks
vramSetPrimaryBanks(VRAM_A_MAIN_BG, vramSetPrimaryBanks(VRAM_A_MAIN_BG,
VRAM_B_MAIN_SPRITE, VRAM_B_MAIN_SPRITE,
VRAM_C_LCD, VRAM_C_SUB_BG,
VRAM_D_LCD); VRAM_D_LCD);
// Subscreen as a console
videoSetModeSub(MODE_0_2D);
vramSetBankH(VRAM_H_SUB_BG);
vramSetBankI(VRAM_I_SUB_SPRITE); vramSetBankI(VRAM_I_SUB_SPRITE);
consoleInit(&bottomConsoleBG, 1, BgType_Text4bpp, BgSize_T_256x256, 7, 0, false, true);
consoleInit(&bottomConsole, 0, BgType_Text4bpp, BgSize_T_256x256, 15, 0, false, true); // Init built-in font
font = new Font("/font.frf");
// Display GM9i logo // Display GM9i logo
bg3 = bgInit(3, BgType_Bmp8, BgSize_B8_256x256, 1, 0); bg3 = bgInit(3, BgType_Bmp8, BgSize_B8_256x256, 0, 0);
bgInit(2, BgType_Bmp8, BgSize_B8_256x256, 3, 0);
bgInitSub(2, BgType_Bmp8, BgSize_B8_256x256, 3, 0);
decompress(gm9i_logoBitmap, bgGetGfxPtr(bg3), LZ77Vram); decompress(gm9i_logoBitmap, bgGetGfxPtr(bg3), LZ77Vram);
tonccpy(BG_PALETTE, gm9i_logoPal, gm9i_logoPalLen); tonccpy(BG_PALETTE, gm9i_logoPal, gm9i_logoPalLen);
printf ("\x1b[1;1H"); font->print(1, 1, false, titleName);
printf(titleName); font->print(1, 2, false, "---------------------------------------");
printf ("\x1b[2;1H"); font->print(1, 3, false, "https:/github.com/DS-Homebrew/GodMode9i");
printf ("------------------------------");
printf ("\x1b[3;1H");
printf ("https:/github.com/");
printf ("\x1b[4;10H");
printf ("DS-Homebrew/GodMode9i");
fifoWaitValue32(FIFO_USER_06); fifoWaitValue32(FIFO_USER_06);
if (fifoGetValue32(FIFO_USER_03) == 0) arm7SCFGLocked = true; if (fifoGetValue32(FIFO_USER_03) == 0) arm7SCFGLocked = true;
@ -178,36 +118,27 @@ int main(int argc, char **argv) {
if (isDSiMode()) { if (isDSiMode()) {
if (!arm7SCFGLocked) { if (!arm7SCFGLocked) {
printf ("\x1b[20;1H"); font->print(-2, -4, false, "X Held - Disable NAND access", Alignment::right);
printf ("X Held - Disable NAND access"); font->print(-2, -3, false, "Y Held - Disable cart access", Alignment::right);
printf ("\x1b[21;1H"); font->print(-2, -2, false, "Do these if it crashes here", Alignment::right);
printf ("Y Held - Disable cart access");
printf ("\x1b[22;4H");
printf ("Do these if it crashes here");
} else { } else {
printf ("\x1b[21;1H"); font->print(-2, -3, false, "X Held - Disable NAND access", Alignment::right);
printf ("X Held - Disable NAND access"); font->print(-2, -2, false, "Do this if it crashes here", Alignment::right);
printf ("\x1b[22;5H");
printf ("Do this if it crashes here");
} }
} }
// Display for 2 seconds // Display for 2 seconds
font->update(false);
for (int i = 0; i < 60*2; i++) { for (int i = 0; i < 60*2; i++) {
swiWaitForVBlank(); swiWaitForVBlank();
} }
if (isDSiMode()) { font->clear(false);
printf ("\x1b[20;1H"); font->print(1, 1, false, titleName);
printf (" "); font->print(1, 2, false, "---------------------------------------");
printf ("\x1b[21;1H"); font->print(1, 3, false, "https:/github.com/DS-Homebrew/GodMode9i");
printf (" "); font->print(-2, -2, false, "Mounting drive(s)...", Alignment::right);
printf ("\x1b[22;4H"); font->update(false);
printf (" "); // Clear "Y Held" text
}
printf ("\x1b[22;11H");
printf ("mounting drive(s)...");
//printf ("%X %X", *(u32*)0x2FFFD00, *(u32*)0x2FFFD04);
sysSetCartOwner (BUS_OWNER_ARM9); // Allow arm9 to access GBA ROM sysSetCartOwner (BUS_OWNER_ARM9); // Allow arm9 to access GBA ROM
@ -243,11 +174,11 @@ int main(int argc, char **argv) {
flashcardMountSkipped = false; flashcardMountSkipped = false;
} }
// Top screen as a console bgHide(bg3);
videoSetMode(MODE_0_2D);
vramSetBankG(VRAM_G_MAIN_BG); // TODO: better
consoleInit(&topConsoleBG, 1, BgType_Text4bpp, BgSize_T_256x256, 7, 0, true, true); delete font;
consoleInit(&topConsole, 0, BgType_Text4bpp, BgSize_T_256x256, 15, 0, true, true); font = new Font("/font.frf");
// Overwrite background white color // Overwrite background white color
BG_PALETTE[15+(7*16)] = 0x656A; BG_PALETTE[15+(7*16)] = 0x656A;
@ -261,8 +192,6 @@ int main(int argc, char **argv) {
dmaFillWords(0xFFFFFFFF, (void*)0x6000040, 8*8); // Top screen dmaFillWords(0xFFFFFFFF, (void*)0x6000040, 8*8); // Top screen
dmaFillWords(0xFFFFFFFF, (void*)0x6200040, 8*8); // Bottom screen dmaFillWords(0xFFFFFFFF, (void*)0x6200040, 8*8); // Bottom screen
printBorderTop();
keysSetRepeat(25,5); keysSetRepeat(25,5);
appInited = true; appInited = true;
@ -302,34 +231,34 @@ int main(int argc, char **argv) {
} }
} }
fclose(argfile); fclose(argfile);
filename = argarray.at(0); filename = argarray[0];
} else { } else {
argarray.push_back(strdup(filename.c_str())); argarray.push_back(strdup(filename.c_str()));
} }
if (extension(filename, {"nds", "dsi", "ids", "app", "srl"})) { if (extension(filename, {"nds", "dsi", "ids", "app", "srl"})) {
char *name = argarray.at(0); char *name = argarray[0];
strcpy (filePath + pathLen, name); strcpy (filePath + pathLen, name);
free(argarray.at(0)); free(argarray[0]);
argarray.at(0) = filePath; argarray[0] = filePath;
consoleClear(); font->clear(false);
iprintf ("Running %s with %d parameters\n", argarray[0], argarray.size()); font->printf(0, 0, false, Alignment::left, Palette::white, "Running %s with %d parameters\n", argarray[0], argarray.size());
int err = runNdsFile (argarray[0], argarray.size(), (const char **)&argarray[0]); int err = runNdsFile(argarray[0], argarray.size(), (const char **)&argarray[0]);
iprintf ("\x1b[31mStart failed. Error %i\n", err); font->printf(0, 1, false, Alignment::left, Palette::white, "Start failed. Error %i\n", err);
} }
if (extension(filename, {"firm"})) { if (extension(filename, {"firm"})) {
char *name = argarray.at(0); char *name = argarray[0];
strcpy (filePath + pathLen, name); strcpy (filePath + pathLen, name);
free(argarray.at(0)); free(argarray[0]);
argarray.at(0) = filePath; argarray[0] = filePath;
fcopy(argarray[0], "sd:/bootonce.firm"); fcopy(argarray[0], "sd:/bootonce.firm");
fifoSendValue32(FIFO_USER_02, 1); // Reboot into selected .firm payload fifoSendValue32(FIFO_USER_02, 1); // Reboot into selected .firm payload
swiWaitForVBlank(); swiWaitForVBlank();
} }
while(argarray.size() !=0 ) { while(argarray.size() !=0 ) {
free(argarray.at(0)); free(argarray[0]);
argarray.erase(argarray.begin()); argarray.erase(argarray.begin());
} }

View File

@ -1,13 +1,12 @@
#include "ndsInfo.h" #include "ndsInfo.h"
#include "date.h" #include "date.h"
#include "font.h"
#include "tonccpy.h" #include "tonccpy.h"
#include <nds.h> #include <nds.h>
#include <stdio.h> #include <stdio.h>
extern PrintConsole bottomConsole, bottomConsoleBG, topConsole;
constexpr const char *langNames[8] { constexpr const char *langNames[8] {
"Japanese", "Japanese",
"English", "English",
@ -19,8 +18,6 @@ constexpr const char *langNames[8] {
"Korean" "Korean"
}; };
extern void reinitConsoles(void);
void ndsInfo(const char *path) { void ndsInfo(const char *path) {
FILE *file = fopen(path, "rb"); FILE *file = fopen(path, "rb");
if(!file) if(!file)
@ -35,7 +32,10 @@ void ndsInfo(const char *path) {
u32 ofs; u32 ofs;
fseek(file, 0x68, SEEK_SET); fseek(file, 0x68, SEEK_SET);
fread(&ofs, sizeof(u32), 1, file); fread(&ofs, sizeof(u32), 1, file);
fseek(file, ofs, SEEK_SET); if(ofs < 0x8000 || fseek(file, ofs, SEEK_SET) != 0) {
fclose(file);
return;
}
u16 version; u16 version;
fread(&version, sizeof(u16), 1, file); fread(&version, sizeof(u16), 1, file);
@ -51,10 +51,13 @@ void ndsInfo(const char *path) {
fread(iconAnimation, 2, 0x40, file); fread(iconAnimation, 2, 0x40, file);
fseek(file, ofs + 0x240, SEEK_SET); fseek(file, ofs + 0x240, SEEK_SET);
} else { // DS } else if((version & ~3) == 0) { // DS
fseek(file, 0x20 - 2, SEEK_CUR); fseek(file, 0x20 - 2, SEEK_CUR);
fread(iconBitmap, 1, 0x200, file); fread(iconBitmap, 1, 0x200, file);
fread(iconPalette, 2, 0x10, file); fread(iconPalette, 2, 0x10, file);
} else {
fclose(file);
return;
} }
int languages = 5 + (version & 0x3); int languages = 5 + (version & 0x3);
@ -76,26 +79,23 @@ void ndsInfo(const char *path) {
u16 pressed = 0, held = 0; u16 pressed = 0, held = 0;
int animationFrame = 0, frameDelay = 0, lang = 1; int animationFrame = 0, frameDelay = 0, lang = 1;
while(1) { while(1) {
consoleClear(); font->clear(false);
font->printf(0, 0, false, Alignment::left, Palette::white, "Header Title: %s", headerTitle);
font->printf(0, 1, false, Alignment::left, Palette::white, "Title ID: %s", tid);
font->printf(0, 2, false, Alignment::left, Palette::white, "Title: (%s)", langNames[lang]);
font->print(2, 3, false, titles + lang * 0x80);
font->update(false);
iprintf("Header Title: %s\n", headerTitle);
iprintf("Title ID: %s\n", tid);
iprintf("Title: (%s)\n ", langNames[lang]);
for(int j = 0; j < 0x80 && titles[lang * 0x80 + j]; j++) {
if(titles[lang * 0x80 + j] == '\n')
iprintf("\n ");
else
iprintf("%c", titles[lang * 0x80 + j]);
}
iprintf("\n");
consoleSelect(&topConsole);
do { do {
swiWaitForVBlank(); swiWaitForVBlank();
scanKeys(); scanKeys();
pressed = keysDown(); pressed = keysDown();
held = keysDownRepeat(); held = keysDownRepeat();
iprintf("\x1B[30m\x1B[0;26H %s", RetTime().c_str()); // Print time
// Print time
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
font->update(true);
if(iconAnimation[animationFrame] && animationFrame < 0x40) { if(iconAnimation[animationFrame] && animationFrame < 0x40) {
if(frameDelay < (iconAnimation[animationFrame] & 0xFF) - 1) { if(frameDelay < (iconAnimation[animationFrame] & 0xFF) - 1) {
frameDelay++; frameDelay++;
@ -111,7 +111,6 @@ void ndsInfo(const char *path) {
} }
} }
} while(!held); } while(!held);
consoleSelect(&bottomConsole);
if(held & KEY_UP) { if(held & KEY_UP) {
if(lang > 0) if(lang > 0)

View File

@ -1,117 +1,129 @@
#include <nds.h>
#include <stdlib.h>
#include <stdio.h>
#include <fat.h>
#include "screenshot.h" #include "screenshot.h"
#include "bmp.h" #include "bmp.h"
#include "driveOperations.h"
#include "file_browse.h"
#include "font.h"
#include "date.h"
#include <dirent.h>
#include <fat.h>
#include <nds.h>
#include <stdio.h>
void wait(); void wait();
void screenshot(u8* buffer) { void write16(void *address, u16 value) {
u8 vram_cr_temp=VRAM_A_CR; u8* first = (u8*)address;
VRAM_A_CR=VRAM_A_LCD; u8* second = first + 1;
u8* vram_temp=(u8*)malloc(128*1024); *first = value & 0xff;
dmaCopy(VRAM_A, vram_temp, 128*1024); *second = value >> 8;
}
REG_DISPCAPCNT=DCAP_BANK(0)|DCAP_ENABLE|DCAP_SIZE(3); void write32(void *address, u32 value) {
u8* first = (u8*)address;
u8* second = first + 1;
u8* third = first + 2;
u8* fourth = first + 3;
*first = value & 0xff;
*second = (value >> 8) & 0xff;
*third = (value >> 16) & 0xff;
*fourth = (value >> 24) & 0xff;
}
bool screenshotbmp(const char* filename) {
FILE *file = fopen(filename, "wb");
if(!file)
return false;
REG_DISPCAPCNT = DCAP_BANK(DCAP_BANK_VRAM_D) | DCAP_SIZE(DCAP_SIZE_256x192) | DCAP_ENABLE;
while(REG_DISPCAPCNT & DCAP_ENABLE); while(REG_DISPCAPCNT & DCAP_ENABLE);
dmaCopy(VRAM_A, buffer, 256*192*2); u8* temp = new u8[256 * 192 * 2 + sizeof(INFOHEADER) + sizeof(HEADER)];
dmaCopy(vram_temp, VRAM_A, 128*1024);
VRAM_A_CR=vram_cr_temp;
free(vram_temp);
} if(!temp) {
fclose(file);
return false;
}
void screenshot(char* filename) { HEADER *header= (HEADER*)temp;
INFOHEADER *infoheader = (INFOHEADER*)(temp + sizeof(HEADER));
//fatInitDefault();
FILE* file=fopen(filename, "w");
u8* temp=(u8*)malloc(256*192*2);
dmaCopy(VRAM_B, temp, 256*192*2);
fwrite(temp, 1, 256*192*2, file);
fclose(file);
free(temp);
}
void write16(u16* address, u16 value) {
u8* first=(u8*)address;
u8* second=first+1;
*first=value&0xff;
*second=value>>8;
}
void write32(u32* address, u32 value) {
u8* first=(u8*)address;
u8* second=first+1;
u8* third=first+2;
u8* fourth=first+3;
*first=value&0xff;
*second=(value>>8)&0xff;
*third=(value>>16)&0xff;
*fourth=(value>>24)&0xff;
}
void screenshotbmp(const char* filename) {
//fatInitDefault();
FILE* file=fopen(filename, "wb");
REG_DISPCAPCNT=DCAP_BANK(3)|DCAP_ENABLE|DCAP_SIZE(3);
while(REG_DISPCAPCNT & DCAP_ENABLE);
u8* temp=(u8*)malloc(256*192*3+sizeof(INFOHEADER)+sizeof(HEADER));
HEADER* header=(HEADER*)temp;
INFOHEADER* infoheader=(INFOHEADER*)(temp+sizeof(HEADER));
write16(&header->type, 0x4D42); write16(&header->type, 0x4D42);
write32(&header->size, 256*192*3+sizeof(INFOHEADER)+sizeof(HEADER)); write32(&header->size, 256 * 192 * 2 + sizeof(INFOHEADER) + sizeof(HEADER));
write32(&header->offset, sizeof(INFOHEADER)+sizeof(HEADER)); write32(&header->reserved1, 0);
write16(&header->reserved1, 0); write32(&header->reserved2, 0);
write16(&header->reserved2, 0); write32(&header->offset, sizeof(INFOHEADER) + sizeof(HEADER));
write16(&infoheader->bits, 24);
write32(&infoheader->size, sizeof(INFOHEADER)); write32(&infoheader->size, sizeof(INFOHEADER));
write32(&infoheader->compression, 0);
write32(&infoheader->width, 256); write32(&infoheader->width, 256);
write32(&infoheader->height, 192); write32(&infoheader->height, 192);
write16(&infoheader->planes, 1); write16(&infoheader->planes, 1);
write32(&infoheader->imagesize, 256*192*3); write16(&infoheader->bits, 16);
write32(&infoheader->xresolution, 0); write32(&infoheader->compression, 3);
write32(&infoheader->yresolution, 0); write32(&infoheader->imagesize, 256 * 192 * 2);
write32(&infoheader->importantcolours, 0); write32(&infoheader->xresolution, 2835);
write32(&infoheader->yresolution, 2835);
write32(&infoheader->ncolours, 0); write32(&infoheader->ncolours, 0);
write32(&infoheader->importantcolours, 0);
write32(&infoheader->redBitmask, 0xF800);
write32(&infoheader->greenBitmask, 0x07E0);
write32(&infoheader->blueBitmask, 0x001F);
write32(&infoheader->reserved, 0);
for(int y=0;y<192;y++) u16 *ptr = (u16*)(temp + sizeof(HEADER) + sizeof(INFOHEADER));
{ for(int y = 0; y < 192; y++) {
for(int x=0;x<256;x++) for(int x = 0; x < 256; x++) {
{ u16 color = VRAM_D[256 * 191 - y * 256 + x];
u16 color=VRAM_D[256*191-y*256+x]; *(ptr++) = ((color >> 10) & 0x1F) | (color & (0x1F << 5)) << 1 | ((color & 0x1F) << 11);
u8 b=(color&31)<<3;
u8 g=((color>>5)&31)<<3;
u8 r=((color>>10)&31)<<3;
temp[((y*256)+x)*3+sizeof(INFOHEADER)+sizeof(HEADER)]=r;
temp[((y*256)+x)*3+1+sizeof(INFOHEADER)+sizeof(HEADER)]=g;
temp[((y*256)+x)*3+2+sizeof(INFOHEADER)+sizeof(HEADER)]=b;
} }
} }
DC_FlushAll(); DC_FlushAll();
fwrite(temp, 1, 256*192*3+sizeof(INFOHEADER)+sizeof(HEADER), file); fwrite(temp, 1, 256 * 192 * 2 + sizeof(INFOHEADER) + sizeof(HEADER), file);
fclose(file); fclose(file);
free(temp); delete[] temp;
return true;
} }
bool screenshot(void) {
if (!(sdMounted || flashcardMounted))
return false;
if (access((sdMounted ? "sd:/gm9i" : "fat:/gm9i"), F_OK) != 0) {
mkdir((sdMounted ? "sd:/gm9i" : "fat:/gm9i"), 0777);
}
if (access((sdMounted ? "sd:/gm9i/out" : "fat:/gm9i/out"), F_OK) != 0) {
mkdir((sdMounted ? "sd:/gm9i/out" : "fat:/gm9i/out"), 0777);
}
std::string fileTimeText = RetTimeForFilename();
char snapPath[40];
// Take top screenshot
snprintf(snapPath, sizeof(snapPath), "%s:/gm9i/out/snap_%s_top.bmp", (sdMounted ? "sd" : "fat"), fileTimeText.c_str());
if(!screenshotbmp(snapPath))
return false;
// Seamlessly swap top and bottom screens
font->mainOnTop(false);
font->update(false);
font->update(true);
lcdMainOnBottom();
// Take bottom screenshot
snprintf(snapPath, sizeof(snapPath), "%s:/gm9i/out/snap_%s_bot.bmp", (sdMounted ? "sd" : "fat"), fileTimeText.c_str());
if(!screenshotbmp(snapPath))
return false;
font->mainOnTop(true);
font->update(true);
font->update(false);
lcdMainOnTop();
return true;
}

BIN
data/font_default.frf Normal file

Binary file not shown.