From 2ca30a9bd252d4b1e6feeac4b90ed4137f5f7170 Mon Sep 17 00:00:00 2001 From: Pk11 Date: Sun, 8 Aug 2021 07:31:00 -0500 Subject: [PATCH] WIP: Use bitmap mode and clean up some things --- .gitignore | 2 +- Makefile | 163 +-------- arm9/Makefile | 36 +- arm9/include/bmp.h | 14 +- arm9/include/screenshot.h | 8 +- arm9/source/driveMenu.cpp | 483 ++++++++++---------------- arm9/source/driveOperations.cpp | 16 +- arm9/source/driveOperations.h | 4 +- arm9/source/dumpOperations.cpp | 270 +++++++-------- arm9/source/fileOperations.cpp | 239 +++++-------- arm9/source/fileOperations.h | 5 +- arm9/source/file_browse.cpp | 596 ++++++++++++++------------------ arm9/source/font.cpp | 369 ++++++++++++++++++++ arm9/source/font.h | 94 +++++ arm9/source/hexEditor.cpp | 204 +++++------ arm9/source/main.cpp | 155 +++------ arm9/source/ndsInfo.cpp | 41 ++- arm9/source/screenshot.cpp | 192 +++++----- data/font_default.frf | Bin 0 -> 5796 bytes 19 files changed, 1443 insertions(+), 1448 deletions(-) create mode 100644 arm9/source/font.cpp create mode 100644 arm9/source/font.h create mode 100644 data/font_default.frf diff --git a/.gitignore b/.gitignore index 58b245c..c67ec00 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ *.nds *.cia *.elf -data/* +data/*.bin .vscode *.DS_Store diff --git a/Makefile b/Makefile index b6f4390..a3e25ba 100644 --- a/Makefile +++ b/Makefile @@ -9,149 +9,32 @@ endif include $(DEVKITARM)/ds_rules -export VERSION_MAJOR := 1 -export VERSION_MINOR := 1 -export VERSION_PATCH := 0 +export TARGET := GodMode9i - -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) -.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 dsi: $(TARGET).dsi - -dist: all - @rm -fr hbmenu - @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 \ + +$(TARGET).nds: arm7/$(TARGET).elf arm9/$(TARGET).elf + ndstool -c $(TARGET).nds -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \ -b icon.bmp "GodMode9i;RocketRobz" \ -z 80040000 -u 00030004 python fix_ndsheader.py $(CURDIR)/$(TARGET).nds - -$(TARGET).dsi: $(TARGET).arm7 $(TARGET).arm9 - ndstool -c $(TARGET).dsi -7 $(TARGET).arm7.elf -9 $(TARGET).arm9.elf \ + +$(TARGET).dsi: arm7/$(TARGET).elf arm9/$(TARGET).elf + ndstool -c $(TARGET).dsi -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \ -b icon.bmp "GodMode9i;RocketRobz" \ -g HGMA 00 "GODMODE9I" -z 80040000 -u 00030004 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: @$(MAKE) -C arm7 - + #--------------------------------------------------------------------------------- arm9/$(TARGET).elf: @$(MAKE) -C arm9 @@ -163,7 +46,7 @@ arm9/$(TARGET).elf: #--------------------------------------------------------------------------------- clean: @echo clean ... - @rm -fr data + @rm -fr data/*.bin @rm -fr $(BUILD) $(TARGET).elf $(TARGET).nds @rm -fr $(TARGET).arm7.elf @rm -fr $(TARGET).arm9.elf @@ -172,32 +55,8 @@ clean: @$(MAKE) -C arm9 clean @$(MAKE) -C arm7 clean -data: - @mkdir -p data - bootloader: data @$(MAKE) -C bootloader LOADBIN=$(CURDIR)/data/load.bin - + bootstub: data @$(MAKE) -C bootstub - -#--------------------------------------------------------------------------------- -else - -#--------------------------------------------------------------------------------- -# main targets -#--------------------------------------------------------------------------------- -#$(OUTPUT).nds : $(OUTPUT).elf -#$(OUTPUT).elf : $(OFILES) - -#--------------------------------------------------------------------------------- -%.bin.o : %.bin -#--------------------------------------------------------------------------------- - @echo $(notdir $<) - $(bin2o) - --include $(DEPSDIR)/*.d - -#--------------------------------------------------------------------------------------- -endif -#--------------------------------------------------------------------------------------- diff --git a/arm9/Makefile b/arm9/Makefile index fe7f79e..61aa9f4 100644 --- a/arm9/Makefile +++ b/arm9/Makefile @@ -37,8 +37,8 @@ endif #--------------------------------------------------------------------------------- TARGET := GodMode9i BUILD := build -SOURCES := source dldi-include ramdrive-include mbedtls -INCLUDES := include dldi-include ramdrive-include source +SOURCES := source source/graphics dldi-include ramdrive-include mbedtls +INCLUDES := include dldi-include ramdrive-include source source/graphics DATA := ../data GRAPHICS := ../gfx @@ -53,7 +53,7 @@ CFLAGS := -g -Wall -O2\ $(ARCH) CFLAGS += $(INCLUDE) -DARM9 -D_NO_BOOTSTUB_ -CXXFLAGS := $(CFLAGS) -fno-exceptions -std=gnu++11 +CXXFLAGS := $(CFLAGS) -fno-exceptions -std=gnu++17 ASFLAGS := -g $(ARCH) 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) #--------------------------------------------------------------------------------- LIBS := -lfat -lnds9 - - + #--------------------------------------------------------------------------------- # list of directories containing libraries, this must be the top level containing # include and lib #--------------------------------------------------------------------------------- LIBDIRS := $(CURDIR) ../ $(LIBNDS) - + #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional # 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))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) - + #--------------------------------------------------------------------------------- # use CXX for linking C++ projects, CC for standard C #--------------------------------------------------------------------------------- @@ -110,20 +109,20 @@ export OFILES := $(addsuffix .o,$(BINFILES)) \ $(BMPFILES:.bmp=.o) \ $(PNGFILES:.png=.o) \ $(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) export OUTPUT := $(CURDIR)/$(TARGET) - + .PHONY: $(BUILD) clean all : $(BUILD) - + #--------------------------------------------------------------------------------- $(BUILD): @[ -d $@ ] || mkdir -p $@ @@ -135,38 +134,43 @@ clean: #--------------------------------------------------------------------------------- else - + DEPENDS := $(OFILES:.o=.d) #--------------------------------------------------------------------------------- # main targets #--------------------------------------------------------------------------------- $(OUTPUT).elf : $(OFILES) - + #--------------------------------------------------------------------------------- %.bin.o : %.bin #--------------------------------------------------------------------------------- @echo $(notdir $<) $(bin2o) +#--------------------------------------------------------------------------------- +%.frf.o : %.frf +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + $(bin2o) + #--------------------------------------------------------------------------------- # This rule creates assembly source files using grit # 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 -# you use in the graphics folders +# you use in the graphics folders #--------------------------------------------------------------------------------- %.s %.h : %.bmp %.grit #--------------------------------------------------------------------------------- grit $< -fts -o$* - #--------------------------------------------------------------------------------- %.s %.h : %.png %.grit #--------------------------------------------------------------------------------- grit $< -fts -o$* -include $(DEPSDIR)/*.d - + #--------------------------------------------------------------------------------------- endif #--------------------------------------------------------------------------------------- diff --git a/arm9/include/bmp.h b/arm9/include/bmp.h index 0c7ea60..4d7b0cc 100644 --- a/arm9/include/bmp.h +++ b/arm9/include/bmp.h @@ -1,23 +1,29 @@ #ifndef _bmp_h_ #define _bmp_h_ +#include + typedef struct { u16 type; /* Magic identifier */ - u32 size; /* File size in bytes */ + u32 size; /* File size in bytes */ u16 reserved1, reserved2; - u32 offset; /* Offset to image data, bytes */ + u32 offset; /* Offset to image data, bytes */ } PACKED HEADER; typedef struct { 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 bits; /* Bits per pixel */ u32 compression; /* Compression type */ u32 imagesize; /* Image size in bytes */ - u32 xresolution,yresolution; /* Pixels per meter */ + u32 xresolution, yresolution; /* Pixels per meter */ u32 ncolours; /* Number of colours */ u32 importantcolours; /* Important colours */ + u32 redBitmask; /* Red bitmask */ + u32 greenBitmask; /* Green bitmask */ + u32 blueBitmask; /* Blue bitmask */ + u32 reserved; } PACKED INFOHEADER; #endif //_bmp_h_ diff --git a/arm9/include/screenshot.h b/arm9/include/screenshot.h index 454bec4..12d23a6 100644 --- a/arm9/include/screenshot.h +++ b/arm9/include/screenshot.h @@ -1,2 +1,6 @@ -void screenshot(const char* filename); -void screenshotbmp(const char* filename); +#ifndef SCREENSHOT_H +#define SCREENSHOT_H + +bool screenshot(void); + +#endif // SCREENSHOT_H diff --git a/arm9/source/driveMenu.cpp b/arm9/source/driveMenu.cpp index c2b9683..67e41a0 100644 --- a/arm9/source/driveMenu.cpp +++ b/arm9/source/driveMenu.cpp @@ -33,214 +33,178 @@ #include "dumpOperations.h" #include "driveOperations.h" #include "fileOperations.h" +#include "font.h" -#define SCREEN_COLS 32 -#define ENTRIES_PER_SCREEN 22 #define ENTRIES_START_ROW 1 #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; bool flashcardMountSkipped = true; static bool flashcardMountRan = true; -static bool dmTextPrinted = false; static int dmCursorPosition = 0; -static int dmAssignedOp[sizeOfdmAssignedOp] = {-1}; -static int dmMaxCursors = -1; +static std::vector dmOperations; static u8 gbaFixedValue = 0; extern bool arm7SCFGLocked; 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) { - /*if (!ramDumped) { - printf ("Dumping RAM..."); - FILE* destinationFile = fopen("sd:/ramdump.bin", "wb"); - fwrite((void*)0x02000000, 1, 0x400000, destinationFile); - fclose(destinationFile); - consoleClear(); - ramDumped = true; - }*/ + font->clear(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 - printf ("\x1b[0;27H"); - printf (RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); - printf ("\x1b[0;0H"); - printf ("[root]"); - printf ("\x1B[47m"); // Print foreground white color - - // Move to 2nd row - printf ("\x1b[1;0H"); - - if (dmMaxCursors == -1) { - printf ("No drives found!"); + if (dmOperations.size() == 0) { + font->print(0, 1, true, "No drives found!", Alignment::left, Palette::blackGreen); } else - for (int i = 0; i <= dmMaxCursors; i++) { - iprintf ("\x1b[%d;0H", i + ENTRIES_START_ROW); - if (dmCursorPosition == i) { - printf ("\x1B[47m"); // Print foreground white color - } else { - printf ("\x1B[40m"); // Print foreground black color - } - if (dmAssignedOp[i] == 0) { - printf ("[sd:] SDCARD"); - if (sdLabel[0] != '\0') { - iprintf (" (%s)", sdLabel); - } - } else if (dmAssignedOp[i] == 1) { - printf ("[fat:] FLASHCART"); - if (fatLabel[0] != '\0') { - iprintf (" (%s)", fatLabel); - } - } else if (dmAssignedOp[i] == 2) { - printf ("GBA GAMECART"); - if (gbaFixedValue != 0x96) { - iprintf ("\x1b[%d;29H", i + ENTRIES_START_ROW); - printf ("[x]"); - } - } else if (dmAssignedOp[i] == 3) { - printf ("[nitro:] NDS GAME IMAGE"); - if ((sdMounted && nitroCurrentDrive==0) - || (flashcardMounted && nitroCurrentDrive==1) - || (ramdrive1Mounted && nitroCurrentDrive==2) - || (ramdrive2Mounted && nitroCurrentDrive==3) - || (nandMounted && nitroCurrentDrive==4) - || (imgMounted && nitroCurrentDrive==6)) - { - // Do nothing - } - else - { - iprintf ("\x1b[%d;29H", i + ENTRIES_START_ROW); - printf ("[x]"); - } - } 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); + for (int i = 0; i < (int)dmOperations.size(); i++) { + Palette pal = dmCursorPosition == i ? Palette::white : Palette::gray; + switch(dmOperations[i]) { + case DriveMenuOperation::sdCard: + font->printf(0, i + 1, true, Alignment::left, pal, "[sd:] SDCARD (%s)", sdLabel[0] == 0 ? "UNTITLED" : sdLabel); + break; + case DriveMenuOperation::flashcard: + font->printf(0, i + 1, true, Alignment::left, pal, "[fat:] FLASHCARD (%s)", fatLabel[0] == 0 ? "UNTITLED" : fatLabel); + break; + case DriveMenuOperation::ramDrive1: + font->print(0, i + 1, true, "[ram1:] RAMDRIVE", Alignment::left, pal); + break; + case DriveMenuOperation::ramDrive2: + font->print(0, i + 1, true, "[ram2:] RAMDRIVE", Alignment::left, pal); + break; + case DriveMenuOperation::sysNand: + font->print(0, i + 1, true, "[nand:] SYSNAND", Alignment::left, pal); + break; + case DriveMenuOperation::nitroFs: + font->print(0, i + 1, true, "[nitro:] NDS GAME IMAGE", Alignment::left, pal); + if (!((sdMounted && nitroCurrentDrive==0) + || (flashcardMounted && nitroCurrentDrive==1) + || (ramdrive1Mounted && nitroCurrentDrive==2) + || (ramdrive2Mounted && nitroCurrentDrive==3) + || (nandMounted && nitroCurrentDrive==4) + || (imgMounted && nitroCurrentDrive==6))) + font->print(256 - font->width(), i + 1, true, "[x]", Alignment::right, pal); + break; + case DriveMenuOperation::fatImage: + if ((sdMounted && imgCurrentDrive==0) + || (flashcardMounted && imgCurrentDrive==1) + || (ramdrive1Mounted && imgCurrentDrive==2) + || (ramdrive2Mounted && imgCurrentDrive==3) + || (nandMounted && imgCurrentDrive==4)) { + font->printf(0, i + 1, true, Alignment::left, pal, "[nitro:] FAT IMAGE (%s)", imgLabel[0] == 0 ? "UNTITLED" : imgLabel); + } else { + font->print(0, i + 1, true, "[nitro:] FAT IMAGE", Alignment::left, pal); + font->print(256 - font->width(), i + 1, true, "[x]", Alignment::right, pal); } - } - else - { - iprintf ("\x1b[%d;29H", i + ENTRIES_START_ROW); - printf ("[x]"); - } + break; + case DriveMenuOperation::gbaCart: + font->print(0, i + 1, true, "GBA GAMECART", Alignment::left, pal); + if (gbaFixedValue != 0x96) + 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) { - consoleClear(); + font->clear(false); - printf ("\x1B[47m"); // Print foreground white color - printf ("\x1b[23;0H"); - printf (titleName); - if (nitroMounted || imgMounted) { - printf ("\n"); - printf (IMAGETEXT); + int row = -1; + + if (!isDSiMode() && isRegularDS) { + font->print(0, row--, false, POWERTEXT_DS); + } else if (is3DS) { + font->print(0, row--, false, HOMETEXT); + font->print(0, row--, false, POWERTEXT_3DS); + } else { + font->print(0, row--, false, POWERTEXT); } + if (sdMountedDone) { if (isRegularDS || sdMounted) { - printf ("\n"); - printf (sdMounted ? "R+B - Unmount SD card" : "R+B - Remount SD card"); + font->print(0, row--, false, sdMounted ? "R+B - Unmount SD card" : "R+B - Remount SD card"); } } else { - printf ("\n"); - printf (flashcardMounted ? "R+B - Unmount Flashcard" : "R+B - Remount Flashcard"); + font->print(0, row--, false, flashcardMounted ? "R+B - Unmount Flashcard" : "R+B - Remount Flashcard"); } if (sdMounted || flashcardMounted) { - printf ("\n"); - printf (SCREENSHOTTEXT); - } - printf ("\n"); - if (!isDSiMode() && isRegularDS) { - printf (POWERTEXT_DS); - } else if (is3DS) { - printf (POWERTEXT_3DS); - printf ("\n"); - printf (HOMETEXT); - } else { - printf (POWERTEXT); + font->print(0, row--, false, SCREENSHOTTEXT); } - printf ("\x1B[40m"); // Print foreground black color - printf ("\x1b[0;0H"); - if (dmAssignedOp[dmCursorPosition] == 0) { - printf ("[sd:] SDCARD"); - if (sdLabel[0] != '\0') { - iprintf (" (%s)", sdLabel); - } - printf ("\n(SD FAT, "); - printDriveBytes(sdSize); - printf(")\n"); - printDriveBytes(getBytesFree("sd:/")); - printf(" space free"); - } else if (dmAssignedOp[dmCursorPosition] == 1) { - printf ("[fat:] FLASHCART"); - if (fatLabel[0] != '\0') { - iprintf (" (%s)", fatLabel); - } - printf ("\n(Slot-1 SD FAT, "); - printDriveBytes(fatSize); - printf(")\n"); - printDriveBytes(getBytesFree("fat:/")); - printf(" space free"); - } else if (dmAssignedOp[dmCursorPosition] == 2) { - printf ("GBA GAMECART\n"); - printf ("(GBA Game)"); - } else if (dmAssignedOp[dmCursorPosition] == 3) { - printf ("[nitro:] NDS GAME IMAGE\n"); - printf ("(Game Virtual)"); - } else if (dmAssignedOp[dmCursorPosition] == 4) { - printf ("NDS GAMECARD\n"); - printf ("(NDS Game)"); - } else if (dmAssignedOp[dmCursorPosition] == 5) { - printf ("[ram1:] RAMDRIVE\n"); - printf ("(RAMdrive FAT, 9 MB)"); - } else if (dmAssignedOp[dmCursorPosition] == 6) { - printf ("[ram2:] RAMDRIVE\n"); - printf ("(RAMdrive FAT, 16 MB)"); - } else if (dmAssignedOp[dmCursorPosition] == 7) { - printf ("[nand:] SYSNAND"); - printf ("\n(SysNAND FAT, "); - printDriveBytes(nandSize); - printf(")\n"); - printDriveBytes(getBytesFree("nand:/")); - printf(" space free"); - } else if (dmAssignedOp[dmCursorPosition] == 8) { - printf ("[img:] FAT IMAGE"); - printf ("\n(Image FAT, "); - printDriveBytes(imgSize); - printf(")"); + font->print(0, row--, false, IMAGETEXT); + font->print(0, row--, false, titleName); + + switch(dmOperations[dmCursorPosition]) { + case DriveMenuOperation::sdCard: + 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()); + font->printf(0, 2, false, Alignment::left, Palette::white, "%s free", getDriveBytes(getBytesFree("sd:/")).c_str()); + break; + case DriveMenuOperation::flashcard: + font->printf(0, 0, false, Alignment::left, Palette::white, "[fat:] FLASHCARD (%s)", fatLabel[0] == 0 ? "UNTITLED" : fatLabel); + font->printf(0, 1, false, Alignment::left, Palette::white, "(Slot-1 SD FAT, %s)", getDriveBytes(fatSize).c_str()); + font->printf(0, 2, false, Alignment::left, Palette::white, "%s free", getDriveBytes(getBytesFree("fat:/")).c_str()); + break; + case DriveMenuOperation::gbaCart: + font->print(0, 0, false, "GBA GAMECART"); + font->print(0, 1, false, "(GBA Game)"); + break; + case DriveMenuOperation::nitroFs: + font->print(0, 0, false, "[nitro:] NDS GAME IMAGE\n"); + font->print(0, 1, false, "(Game Virtual)"); + break; + case DriveMenuOperation::ndsCard: + font->print(0, 0, false, "NDS GAMECARD\n"); + font->print(0, 1, false, "(NDS Game)"); + break; + case DriveMenuOperation::ramDrive1: + font->print(0, 0, false, "[ram1:] RAMDRIVE\n"); + font->print(0, 1, false, "(RAMdrive FAT, 9 MB)"); + break; + case DriveMenuOperation::ramDrive2: + font->print(0, 0, false, "[ram2:] RAMDRIVE\n"); + font->print(0, 1, false, "(RAMdrive FAT, 16 MB)"); + break; + case DriveMenuOperation::sysNand: + font->print(0, 0, false, "[nand:] SYSNAND"); + font->printf(0, 1, false, Alignment::left, Palette::white, "(SysNAND FAT, %s)", getDriveBytes(fatSize).c_str()); + font->printf(0, 2, false, Alignment::left, Palette::white, "%s free", getDriveBytes(getBytesFree("nand:/")).c_str()); + break; + case DriveMenuOperation::fatImage: + font->print(0, 0, false, "[img:] FAT IMAGE"); + font->printf(0, 1, false, Alignment::left, Palette::white, "(Image FAT, %s)", getDriveBytes(imgSize).c_str()); + break; + case DriveMenuOperation::none: + break; } + + font->update(false); } void driveMenu (void) { @@ -252,71 +216,38 @@ void driveMenu (void) { gbaFixedValue = *(u8*)(0x080000B2); } - for (int i = 0; i < sizeOfdmAssignedOp; i++) { - dmAssignedOp[i] = -1; - } - dmMaxCursors = -1; - if (sdMounted){ - dmMaxCursors++; - dmAssignedOp[dmMaxCursors] = 0; - } - if (nandMounted) { - dmMaxCursors++; - dmAssignedOp[dmMaxCursors] = 7; - } - if (flashcardMounted) { - dmMaxCursors++; - dmAssignedOp[dmMaxCursors] = 1; - } - if (ramdrive1Mounted) { - dmMaxCursors++; - dmAssignedOp[dmMaxCursors] = 5; - } - if (ramdrive2Mounted) { - dmMaxCursors++; - dmAssignedOp[dmMaxCursors] = 6; - } - if (imgMounted) { - dmMaxCursors++; - dmAssignedOp[dmMaxCursors] = 8; - } + dmOperations.clear(); + if (sdMounted) + dmOperations.push_back(DriveMenuOperation::sdCard); + if (nandMounted) + dmOperations.push_back(DriveMenuOperation::sysNand); + if (flashcardMounted) + dmOperations.push_back(DriveMenuOperation::flashcard); + if (ramdrive1Mounted) + dmOperations.push_back(DriveMenuOperation::ramDrive1); + if (ramdrive2Mounted) + dmOperations.push_back(DriveMenuOperation::ramDrive2); + if (imgMounted) + dmOperations.push_back(DriveMenuOperation::fatImage); if (expansionPakFound || (io_dldi_data->ioInterface.features & FEATURE_SLOT_GBA) - || (isDSiMode() && !arm7SCFGLocked && !(REG_SCFG_MC & BIT(0)))) { - dmMaxCursors++; - dmAssignedOp[dmMaxCursors] = 4; - } - if (!isDSiMode() && isRegularDS) { - dmMaxCursors++; - dmAssignedOp[dmMaxCursors] = 2; - } - if (nitroMounted) { - dmMaxCursors++; - dmAssignedOp[dmMaxCursors] = 3; - } + || (isDSiMode() && !arm7SCFGLocked && !(REG_SCFG_MC & BIT(0)))) + dmOperations.push_back(DriveMenuOperation::ndsCard); + if (!isDSiMode() && isRegularDS) + dmOperations.push_back(DriveMenuOperation::gbaCart); + if (nitroMounted) + dmOperations.push_back(DriveMenuOperation::nitroFs); - if (dmCursorPosition < 0) dmCursorPosition = dmMaxCursors; // Wrap around to bottom of list - if (dmCursorPosition > dmMaxCursors) dmCursorPosition = 0; // Wrap around to top of list - - if (!dmTextPrinted) { - consoleSelect(&bottomConsole); - dm_drawBottomScreen(); - consoleSelect(&topConsole); - dm_drawTopScreen(); - - dmTextPrinted = true; - } + dm_drawBottomScreen(); + dm_drawTopScreen(); 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 do { - // Move to right side of screen - printf ("\x1b[0;27H"); // Print time - printf (RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDownRepeat(); @@ -325,52 +256,53 @@ void driveMenu (void) { if (!isDSiMode() && isRegularDS) { if (*(u8*)(0x080000B2) != gbaFixedValue) { - dmTextPrinted = false; break; } } else if (isDSiMode()) { if (REG_SCFG_MC != stored_SCFG_MC) { - dmTextPrinted = false; 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 - && !(pressed & KEY_TOUCH) + | KEY_TOUCH #endif - ); - - printf ("\x1B[47m"); // Print foreground white color + ))); - if ((pressed & KEY_UP) && dmMaxCursors != -1) { - dmCursorPosition -= 1; - dmTextPrinted = false; + if(dmOperations.size() != 0) { + if (pressed & KEY_UP) { + 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 (dmAssignedOp[dmCursorPosition] == 0 && sdMounted) { - dmTextPrinted = false; + if (dmOperations[dmCursorPosition] == DriveMenuOperation::sdCard && sdMounted) { currentDrive = 0; chdir("sd:/"); screenMode = 1; break; - } else if (dmAssignedOp[dmCursorPosition] == 1 && flashcardMounted) { - dmTextPrinted = false; + } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::flashcard && flashcardMounted) { currentDrive = 1; chdir("fat:/"); screenMode = 1; break; - } else if (dmAssignedOp[dmCursorPosition] == 2 && isRegularDS && flashcardMounted && gbaFixedValue == 0x96) { - dmTextPrinted = false; + } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::gbaCart && isRegularDS && flashcardMounted && gbaFixedValue == 0x96) { gbaCartDump(); - } else if (dmAssignedOp[dmCursorPosition] == 3 && nitroMounted) { + } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::nitroFs && nitroMounted) { if ((sdMounted && nitroCurrentDrive==0) || (flashcardMounted && nitroCurrentDrive==1) || (ramdrive1Mounted && nitroCurrentDrive==2) @@ -378,41 +310,35 @@ void driveMenu (void) { || (nandMounted && nitroCurrentDrive==4) || (imgMounted && nitroCurrentDrive==6)) { - dmTextPrinted = false; currentDrive = 5; chdir("nitro:/"); screenMode = 1; break; } - } else if (dmAssignedOp[dmCursorPosition] == 4 && (sdMounted || flashcardMounted)) { - dmTextPrinted = false; + } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::ndsCard && (sdMounted || flashcardMounted)) { ndsCardDump(); - } else if (dmAssignedOp[dmCursorPosition] == 5 && isDSiMode() && ramdrive1Mounted) { - dmTextPrinted = false; + } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::ramDrive1 && isDSiMode() && ramdrive1Mounted) { currentDrive = 2; chdir("ram1:/"); screenMode = 1; break; - } else if (dmAssignedOp[dmCursorPosition] == 6 && isDSiMode() && ramdrive2Mounted) { - dmTextPrinted = false; + } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::ramDrive2 && isDSiMode() && ramdrive2Mounted) { currentDrive = 3; chdir("ram2:/"); screenMode = 1; break; - } else if (dmAssignedOp[dmCursorPosition] == 7 && isDSiMode() && nandMounted) { - dmTextPrinted = false; + } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::sysNand && isDSiMode() && nandMounted) { currentDrive = 4; chdir("nand:/"); screenMode = 1; break; - } else if (dmAssignedOp[dmCursorPosition] == 8 && imgMounted) { + } else if (dmOperations[dmCursorPosition] == DriveMenuOperation::fatImage && imgMounted) { if ((sdMounted && imgCurrentDrive==0) || (flashcardMounted && imgCurrentDrive==1) || (ramdrive1Mounted && imgCurrentDrive==2) || (ramdrive2Mounted && imgCurrentDrive==3) || (nandMounted && imgCurrentDrive==4)) { - dmTextPrinted = false; currentDrive = 6; chdir("img:/"); screenMode = 1; @@ -423,7 +349,6 @@ void driveMenu (void) { // Unmount/Remount FAT image if ((held & KEY_R) && (pressed & KEY_X)) { - dmTextPrinted = false; if (nitroMounted) { currentDrive = 5; chdir("nitro:/"); @@ -437,7 +362,6 @@ void driveMenu (void) { // Unmount/Remount SD card if ((held & KEY_R) && (pressed & KEY_B)) { - dmTextPrinted = false; if (isDSiMode() && sdMountedDone) { if (sdMounted) { currentDrive = 0; @@ -467,40 +391,7 @@ void driveMenu (void) { // Make a screenshot if ((held & KEY_R) && (pressed & KEY_L)) { - if (sdMounted || flashcardMounted) { - 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(); - } + screenshot(); } if (isDSiMode() && !flashcardMountSkipped && !pressed && !held) { diff --git a/arm9/source/driveOperations.cpp b/arm9/source/driveOperations.cpp index fc4a1e2..617c281 100644 --- a/arm9/source/driveOperations.cpp +++ b/arm9/source/driveOperations.cpp @@ -1,9 +1,10 @@ #include #include #include +#include +#include #include #include -#include #include "main.h" #include "dldi-include.h" @@ -58,19 +59,22 @@ static float getTbNumber(u64 bytes) { return tbNumber; } -void printDriveBytes(u64 bytes) +std::string getDriveBytes(u64 bytes) { + char buffer[12]; 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)) - printf("%d MB", (int)bytes / 1024 / 1024); + sniprintf(buffer, sizeof(buffer), "%d MB", (int)bytes / 1024 / 1024); else if (bytes >= 0x40000000 && bytes < 0x10000000000) - printf("%.1f GB", getGbNumber(bytes)); + snprintf(buffer, sizeof(buffer), "%.1f GB", getGbNumber(bytes)); else - printf("%.1f TB", getTbNumber(bytes)); + snprintf(buffer, sizeof(buffer), "%.1f TB", getTbNumber(bytes)); + + return buffer; } const char* getDrivePath(void) { diff --git a/arm9/source/driveOperations.h b/arm9/source/driveOperations.h index c2546d3..6f23371 100644 --- a/arm9/source/driveOperations.h +++ b/arm9/source/driveOperations.h @@ -1,6 +1,8 @@ #ifndef FLASHCARD_H #define FLASHCARD_H +#include + extern u8 stored_SCFG_MC; extern bool nandMounted; @@ -24,7 +26,7 @@ extern u32 nandSize; extern u64 sdSize; extern u64 fatSize; extern u64 imgSize; -extern void printDriveBytes(u64 bytes); +extern std::string getDriveBytes(u64 bytes); extern const char* getDrivePath(void); diff --git a/arm9/source/dumpOperations.cpp b/arm9/source/dumpOperations.cpp index d9069d7..70692ea 100644 --- a/arm9/source/dumpOperations.cpp +++ b/arm9/source/dumpOperations.cpp @@ -1,23 +1,23 @@ -#include -#include -#include -#include -#include -#include +#include "dumpOperations.h" #include "auxspi.h" #include "date.h" #include "driveOperations.h" +#include "font.h" #include "ndsheaderbanner.h" #include "read_card.h" #include "tonccpy.h" +#include +#include +#include +#include +#include + extern u8 copyBuf[]; extern bool expansionPakFound; -extern PrintConsole topConsole, bottomConsole; - static sNDSHeaderExt ndsCardHeader; //--------------------------------------------------------------------------------- @@ -138,9 +138,11 @@ uint32 cardEepromGetSizeFixed() { void ndsCardSaveDump(const char* filename) { FILE *out = fopen(filename, "wb"); if(out) { - consoleClear(); - iprintf("Dumping save...\n"); - iprintf("Do not remove the NDS card.\n"); + font->clear(false); + font->print(0, 0, false, "Dumping save..."); + font->print(0, 1, false, "Do not remove the NDS card."); + font->update(false); + unsigned char *buffer; auxspi_extra card_type = auxspi_has_extra(); if(card_type == AUXSPI_INFRARED) { @@ -168,22 +170,17 @@ void ndsCardSaveDump(const char* filename) { } void ndsCardSaveRestore(const char *filename) { - consoleSelect(&bottomConsole); - consoleClear(); - iprintf("\x1B[47m"); // Print foreground white color - iprintf("Restore the selected save to the"); // Line is 32 chars - iprintf("inserted game card?\n"); // Line is 32 chars - iprintf("( yes, no)\n"); + font->clear(false); + font->print(0, 0, false, "Restore the selected save to the inserted game card?"); + font->print(0, 2, false, "( yes, no)\n"); + font->update(false); - 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 u16 pressed; do { - // Move to right side of screen - iprintf ("\x1b[0;26H"); // Print time - iprintf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDownRepeat(); @@ -191,9 +188,6 @@ void ndsCardSaveRestore(const char *filename) { } while (!(pressed & (KEY_A | KEY_B))); if(pressed & KEY_A) { - consoleSelect(&bottomConsole); - consoleClear(); - auxspi_extra card_type = auxspi_has_extra(); bool auxspi = card_type == AUXSPI_INFRARED; FILE *in = fopen(filename, "rb"); @@ -228,23 +222,20 @@ void ndsCardSaveRestore(const char *filename) { fseek(in, 0, SEEK_END); length = ftell(in); fseek(in, 0, SEEK_SET); - if(length != (auxspi ? (int)(LEN*num_blocks) : size)) { + if(length != (auxspi ? (int)(LEN * num_blocks) : size)) { 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("( OK)\n"); - consoleSelect(&topConsole); - iprintf ("\x1B[30m"); // Print black color + const std::string_view sizeError = "The size of this save doesn't match the size of the inserted game card.\n\nWrite cancelled!"; + + font->clear(false); + font->print(0, 0, false, sizeError, Alignment::left, Palette::red); + font->print(0, font->calcHeight(sizeError) + 1, false, "( OK)"); + font->update(false); + do { - // Move to right side of screen - iprintf ("\x1b[0;26H"); // Print time - iprintf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDownRepeat(); @@ -252,7 +243,13 @@ void ndsCardSaveRestore(const char *filename) { } while (!(pressed & KEY_A)); 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(auxspi) auxspi_erase(card_type); @@ -261,9 +258,12 @@ void ndsCardSaveRestore(const char *filename) { } if(auxspi){ buffer = new unsigned char[LEN]; + font->print(0, 5, false, "["); + font->print(-1, 5, false, "]"); for(unsigned int i = 0; i < num_blocks; i++) { - iprintf ("\x1b[9;0H"); - iprintf ("%d/%d Bytes", i * LEN, length); + font->print((i * (SCREEN_COLS - 2) / num_blocks) + 1, 5, false, "="); + font->printf(0, 6, false, Alignment::left, Palette::white, "%d/%d Bytes", i * LEN, length); + font->update(false); fread(buffer, 1, LEN, in); auxspi_write_data(i << shift, buffer, LEN, type, card_type); @@ -272,9 +272,13 @@ void ndsCardSaveRestore(const char *filename) { int blocks = size / 32; int written = 0; buffer = new unsigned char[blocks]; + font->print(0, 5, false, "["); + font->print(-1, 5, false, "]"); for(unsigned int i = 0; i < 32; i++) { - iprintf ("\x1b[9;0H"); - iprintf ("%d/%d Bytes", i * blocks, length); + font->print((i * (SCREEN_COLS - 2) / 32) + 1, 5, false, "="); + font->printf(0, 6, false, Alignment::left, Palette::white, "%d/%d Bytes", i * LEN, length); + font->update(false); + fread(buffer, 1, blocks, in); cardWriteEeprom(written, buffer, blocks, type); written += blocks; @@ -287,8 +291,10 @@ void ndsCardSaveRestore(const char *filename) { } void dumpFailMsg(void) { - consoleClear(); - iprintf("Failed to dump the ROM.\n"); + font->clear(false); + font->print(0, 0, false, "Failed to dump the ROM."); + font->update(false); + for (int i = 0; i < 60*2; i++) { swiWaitForVBlank(); } @@ -298,49 +304,44 @@ void ndsCardDump(void) { int pressed = 0; //bool showGameCardMsgAgain = false; - consoleSelect(&bottomConsole); - consoleClear(); - iprintf("\x1B[47m"); // Print foreground white color - iprintf("Dump NDS card ROM to\n"); - iprintf("\"%s:/gm9i/out\"?\n", (sdMounted ? "sd" : "fat")); - iprintf("( yes, trim, no,\n"); - iprintf(" save only)"); + font->clear(false); + font->printf(0, 0, false, Alignment::left, Palette::white, "Dump NDS card ROM to\n\"%s:/gm9i/out\"?", sdMounted ? "sd:" : "fat:"); + font->print(0, 2, false, "( yes, trim, no, save only)"); + font->update(false); - 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 do { - // Move to right side of screen - iprintf ("\x1b[0;26H"); // Print time - iprintf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDownRepeat(); swiWaitForVBlank(); } while (!(pressed & (KEY_A | KEY_Y | KEY_B | KEY_X))); - consoleSelect(&bottomConsole); - iprintf ("\x1B[47m"); // Print foreground white color - if (pressed & KEY_X) { - consoleClear(); char folderPath[2][256]; sprintf(folderPath[0], "%s:/gm9i", (sdMounted ? "sd" : "fat")); sprintf(folderPath[1], "%s:/gm9i/out", (sdMounted ? "sd" : "fat")); 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); } if (access(folderPath[1], F_OK) != 0) { - iprintf("\x1b[0;0H"); - iprintf("Creating directory..."); + font->clear(false); + font->print(0, 0, false, "Creating directory..."); + font->update(false); mkdir(folderPath[1], 0777); } - consoleClear(); + if (cardInit(&ndsCardHeader) != 0) { - iprintf("Unable to dump the save.\n"); - for (int i = 0; i < 60*2; i++) { + font->clear(false); + font->print(0, 0, false, "Unable to dump the save."); + font->update(false); + for (int i = 0; i < 60 * 2; i++) { swiWaitForVBlank(); } return; @@ -354,44 +355,41 @@ void ndsCardDump(void) { ndsCardSaveDump(destSavPath); } else if ((pressed & KEY_A) || (pressed & KEY_Y)) { - consoleClear(); bool trimRom = (pressed & KEY_Y); char folderPath[2][256]; sprintf(folderPath[0], "%s:/gm9i", (sdMounted ? "sd" : "fat")); sprintf(folderPath[1], "%s:/gm9i/out", (sdMounted ? "sd" : "fat")); 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); } if (access(folderPath[1], F_OK) != 0) { - iprintf("\x1b[0;0H"); - iprintf("Creating directory..."); + font->clear(false); + font->print(0, 0, false, "Creating directory..."); + font->update(false); mkdir(folderPath[1], 0777); } /*if (expansionPakFound) { - consoleClear(); - printf("Please switch to the\ngame card, then press A.\n"); + font->clear(false) + font->print(0, 0, false, "Please switch to the game card, then press A."); + font->update(false); //flashcardUnmount(); 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 do { - // Move to right side of screen - printf ("\x1b[0;26H"); // Print time - printf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDownRepeat(); swiWaitForVBlank(); } while (!(pressed & KEY_A)); - - consoleSelect(&bottomConsole); - printf ("\x1B[47m"); // Print foreground white color }*/ - consoleClear(); + int cardInited = cardInit(&ndsCardHeader); char gameTitle[13] = {0}; char gameCode[7] = {0}; @@ -426,10 +424,14 @@ void ndsCardDump(void) { sprintf(destSavPath, "%s:/gm9i/out/%s.sav", (sdMounted ? "sd" : "fat"), fileName); if (cardInited == 0) { - iprintf("%s.nds\nis dumping...\n", fileName); - iprintf("Do not remove the NDS card.\n"); + font->clear(false); + 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 { - 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++) { swiWaitForVBlank(); } @@ -498,14 +500,11 @@ void ndsCardDump(void) { //flashcardUnmount(); 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 do { - // Move to right side of screen - iprintf ("\x1b[0;26H"); // Print time - iprintf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDownRepeat(); @@ -521,12 +520,9 @@ void ndsCardDump(void) { // Read from game card 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 - iprintf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); consoleSelect(&bottomConsole); iprintf ("\x1B[47m"); // Print foreground white color @@ -537,14 +533,11 @@ void ndsCardDump(void) { } iprintf("\x1b[15;0H"); 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 do { - // Move to right side of screen - printf ("\x1b[0;26H"); // Print time - printf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDownRepeat(); @@ -568,12 +561,9 @@ void ndsCardDump(void) { destinationFileOpened = true; } 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 - iprintf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); consoleSelect(&bottomConsole); printf ("\x1B[47m"); // Print foreground white color @@ -590,19 +580,19 @@ void ndsCardDump(void) { u32 currentSize = romSize; FILE* destinationFile = fopen(destPath, "wb"); 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); - iprintf ("\x1B[47m"); // Print foreground white color - iprintf ("\x1b[8;0H"); - iprintf ("Progress:\n"); - iprintf ("%i/%i Bytes", (int)src, (int)romSize); + font->print(0, 4, false, "Progress:"); + font->print(0, 5, false, "["); + font->print(-1, 5, false, "]"); + for (u32 src = 0; src < romSize; src += 0x8000) { + // 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) { cardRead (src+i, copyBuf+i); } @@ -639,21 +629,16 @@ void readChange(void) { void gbaCartDump(void) { int pressed = 0; - consoleSelect(&bottomConsole); - consoleClear(); - iprintf("\x1B[47m"); // Print foreground white color - iprintf("Dump GBA cart ROM to\n"); - iprintf("\"fat:/gm9i/out\"?\n"); - iprintf("( yes, no)"); + font->clear(false); + font->printf(0, 0, false, Alignment::left, Palette::white, "Dump GBA cart ROM to\n\"%s:/gm9i/out\"?", sdMounted ? "sd:" : "fat:"); + font->print(0, 2, false, "( yes, no)"); + font->update(false); - 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 do { - // Move to right side of screen - iprintf ("\x1b[0;26H"); // Print time - iprintf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDownRepeat(); @@ -661,22 +646,23 @@ void gbaCartDump(void) { } while (!(pressed & KEY_A) && !(pressed & KEY_B)); if (pressed & KEY_A) { - iprintf ("\x1b[0;27H"); - iprintf (" "); // Clear time + // 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) { consoleClear(); 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); } if (access("fat:/gm9i/out", F_OK) != 0) { - iprintf ("\x1b[0;0H"); - iprintf("Creating directory..."); + font->clear(false); + font->print(0, 0, false, "Creating directory..."); + font->update(false); mkdir("fat:/gm9i/out", 0777); } char gbaHeaderGameTitle[13] = "\0"; @@ -716,9 +702,12 @@ void gbaCartDump(void) { char destSavPath[256] = {0}; sprintf(destPath, "fat:/gm9i/out/%s.gba", fileName); sprintf(destSavPath, "fat:/gm9i/out/%s.sav", fileName); - consoleClear(); - iprintf("%s.gba\nis dumping...\n", fileName); - iprintf("Do not remove the GBA cart.\n"); + + font->clear(false); + 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 u32 romSize = 0x02000000; for (u32 i = 0x09FE0000; i > 0x08000000; i -= 0x20000) { @@ -772,6 +761,7 @@ void gbaCartDump(void) { } else { dumpFailMsg(); } + // Save file remove(destSavPath); destinationFile = fopen(destSavPath, "wb"); diff --git a/arm9/source/fileOperations.cpp b/arm9/source/fileOperations.cpp index c8777bb..428c236 100644 --- a/arm9/source/fileOperations.cpp +++ b/arm9/source/fileOperations.cpp @@ -7,56 +7,38 @@ #include "date.h" #include "file_browse.h" +#include "font.h" #define copyBufSize 0x8000 #define shaChunkSize 0x10000 u32 copyBuf[copyBufSize]; -extern PrintConsole topConsole, bottomConsole; - std::vector clipboard; bool clipboardOn = false; bool clipboardUsed = false; -void printBytes(int bytes) -{ +std::string getBytes(int bytes) { + char buffer[11]; if (bytes == 1) - iprintf("%d Byte", bytes); + sniprintf(buffer, sizeof(buffer), "%d Byte", bytes); else if (bytes < 1024) - iprintf("%d Bytes", bytes); + sniprintf(buffer, sizeof(buffer), "%d Bytes", bytes); else if (bytes < (1024 * 1024)) - printf("%d KB", bytes / 1024); + sniprintf(buffer, sizeof(buffer), "%d KB", bytes / 1024); else if (bytes < (1024 * 1024 * 1024)) - printf("%d MB", bytes / 1024 / 1024); + sniprintf(buffer, sizeof(buffer), "%d MB", bytes / 1024 / 1024); else - printf("%d GB", bytes / 1024 / 1024 / 1024); + sniprintf(buffer, sizeof(buffer), "%d GB", bytes / 1024 / 1024 / 1024); + + return buffer; } -void printBytesAlign(int bytes) -{ - 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) -{ +off_t getFileSize(const char *fileName) { FILE* fp = fopen(fileName, "rb"); off_t fsize = 0; if (fp) { @@ -69,17 +51,24 @@ off_t getFileSize(const char *fileName) return fsize; } -bool calculateSHA1(const char *fileName, u8 *sha1) -{ +bool calculateSHA1(const char *fileName, u8 *sha1) { off_t fsize = getFileSize(fileName); u8 *buf = (u8*) malloc(shaChunkSize); 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; } FILE* fp = fopen(fileName, "rb"); 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); return false; } @@ -87,6 +76,18 @@ bool calculateSHA1(const char *fileName, u8 *sha1) swiSHA1context_t ctx; ctx.sha_block=0; //this is weird but it has to be done 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, "( to cancel)"); + + font->print(0, nameHeight + 4, false, "Progress:"); + font->print(0, nameHeight + 5, false, "["); + font->print(-1, nameHeight + 5, false, "]"); + while (true) { size_t ret = fread(buf, 1, shaChunkSize, fp); if (!ret) break; @@ -94,26 +95,27 @@ bool calculateSHA1(const char *fileName, u8 *sha1) scanKeys(); int keys = keysHeld(); 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); free(buf); 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 dirContents; dirContents.clear(); - if (entry->isDirectory) chdir((sourcePath + ("/" + entry->name)).c_str()); + if (entry.isDirectory) chdir((sourcePath + ("/" + entry.name)).c_str()); getDirectoryContents(dirContents); - 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) mkdir((destinationPath + ("/" + entry.name)).c_str(), 0777); + if (((int)dirContents.size()) != 1) fcopy((sourcePath + ("/" + entry.name)).c_str(), (destinationPath + ("/" + entry.name)).c_str()); } -int fcopy(const char *sourcePath, const char *destinationPath) -{ - DIR *isDir = opendir (sourcePath); +int fcopy(const char *sourcePath, const char *destinationPath) { + DIR *isDir = opendir(sourcePath); if (isDir != NULL) { closedir(isDir); @@ -122,17 +124,15 @@ int fcopy(const char *sourcePath, const char *destinationPath) chdir(sourcePath); std::vector dirContents; getDirectoryContents(dirContents); - DirEntry* entry = NULL; mkdir(destinationPath, 0777); for (int i = 1; i < ((int)dirContents.size()); i++) { chdir(sourcePath); - entry = &dirContents.at(i); - dirCopy(entry, i, destinationPath, sourcePath); + dirCopy(dirContents[i], i, destinationPath, sourcePath); } - chdir (destinationPath); - chdir (".."); + chdir(destinationPath); + chdir(".."); return 1; } else { closedir(isDir); @@ -142,26 +142,26 @@ int fcopy(const char *sourcePath, const char *destinationPath) off_t fsize = 0; if (sourceFile) { 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); } else { - fclose(sourceFile); return -1; } FILE* destinationFile = fopen(destinationPath, "wb"); - //if (destinationFile) { - fseek(destinationFile, 0, SEEK_SET); - /*} else { + if (!destinationFile) { fclose(sourceFile); - fclose(destinationFile); 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; int numr; - while (1) - { + while (1) { scanKeys(); if (keysHeld() & KEY_B) { // Cancel copying @@ -170,18 +170,14 @@ int fcopy(const char *sourcePath, const char *destinationPath) return -1; 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); - printf ("\x1B[47m"); // Print foreground white color - printf ("\x1b[16;0H"); - printf ("Progress:\n"); - printf ("%i/%i Bytes ", (int)offset, (int)fsize); + // Print time + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); + + 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 numr = fread(copyBuf, 1, copyBufSize, sourceFile); @@ -192,10 +188,6 @@ int fcopy(const char *sourcePath, const char *destinationPath) fclose(sourceFile); fclose(destinationFile); - printf ("\x1b[17;0H"); - printf ("%i/%i Bytes ", (int)fsize, (int)fsize); - for (int i = 0; i < 30; i++) swiWaitForVBlank(); - return 1; break; } @@ -205,62 +197,29 @@ int fcopy(const char *sourcePath, const char *destinationPath) } } -void changeFileAttribs(DirEntry* entry) { - consoleClear(); +void changeFileAttribs(const DirEntry *entry) { int pressed = 0; - int cursorScreenPos = 0; + int cursorScreenPos = font->calcHeight(entry->name); uint8_t currentAttribs = FAT_getAttr(entry->name.c_str()); 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) { - consoleSelect(&bottomConsole); - printf ("\x1B[47m"); // Print foreground white color - printf ("\x1b[%i;1H", 5+cursorScreenPos); - printf ((newAttribs & ATTR_READONLY) ? "X" : " "); - printf ("\x1b[%i;18H", 5+cursorScreenPos); - printf ((newAttribs & ATTR_HIDDEN) ? "X" : " "); - printf ("\x1b[%i;1H", 6+cursorScreenPos); - printf ((newAttribs & ATTR_SYSTEM) ? "X" : " "); - printf ("\x1b[%i;18H", 6+cursorScreenPos); - printf ((newAttribs & ATTR_ARCHIVE) ? "X" : " "); - printf ("\x1b[%i;0H", 11+cursorScreenPos); - printf ((currentAttribs==newAttribs) ? "( to continue) " : "( to apply, to cancel)"); + font->clear(false); + font->print(0, 0, false, entry->name); + if (!entry->isDirectory) + font->printf(0, cursorScreenPos + 1, false, Alignment::left, Palette::white, "filesize: %s", getBytes(entry->size).c_str()); + font->printf(0, cursorScreenPos + 3, false, Alignment::left, Palette::white, "[%c] ↑ read-only [%c] ↓ hidden", (newAttribs & ATTR_READONLY) ? 'X' : ' ', (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' : ' '); + font->printf(0, cursorScreenPos + 5, false, Alignment::left, Palette::white, "[%c] virtual", (newAttribs & ATTR_VOLUME) ? 'X' : ' '); + font->printf(0, cursorScreenPos + 6, false, Alignment::left, Palette::white, "(↑↓→← to change attributes)"); + font->print(0, cursorScreenPos + 8, false, (currentAttribs == newAttribs) ? "( to continue)" : "( to apply, to cancel)"); + font->update(false); - 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 do { - // Move to right side of screen - printf ("\x1b[0;26H"); // Print time - printf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDown(); @@ -269,43 +228,17 @@ void changeFileAttribs(DirEntry* entry) { && !(pressed & KEY_A) && !(pressed & KEY_B)); if (pressed & KEY_UP) { - if (newAttribs & ATTR_READONLY) { - newAttribs -= ATTR_READONLY; - } else { - newAttribs += ATTR_READONLY; - } - } - - if (pressed & KEY_DOWN) { - 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)) { + newAttribs ^= ATTR_READONLY; + } else if (pressed & KEY_DOWN) { + newAttribs ^= ATTR_HIDDEN; + } else if (pressed & KEY_RIGHT) { + newAttribs ^= ATTR_SYSTEM; + } else if (pressed & KEY_LEFT) { + newAttribs ^= ATTR_ARCHIVE; + } else if ((pressed & KEY_A) && (currentAttribs != newAttribs)) { FAT_setAttr(entry->name.c_str(), newAttribs); break; - } - - if ((pressed & KEY_A) || (pressed & KEY_B)) { + } else if (pressed & (KEY_A | KEY_B)) { break; } } diff --git a/arm9/source/fileOperations.h b/arm9/source/fileOperations.h index a7e0931..c07647f 100644 --- a/arm9/source/fileOperations.h +++ b/arm9/source/fileOperations.h @@ -19,12 +19,11 @@ extern std::vector clipboard; extern bool clipboardOn; extern bool clipboardUsed; -extern void printBytes(int bytes); -extern void printBytesAlign(int bytes); +extern std::string getBytes(int bytes); extern off_t getFileSize(const char *fileName); extern bool calculateSHA1(const char *fileName, u8 *sha1); extern int fcopy(const char *sourcePath, const char *destinationPath); -void changeFileAttribs(DirEntry* entry); +void changeFileAttribs(const DirEntry *entry); #endif // FILE_COPY diff --git a/arm9/source/file_browse.cpp b/arm9/source/file_browse.cpp index 9a2171b..7333d3f 100644 --- a/arm9/source/file_browse.cpp +++ b/arm9/source/file_browse.cpp @@ -38,24 +38,16 @@ #include "driveMenu.h" #include "driveOperations.h" #include "dumpOperations.h" +#include "font.h" #include "hexEditor.h" #include "ndsInfo.h" #include "nitrofs.h" #include "inifile.h" #include "nds_loader_arm9.h" -#define SCREEN_COLS 22 -#define ENTRIES_PER_SCREEN 23 #define ENTRIES_START_ROW 1 #define OPTIONS_ENTRIES_START_ROW 2 #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]; @@ -84,7 +76,7 @@ bool dirEntryPredicate (const DirEntry& lhs, const DirEntry& rhs) { return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0; } -void getDirectoryContents (std::vector& dirContents) { +void getDirectoryContents(std::vector& dirContents) { struct stat st; dirContents.clear(); @@ -92,7 +84,8 @@ void getDirectoryContents (std::vector& dirContents) { DIR *pdir = opendir ("."); if (pdir == NULL) { - iprintf ("Unable to open the directory.\n"); + font->print(0, 0, true, "Unable to open the directory."); + font->update(true); } else { while(true) { @@ -138,145 +131,150 @@ void getDirectoryContents (std::vector& dirContents) { void showDirectoryContents (const std::vector& dirContents, int fileOffset, int startRow) { 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 - printf ("\x1B[30m"); // Print black color - // Print time - printf ("\x1b[0;27H"); - printf (RetTime().c_str()); - - 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"); + if(font->calcWidth(path) > SCREEN_COLS - 6) + font->print(-6 - 1, 0, true, path, Alignment::right, Palette::blackGreen); + else + font->print(0, 0, true, path, Alignment::left, Palette::blackGreen); // Print directory listing 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 - iprintf ("\x1b[%d;0H", i + ENTRIES_START_ROW); + Palette pal; if ((fileOffset - startRow) == i) { - printf ("\x1B[47m"); // Print foreground white color + pal = Palette::white; } else if (entry->selected) { - printf ("\x1B[33m"); // Print custom yellow color + pal = Palette::yellow; } else if (entry->isDirectory) { - printf ("\x1B[37m"); // Print custom blue color + pal = Palette::blue; } 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 == "..") { - printf ("\x1b[%d;28H", i + ENTRIES_START_ROW); - printf ("(..)"); + font->print(-1, i + 1, true, "(..)", Alignment::right, pal); } else if (entry->isDirectory) { - printf ("\x1b[%d;27H", i + ENTRIES_START_ROW); - printf ("(dir)"); + font->print(-1, i + 1, true, "(dir)", Alignment::right, pal); } else { - printf ("\x1b[%d;23H", i + ENTRIES_START_ROW); - printBytesAlign((int)entry->size); + font->printf(-1, i + 1, true, Alignment::right, pal, "(%s)", getBytes(entry->size).c_str()); } } - printf ("\x1B[47m"); // Print foreground white color + font->update(true); } FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { int pressed = 0; - FileOperation assignedOp[4] = {FileOperation::none}; + std::vector operations; int optionOffset = 0; - int cursorScreenPos = 0; - int maxCursors = -1; + std::string fullPath = path + entry->name; + 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->isApp) { - assignedOp[++maxCursors] = FileOperation::bootFile; - if (extension(entry->name, {"firm"})) { - printf(" Boot file\n"); - } else { - printf(" Boot file (Direct)\n"); - assignedOp[++maxCursors] = FileOperation::bootstrapFile; - printf(" Bootstrap file\n"); + operations.push_back(FileOperation::bootFile); + if (!extension(entry->name, {"firm"})) { + operations.push_back(FileOperation::bootstrapFile); } } - 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( select, 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); - printf ("\x1B[30m"); // Print black color for time text + if(extension(entry->name, {"nds", "dsi", "ids", "app"})) { + 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, "( select, 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 do { - // Move to right side of screen - printf ("\x1b[0;26H"); // Print time - printf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDownRepeat(); @@ -288,26 +286,26 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { #endif ); - consoleSelect(&bottomConsole); - printf ("\x1B[47m"); // Print foreground white color - if (pressed & KEY_UP) optionOffset -= 1; if (pressed & KEY_DOWN) optionOffset += 1; - if (optionOffset < 0) optionOffset = maxCursors; // Wrap around to bottom of list - if (optionOffset > maxCursors) optionOffset = 0; // Wrap around to top of list + if (optionOffset < 0) // Wrap around to bottom of list + optionOffset = operations.size() - 1; + + if (optionOffset >= (int)operations.size()) // Wrap around to top of list + optionOffset = 0; if (pressed & KEY_A) { - switch(assignedOp[optionOffset]) { + switch(operations[optionOffset]) { case FileOperation::bootFile: { applaunch = true; - iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos); - printf("Now loading..."); + font->print(3, optionOffset + y, false, "Now loading..."); + font->update(false); break; } case FileOperation::bootstrapFile: { char baseFile[256], savePath[PATH_MAX]; //, bootstrapConfigPath[32]; //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; snprintf(savePath, PATH_MAX, "%s%s%s.sav", path, !access("saves", F_OK) ? "saves/" : "", baseFile); CIniFile bootstrapConfig("/_nds/nds-bootstrap.ini"); @@ -327,19 +325,19 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { break; } case FileOperation::copySdOut: { if (access("sd:/gm9i", F_OK) != 0) { - iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos); - printf("Creating directory..."); + font->print(3, optionOffset + y, false, "Creating directory..."); + font->update(false); mkdir("sd:/gm9i", 0777); } if (access("sd:/gm9i/out", F_OK) != 0) { - iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos); - printf("Creating directory..."); + font->print(3, optionOffset + y, false, "Creating directory..."); + font->update(false); mkdir("sd:/gm9i/out", 0777); } char destPath[256]; snprintf(destPath, sizeof(destPath), "sd:/gm9i/out/%s", entry->name.c_str()); - iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos); - printf("Copying... "); + font->print(3, optionOffset + y, false, "Copying..."); + font->update(false); remove(destPath); char sourceFolder[PATH_MAX]; getcwd(sourceFolder, PATH_MAX); @@ -350,19 +348,19 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { break; } case FileOperation::copyFatOut: { if (access("fat:/gm9i", F_OK) != 0) { - iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos); - printf("Creating directory..."); + font->print(3, optionOffset + y, false, "Creating directory..."); + font->update(false); mkdir("fat:/gm9i", 0777); } if (access("fat:/gm9i/out", F_OK) != 0) { - iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos); - printf("Creating directory..."); + font->print(3, optionOffset + y, false, "Creating directory..."); + font->update(false); mkdir("fat:/gm9i/out", 0777); } char destPath[256]; snprintf(destPath, sizeof(destPath), "fat:/gm9i/out/%s", entry->name.c_str()); - iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos); - printf("Copying... "); + font->print(3, (optionOffset + y), false, "Copying..."); + font->update(false); remove(destPath); char 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); break; } case FileOperation::calculateSHA1: { - iprintf("\x1b[2J"); - iprintf("Calculating SHA1 hash of:\n%s\n", entry->name.c_str()); - iprintf("Press to cancel\n\n"); u8 sha1[20] = {0}; bool ret = calculateSHA1(strcat(getcwd(path, PATH_MAX), entry->name.c_str()), sha1); - if (!ret) break; - iprintf("SHA1 hash is: \n"); - for (int i = 0; i < 20; ++i) iprintf("%02X", sha1[i]); - consoleSelect(&topConsole); - iprintf ("\x1B[30m"); // Print black color + if (!ret) + break; + + font->clear(false); + 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, "( 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 int pressed; do { - // Move to right side of screen - iprintf ("\x1b[0;26H"); // Print time - iprintf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); + scanKeys(); pressed = keysDownRepeat(); swiWaitForVBlank(); @@ -423,7 +425,7 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { break; } } - return assignedOp[optionOffset]; + return operations[optionOffset]; } if (pressed & KEY_B) { return FileOperation::none; @@ -441,43 +443,33 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { bool fileBrowse_paste(char dest[256]) { int pressed = 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("( select, cancel)"); - consoleSelect(&bottomConsole); - printf ("\x1B[47m"); // Print foreground white color while (true) { - // Clear old cursors - for (int i = OPTIONS_ENTRIES_START_ROW; i < (maxCursors+1) + OPTIONS_ENTRIES_START_ROW; i++) { - iprintf ("\x1b[%d;0H ", i); - } - // Show cursor - iprintf ("\x1b[%d;0H->", optionOffset + OPTIONS_ENTRIES_START_ROW); + font->clear(false); + + font->print(0, 0, false, "Paste clipboard here?"); + + int row = OPTIONS_ENTRIES_START_ROW, maxCursors = 0; + 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, "( select, 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 do { - // Move to right side of screen - printf ("\x1b[0;26H"); // Print time - printf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDownRepeat(); @@ -489,10 +481,6 @@ bool fileBrowse_paste(char dest[256]) { #endif ); - - consoleSelect(&bottomConsole); - printf ("\x1B[47m"); // Print foreground white color - if (pressed & KEY_UP) 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 (pressed & KEY_A) { - iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW); - printf(optionOffset ? "Moving... " : "Copying..."); + font->print(3, optionOffset + OPTIONS_ENTRIES_START_ROW, false, optionOffset ? "Moving... " : "Copying..."); for (auto &file : clipboard) { std::string destPath = dest + file.name; if (file.path == destPath) @@ -537,14 +524,14 @@ bool fileBrowse_paste(char dest[256]) { } void recRemove(const char *path, std::vector dirContents) { - DirEntry *entry = NULL; chdir (path); getDirectoryContents(dirContents); for (int i = 1; i < ((int)dirContents.size()); i++) { - entry = &dirContents.at(i); - if (entry->isDirectory) recRemove(entry->name.c_str(), dirContents); - if (!(FAT_getAttr(entry->name.c_str()) & ATTR_READONLY)) { - remove(entry->name.c_str()); + DirEntry &entry = dirContents[i]; + if (entry.isDirectory) + recRemove(entry.name.c_str(), dirContents); + if (!(FAT_getAttr(entry.name.c_str()) & ATTR_READONLY)) { + remove(entry.name.c_str()); } } chdir (".."); @@ -552,51 +539,52 @@ void recRemove(const char *path, std::vector dirContents) { } void fileBrowse_drawBottomScreen(DirEntry* entry) { - consoleClear(); - printf ("\x1B[47m"); // Print foreground white color - printf ("\x1b[22;0H"); - 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"); + font->clear(false); + + int row = -1; + if (!isDSiMode() && isRegularDS) { - printf (POWERTEXT_DS); + font->print(0, row--, false, POWERTEXT_DS); } else if (is3DS) { - printf ("%s\n%s", POWERTEXT_3DS, HOMETEXT); + font->print(0, row--, false, HOMETEXT); + font->print(0, row--, false, POWERTEXT_3DS); } 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 - printf ("\x1b[0;0H"); - printf ("%s\n", entry->name.c_str()); + font->print(0, row--, false, clipboardOn ? "SELECT - Clear Clipboard" : "SELECT - Restore Clipboard"); + if (sdMounted || flashcardMounted) { + 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->isDirectory) { - printf ("(dir)"); + font->print(0, font->calcHeight(entry->name), false, "(dir)", Alignment::left, pal); } 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 { - printf ("%i Bytes", (int)entry->size); + font->printf(0, font->calcHeight(entry->name), false, Alignment::left, pal, "%i Bytes", entry->size); } } if (clipboardOn) { - printf ("\x1b[9;0H"); - printf ("\x1B[47m"); // Print foreground white color - printf ("[CLIPBOARD]\n"); + font->print(0, 6, false, "[CLIPBOARD]"); 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) { - 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 { - 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; } } } + + font->update(false); } std::string browseForFile (void) { @@ -609,23 +597,18 @@ std::string browseForFile (void) { getDirectoryContents (dirContents); while (true) { - DirEntry* entry = &dirContents.at(fileOffset); + DirEntry* entry = &dirContents[fileOffset]; - consoleSelect(&bottomConsole); fileBrowse_drawBottomScreen(entry); - consoleSelect(&topConsole); - showDirectoryContents (dirContents, fileOffset, screenOffset); + showDirectoryContents(dirContents, fileOffset, screenOffset); 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 do { - // Move to right side of screen - printf ("\x1b[0;26H"); // Print time - printf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDownRepeat(); @@ -639,16 +622,7 @@ std::string browseForFile (void) { if ((held & KEY_R) && (pressed & KEY_L)) { break; } - } while (!(pressed & KEY_UP) && !(pressed & KEY_DOWN) && !(pressed & KEY_LEFT) && !(pressed & KEY_RIGHT) - && !(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); + } while (!pressed); if (isDSiMode() && !pressed && currentDrive == 1 && REG_SCFG_MC == 0x11 && flashcardMounted) { flashcardUnmount(); @@ -692,10 +666,11 @@ std::string browseForFile (void) { screenMode = 0; return "null"; } 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 chdir (entry->name.c_str()); - getDirectoryContents (dirContents); + getDirectoryContents(dirContents); screenOffset = 0; fileOffset = 0; } else { @@ -748,8 +723,10 @@ std::string browseForFile (void) { // Rename file/folder if ((held & KEY_R) && (pressed & KEY_X) && (entry->name != ".." && strncmp(path, "nitro:/", 7) != 0)) { - printf ("\x1b[0;27H"); - printf (" "); // Clear time + // Clear time + font->print(-1, 0, true, " ", Alignment::right, Palette::blackGreen); + font->update(true); + pressed = 0; consoleDemoInit(); Keyboard *kbd = keyboardDemoInit(); @@ -757,13 +734,13 @@ std::string browseForFile (void) { kbd->OnKeyPressed = OnKeyPressed; keyboardShow(); - printf("Rename to: \n"); + iprintf("Rename to:\n"); fgets(newName, 256, stdin); newName[strlen(newName)-1] = 0; keyboardHide(); - consoleClear(); - reinitConsoles(); + videoSetModeSub(MODE_5_2D); + bgShow(bgInitSub(2, BgType_Bmp8, BgSize_B8_256x256, 3, 0)); if (newName[0] != '\0') { // Check for unsupported characters @@ -782,49 +759,44 @@ std::string browseForFile (void) { } } if (rename(entry->name.c_str(), newName) == 0) { - getDirectoryContents (dirContents); + getDirectoryContents(dirContents); } } } // Delete action if ((pressed & KEY_X) && (entry->name != ".." && strncmp(path, "nitro:/", 7) != 0)) { - consoleSelect(&bottomConsole); - consoleClear(); - printf ("\x1B[47m"); // Print foreground white color + font->clear(false); int selections = std::count_if(dirContents.begin(), dirContents.end(), [](const DirEntry &x){ return x.selected; }); 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++) { 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++; } } if(selections > 5) - iprintf("\x1B[41m- and %d more...\n", selections - 5); - iprintf("\x1B[47m"); + font->printf(0, 7, false, Alignment::left, Palette::red, "- and %d more...", selections - 5); } 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 ("( yes, no)"); - consoleSelect(&topConsole); - printf ("\x1B[30m"); // Print black color for time text + font->print(0, (!entry->selected || selections == 1) ? 2 : (selections > 5 ? 9 : selections + 3), false, "( yes, no)"); + font->update(false); + while (true) { - // Move to right side of screen - printf ("\x1b[0;26H"); // Print time - printf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDownRepeat(); swiWaitForVBlank(); if (pressed & KEY_A) { - consoleSelect(&bottomConsole); - consoleClear(); - printf ("\x1B[47m"); // Print foreground white color 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; for (auto &item : dirContents) { if(item.selected) { @@ -839,19 +811,16 @@ std::string browseForFile (void) { } fileOffset = 0; } else if (FAT_getAttr(entry->name.c_str()) & ATTR_READONLY) { - printf ("Failed deleting:\n"); - printf (entry->name.c_str()); - printf ("\n"); - printf ("\n"); - printf ("( to continue)"); + font->clear(false); + font->print(0, 0, false, "Failed deleting:"); + font->print(0, 1, false, entry->name); + font->print(0, 3, false, "( to continue)"); pressed = 0; - consoleSelect(&topConsole); - printf ("\x1B[30m"); // Print black color for time text + while (!(pressed & KEY_A)) { - // Move to right side of screen - printf ("\x1b[0;26H"); // Print time - printf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDown(); @@ -860,10 +829,14 @@ std::string browseForFile (void) { for (int i = 0; i < 15; i++) swiWaitForVBlank(); } else { 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); } 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()); } fileOffset--; @@ -881,8 +854,10 @@ std::string browseForFile (void) { // Create new folder if ((held & KEY_R) && (pressed & KEY_Y) && (strncmp(path, "nitro:/", 7) != 0)) { - printf ("\x1b[0;27H"); - printf (" "); // Clear time + // Clear time + font->print(-1, 0, true, " ", Alignment::right, Palette::blackGreen); + font->update(true); + pressed = 0; consoleDemoInit(); Keyboard *kbd = keyboardDemoInit(); @@ -890,13 +865,13 @@ std::string browseForFile (void) { kbd->OnKeyPressed = OnKeyPressed; keyboardShow(); - printf("Name for new folder: \n"); + iprintf("Name for new folder:\n"); fgets(newName, 256, stdin); newName[strlen(newName)-1] = 0; keyboardHide(); - consoleClear(); - reinitConsoles(); + videoSetModeSub(MODE_5_2D); + bgShow(bgInitSub(2, BgType_Bmp8, BgSize_B8_256x256, 3, 0)); if (newName[0] != '\0') { // Check for unsupported characters @@ -921,17 +896,14 @@ std::string browseForFile (void) { } // Add to selection - if (pressed & KEY_L && entry->name != "..") { + if ((pressed & KEY_L && !(held & KEY_R)) && entry->name != "..") { bool select = !entry->selected; entry->selected = select; while(held & KEY_L) { do { - // Move to right side of screen - printf ("\x1b[0;26H"); - // Print black color for time text - printf ("\x1B[30m"); // Print time - printf (" %s" ,RetTime().c_str()); + font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen); + font->update(true); scanKeys(); pressed = keysDownRepeat(); @@ -980,10 +952,8 @@ std::string browseForFile (void) { } } - consoleSelect(&bottomConsole); fileBrowse_drawBottomScreen(entry); - consoleSelect(&topConsole); - showDirectoryContents (dirContents, fileOffset, screenOffset); + showDirectoryContents(dirContents, fileOffset, screenOffset); } } @@ -1025,48 +995,8 @@ std::string browseForFile (void) { // Make a screenshot if ((held & KEY_R) && (pressed & KEY_L)) { - if (sdMounted || flashcardMounted) { - if (access((sdMounted ? "sd:/gm9i" : "fat:/gm9i"), F_OK) != 0) { - 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(); - } + if(screenshot()) + getDirectoryContents(dirContents); } } } diff --git a/arm9/source/font.cpp b/arm9/source/font.cpp new file mode 100644 index 0000000..a44e790 --- /dev/null +++ b/arm9/source/font.cpp @@ -0,0 +1,369 @@ +#include "font.h" + +#include "font_default_frf.h" +#include "tonccpy.h" + +#include +#include +#include +#include +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(§ion_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(§ion_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(§ion_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; + } +} diff --git a/arm9/source/font.h b/arm9/source/font.h new file mode 100644 index 0000000..a9962e6 --- /dev/null +++ b/arm9/source/font.h @@ -0,0 +1,94 @@ +#ifndef FONT_H +#define FONT_H + +#include "tonccpy.h" + +#include +#include +#include +#include + +#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 \ No newline at end of file diff --git a/arm9/source/hexEditor.cpp b/arm9/source/hexEditor.cpp index 2a1c823..82813ee 100644 --- a/arm9/source/hexEditor.cpp +++ b/arm9/source/hexEditor.cpp @@ -1,41 +1,37 @@ #include "hexEditor.h" #include "date.h" -#include "tonccpy.h" #include "file_browse.h" +#include "font.h" +#include "tonccpy.h" #include #include #include -extern PrintConsole bottomConsole, bottomConsoleBG, topConsole; - -extern void reinitConsoles(void); - u32 jumpToOffset(u32 offset) { - consoleSelect(&bottomConsoleBG); - consoleClear(); - consoleSelect(&bottomConsole); - consoleClear(); - u8 cursorPosition = 0; u16 pressed = 0, held = 0; while(1) { - printf("\x1B[9;6H\x1B[47m-------------------"); - printf("\x1B[10;8H\x1B[47mJump to Offset"); - printf("\x1B[12;11H\x1B[37m%08lX", offset); - printf("\x1B[12;%dH\x1B[41m%lX", 17 - cursorPosition, (offset >> ((cursorPosition + 1) * 4)) & 0xF); - printf("\x1B[13;6H\x1B[47m-------------------"); + int y = (ENTRIES_PER_SCREEN - 4) / 2; + font->clear(false); + font->print(0, y, false, "--------------------", Alignment::center); + font->print(0, y + 1, false, "Jump to Offset", Alignment::center); + 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 { swiWaitForVBlank(); scanKeys(); pressed = keysDown(); 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); - consoleSelect(&bottomConsole); if(held & KEY_UP) { 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) { - consoleSelect(&bottomConsoleBG); - consoleClear(); - consoleSelect(&bottomConsole); - consoleClear(); - u8 cursorPosition = 0; u16 pressed = 0, held = 0; while(1) { - printf("\x1B[9;4H\x1B[47m-----------------------"); - printf("\x1B[10;5H%c Search for String %c", cursorPosition == 0 ? '>' : ' ', cursorPosition == 0 ? '<' : ' '); - printf("\x1B[11;5H%c Search for Data %c", cursorPosition == 1 ? '>' : ' ', cursorPosition == 1 ? '<' : ' '); - printf("\x1B[12;4H-----------------------"); + int y = (ENTRIES_PER_SCREEN - 3) / 2; + font->clear(false); + font->print(0, y, false, "--------------------", Alignment::center); + 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 { swiWaitForVBlank(); scanKeys(); pressed = keysDown(); 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); - consoleSelect(&bottomConsole); if(held & (KEY_UP | KEY_DOWN)) { cursorPosition ^= 1; @@ -98,10 +93,9 @@ u32 search(u32 offset, FILE *file) { printf("Search for:\n"); fgets(str, sizeof(str), stdin); keyboardHide(); - consoleClear(); - reinitConsoles(); - consoleSelect(&bottomConsole); + videoSetModeSub(MODE_5_2D); + bgShow(bgInitSub(2, BgType_Bmp8, BgSize_B8_256x256, 3, 0)); BG_PALETTE_SUB[0x1F] = 0x9CF7; BG_PALETTE_SUB[0x2F] = 0xB710; @@ -114,27 +108,27 @@ u32 search(u32 offset, FILE *file) { str[strLen] = 0; // Remove ending \n that fgets has } else { - consoleClear(); - cursorPosition = 0; while(1) { - printf("\x1B[9;6H\x1B[47m------------------"); - printf("\x1B[10;9HEnter value:"); - u8 pos = 15 - strLen; - for(size_t i = 0; i < strLen * 2; i++) { - printf("\x1B[12;%dH\x1B[%dm%X", pos + i, (i == cursorPosition ? 31 : ((i / 2 % 2) ? 33 : 32)), str[i / 2] >> (!(i % 2) * 4) & 0xF); - } - printf("\x1B[13;6H\x1B[47m------------------"); + int y = (ENTRIES_PER_SCREEN - 4) / 2; + font->clear(false); + font->print(0, y, false, "--------------------", Alignment::center); + font->print(0, y + 1, false, "Enter value:", Alignment::center); + 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); + font->print(0, y + 4, false, "--------------------", Alignment::center); + font->update(false); - consoleSelect(&topConsole); do { swiWaitForVBlank(); scanKeys(); pressed = keysDown(); 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); - consoleSelect(&bottomConsole); if(held & KEY_UP) { char val = str[cursorPosition / 2]; @@ -164,17 +158,20 @@ u32 search(u32 offset, FILE *file) { strLen--; if(cursorPosition > strLen * 2 - 1) cursorPosition -= 2; - consoleClear(); } } } } - consoleClear(); - printf("\x1B[9;6H\x1B[47m---------------------"); - printf("\x1B[10;12HSearching"); - printf("\x1B[14;8HPress B to cancel"); - printf("\x1B[15;6H---------------------"); + int y = (ENTRIES_PER_SCREEN - 7) / 2; + font->clear(false); + font->print(0, y, false, "--------------------", Alignment::center); + font->print(0, y + 1, false, "Searching", Alignment::center); + 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; fseek(file, 0, SEEK_END); @@ -187,7 +184,10 @@ u32 search(u32 offset, FILE *file) { 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) break; @@ -204,64 +204,53 @@ u32 search(u32 offset, FILE *file) { } while(len == 32 << 10); delete[] buf; - consoleClear(); - printf("\x1B[9;5H\x1B[47m---------------------"); - printf("\x1B[10;6HReached end of file"); - printf("\x1B[11;8Hwith no results"); - printf("\x1B[12;5H---------------------"); + y = (ENTRIES_PER_SCREEN - 3) / 2; + font->clear(false); + font->print(0, y, false, "--------------------", Alignment::center); + font->print(0, y + 1, false, "Reached end of file", Alignment::center); + font->print(0, y + 2, false, "with no results", Alignment::center); + font->print(0, y + 3, false, "--------------------", Alignment::center); + font->update(false); do { swiWaitForVBlank(); 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()); return offset; } 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"); if(!file) return; - consoleClear(); - fseek(file, 0, SEEK_END); u32 fileSize = ftell(file); 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); - u8 cursorPosition = 0, mode = 0; u16 pressed = 0, held = 0; - u32 offset = 0; + u32 offset = 0, cursorPosition = 0, mode = 0; char data[8 * maxLines]; fseek(file, offset, SEEK_SET); fread(data, 1, sizeof(data), file); while(1) { - consoleSelect(&bottomConsoleBG); - 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"); + font->clear(false); - 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"); - - printf("\x1B[0;0H\x1B[30m%04lX", offset >> 0x10); + font->printf(0, 0, false, Alignment::left, Palette::blackBlue, "%04lX", offset >> 0x10); if(mode < 2) { 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); } - for(int i = 0; i < maxLines; i++) { - printf("\x1B[%d;0H\x1B[37m%04lX", i + 1, (offset + i * 8) & 0xFFFF); + for(u32 i = 0; i < maxLines; i++) { + font->printf(0, i + 1, false, Alignment::left, Palette::blue, "%04lX", (offset + i * 8) & 0xFFFF); 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++) - 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}; for(int j = 0; j < 8; j++) { char c = data[i * 8 + j]; if(c < ' ' || c > 127) - line[j] = ' '; + line[j] = '.'; else 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) { - 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 { swiWaitForVBlank(); scanKeys(); pressed = keysDown(); 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); - consoleSelect(&bottomConsole); if(mode == 0) { 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); } else if(pressed & KEY_A) { mode = 1; + cursorPosition = std::min(cursorPosition, fileSize - offset - 1); } else if(pressed & KEY_B) { break; } else if(pressed & KEY_X) { offset = std::min(search(offset, file), maxSize); - consoleClear(); } else if(pressed & KEY_Y) { offset = std::min(jumpToOffset(offset), maxSize); - consoleClear(); } } else if(mode == 1) { if(held & KEY_UP) { if(cursorPosition >= 8) cursorPosition -= 8; - else if(offset > 8) + else if(offset >= 8) offset -= 8; } else if(held & KEY_DOWN) { - if(cursorPosition < 8 * 22) + if(cursorPosition < 8u * (maxLines - 1)) cursorPosition += 8; else if(offset < fileSize - 8 * maxLines && fileSize > 8 * maxLines) offset += 8; - cursorPosition = std::min(cursorPosition, (u8)(fileSize - offset - 1)); + cursorPosition = std::min(cursorPosition, fileSize - offset - 1); } else if(held & KEY_LEFT) { if(cursorPosition > 0) cursorPosition--; } else if(held & KEY_RIGHT) { - if(cursorPosition < 8 * maxLines - 1) - cursorPosition = std::min((u8)(cursorPosition + 1), (u8)(fileSize - offset - 1)); + if(cursorPosition < 8u * maxLines - 1) + cursorPosition = std::min(cursorPosition + 1, fileSize - offset - 1); } else if(pressed & KEY_A) { if(drive < 4) { 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) { mode = 0; } else if(pressed & KEY_X) { offset = std::min(search(offset, file), maxSize); - consoleClear(); } else if(pressed & KEY_Y) { offset = std::min(jumpToOffset(offset), maxSize); - consoleClear(); } } else if(mode == 2) { if(held & KEY_UP) { @@ -380,23 +364,9 @@ void hexEditor(const char *path, int drive) { mode = 1; fseek(file, offset + cursorPosition, SEEK_SET); 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); - - // 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(); } diff --git a/arm9/source/main.cpp b/arm9/source/main.cpp index bbe0384..2af687b 100644 --- a/arm9/source/main.cpp +++ b/arm9/source/main.cpp @@ -34,6 +34,7 @@ #include "driveOperations.h" #include "file_browse.h" #include "fileOperations.h" +#include "font.h" #include "tonccpy.h" #include "version.h" @@ -57,8 +58,6 @@ bool applaunch = false; static int bg3; -PrintConsole topConsoleBG, topConsole, bottomConsoleBG, bottomConsole; - //--------------------------------------------------------------------------------- void stop (void) { //--------------------------------------------------------------------------------- @@ -69,60 +68,6 @@ void stop (void) { 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) { //--------------------------------------------------------------------------------- @@ -141,34 +86,29 @@ int main(int argc, char **argv) { sprintf(titleName, "GodMode9i %s", VER_NUMBER); // initialize video mode - videoSetMode(MODE_4_2D); + videoSetMode(MODE_5_2D); + videoSetModeSub(MODE_5_2D); // initialize VRAM banks vramSetPrimaryBanks(VRAM_A_MAIN_BG, VRAM_B_MAIN_SPRITE, - VRAM_C_LCD, + VRAM_C_SUB_BG, VRAM_D_LCD); - - // Subscreen as a console - videoSetModeSub(MODE_0_2D); - vramSetBankH(VRAM_H_SUB_BG); 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 - 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); tonccpy(BG_PALETTE, gm9i_logoPal, gm9i_logoPalLen); - printf ("\x1b[1;1H"); - printf(titleName); - printf ("\x1b[2;1H"); - printf ("------------------------------"); - printf ("\x1b[3;1H"); - printf ("https:/github.com/"); - printf ("\x1b[4;10H"); - printf ("DS-Homebrew/GodMode9i"); + font->print(1, 1, false, titleName); + font->print(1, 2, false, "---------------------------------------"); + font->print(1, 3, false, "https:/github.com/DS-Homebrew/GodMode9i"); fifoWaitValue32(FIFO_USER_06); if (fifoGetValue32(FIFO_USER_03) == 0) arm7SCFGLocked = true; @@ -178,36 +118,27 @@ int main(int argc, char **argv) { if (isDSiMode()) { if (!arm7SCFGLocked) { - printf ("\x1b[20;1H"); - printf ("X Held - Disable NAND access"); - printf ("\x1b[21;1H"); - printf ("Y Held - Disable cart access"); - printf ("\x1b[22;4H"); - printf ("Do these if it crashes here"); + font->print(-2, -4, false, "X Held - Disable NAND access", Alignment::right); + font->print(-2, -3, false, "Y Held - Disable cart access", Alignment::right); + font->print(-2, -2, false, "Do these if it crashes here", Alignment::right); } else { - printf ("\x1b[21;1H"); - printf ("X Held - Disable NAND access"); - printf ("\x1b[22;5H"); - printf ("Do this if it crashes here"); + font->print(-2, -3, false, "X Held - Disable NAND access", Alignment::right); + font->print(-2, -2, false, "Do this if it crashes here", Alignment::right); } } // Display for 2 seconds + font->update(false); for (int i = 0; i < 60*2; i++) { swiWaitForVBlank(); } - if (isDSiMode()) { - printf ("\x1b[20;1H"); - printf (" "); - printf ("\x1b[21;1H"); - printf (" "); - printf ("\x1b[22;4H"); - printf (" "); // Clear "Y Held" text - } - printf ("\x1b[22;11H"); - printf ("mounting drive(s)..."); - //printf ("%X %X", *(u32*)0x2FFFD00, *(u32*)0x2FFFD04); + font->clear(false); + font->print(1, 1, false, titleName); + font->print(1, 2, false, "---------------------------------------"); + font->print(1, 3, false, "https:/github.com/DS-Homebrew/GodMode9i"); + font->print(-2, -2, false, "Mounting drive(s)...", Alignment::right); + font->update(false); sysSetCartOwner (BUS_OWNER_ARM9); // Allow arm9 to access GBA ROM @@ -243,11 +174,11 @@ int main(int argc, char **argv) { flashcardMountSkipped = false; } - // 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); + bgHide(bg3); + + // TODO: better + delete font; + font = new Font("/font.frf"); // Overwrite background white color 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*)0x6200040, 8*8); // Bottom screen - printBorderTop(); - keysSetRepeat(25,5); appInited = true; @@ -302,34 +231,34 @@ int main(int argc, char **argv) { } } fclose(argfile); - filename = argarray.at(0); + filename = argarray[0]; } else { argarray.push_back(strdup(filename.c_str())); } if (extension(filename, {"nds", "dsi", "ids", "app", "srl"})) { - char *name = argarray.at(0); + char *name = argarray[0]; strcpy (filePath + pathLen, name); - free(argarray.at(0)); - argarray.at(0) = filePath; - consoleClear(); - iprintf ("Running %s with %d parameters\n", argarray[0], argarray.size()); - int err = runNdsFile (argarray[0], argarray.size(), (const char **)&argarray[0]); - iprintf ("\x1b[31mStart failed. Error %i\n", err); + free(argarray[0]); + argarray[0] = filePath; + font->clear(false); + 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]); + font->printf(0, 1, false, Alignment::left, Palette::white, "Start failed. Error %i\n", err); } if (extension(filename, {"firm"})) { - char *name = argarray.at(0); + char *name = argarray[0]; strcpy (filePath + pathLen, name); - free(argarray.at(0)); - argarray.at(0) = filePath; + free(argarray[0]); + argarray[0] = filePath; fcopy(argarray[0], "sd:/bootonce.firm"); fifoSendValue32(FIFO_USER_02, 1); // Reboot into selected .firm payload swiWaitForVBlank(); } while(argarray.size() !=0 ) { - free(argarray.at(0)); + free(argarray[0]); argarray.erase(argarray.begin()); } diff --git a/arm9/source/ndsInfo.cpp b/arm9/source/ndsInfo.cpp index 35357dd..f5bd502 100644 --- a/arm9/source/ndsInfo.cpp +++ b/arm9/source/ndsInfo.cpp @@ -1,13 +1,12 @@ #include "ndsInfo.h" #include "date.h" +#include "font.h" #include "tonccpy.h" #include #include -extern PrintConsole bottomConsole, bottomConsoleBG, topConsole; - constexpr const char *langNames[8] { "Japanese", "English", @@ -19,8 +18,6 @@ constexpr const char *langNames[8] { "Korean" }; -extern void reinitConsoles(void); - void ndsInfo(const char *path) { FILE *file = fopen(path, "rb"); if(!file) @@ -35,7 +32,10 @@ void ndsInfo(const char *path) { u32 ofs; fseek(file, 0x68, SEEK_SET); 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; fread(&version, sizeof(u16), 1, file); @@ -51,10 +51,13 @@ void ndsInfo(const char *path) { fread(iconAnimation, 2, 0x40, file); fseek(file, ofs + 0x240, SEEK_SET); - } else { // DS + } else if((version & ~3) == 0) { // DS fseek(file, 0x20 - 2, SEEK_CUR); fread(iconBitmap, 1, 0x200, file); fread(iconPalette, 2, 0x10, file); + } else { + fclose(file); + return; } int languages = 5 + (version & 0x3); @@ -76,26 +79,23 @@ void ndsInfo(const char *path) { u16 pressed = 0, held = 0; int animationFrame = 0, frameDelay = 0, lang = 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 { swiWaitForVBlank(); scanKeys(); pressed = keysDown(); 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(frameDelay < (iconAnimation[animationFrame] & 0xFF) - 1) { frameDelay++; @@ -111,7 +111,6 @@ void ndsInfo(const char *path) { } } } while(!held); - consoleSelect(&bottomConsole); if(held & KEY_UP) { if(lang > 0) diff --git a/arm9/source/screenshot.cpp b/arm9/source/screenshot.cpp index 2e83c1b..48fdd01 100644 --- a/arm9/source/screenshot.cpp +++ b/arm9/source/screenshot.cpp @@ -1,117 +1,129 @@ -#include -#include -#include - -#include - #include "screenshot.h" + #include "bmp.h" +#include "driveOperations.h" +#include "file_browse.h" +#include "font.h" +#include "date.h" + +#include +#include +#include +#include void wait(); -void screenshot(u8* buffer) { +void write16(void *address, u16 value) { - u8 vram_cr_temp=VRAM_A_CR; - VRAM_A_CR=VRAM_A_LCD; + u8* first = (u8*)address; + u8* second = first + 1; - u8* vram_temp=(u8*)malloc(128*1024); - dmaCopy(VRAM_A, vram_temp, 128*1024); + *first = value & 0xff; + *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); - dmaCopy(VRAM_A, buffer, 256*192*2); - dmaCopy(vram_temp, VRAM_A, 128*1024); - - VRAM_A_CR=vram_cr_temp; - - free(vram_temp); + u8* temp = new u8[256 * 192 * 2 + sizeof(INFOHEADER) + sizeof(HEADER)]; -} + if(!temp) { + fclose(file); + return false; + } -void screenshot(char* filename) { - - //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)); + HEADER *header= (HEADER*)temp; + INFOHEADER *infoheader = (INFOHEADER*)(temp + sizeof(HEADER)); write16(&header->type, 0x4D42); - write32(&header->size, 256*192*3+sizeof(INFOHEADER)+sizeof(HEADER)); - write32(&header->offset, sizeof(INFOHEADER)+sizeof(HEADER)); - write16(&header->reserved1, 0); - write16(&header->reserved2, 0); + write32(&header->size, 256 * 192 * 2 + sizeof(INFOHEADER) + sizeof(HEADER)); + write32(&header->reserved1, 0); + write32(&header->reserved2, 0); + write32(&header->offset, sizeof(INFOHEADER) + sizeof(HEADER)); - write16(&infoheader->bits, 24); write32(&infoheader->size, sizeof(INFOHEADER)); - write32(&infoheader->compression, 0); write32(&infoheader->width, 256); write32(&infoheader->height, 192); write16(&infoheader->planes, 1); - write32(&infoheader->imagesize, 256*192*3); - write32(&infoheader->xresolution, 0); - write32(&infoheader->yresolution, 0); - write32(&infoheader->importantcolours, 0); + write16(&infoheader->bits, 16); + write32(&infoheader->compression, 3); + write32(&infoheader->imagesize, 256 * 192 * 2); + write32(&infoheader->xresolution, 2835); + write32(&infoheader->yresolution, 2835); 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++) - { - for(int x=0;x<256;x++) - { - u16 color=VRAM_D[256*191-y*256+x]; - - 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; + u16 *ptr = (u16*)(temp + sizeof(HEADER) + sizeof(INFOHEADER)); + for(int y = 0; y < 192; y++) { + for(int x = 0; x < 256; x++) { + u16 color = VRAM_D[256 * 191 - y * 256 + x]; + *(ptr++) = ((color >> 10) & 0x1F) | (color & (0x1F << 5)) << 1 | ((color & 0x1F) << 11); } } 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); - 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; +} \ No newline at end of file diff --git a/data/font_default.frf b/data/font_default.frf new file mode 100644 index 0000000000000000000000000000000000000000..93446533c7b92ebe602445a4e4526a9049468afb GIT binary patch literal 5796 zcmZu#dypJu5&y|;SzFfqF=cI8t97Mwmz{IesI8tl!m1N8zyKRqV1NYlOD8C$q9clwyejxs@_dT3C@ThdUS5u+0Z}oK>qkNLI#8)lk(S~Z zQPRq$l88c2+MGv+MoEQs3skR|v|10-yjrC+kNXwJtNU?3F=Yj5@8MYwjfyRnxGMkLezc5MiBq6WgXhe-h-=uLIn+N2Pl$XbG zKaSO+L$Mn->AXtJh+2v0u;;1e5yml1)zc(xciX8-X_ur{lIbj?mZGVwYK#4(eA z8BnQ6!K3?BcT{O6P5zAI&QvnhF#$a^Un-S}R?=dwk)kAzb=)w+<2(RN zT8XI36`Ow^bZU)(L!Q^AG;Q}RekyZPU};8D!{EH8>!PK)#+&L`1l=4JB zHL4XVY20JhsaKlod@BlbS9&(uKBB~Lr;S>b@l93VNYdH0YiBjt+|$qu0~+bdv|)%3 zTLZvJW>?Ry<~VVgsXSNQ)aZJCPZ>Z@7tBoX0lcd8y zR*Pp%Uk1qI#I71AIs>Fja=dz@KB!f$u9ml(bju>>;y0*JwdZP8?G;s9BEPIqjuW~^ zPdTnYoUK4hy^z!DD(Op6q14sD*xClj<`|4Cr0TO9rwNnutyGN|K#Sj?R_OtUY1Pt7 zfv8&`C$(BFdK0VPg;(p>^`R~eGpCqDF%f6w5Bi6^!31qEpCf(J{BTlshG`-$inXA9 zt?t*kL)3|yaU8KYR0!_B#dh4}IB`72$~sMj>xg3GN#Dt|vQn+*ZoAz!35zi8rIXh6 z>W#kN_c_UHldj?E2yUeervh%%B!gA9Bm-pFwhyE$=2lpv&~c7eATCa|O#$4y+il-h z7A($&z_9d{!;q4c$6i*nvpMLC#3rHHs6qzKH>8h*!BjEB;o2@E{=d{UsoB`lJI$XF z8>r$mLnOa=aYg+!aYO=?S} zn+MX()eQ>Crmjl#S5twa`MyC)sax>E%>(I%szImAPfvAiG6Qa2r#p~tP>Iq8H!n?b z9V%6$b0#w0MulX*W0fujWN&mUS1Dg49qV*WYPxeuYSfYa-14!$q>+-mWbnE)8@!&F z8N8S}Muol2n~SwaQJ#|*cYFO_H&$t$M{L3bmEn=*$Z)Y;t+3*u2kTa>bAGIw?awMi ze+7B7eQNaQX6I(tDPn?8yHl$xTD8}#`F&T@O!hptN~TxS(+-84*utn%hw8PO=k>a( z+h{f#)2`zA(~%d|qO3uzVA2rFEJ@lvdHhZ?Lo5RxdF$jEs0tDK$i1-T05?EIX2wn# zv{#=n6wc_wX#=$q!S8!{6i4KhNGmV4!&t3EVACL}Zi(1J;k(|Hqej@|wCoiVEk(5w zX}QNPR&h2!UM_7r@z+WOy5mQd*+n=>ZQ?Mb_4*S}W1;uy~2_^hy$@3A4CX zB1#J$CI@rWV#3aJhsWa90D(uoJjysPW@-~1=PB3Low9w#R%G5Yraia!uo5oei=iw94iyn4dZg`I|Vmv(onyc$&+8FP(CHwnX~^NASy zsAA*D?Kh@ElSU1PdNk+a+~=@~NH;2d=3=>~*TO7I8I5<;0yMYPKYI?Ev1qGNBj44K zVkWrT)oLZ0?CNEMQ6uG*(2t96UOtJ~L#jR3OAHh%&lB$Yj0V34H^`iAFegvH9n!ks zrIbAV4$~@;pX)7N{2!4|`Jl}W)(!)53^VUklZ6gn> zrduLrShvOna9oL0mwkgfvFbXc{uR|&BA$U7jjEpFYw-Zlm&QbE;GaXNu&dZLrB))Y zILmBaz5j3$vob*JW*RMBQHfYvaj#{YJo`q0>|SB_2#q>ptnUhMF10R^NB8S9`eZZ~ zHivDZcFL$}_cJuC8>1o4(c%TENsVlPhFb$8;F+-9&NSN@=Qv535qf4NsfOaBY7Q>p6i~p-s zYP{c9dZ}VOm*vcHlHX*?QmR6ea)&7FG3yF@ntu$9&PL)tU z5jWvxti>(36}RDb+<`lB7uMl!+=F{@AMVEkcn}ZakN6WF#v^zXkKu7VfhX}4p2m9o z8Gpeu_$&T~zvEeKz;k#WFJL2H#7lS?oA3%=#Xs;GUdJ1F6Pxi*{0sla9R7p1@HSSx z>&&4a4P7vF@z7;Mmk-SjT{*O7=;oodL$?l%`Az>6{}O+-KQ_8@^o-F}qi2quHG1~w zrK9g3`@q--$38Unk+F2_ps}g3#baB|^XB>UM&}{eDi{vDpcd4Fk)RRG4<>?U&<=JB zb`SOlvS80(uVC+>6YLZ08|)WM2KxsG1P2D);Gp2(;E-S{SR5<~4h@zDhXu=m<-v4t zYOo?WEjT?`8JrQU3eF794ORzN1lI)D1#5%5f=7Z!gRRH68E=lq7RfHMD^D=H$?md;WU{C1C3{Op_K|&MKbe&M zlEQiRHES4p5s4SJkWSK0NX*pbukRzohN6FE0jLgWf@)bFb_qOBZ1UXT1 zIY~~IQ{+_ns(ekpE-U04@=f`coF?Cv@5p!MboriqUw$Af`jeE9EM=T7Do7^sU$enVRtdqOt9=TWUll$cXc~BmbKgyrvVR=Lz zmB-|9c|x9)r{rl_FMpQ5$TRX+`J4P*o|O&qoIEct$VPclUXqt(le{9Y%0J{ad0pO+ VH)XT@Q~o9YmO1&4yd`hTe*rqV4toFq literal 0 HcmV?d00001