diff --git a/.gitignore b/.gitignore index 349553ac..48679ecb 100644 --- a/.gitignore +++ b/.gitignore @@ -15,8 +15,6 @@ *.d *.opk *.klf -*.raw -*.wav *.iso *.gdi *.cdi diff --git a/Makefile b/Makefile index 63cb0af2..a7ab126a 100644 --- a/Makefile +++ b/Makefile @@ -11,10 +11,14 @@ # TARGET = DS -TARGET_NAME = DreamShell_v4.0.0_Release +TARGET_NAME = DreamShell_v4.0.1_Release TARGET_BIN = $(TARGET)_CORE.BIN TARGET_BIN_CD = 1$(TARGET_BIN) -TRAGET_VERSION = -DVER_MAJOR=4 -DVER_MINOR=0 -DVER_MICRO=0 -DVER_BUILD=0x30 +# Build types: 0x0N - Alpha, 0x1N - Beta, 0x2N - RC, 0x3N - Release +TRAGET_VERSION = -DVER_MAJOR=4 \ + -DVER_MINOR=0 \ + -DVER_MICRO=1 \ + -DVER_BUILD=0x30 # TARGET_DEBUG = 1 # or 2 for GDB # TARGET_EMU = 1 # TARGET_PROF = 1 @@ -30,9 +34,12 @@ SRC_DIR = $(DS_BASE)/src LIB_DIR = $(DS_BASE)/lib KOS_LDFLAGS += -L$(LIB_DIR) -KOS_CFLAGS += -I$(INC_DIR) -I$(INC_DIR)/SDL -I$(INC_DIR)/fatfs \ +KOS_CFLAGS += -I$(INC_DIR) -I$(INC_DIR)/img -I$(INC_DIR)/SDL -I$(INC_DIR)/fatfs \ + -I$(INC_DIR)/tsunami \ -DHAVE_SDLIMAGE $(TRAGET_VERSION) +KOS_CPPFLAGS += -Wno-template-id-cdtor + ifdef TARGET_DEBUG KOS_CFLAGS += -g -DDS_DEBUG=$(TARGET_DEBUG) endif @@ -46,21 +53,26 @@ SDL_IMAGE_VER = 1.2.12 SDL_TTF_VER = 2.0.11 SDL_RTF_VER = 0.1.1 LUA_VER = 5.1.4-2 +TSUNAMI_VER = 2.0.0 +PARALLAX_VER = 2.0.0 +FREETYPE_VER = 2.4.4 -EXTRA_LIBS = -lcfg -lmxml -lparallax +EXTRA_LIBS = -lcfg -lmxml SDL_LIBS = -lSDL_$(SDL_VER) \ -lSDL_image_$(SDL_IMAGE_VER) \ -lSDL_ttf_$(SDL_TTF_VER) \ -lSDL_rtf_$(SDL_RTF_VER) \ -lSDL_gfx_$(SDL_GFX_VER) \ - -lfreetype - + -lfreetype_$(FREETYPE_VER) + IMAGE_LIBS = -lkmg -ljpeg -lpng -lz LUA_LIBS = -llua_$(LUA_VER) KLIBS = -lkosext2fs -lkosutils -lstdc++ -lm +GRAPHICS_LIBS = -ltsunami_$(TSUNAMI_VER) \ + -lparallax_$(PARALLAX_VER) -CORE_LIBS = $(EXTRA_LIBS) $(SDL_LIBS) $(IMAGE_LIBS) $(LUA_LIBS) $(KLIBS) +CORE_LIBS = $(EXTRA_LIBS) $(SDL_LIBS) $(GRAPHICS_LIBS) $(IMAGE_LIBS) $(LUA_LIBS) $(KLIBS) SDL_GUI = $(LIB_DIR)/SDL_gui SDL_CONSOLE = $(LIB_DIR)/SDL_Console/src @@ -99,7 +111,10 @@ OBJS = $(SRC_DIR)/main.o $(SRC_DIR)/video.o $(SRC_DIR)/console.o \ $(SRC_DIR)/module.o $(SRC_DIR)/events.o $(SRC_DIR)/fs/fs.o \ $(SRC_DIR)/lua/lua.o $(SRC_DIR)/lua/lua_ds.o $(SRC_DIR)/lua/packlib.o \ $(SRC_DIR)/app/app.o $(SRC_DIR)/app/load.o $(SRC_DIR)/list.o \ - $(SRC_DIR)/img/pvr.o $(SRC_DIR)/cmd_elf.o $(SRC_DIR)/vmu/vmu.o \ + $(SRC_DIR)/img/SegaPVRImage.o $(SRC_DIR)/img/stb_image.o \ + $(SRC_DIR)/img/load.o $(SRC_DIR)/img/utils.o \ + $(SRC_DIR)/img/decode.o $(SRC_DIR)/img/convert.o $(SRC_DIR)/img/copy.o \ + $(SRC_DIR)/cmd_elf.o $(SRC_DIR)/vmu/vmu.o \ $(SRC_DIR)/irq/exceptions.o $(SRC_DIR)/irq/setjmp.o \ $(SRC_DIR)/settings.o $(SRC_DIR)/sfx.o \ $(DRIVERS_OBJ) $(GUI_OBJS) $(CONSOLE_OBJ) \ @@ -165,9 +180,9 @@ $(LIB_DIR)/libSDL_$(SDL_VER).a: build: $(TARGET) @echo Building modules, commands, applications and firmwares... - cd $(DS_BASE)/modules && make && make install - cd $(DS_BASE)/commands && make && make install - cd $(DS_BASE)/applications && make && make install + cd $(DS_BASE)/modules && make + cd $(DS_BASE)/commands && make + cd $(DS_BASE)/applications && make cd $(DS_BASE)/firmware/isoldr && make && make install cd $(DS_BASE)/firmware/bootloader && make && make install # cd $(DS_BASE)/firmware/aica && make && make install @@ -176,6 +191,7 @@ clean-all: clean @-rm -rf $(DS_BUILD)/.* 2> /dev/null @-rm -rf $(DS_BASE)/release @-rm -rf $(DS_BASE)/build + @-rm -rf $(DS_SDK)/lib/*.a cd $(DS_BASE)/lib && make clean cd $(DS_BASE)/modules && make clean cd $(DS_BASE)/commands && make clean @@ -198,6 +214,32 @@ release: build cdi @echo @echo "\033[42m Complete $(DS_BASE)/release \033[0m" +update: + @echo Fetching DreamShell from GitHub... + @git fetch && git checkout origin/master + @echo Fetching KallistiOS from GitHub... + @cd $(KOS_BASE) && git fetch && git checkout `cat $(DS_BASE)/sdk/doc/KallistiOS.txt` + @echo Fetching kos-ports from GitHub... + @cd $(KOS_BASE)/../kos-ports && git fetch && git checkout origin/master + +update-build: + @echo Fetching DreamShell from GitHub... + @git fetch && git checkout origin/master + @echo Fetching and build KallistiOS from GitHub... + @cd $(KOS_BASE) && git fetch && git checkout `cat $(DS_BASE)/sdk/doc/KallistiOS.txt` && make clean && make + @echo Fetching and build kos-ports from GitHub... + @mv include include_ + @cd $(KOS_BASE)/../kos-ports && git fetch && git checkout origin/master && ./utils/build-all.sh + @mv include_ include + @echo Building DreamShell... + @make clean-all && make build + +toolchain: + @cp $(DS_SDK)/toolchain/environ.sh $(KOS_BASE)/environ.sh + @cp $(DS_SDK)/toolchain/patches/*.diff $(KOS_BASE)/utils/dc-chain/patches + @source $(KOS_BASE)/environ.sh + @cd $(KOS_BASE)/utils/dc-chain && cp Makefile.default.cfg Makefile.cfg && make + $(TARGET): libs $(TARGET_BIN) make-build $(TARGET).elf: $(OBJS) @@ -273,7 +315,7 @@ lxdgdb: $(TARGET).cdi $(KOS_CC_BASE)/bin/$(KOS_CC_PREFIX)-gdb $(TARGET)-DBG.elf --eval-command "target remote localhost:2000" flycast: $(TARGET).cdi - Flycast -config config:Debug.SerialConsoleEnabled=yes + Flycast -config config:Debug.SerialConsoleEnabled=yes ./$(TARGET).cdi gprof: @sh-elf-gprof $(TARGET)-DBG.elf $(DS_BUILD)/kernel_gmon.out > gprof.out diff --git a/README.md b/README.md index d6c8f78d..2153a8e0 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,16 @@ There are also large subproject is the ISO Loader, which contains emulation of B ### Setup environment ##### Packages ```console -sudo apt-get install -y genisoimage squashfs-tools -sudo apt-get install -y libpng-dev libjpeg-dev liblzo2-dev liblua5.2-dev -cd /tmp && git clone https://github.com/LuaDist/tolua.git && cd tolua -mkdir build && cd ./build +sudo apt update +sudo apt install -y gawk patch bzip2 tar make cmake pkg-config +sudo apt install -y gettext wget bison flex sed meson ninja-build +sudo apt install -y build-essential diffutils curl python3 rake +sudo apt install -y genisoimage squashfs-tools texinfo git +sudo apt install -y libgmp-dev libmpfr-dev libmpc-dev libelf-dev libisofs-dev +sudo apt install -y libpng-dev libjpeg-dev liblzo2-dev liblua5.2-dev +cd /tmp +git clone https://github.com/LuaDist/tolua.git +cd /tmp/tolua && mkdir build && cd ./build cmake ../ && make && sudo make install ``` ##### Code @@ -24,7 +30,8 @@ sudo mkdir -p /usr/local/dc/kos sudo chown -R $(id -u):$(id -g) /usr/local/dc cd /usr/local/dc/kos git clone https://github.com/KallistiOS/kos-ports.git -git clone https://github.com/KallistiOS/KallistiOS.git kos && cd kos +git clone https://github.com/KallistiOS/KallistiOS.git kos +cd /usr/local/dc/kos/kos git clone https://github.com/DC-SWAT/DreamShell.git ds git checkout `cat ds/sdk/doc/KallistiOS.txt` cp ds/sdk/toolchain/environ.sh environ.sh @@ -35,20 +42,16 @@ cp ds/sdk/toolchain/patches/*.diff utils/dc-chain/patches sudo mkdir -p /opt/toolchains/dc sudo chown -R $(id -u):$(id -g) /opt/toolchains/dc cd /usr/local/dc/kos/kos/utils/dc-chain -cp config/config.mk.stable.sample config.mk -make && cd ../../ +cp Makefile.default.cfg Makefile.cfg +make ``` ##### SDK ```console cd /usr/local/dc/kos/kos source ./environ.sh -make && cd ../kos-ports && ./utils/build-all.sh && cd ./lib -mv libfreetype.a libfreetype-kos.a -mv liboggvorbisplay.a liboggvorbisplay-kos.a -mv libogg.a libogg-kos.a -mv libvorbis.a libvorbis-kos.a -mv libwav.a libwav-kos.a -cd ../../kos/ds/sdk/bin/src && make && make install && cd ../../../ +make && cd ../kos-ports && ./utils/build-all.sh +cd ${KOS_BASE}/ds/sdk/bin/src && make && make install +cd ${KOS_BASE}/ds ln -nsf `which tolua` sdk/bin/tolua ln -nsf `which mkisofs` sdk/bin/mkisofs ln -nsf `which mksquashfs` sdk/bin/mksquashfs @@ -61,28 +64,44 @@ cd /usr/local/dc/kos/kos/ds && source ../environ.sh ``` ### Build code -##### Core and libraries +##### Full build ```console -make +make build ``` -##### Modules, applications and commands +##### Full clean ```console -cd ./modules && make && make install && cd ../ -cd ./commands && make && make install && cd ../ -cd ./applications && make && make install && cd ../ +make clean-all ``` -##### Firmwares +##### Make release package ```console -cd ./firmware/bootloader && make && make release && cd ../../ -cd ./firmware/isoldr && make && make install && cd ../../../ +make release ``` -##### Full build (modules, apps etc) +##### Update code from GitHub ```console -make build +make update ``` -##### Make release package +##### Update code from GitHub and re-build ```console -make release +make update-build +``` +##### Re-build toochain (if updated) +```console +make toolchain +``` +##### Core and libraries only +```console +make +``` +##### Modules, applications and commands only +```console +cd ${KOS_BASE}/ds/modules && make +cd ${KOS_BASE}/ds/commands && make +cd ${KOS_BASE}/ds/applications && make +``` +##### Firmwares only +```console +cd ${KOS_BASE}/ds/firmware/bootloader && make && make release +cd ${KOS_BASE}/ds/firmware/isoldr && make && make install ``` ### Running @@ -90,6 +109,7 @@ make release - dc-tool-serial: `make run-serial` - lxdream emulator: `make lxdream` - nulldc emulator: `make nulldc` +- flycast emulator: `make flycast` - make cdi image: `make cdi` ## Links diff --git a/applications/Makefile b/applications/Makefile index 6f72c730..c38792d0 100644 --- a/applications/Makefile +++ b/applications/Makefile @@ -6,13 +6,12 @@ _SUBDIRS = main filemanager region_changer iso_loader \ bios_flasher gd_ripper speedtest vmu_manager \ - settings gdplay dreameye network - + settings gdplay dreameye network games_menu all: $(patsubst %, _dir_%, $(_SUBDIRS)) $(patsubst %, _dir_%, $(_SUBDIRS)): - $(MAKE) -C $(patsubst _dir_%, %, $@) + $(MAKE) -C $(patsubst _dir_%, %, $@) all install clean: $(patsubst %, _clean_dir_%, $(_SUBDIRS)) diff --git a/applications/bios_flasher/app.xml b/applications/bios_flasher/app.xml index 6e227fef..7812d1ed 100644 --- a/applications/bios_flasher/app.xml +++ b/applications/bios_flasher/app.xml @@ -18,7 +18,7 @@ - + @@ -31,14 +31,14 @@ - + - + diff --git a/applications/filemanager/lua/main.lua b/applications/filemanager/lua/main.lua index 8643dbde..fdf63e80 100644 --- a/applications/filemanager/lua/main.lua +++ b/applications/filemanager/lua/main.lua @@ -25,7 +25,7 @@ FileManager = { audio = { ext = { ".mp1", ".mp2", ".mp3", ".ogg", ".adx", ".s3m", ".wav", - ".raw" + ".raw", ".pcm", "dpcm" } }, @@ -570,8 +570,8 @@ function FileManager:openFile() local mod = string.sub(ext, -3); - if mod == "raw" then - mod = "wav" + if mod == "raw" or mod == "wav" or mod == "pcm" then + mod = "wave" elseif mod == "ogg" then mod = "oggvorbis" elseif mod == "mp1" or mod == "mp2" or mod == "mp3" then diff --git a/applications/games_menu/Makefile b/applications/games_menu/Makefile new file mode 100644 index 00000000..fe42b88c --- /dev/null +++ b/applications/games_menu/Makefile @@ -0,0 +1,39 @@ +# +# GAMES Loader App for DreamShell +# Copyright (C) 2024 MANIAC VERA +# http://www.dc-swat.ru +# + +SUBDIRS = modules + +include ../../sdk/Makefile.cfg + +APP_NAME = games_menu +APP_DIR = $(DS_BUILD)/apps/$(APP_NAME) +DEPS = modules/app_$(APP_NAME).klf + +KOS_CFLAGS += -I./ + +all: install + +$(DEPS): modules/module.c + cd modules && make + +clean: + cd modules && make clean && cd ../ + +install: app.xml $(DEPS) + -mkdir -p $(APP_DIR) + -mkdir -p $(APP_DIR)/modules + -mkdir -p $(APP_DIR)/images + -mkdir -p $(APP_DIR)/covers + -mkdir -p $(APP_DIR)/fonts + -mkdir -p $(APP_DIR)/music + -mkdir -p $(APP_DIR)/presets + -mkdir -p $(APP_DIR)/resources + cp modules/app_$(APP_NAME).klf $(APP_DIR)/modules/app_$(APP_NAME).klf + cp app.xml $(APP_DIR)/app.xml + cp -R images $(APP_DIR) + cp -R fonts $(APP_DIR) + cp -R resources $(APP_DIR) + \ No newline at end of file diff --git a/applications/games_menu/app.xml b/applications/games_menu/app.xml new file mode 100644 index 00000000..5c873be8 --- /dev/null +++ b/applications/games_menu/app.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + diff --git a/applications/games_menu/fonts/arial.txf b/applications/games_menu/fonts/arial.txf new file mode 100644 index 00000000..756018e7 Binary files /dev/null and b/applications/games_menu/fonts/arial.txf differ diff --git a/applications/games_menu/fonts/axaxax.txf b/applications/games_menu/fonts/axaxax.txf new file mode 100644 index 00000000..20496e20 Binary files /dev/null and b/applications/games_menu/fonts/axaxax.txf differ diff --git a/applications/games_menu/fonts/default.txf b/applications/games_menu/fonts/default.txf new file mode 100644 index 00000000..5affe94a Binary files /dev/null and b/applications/games_menu/fonts/default.txf differ diff --git a/applications/games_menu/fonts/helvetica.txf b/applications/games_menu/fonts/helvetica.txf new file mode 100644 index 00000000..61ad84ff Binary files /dev/null and b/applications/games_menu/fonts/helvetica.txf differ diff --git a/applications/games_menu/fonts/m23.txf b/applications/games_menu/fonts/m23.txf new file mode 100644 index 00000000..0e5ddb50 Binary files /dev/null and b/applications/games_menu/fonts/m23.txf differ diff --git a/applications/games_menu/fonts/message.txf b/applications/games_menu/fonts/message.txf new file mode 100644 index 00000000..5affe94a Binary files /dev/null and b/applications/games_menu/fonts/message.txf differ diff --git a/applications/games_menu/fonts/msjh.txf b/applications/games_menu/fonts/msjh.txf new file mode 100644 index 00000000..32312f5b Binary files /dev/null and b/applications/games_menu/fonts/msjh.txf differ diff --git a/applications/games_menu/fonts/no_continue.txf b/applications/games_menu/fonts/no_continue.txf new file mode 100644 index 00000000..ab113109 Binary files /dev/null and b/applications/games_menu/fonts/no_continue.txf differ diff --git a/applications/games_menu/fonts/serpent_font.txf b/applications/games_menu/fonts/serpent_font.txf new file mode 100644 index 00000000..9f90485a Binary files /dev/null and b/applications/games_menu/fonts/serpent_font.txf differ diff --git a/applications/games_menu/fonts/typewriter.txf b/applications/games_menu/fonts/typewriter.txf new file mode 100644 index 00000000..a3abc092 Binary files /dev/null and b/applications/games_menu/fonts/typewriter.txf differ diff --git a/applications/games_menu/images/btn_a.png b/applications/games_menu/images/btn_a.png new file mode 100644 index 00000000..abad0f08 Binary files /dev/null and b/applications/games_menu/images/btn_a.png differ diff --git a/applications/games_menu/images/btn_b.png b/applications/games_menu/images/btn_b.png new file mode 100644 index 00000000..cfcc2db3 Binary files /dev/null and b/applications/games_menu/images/btn_b.png differ diff --git a/applications/games_menu/images/btn_lt.png b/applications/games_menu/images/btn_lt.png new file mode 100644 index 00000000..631ac3ba Binary files /dev/null and b/applications/games_menu/images/btn_lt.png differ diff --git a/applications/games_menu/images/btn_rt.png b/applications/games_menu/images/btn_rt.png new file mode 100644 index 00000000..2f3fdcfe Binary files /dev/null and b/applications/games_menu/images/btn_rt.png differ diff --git a/applications/games_menu/images/btn_start.png b/applications/games_menu/images/btn_start.png new file mode 100644 index 00000000..898a45b2 Binary files /dev/null and b/applications/games_menu/images/btn_start.png differ diff --git a/applications/games_menu/images/btn_x.png b/applications/games_menu/images/btn_x.png new file mode 100644 index 00000000..e930f9c4 Binary files /dev/null and b/applications/games_menu/images/btn_x.png differ diff --git a/applications/games_menu/images/btn_y.png b/applications/games_menu/images/btn_y.png new file mode 100644 index 00000000..4f1cd348 Binary files /dev/null and b/applications/games_menu/images/btn_y.png differ diff --git a/applications/games_menu/images/cover.png b/applications/games_menu/images/cover.png new file mode 100644 index 00000000..bc6e4fa6 Binary files /dev/null and b/applications/games_menu/images/cover.png differ diff --git a/applications/games_menu/images/gd.jpg b/applications/games_menu/images/gd.jpg new file mode 100644 index 00000000..f4b8be07 Binary files /dev/null and b/applications/games_menu/images/gd.jpg differ diff --git a/applications/games_menu/images/gd.png b/applications/games_menu/images/gd.png new file mode 100644 index 00000000..f5d1d026 Binary files /dev/null and b/applications/games_menu/images/gd.png differ diff --git a/applications/games_menu/images/icon.png b/applications/games_menu/images/icon.png new file mode 100644 index 00000000..59eee9b9 Binary files /dev/null and b/applications/games_menu/images/icon.png differ diff --git a/applications/games_menu/images/label_no_cover.png b/applications/games_menu/images/label_no_cover.png new file mode 100644 index 00000000..8625612d Binary files /dev/null and b/applications/games_menu/images/label_no_cover.png differ diff --git a/applications/games_menu/images/no_cover.png b/applications/games_menu/images/no_cover.png new file mode 100644 index 00000000..7da0b943 Binary files /dev/null and b/applications/games_menu/images/no_cover.png differ diff --git a/applications/games_menu/images/save.png b/applications/games_menu/images/save.png new file mode 100644 index 00000000..c4100528 Binary files /dev/null and b/applications/games_menu/images/save.png differ diff --git a/applications/games_menu/modules/Makefile b/applications/games_menu/modules/Makefile new file mode 100644 index 00000000..50f6bf5f --- /dev/null +++ b/applications/games_menu/modules/Makefile @@ -0,0 +1,27 @@ +# +# GAMES app module for DreamShell +# Copyright (C) 2024 Maniac Vera +# http://www.dc-swat.ru +# + +APP_NAME = games_menu +TARGET_NAME = app_$(APP_NAME) +OBJS = module.o utils.o menu.o system_menu.o preset.o +DBG_LIBS = -lds -lisofs -lisoldr -lwave +EXPORTS_FILE = exports.txt + +VER_MAJOR = 0 +VER_MINOR = 1 +VER_MICRO = 0 + +all: rm-elf + +include ../../../sdk/Makefile.loadable + +rm-elf: + -rm -f $(TARGET) + +install: $(TARGET) + -rm $(DS_BUILD)/apps/$(APP_NAME)/modules/$(TARGET) + cp $(TARGET) $(DS_BUILD)/apps/$(APP_NAME)/modules/$(TARGET) + diff --git a/applications/games_menu/modules/app_definition.h b/applications/games_menu/modules/app_definition.h new file mode 100644 index 00000000..8b353a01 --- /dev/null +++ b/applications/games_menu/modules/app_definition.h @@ -0,0 +1,246 @@ +/* + Tsunami for KallistiOS ##version## + + menudefinition.h + + Copyright (C) 2024 Maniac Vera + +*/ + +#ifndef __APP_DEFINITION_H +#define __APP_DEFINITION_H + +#define MAX_SIZE_ITEMS 12 +#define MAX_BUTTONS 7 +#define MAX_SCAN_COUNT 2 +#define MAX_MENU 3 +#define FIRMWARE_SIZE 8 +#define MAX_VMU 999 +#define DEFAULT_VMU_NUMBER "001" +#define SCREENSHOT_HOTKEY (CONT_START | CONT_A | CONT_B) +#define ALT_BOOT_FILE "2ND_READ.BIN" + +#include + +enum PresetControlEnum +{ + SAVE_CONTROL_ID = 1, + DMA_CONTROL_ID, + ASYNC_CONTROL_ID, + BYPASS_CONTROL_ID, + IRQ_CONTROL_ID, + OS_CONTROL_ID, + LOADER_CONTROL_ID, + BOOT_CONTROL_ID, + FASTBOOT_CONTROL_ID, + LOWLEVEL_CONTROL_ID, + MEMORY_CONTROL_ID, + CUSTOM_MEMORY_CONTROL_ID, + HEAP_CONTROL_ID, + + CDDA_CONTROL_ID, + CDDA_SOURCE_CONTROL_ID, + CDDA_DESTINATION_CONTROL_ID, + CDDA_POSITION_CONTROL_ID, + CDDA_CHANNEL_CONTROL_ID, + + PATCH_ADDRESS1_CONTROL_ID, + PATCH_VALUE1_CONTROL_ID, + PATCH_ADDRESS2_CONTROL_ID, + PATCH_VALUE2_CONTROL_ID, + + ALTERBOOT_CONTROL_ID, + SCREENSHOT_CONTROL_ID, + BUTTON_START_CONTROL_ID, + BUTTON_X_CONTROL_ID, + BUTTON_Y_CONTROL_ID, + BUTTON_Z_CONTROL_ID, + BUTTON_LT_CONTROL_ID, + BUTTON_A_CONTROL_ID, + BUTTON_B_CONTROL_ID, + BUTTON_C_CONTROL_ID, + BUTTON_RT_CONTROL_ID, + + VMU_CONTROL_ID, + VMUSELECTOR_CONTROL_ID, + + SHORTCUT_SIZE_CONTROL_ID, + SHORTCUT_ROTATE_CONTROL_ID, + SHORTCUT_NAME_CONTROL_ID, + SHORTCUT_DONTSHOWNAME_CONTROL_ID, + SHORTCUT_CREATE_CONTROL_ID, + SHORTCUT_COVER_CONTROL_ID, +}; + +enum StateAppEnum +{ + SA_NONE = 0, + SA_GAMES_MENU, + SA_SYSTEM_MENU, + SA_PRESET_MENU, + SA_SCAN_COVER, + SA_OPTIMIZE_COVER, + SA_CONTROL +}; + +enum DirectionMenuDisplacementEnum +{ + DMD_NONE = 0, + DMD_LEFT = 1, + DMD_RIGHT = 2, + DMD_UP = 3, + DMD_DOWN = 4 +}; + +enum SearchCoverEnum +{ + SC_WITHOUT_SEARCHING = -1, + SC_NOT_EXIST = 0, + SC_EXISTS = 1, + SC_DEFAULT = 2 +}; + +enum CoverStatusEnum +{ + CSE_EXISTS = 1, + CSE_PROCESSING = 2, + CSE_COMPLETED = 3 +}; + +enum CheckCDDAGameEnum +{ + CCGE_NOT_CHECKED = -1, + CCGE_CDDA = 1, + CCGE_CDDA_BIG_SIZE = 2, + CCGE_NOT_CDDA = 3, + CCGE_CANDIDATE = 4 +}; + +typedef struct FirmwareStructure +{ + char file[FIRMWARE_SIZE+1]; +} FirmwareStruct; + +typedef struct PresetStructure +{ + int game_index; + int use_dma; + int emu_async; + int use_irq; + int alt_read; + int fastboot; + int low; + int scr_hotkey; + int boot_mode; + int bin_type; + int alt_boot; + int screenshot; + int cdda; + int rotate_image; + int dont_show_name; + int icon_size; + int vmu_mode; + + uint32 emu_vmu; + uint32 emu_cdda; + uint32 heap; + uint32 pa[2]; + uint32 pv[2]; + + char title[32]; + char device[FIRMWARE_SIZE+1]; + char memory[12]; + char custom_memory[12]; + char heap_memory[12]; + char bin_file[12]; + char patch_a[2][10]; + char patch_v[2][10]; + char vmu_file[32]; + char shortcut_name[33]; + char preset_file_name[100]; +} PresetStruct; + +typedef struct SectorDataStructure +{ + int image_type; + int sector_size; + uint8 md5[16]; + uint8 boot_sector[2048]; + uint32 *addr; +} SectorDataStruct; + +typedef struct MenuOptionStructure +{ + int max_page_size; + int max_columns; + int size_items_column; + int init_position_x; + int init_position_y; + int padding_x; + int padding_y; + float image_size; +} MenuOptionStruct; + +typedef struct GameItemStructure +{ + uint8 device; + char *game; + char *folder; + char *folder_name; + bool is_folder_name; + int16 exists_cover[MAX_MENU]; + uint64 cover_type; + bool check_pvr; + bool is_pvr_cover; + bool check_optimized; + bool is_gdi_optimized; + int16 is_cdda; + +} GameItemStruct; + +typedef struct CoverStructure +{ + char *cover; + uint8 device; + uint8 menu_type; + uint8 image_type[MAX_MENU]; + +} CoverStruct; + +typedef struct ImageDimensionStructure +{ + unsigned int width; + unsigned int height; +} ImageDimensionStruct; + +#pragma pack(push, 4) +typedef struct CoverScannedStructure +{ + char last_game_scanned[NAME_MAX]; + uint32 last_game_index; + uint32 last_game_status; + uint32 games_count; + uint32 scan_count; +} CoverScannedStruct; +#pragma pack(pop) + +#pragma pack(push, 4) +typedef struct AppConfigStructure +{ + int initial_view; + int save_preset; + int cover_background; + int change_page_with_pad; +} AppConfigStruct; +#pragma pack(pop) + +typedef struct isoldr_conf_structure +{ + const char *name; + int conf_type; + void *pointer; +} isoldr_conf; + +typedef isoldr_conf GenericConfigStruct; + +#endif //__APP_DEFINITION_H diff --git a/applications/games_menu/modules/app_menu.h b/applications/games_menu/modules/app_menu.h new file mode 100644 index 00000000..72050f78 --- /dev/null +++ b/applications/games_menu/modules/app_menu.h @@ -0,0 +1,128 @@ +#ifndef __APP_MENU_H +#define __APP_MENU_H + +#include +#include +#include "app_utils.h" +#include "app_definition.h" +#include "tsunami/tsudefinition.h" + +#define MOTOSHORT(p) ((*(p)) << 8) + *(p + 1) +#define IN_CACHE_GAMES +#define MAX_SIZE_CDDA 0 +#define MAX_TRIGGER_VALUE 255 +#define PLANE_TEXT_MENU_FOLDER "" +#define IMAGE_TEXT_64_5X2_MENU_FOLDER "/64" +#define IMAGE_128_4X3_FOLDER "/128" +#define IMAGE_TYPE_SUPPORTED (IT_PNG | IT_JPG | IT_PVR) + +typedef void SendMessageCallBack(const char *fmt, const char *message); +typedef void PostPVRCoverCallBack(bool new_cover); +typedef void PostOptimizerCoverCallBack(); + +struct menu_structure +{ + bool save_preset; + bool cover_background; + bool change_page_with_pad; + bool ide; + bool sd; + bool cd; + bool rescan_covers; + bool convert_pvr_to_png; + volatile bool finish_menu; + volatile bool finished_menu; + volatile bool cdda_game_changed; + volatile bool stop_load_pvr_cover; + volatile bool stop_optimize_game_cover; + int default_cover_type; + int state_app; + int menu_type; + int games_array_count; + int covers_array_count; + int firmware_array_count; + int current_dev; + int vmu_mode; + + char default_dir[20]; + char games_path[NAME_MAX]; + char covers_path[50]; + + char default_dir_sd[20]; + char games_path_sd[NAME_MAX]; + char covers_path_sd[50]; + + PresetStruct *preset; + + AppConfigStruct app_config; + CoverScannedStruct cover_scanned_app; + MenuOptionStruct menu_option; + GameItemStruct *games_array; + CoverStruct *covers_array; + FirmwareStruct *firmware_array; + + SendMessageCallBack *send_message_scan; + SendMessageCallBack *send_message_optimizer; + PostPVRCoverCallBack *post_pvr_cover; + PostOptimizerCoverCallBack *post_optimizer_cover; + + kthread_t *play_cdda_thread; + kthread_t *load_pvr_cover_thread; + kthread_t *optimize_game_cover_thread; +}; + +const char* GetDefaultDir(uint8 device); +const char* GetGamesPath(uint8 device); +const char* GetCoversPath(uint8 device); +void CreateMenuData(SendMessageCallBack *send_message_scan, SendMessageCallBack *send_message_optimizer + , PostPVRCoverCallBack *post_pvr_cover, PostOptimizerCoverCallBack *post_optimizer_cover); +void DestroyMenuData(); +bool CheckGdiOptimized(); +const char *GetFullGamePathByIndex(int game_index); +void StopCDDA(); +bool CheckCDDA(int game_index); +void* PlayCDDAThread(void *params); +void PlayCDDA(int game_index); +ImageDimensionStruct *GetImageDimension(const char *image_file); +bool IsValidImage(const char *image_file); +bool ExistsCoverFile(const char *cover_file, int menu_type); +bool CheckCoverImageType(int game_index, int menu_type, uint16 image_type); +int16 CheckCover(int game_index, int menu_type); +bool GetCoverName(int game_index, char **cover_name); +const char* GetCoverFolder(int menu_type); +bool GetGameCoverPath(int game_index, char **game_cover_path, int menu_type); +void SetMenuType(int menu_type); +void FreeGames(); +void FreeGamesForce(); +int GenerateVMUFile(const char* full_path_game, int vmu_mode, uint32 vmu_number); +bool LoadFirmwareFiles(); +void LoadDefaultMenuConfig(); +bool LoadMenuConfig(); +void PatchParseText(PresetStruct *preset); +PresetStruct* GetDefaultPresetGame(const char* full_path_game, SectorDataStruct *sector_data); +PresetStruct* LoadPresetGame(int game_index); +bool SavePresetGame(PresetStruct *preset); +isoldr_info_t* ParsePresetToIsoldr(int game_index, PresetStruct *preset); +void ParseMenuConfigToPresentation(); +void ParsePresentationToMenuConfig(); +bool SaveMenuConfig(); +bool SaveScannedCover(); +bool LoadScannedCover(); +bool HasAnyCover(); +void CleanIncompleteCover(); +void OptimizeGameCovers(); +void OptimizeCover(int game_index, const char *game_name, kos_img_t *img, bool is_alpha); +bool ExtractPVRCover(int game_index); +void* LoadPVRCoverThread(void *params); +void* OptimizeCoverThread(void *param); +bool IsUniqueFileGame(const char *full_path_folder); +void RetrieveCovers(uint8 device, int menu_type); +void RetrieveGamesRecursive(const char *full_path_folder, const char *folder, int level); +bool RetrieveGames(); +uint16 GetCoverType(int game_index, int menu_type); +const char* GetCoverExtensionFromType(uint16 image_type); +void CleanCoverType(int game_index, int menu_type); +void SetCoverType(int game_index, int menu_type, uint16 dw_image_type); +bool ContainsCoverType(int game_index, int menu_type, uint16 dw_image_type); + +#endif // __APP_MENU_H diff --git a/applications/games_menu/modules/app_module.h b/applications/games_menu/modules/app_module.h new file mode 100644 index 00000000..0ff1e3bd --- /dev/null +++ b/applications/games_menu/modules/app_module.h @@ -0,0 +1,16 @@ +/* DreamShell ##version## + + app_module.h - Games app module header + Copyright (C) 2024 Maniac Vera + +*/ + +#include + +void GamesApp_Init(App_t *app); + +void GamesApp_Shutdown(App_t *app); + +void GamesApp_Open(App_t *app); + +void GamesApp_Exit(GUI_Widget *widget); \ No newline at end of file diff --git a/applications/games_menu/modules/app_preset.h b/applications/games_menu/modules/app_preset.h new file mode 100644 index 00000000..3a7e6d82 --- /dev/null +++ b/applications/games_menu/modules/app_preset.h @@ -0,0 +1,130 @@ +/* DreamShell ##version## + + app_system_menu.h + Copyright (C) 2024 Maniac Vera + +*/ + +#ifndef __APP_PRESET_H +#define __APP_PRESET_H + +#include +#include "tsunami/tsudefinition.h" +#include "tsunami/drawable.h" +#include "tsunami/dsapp.h" +#include "tsunami/drawables/scene.h" +#include "tsunami/drawables/form.h" +#include "tsunami/texture.h" + +#include "app_utils.h" +#include "app_definition.h" + +void CreatePresetMenu(DSApp *dsapp_ptr, Scene *scene_ptr, Font *menu_font, Font *message_font); +void DestroyPresetMenu(); +void PresetMenuRemoveAll(); +void OnViewIndexChangedEvent(Drawable *drawable, int view_index); +void CreateGeneralView(Form *form_ptr); +void CreateCDDAView(Form *form_ptr); +void CreatePatchView(Form *form_ptr); +void CreateExtensionsView(Form *form_ptr); +void CreateShortcutView(Form *form_ptr); + +void PresetMenuInputEvent(int type, int key); +int StatePresetMenu(); +void ShowPresetMenu(int game_index); +void HidePresetMenu(); +void PresetMenuSelectedEvent(Drawable *drawable, uint bottom_index, uint column, uint row); + +void ExitPresetMenuClick(Drawable *drawable); +void SavePresetOptionClick(Drawable *drawable); +void DMAOptionClick(Drawable *drawable); +void AsyncOptionClick(Drawable *drawable); +void ByPassOptionClick(Drawable *drawable); +void CDDAOptionClick(Drawable *drawable); +void IRQOptionClick(Drawable *drawable); +void OSOptionClick(Drawable *drawable); + +void LoaderOptionClick(Drawable *drawable); +void BootOptionClick(Drawable *drawable); +void FastOptionClick(Drawable *drawable); +void LowLevelOptionClick(Drawable *drawable); +void MemoryOptionClick(Drawable *drawable); +void CustomMemoryOptionClick(Drawable *drawable); +void HeapOptionClick(Drawable *drawable); + +void CDDASourceOptionClick(Drawable *drawable); +void CDDADestinationOptionClick(Drawable *drawable); +void CDDAPositionOptionClick(Drawable *drawable); +void CDDAChannelOptionClick(Drawable *drawable); + +void PatchAddress1OptionClick(Drawable *drawable); +void PatchValue1OptionClick(Drawable *drawable); +void PatchAddress2OptionClick(Drawable *drawable); +void PatchValue2OptionClick(Drawable *drawable); + +void AlterBootOptionClick(Drawable *drawable); +void SetModeScreenshot(); +int GetModeScreenshot(); +bool AllButtonsDisabled(); +void CheckScreenshot(); +void ScreenshotOptionClick(Drawable *drawable); +void ButtonStartOptionClick(Drawable *drawable); +void ButtonXOptionClick(Drawable *drawable); +void ButtonYOptionClick(Drawable *drawable); +void ButtonZOptionClick(Drawable *drawable); +void ButtonLTOptionClick(Drawable *drawable); +void ButtonAOptionClick(Drawable *drawable); +void ButtonBOptionClick(Drawable *drawable); +void ButtonCOptionClick(Drawable *drawable); +void ButtonRTOptionClick(Drawable *drawable); +void VMUOptionClick(Drawable *drawable); +void VMUSelectorOptionClick(Drawable *drawable); + +void ShortcutSizeOptionClick(Drawable *drawable); +void ShortcutRotateOptionClick(Drawable *drawable); +void ShortcutNameOptionClick(Drawable *drawable); +void ShortcutDontShowNameOptionClick(Drawable *drawable); +void ShortcutCreateOptionClick(Drawable *drawable); + +void SavePresetInputEvent(int type, int key); +void DMAInputEvent(int type, int key); +void AsyncInputEvent(int type, int key); +void ByPassInputEvent(int type, int key); +void CDDAInputEvent(int type, int key); +void IRQInputEvent(int type, int key); +void OSInputEvent(int type, int key); + +void LoaderInputEvent(int type, int key); +void BootInputEvent(int type, int key); +void FastInputEvent(int type, int key); +void LowLevelInputEvent(int type, int key); +void MemoryInputEvent(int type, int key); +void CustomMemoryInputEvent(int type, int key); +void HeapInputEvent(int type, int key); + +void CDDASourceInputEvent(int type, int key); +void CDDADestinationInputEvent(int type, int key); +void CDDAPositionInputEvent(int type, int key); +void CDDAChannelInputEvent(int type, int key); + +void SetPatchByType(TextBox *patch_textbox, int control_type, char *patch_text_variable_ptr, uint32 *patch_variable_ptr); +void PatchAddress1InputEvent(int type, int key); +void PatchValue1InputEvent(int type, int key); +void PatchAddress2InputEvent(int type, int key); +void PatchValue2InputEvent(int type, int key); + +void AlterBootInputEvent(int type, int key); +void ScreenshotInputEvent(int type, int key); +void VMUInputEvent(int type, int key); +void VMUSelectorInputEvent(int type, int key); +void ShortcutSizeInputEvent(int type, int key); +void ShortcutRotateInputEvent(int type, int key); +void ShortcutNameInputEvent(int type, int key); +void ShortcutDontShowNameInputEvent(int type, int key); +void ShortcutCreateInputEvent(int type, int key); +void ShortcutLoadTextureCover(); +void ShortcutChangeCoverSize(); + +void CheckIfShorcutExits(); + +#endif // __APP_PRESET_H diff --git a/applications/games_menu/modules/app_system_menu.h b/applications/games_menu/modules/app_system_menu.h new file mode 100644 index 00000000..35d2759e --- /dev/null +++ b/applications/games_menu/modules/app_system_menu.h @@ -0,0 +1,49 @@ +/* DreamShell ##version## + + app_system_menu.h + Copyright (C) 2024 Maniac Vera + +*/ + +#ifndef __APP_SYSTEM_MENU_H +#define __APP_SYSTEM_MENU_H + +#include +#include "tsunami/tsudefinition.h" +#include "tsunami/drawable.h" +#include "tsunami/dsapp.h" +#include "tsunami/drawables/scene.h" +#include "tsunami/drawables/form.h" + +#include "app_utils.h" +#include "app_definition.h" + +void CreateSystemMenu(DSApp *dsapp_ptr, Scene *scene_ptr, Font *menu_font, Font *message_font); +void CreateSystemMenuView(Form *form_ptr); +void OnSystemViewIndexChangedEvent(Drawable *drawable, int view_index); +void DestroySystemMenu(); +void SystemMenuRemoveAll(); + +void SystemMenuInputEvent(int type, int key); +int StateSystemMenu(); +void ShowSystemMenu(); +void HideSystemMenu(); +void SystemMenuSelectedEvent(Drawable *drawable, uint bottom_index, uint column, uint row); +void OptimizeCoversClick(Drawable *drawable); +void ExitSystemMenuClick(Drawable *drawable); +void ScanMissingCoversClick(Drawable *drawable); +void DefaultSavePresetOptionClick(Drawable *drawable); +void CoverBackgroundOptionClick(Drawable *drawable); +void ChangePageWithPadOptionClick(Drawable *drawable); + +void ShowOptimizeCoverPopup(); +void HideOptimizeCoverPopup(); +void SetMessageOptimizer(const char *fmt, const char *message); +int StopOptimizeCovers(); + +void ShowCoverScan(); +void HideCoverScan(); +void SetMessageScan(const char *fmt, const char *message); +int StopScanCovers(); + +#endif // __APP_SYSTEM_MENU_H diff --git a/applications/games_menu/modules/app_utils.h b/applications/games_menu/modules/app_utils.h new file mode 100644 index 00000000..69b117ad --- /dev/null +++ b/applications/games_menu/modules/app_utils.h @@ -0,0 +1,49 @@ +/* DreamShell ##version## + + utils.h - ISO Loader app utils + Copyright (C) 2024 Maniac Vera + +*/ + +#ifndef __APP_UTILS_H +#define __APP_UTILS_H + +#include "app_definition.h" + +/* Indexes of devices */ +enum { + APP_DEVICE_CD = 0, + APP_DEVICE_SD = 1, + APP_DEVICE_IDE, + APP_DEVICE_PC, + APP_DEVICE_COUNT +}; + +enum { + CONF_END = 0, + CONF_INT = 1, + CONF_STR, + CONF_ULONG, +}; + + + +char *Trim(char *string); +void TrimSpaces(char *input, char *output, int size); +char *TrimSpaces2(char *txt); +char *FixSpaces(char *str); +bool MakeShortcut(PresetStruct *preset, const char* device_dir, const char* full_path_game, bool show_name, const char* game_cover_path, int width, int height, bool yflip); +int ConfigParse(isoldr_conf *cfg, const char *filename); +bool IsGdiOptimized(const char *full_path_game); +const char* GetLastPart(const char *source, const char separator, int option_path); +bool ContainsOnlyNumbers(const char *string); +int GetDeviceType(const char *dir); +int CanUseTrueAsyncDMA(int sector_size, int current_dev, int image_type); +void GetMD5HashISO(const char *file_mount_point, SectorDataStruct *sector_data); +char* MakePresetFilename(const char *default_dir, const char *device_dir, uint8 *md5, const char *app_name); +const char *GetFolderPathFromFile(const char *full_path_file); +size_t GetCDDATrackFilename(int num, const char *full_path_game, char **result); +void PlayCDDATrack(const char *file, int loop); +void StopCDDATrack(); + +#endif // __APP_UTILS_H diff --git a/applications/games_menu/modules/exports.txt b/applications/games_menu/modules/exports.txt new file mode 100644 index 00000000..60563d0d --- /dev/null +++ b/applications/games_menu/modules/exports.txt @@ -0,0 +1,7 @@ +include kos/exports.h +include app_module.h + +# GAMES Loader exports +GamesApp_Init +GamesApp_Shutdown +GamesApp_Open \ No newline at end of file diff --git a/applications/games_menu/modules/menu.c b/applications/games_menu/modules/menu.c new file mode 100644 index 00000000..e3662c12 --- /dev/null +++ b/applications/games_menu/modules/menu.c @@ -0,0 +1,2485 @@ +#include "app_menu.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct menu_structure menu_data; + +void CreateMenuData(SendMessageCallBack *send_message_scan, SendMessageCallBack *send_message_optimizer + , PostPVRCoverCallBack *post_pvr_cover, PostOptimizerCoverCallBack *post_optimizer_cover) +{ + menu_data.save_preset = true; + menu_data.cover_background = true; + menu_data.games_array_count = 0; + menu_data.firmware_array_count = 0; + menu_data.default_cover_type = -1; + menu_data.play_cdda_thread = NULL; + menu_data.load_pvr_cover_thread = NULL; + menu_data.finished_menu = false; + menu_data.cdda_game_changed = false; + menu_data.stop_load_pvr_cover = false; + menu_data.firmware_array = NULL; + menu_data.vmu_mode = 0; + menu_data.preset = NULL; + + menu_data.send_message_scan = send_message_scan; + menu_data.send_message_optimizer = send_message_optimizer; + menu_data.post_pvr_cover = post_pvr_cover; + menu_data.post_optimizer_cover = post_optimizer_cover; + + menu_data.games_array = NULL; + memset(&menu_data.menu_option, 0, sizeof(menu_data.menu_option)); + + memset(menu_data.default_dir, 0, sizeof(menu_data.default_dir)); + memset(menu_data.covers_path, 0, sizeof(menu_data.covers_path)); + memset(menu_data.games_path, 0, sizeof(menu_data.games_path)); + + memset(menu_data.default_dir_sd, 0, sizeof(menu_data.default_dir_sd)); + memset(menu_data.covers_path_sd, 0, sizeof(menu_data.covers_path_sd)); + memset(menu_data.games_path_sd, 0, sizeof(menu_data.games_path_sd)); + + menu_data.ide = (DirExists("/ide/games") == 1); + menu_data.sd = (DirExists("/sd/games") == 1); + menu_data.cd = (!is_custom_bios()); + + if (menu_data.ide) + { + strcpy(menu_data.default_dir, "/ide/DS"); + strcpy(menu_data.games_path, "/ide/games"); + strcpy(menu_data.covers_path, "/ide/DS/apps/games_menu/covers"); + } + + if (menu_data.sd) + { + if (menu_data.ide) + { + strcpy(menu_data.default_dir_sd, "/sd/DS"); + strcpy(menu_data.games_path_sd, "/sd/games"); + strcpy(menu_data.covers_path_sd, "/sd/DS/apps/games_menu/covers"); + } + else + { + strcpy(menu_data.default_dir, "/sd/DS"); + strcpy(menu_data.games_path, "/sd/games"); + strcpy(menu_data.covers_path, "/sd/DS/apps/games_menu/covers"); + } + } + + if ((!menu_data.ide && !menu_data.sd) && menu_data.cd) + { + strcpy(menu_data.default_dir, "/cd"); + strcpy(menu_data.games_path, "/cd/games"); + strcpy(menu_data.covers_path, "/cd/apps/games_menu/covers"); + } + + menu_data.current_dev = GetDeviceType(menu_data.default_dir); + menu_data.convert_pvr_to_png = (menu_data.current_dev == APP_DEVICE_IDE); + + char game_cover_path[NAME_MAX]; + memset(game_cover_path, 0, NAME_MAX); + snprintf(game_cover_path, NAME_MAX, "%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu/images/gd.jpg"); + menu_data.default_cover_type = (FileExists(game_cover_path) ? IT_JPG : IT_PNG); + + LoadMenuConfig(); + LoadFirmwareFiles(); +} + +void DestroyMenuData() +{ + if(menu_data.load_pvr_cover_thread != NULL) + { + menu_data.stop_load_pvr_cover = true; + thd_join(menu_data.load_pvr_cover_thread, NULL); + menu_data.load_pvr_cover_thread = NULL; + } + + if(menu_data.play_cdda_thread != NULL) + { + menu_data.cdda_game_changed = true; + thd_join(menu_data.play_cdda_thread, NULL); + menu_data.play_cdda_thread = NULL; + } + + if (menu_data.firmware_array != NULL) + { + free(menu_data.firmware_array); + menu_data.firmware_array_count = 0; + } + + FreeGamesForce(); +} + +const char* GetDefaultDir(uint8 device) +{ + switch (device) + { + case APP_DEVICE_CD: + return menu_data.default_dir; + + case APP_DEVICE_SD: + { + if (menu_data.ide) + { + return menu_data.default_dir_sd; + } + else + { + return menu_data.default_dir; + } + } + break; + + case APP_DEVICE_IDE: + return menu_data.default_dir; + + case APP_DEVICE_PC: + return menu_data.default_dir; + + default: + return menu_data.default_dir; + } +} + +const char* GetGamesPath(uint8 device) +{ + switch (device) + { + case APP_DEVICE_CD: + return menu_data.games_path; + + case APP_DEVICE_SD: + { + if (menu_data.ide) + { + return menu_data.games_path_sd; + } + else + { + return menu_data.games_path; + } + } + break; + + case APP_DEVICE_IDE: + return menu_data.games_path; + + case APP_DEVICE_PC: + return menu_data.games_path; + + default: + return menu_data.games_path; + } + + return NULL; +} + +const char* GetCoversPath(uint8 device) +{ + switch (device) + { + case APP_DEVICE_CD: + return menu_data.covers_path; + + case APP_DEVICE_SD: + { + if (menu_data.ide) + { + return menu_data.covers_path_sd; + } + else + { + return menu_data.covers_path; + } + } + break; + + case APP_DEVICE_IDE: + return menu_data.covers_path; + + case APP_DEVICE_PC: + return menu_data.covers_path; + + default: + return menu_data.covers_path; + } +} + +const char* GetFullGamePathByIndex(int game_index) +{ + static char full_path_game[NAME_MAX]; + memset(full_path_game, 0, sizeof(full_path_game)); + if (game_index >= 0) + { + if (menu_data.games_array[game_index].folder) + { + snprintf(full_path_game, NAME_MAX, "%s/%s/%s", GetGamesPath(menu_data.games_array[game_index].device), menu_data.games_array[game_index].folder, menu_data.games_array[game_index].game); + } + else + { + snprintf(full_path_game, NAME_MAX, "%s/%s", GetGamesPath(menu_data.games_array[game_index].device), menu_data.games_array[game_index].game); + } + } + + return full_path_game; +} + +void* PlayCDDAThread(void *params) +{ + int game_index = (int)params; + if (game_index >= 0) + { + const char *full_path_game = GetFullGamePathByIndex(game_index); + const uint max_time = 2000; + uint time_elapsed = 0; + + while (!menu_data.cdda_game_changed && time_elapsed < max_time) + { + thd_sleep(50); + time_elapsed += 50; + } + + if (!menu_data.cdda_game_changed) + { + if (CheckCDDA(game_index)) + { + if (!menu_data.cdda_game_changed) + { + size_t track_size = 0; + char *track_file_path = (char *)malloc(NAME_MAX); + srand(time(NULL)); + + do + { + track_size = GetCDDATrackFilename((random() % 15) + 4, full_path_game, &track_file_path); + } while (track_size == 0); + + PlayCDDATrack(track_file_path, 3); + free(track_file_path); + } + } + } + } + + return NULL; +} + +bool CheckCDDA(int game_index) +{ + bool isCDDA = false; + if (game_index >= 0) + { + if (menu_data.games_array[game_index].is_cdda == CCGE_CDDA) + { + isCDDA = true; + } + else + { + if (menu_data.games_array[game_index].is_cdda == CCGE_NOT_CHECKED) + { + const char *full_path_game = GetFullGamePathByIndex(game_index); + size_t track_size = 0; + + char *track_file_path = (char *)malloc(NAME_MAX); + track_size = GetCDDATrackFilename(4, full_path_game, &track_file_path); + + if (track_size > 0 && track_size < 30 * 1024 * 1024) + { + track_size = GetCDDATrackFilename(6, full_path_game, &track_file_path); + } + + if (track_size > 0 && (MAX_SIZE_CDDA == 0 || track_size <= MAX_SIZE_CDDA)) + { + isCDDA = true; + menu_data.games_array[game_index].is_cdda = CCGE_CDDA; + } + else + { + isCDDA = false; + menu_data.games_array[game_index].is_cdda = CCGE_NOT_CDDA; + } + + free(track_file_path); + } + } + } + + return isCDDA; +} + +void StopCDDA() +{ + StopCDDATrack(); + if (menu_data.play_cdda_thread != NULL) + { + menu_data.cdda_game_changed = true; + thd_join(menu_data.play_cdda_thread, NULL); + menu_data.play_cdda_thread = NULL; + menu_data.cdda_game_changed = false; + } +} + +void PlayCDDA(int game_index) +{ + StopCDDA(); + + if ((menu_data.games_array[game_index].is_cdda == CCGE_NOT_CHECKED || menu_data.games_array[game_index].is_cdda == CCGE_CDDA) + && menu_data.current_dev == APP_DEVICE_IDE) + { + menu_data.play_cdda_thread = thd_create(0, PlayCDDAThread, (void *)game_index); + } +} + +ImageDimensionStruct *GetImageDimension(const char *image_file) +{ + ImageDimensionStruct *image = NULL; + char *image_type = strrchr(image_file, '.'); + + if (strcasecmp(image_type, ".kmg") == 0) + { + /* Open the file */ + file_t image_handle = fs_open(image_file, O_RDONLY); + if (image_handle == FILEHND_INVALID) { + return NULL; + } + + kmg_header_t hdr; + /* Read the header */ + if (fs_read(image_handle, &hdr, sizeof(hdr)) != sizeof(hdr)) { + fs_close(image_handle); + return NULL; + } + + /* Verify a few things */ + if (hdr.magic != KMG_MAGIC || hdr.version != KMG_VERSION || + hdr.platform != KMG_PLAT_DC) + { + fs_close(image_handle); + return NULL; + } + fs_close(image_handle); + + image = (ImageDimensionStruct *)malloc(sizeof(ImageDimensionStruct)); + memset(image, 0, sizeof(ImageDimensionStruct)); + image->width = hdr.width; + image->height = hdr.height; + } + else if (strcasecmp(image_type, ".pvr") == 0) + { + file_t image_handle = fs_open(image_file, O_RDONLY); + + if (image_handle == FILEHND_INVALID) + return NULL; + + size_t fsize = 512; + uint8 *data = (uint8 *)memalign(32, fsize); + if (data == NULL) + { + fs_close(image_handle); + return NULL; + } + + if (fs_read(image_handle, data, fsize) == -1) + { + fs_close(image_handle); + return NULL; + } + fs_close(image_handle); + + struct PVRTHeader pvrtHeader; + unsigned int offset = ReadPVRHeader(data, &pvrtHeader); + if (offset == 0) + { + free(data); + return NULL; + } + free(data); + + enum TextureFormatMasks srcFormat = (enum TextureFormatMasks)(pvrtHeader.textureAttributes & 0xFF); + if (srcFormat != TFM_ARGB1555 && srcFormat != TFM_RGB565 && srcFormat != TFM_ARGB4444) + { + return NULL; + } + + image = (ImageDimensionStruct *)malloc(sizeof(ImageDimensionStruct)); + memset(image, 0, sizeof(ImageDimensionStruct)); + image->width = pvrtHeader.width; + image->height = pvrtHeader.height; + } + else if (strcasecmp(image_type, ".jpg") == 0) + { + file_t image_handle = fs_open(image_file, O_RDONLY); + + if (image_handle == FILEHND_INVALID) + return NULL; + + image = (ImageDimensionStruct *)malloc(sizeof(ImageDimensionStruct)); + memset(image, 0, sizeof(ImageDimensionStruct)); + + size_t file_size = fs_total(image_handle); + unsigned char buffer[32]; + int i, j, marker; + + fs_seek(image_handle, 0, SEEK_SET); + fs_read(image_handle, buffer, 32); + i = j = 2; // Start at offset of first marker + marker = 0; // Search for SOF (start of frame) marker + + while (i < 32 && marker != 0xffc0 && j < file_size) + { + marker = (MOTOSHORT(&buffer[i])) & 0xfffc; + if (marker < 0xff00) // invalid marker + { + i += 2; + continue; + } + if (marker == 0xffc0) + break; + + j += 2 + MOTOSHORT(&buffer[i + 2]); // Skip to next marker + if (j < file_size) // need to read more + { + fs_seek(image_handle, j, SEEK_SET); // read some more + fs_read(image_handle, buffer, 32); + i = 0; + } + else + break; + } + + if (marker == 0xffc0) + { + image->height = MOTOSHORT(&buffer[i + 5]); + image->width = MOTOSHORT(&buffer[i + 7]); + } + + fs_close(image_handle); + } + else if (strcasecmp(image_type, ".png") == 0 || strcasecmp(image_type, ".bpm") == 0) + { + file_t image_handle = fs_open(image_file, O_RDONLY); + + if (image_handle == FILEHND_INVALID) + return NULL; + + image = (ImageDimensionStruct *)malloc(sizeof(ImageDimensionStruct)); + memset(image, 0, sizeof(ImageDimensionStruct)); + + if (strcasecmp(image_type, ".png") == 0) + { + fs_seek(image_handle, 16, SEEK_SET); + fs_read(image_handle, (char *)&image->width, sizeof(uint32)); + + fs_seek(image_handle, 20, SEEK_SET); + fs_read(image_handle, (char *)&image->height, sizeof(uint32)); + } + else if (strcasecmp(image_type, ".bmp") == 0) + { + fs_seek(image_handle, 18, SEEK_SET); + fs_read(image_handle, (char *)&image->width, sizeof(uint32)); + + fs_seek(image_handle, 22, SEEK_SET); + fs_read(image_handle, (char *)&image->height, sizeof(uint32)); + } + + image->width = ntohl(image->width); + image->height = ntohl(image->height); + + fs_close(image_handle); + } + + image_type = NULL; + return image; +} + +bool IsValidImage(const char *image_file) +{ + bool isValid = false; + + ImageDimensionStruct *image = GetImageDimension(image_file); + if (image != NULL) + { + isValid = (image->height == 64 && image->width == 64) + || (image->height == 128 && image->width == 128) + || (image->height == 256 && image->width == 256); + + free(image); + } + + return isValid; +} + +bool ExistsCoverFile(const char *cover_file, int menu_type) +{ + bool exists = false; + + if (menu_data.covers_array_count > 0) + { + char first_char_left = (cover_file[0] == 'a' ? 'A' : toupper(cover_file[0])); + char first_char_rigth = '\0'; + bool char_match = false; + + for (int i = 0; i < menu_data.covers_array_count; i++) + { + first_char_rigth = (menu_data.covers_array[i].cover[0] == 'a' ? 'A' : toupper(menu_data.covers_array[i].cover[0])); + + if (char_match && first_char_left != first_char_rigth) + { + break; + } + + if (((menu_data.covers_array[i].menu_type & (1<<(menu_type-1)))) + && first_char_left == first_char_rigth) + { + char_match = true; + if (strcasecmp(cover_file, menu_data.covers_array[i].cover) == 0) + { + exists = true; + break; + } + } + } + } + + return exists; +} + +bool ContainsCoverType(int game_index, int menu_type, uint16 dw_image_type) +{ + bool result = false; + + if (game_index >= 0 && dw_image_type > 0) + { + result = ((GetCoverType(game_index, menu_type) & (dw_image_type)) == dw_image_type); + } + + return result; +} + +uint16 GetCoverType(int game_index, int menu_type) +{ + uint16 dw_image_type = 0; + + if (game_index >= 0) + { + if (menu_data.games_array[game_index].cover_type <= 0) + { + menu_data.games_array[game_index].cover_type = 0; + } + + dw_image_type = ((menu_data.games_array[game_index].cover_type>>((menu_type-1)*16)) & IMAGE_TYPE_MASK); + } + + return dw_image_type; +} + +void CleanCoverType(int game_index, int menu_type) +{ + if (game_index >= 0) + { + if (menu_data.games_array[game_index].cover_type <= 0) + { + menu_data.games_array[game_index].cover_type = 0; + return; + } + + switch (menu_type) + { + case MT_PLANE_TEXT: + menu_data.games_array[game_index].cover_type &= 0xFFFFFFFF0000; + break; + + case MT_IMAGE_TEXT_64_5X2: + menu_data.games_array[game_index].cover_type &= 0xFFFF0000FFFF; + break; + + case MT_IMAGE_128_4X3: + menu_data.games_array[game_index].cover_type &= 0x0000FFFFFFFF; + break; + + default: + break; + } + } +} + +void SetCoverType(int game_index, int menu_type, uint16 dw_image_type) +{ + if (game_index >= 0) + { + if (menu_data.games_array[game_index].cover_type <= 0) + { + menu_data.games_array[game_index].cover_type = 0; + } + + menu_data.games_array[game_index].cover_type |= (uint64)dw_image_type<<((menu_type-1)*16); + } +} + +const char* GetCoverExtensionFromType(uint16 image_type) +{ + switch (image_type) + { + case IT_PNG: + return ".png"; + + case IT_JPG: + return ".jpg"; + + case IT_BPM: + return ".bpm"; + + case IT_PVR: + return ".pvr"; + + case IT_KMG: + return ".kmg"; + + default: + return ".jpg"; + } +} + +bool CheckCoverImageType(int game_index, int menu_type, uint16 image_type) +{ + bool exists_cover = false; + + char *game_without_extension = NULL; + if (GetCoverName(game_index, &game_without_extension)) + { + char cover_file[NAME_MAX]; + memset(cover_file, 0, NAME_MAX); + snprintf(cover_file, NAME_MAX, "%s%s", game_without_extension, GetCoverExtensionFromType(image_type)); + + if (ExistsCoverFile(cover_file, menu_type)) + { + char *game_cover_path = (char *)malloc(NAME_MAX); + memset(game_cover_path, 0, NAME_MAX); + snprintf(game_cover_path, NAME_MAX, "%s%s/%s%s", GetCoversPath(menu_data.current_dev), GetCoverFolder(menu_type), game_without_extension, GetCoverExtensionFromType(image_type)); + + if (IsValidImage(game_cover_path)) + { + menu_data.games_array[game_index].exists_cover[menu_type-1] = SC_EXISTS; + SetCoverType(game_index, menu_type, image_type); + exists_cover = true; + } + else + { + fs_unlink(game_cover_path); + // ds_printf("INVALID IMAGE: %s\n", cover_file); + } + + free(game_cover_path); + } + + free(game_without_extension); + } + + return exists_cover; +} + +int16 CheckCover(int game_index, int menu_type) +{ + int16 exists_cover = SC_WITHOUT_SEARCHING; + + if (menu_data.games_array[game_index].exists_cover[menu_type-1] == SC_WITHOUT_SEARCHING) + { + uint16 image_count = 1; + while (image_count < IMAGE_TYPE_SUPPORTED) + { + if (IMAGE_TYPE_SUPPORTED & image_count) + { + if (CheckCoverImageType(game_index, menu_type, image_count)) + { + exists_cover = menu_data.games_array[game_index].exists_cover[menu_type-1]; + break; + } + } + image_count = image_count<<1; + } + + if (menu_data.games_array[game_index].exists_cover[menu_type-1] == SC_WITHOUT_SEARCHING) + { + SetCoverType(game_index, menu_type, menu_data.default_cover_type); + exists_cover = menu_data.games_array[game_index].exists_cover[menu_type-1] = SC_DEFAULT; + menu_data.games_array[game_index].check_pvr = true; + } + } + else + { + exists_cover = menu_data.games_array[game_index].exists_cover[menu_type-1]; + } + + return exists_cover; +} + +bool GetCoverName(int game_index, char **cover_name) +{ + if (*cover_name != NULL) + { + free(*cover_name); + } + + *cover_name = NULL; + if (game_index >= 0) + { + *cover_name = (char *)malloc(NAME_MAX); + memset(*cover_name, 0, NAME_MAX); + + if (menu_data.games_array[game_index].is_folder_name) + { + strncpy(*cover_name, menu_data.games_array[game_index].folder_name, strlen(menu_data.games_array[game_index].folder_name)); + } + else + { + strncpy(*cover_name, menu_data.games_array[game_index].game, strlen(menu_data.games_array[game_index].game) - 4); + } + } + + return *cover_name != NULL; +} + +const char* GetCoverFolder(int menu_type) +{ + switch (menu_type) + { + case MT_PLANE_TEXT: + return PLANE_TEXT_MENU_FOLDER; + + case MT_IMAGE_TEXT_64_5X2: + return IMAGE_TEXT_64_5X2_MENU_FOLDER; + + case MT_IMAGE_128_4X3: + return IMAGE_128_4X3_FOLDER; + + default: + return PLANE_TEXT_MENU_FOLDER; + } +} + +bool GetGameCoverPath(int game_index, char **game_cover_path, int menu_type) +{ + if (*game_cover_path != NULL) + { + free(*game_cover_path); + } + + *game_cover_path = NULL; + if (game_index >= 0) + { + *game_cover_path = (char *)malloc(NAME_MAX); + memset(*game_cover_path, 0, NAME_MAX); + + if (menu_data.games_array[game_index].exists_cover[menu_type-1] == SC_EXISTS) + { + char *game = NULL; + const char *cover_folder = GetCoverFolder(menu_type); + GetCoverName(game_index, &game); + + uint16 image_count = 1; + while (image_count < IMAGE_TYPE_SUPPORTED) + { + if (IMAGE_TYPE_SUPPORTED & image_count) + { + if (ContainsCoverType(game_index, menu_type, image_count)) + { + snprintf(*game_cover_path, NAME_MAX, "%s%s/%s%s", GetCoversPath(menu_data.current_dev), cover_folder, game, GetCoverExtensionFromType(image_count)); + break; + } + } + image_count = image_count<<1; + } + + if (game != NULL) + { + free(game); + } + } + else + { + if (menu_data.games_array[game_index].exists_cover[menu_type-1] == SC_DEFAULT) + { + if (ContainsCoverType(game_index, menu_type, IT_JPG)) + { + snprintf(*game_cover_path, NAME_MAX, "%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu/images/gd.jpg"); + } + else + { + snprintf(*game_cover_path, NAME_MAX, "%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu/images/gd.png"); + } + } + } + } + + return *game_cover_path != NULL; +} + +void SetMenuType(int menu_type) +{ + menu_data.menu_type = menu_type; + + switch (menu_type) + { + case MT_IMAGE_TEXT_64_5X2: + { + menu_data.menu_option.max_page_size = 10; + menu_data.menu_option.max_columns = 2; + menu_data.menu_option.size_items_column = menu_data.menu_option.max_page_size / menu_data.menu_option.max_columns; + menu_data.menu_option.init_position_x = 20; + menu_data.menu_option.init_position_y = -22; + menu_data.menu_option.padding_x = 320; + menu_data.menu_option.padding_y = 22; + menu_data.menu_option.image_size = 64.0f; + } + break; + + case MT_IMAGE_128_4X3: + { + menu_data.menu_option.max_page_size = 12; + menu_data.menu_option.max_columns = 4; + menu_data.menu_option.size_items_column = menu_data.menu_option.max_page_size / menu_data.menu_option.max_columns; + menu_data.menu_option.init_position_x = 55; + menu_data.menu_option.init_position_y = -45; + menu_data.menu_option.padding_x = 163; + menu_data.menu_option.padding_y = 12; + menu_data.menu_option.image_size = 128.0f; + } + break; + + case MT_IMAGE_256_3X2: + { + menu_data.menu_option.max_page_size = 6; + menu_data.menu_option.max_columns = 3; + menu_data.menu_option.size_items_column = menu_data.menu_option.max_page_size / menu_data.menu_option.max_columns; + menu_data.menu_option.init_position_x = 90; + menu_data.menu_option.init_position_y = -82; + menu_data.menu_option.padding_x = 212.0f; + menu_data.menu_option.padding_y = 12; + menu_data.menu_option.image_size = 200.0f; + } + break; + + case MT_PLANE_TEXT: + { + menu_data.menu_option.max_page_size = 12; + menu_data.menu_option.max_columns = 1; + menu_data.menu_option.size_items_column = menu_data.menu_option.max_page_size / menu_data.menu_option.max_columns; + menu_data.menu_option.init_position_x = 2; + menu_data.menu_option.init_position_y = 25; + menu_data.menu_option.padding_x = 2; + menu_data.menu_option.padding_y = 34; + menu_data.menu_option.image_size = 0.0f; + } + break; + + default: + { + menu_data.menu_option.max_page_size = 10; + menu_data.menu_option.max_columns = 2; + menu_data.menu_option.size_items_column = menu_data.menu_option.max_page_size / menu_data.menu_option.max_columns; + menu_data.menu_option.init_position_x = 20; + menu_data.menu_option.init_position_y = 5; + menu_data.menu_option.padding_x = 320; + menu_data.menu_option.padding_y = 12; + menu_data.menu_option.image_size = 64.0f; + } + break; + } +} + +void FreeGames() +{ +#ifndef IN_CACHE_GAMES + FreeGamesForce(); +#endif +} + +void FreeGamesForce() +{ + if (menu_data.covers_array_count > 0) + { + for (int icount = 0; icount < menu_data.covers_array_count; icount++) + { + if (menu_data.covers_array[icount].cover) + { + free(menu_data.covers_array[icount].cover); + } + + menu_data.covers_array[icount].cover = NULL; + } + menu_data.covers_array_count = 0; + } + + if (menu_data.games_array_count > 0) + { + for (int icount = 0; icount < menu_data.games_array_count; icount++) + { + if (menu_data.games_array[icount].game) + { + free(menu_data.games_array[icount].game); + } + + if (menu_data.games_array[icount].folder) + { + free(menu_data.games_array[icount].folder); + } + + if (menu_data.games_array[icount].folder_name) + { + free(menu_data.games_array[icount].folder_name); + } + + menu_data.games_array[icount].game = NULL; + menu_data.games_array[icount].folder = NULL; + menu_data.games_array[icount].folder_name = NULL; + } + + free(menu_data.games_array); + menu_data.games_array = NULL; + menu_data.games_array_count = 0; + } +} + +int GenerateVMUFile(const char* full_path_game, int vmu_mode, uint32 vmu_number) +{ + if (full_path_game != NULL && vmu_mode > 0) + { + char vmu_file_path[NAME_MAX]; + memset(vmu_file_path, 0, NAME_MAX); + + char private_path[NAME_MAX]; + snprintf(private_path, NAME_MAX, "%s/vmu%03ld.vmd", GetFolderPathFromFile(full_path_game), vmu_number); + + int private_size = FileSize(private_path); + if (vmu_mode == 1) // SHARED + { + if (private_size > 0) + { + fs_unlink(private_path); + } + + return 0; + } + + switch(vmu_mode) + { + case 2: // 200 BLOCKS + { + snprintf(vmu_file_path, sizeof(vmu_file_path), + "%s/apps/%s/resources/empty_vmu_128kb.vmd", + GetDefaultDir(menu_data.current_dev), "games_menu"); + } + break; + + case 3: // 1800 BLOCKS + { + snprintf(vmu_file_path, sizeof(vmu_file_path), + "%s/apps/%s/resources/empty_vmu_1024kb.vmd", + GetDefaultDir(menu_data.current_dev), "games_menu"); + } + break; + } + + int source_size = FileSize(vmu_file_path); + if (source_size > 0 && private_size != source_size) + { + if (private_size > 0) + { + fs_unlink(private_path); + } + + CopyFile(vmu_file_path, private_path, 0); + return 0; + } + else { + return 1; + } + } + + return 0; +} + +static int AppCompareFirmwares(const void *a, const void *b) +{ + const FirmwareStruct *left = (const FirmwareStruct *)a; + const FirmwareStruct *right = (const FirmwareStruct *)b; + + if (ContainsOnlyNumbers(left->file) && ContainsOnlyNumbers(right->file)) + { + return atoi(left->file) - atoi(right->file); + } + else if (ContainsOnlyNumbers(left->file)) + { + return -1; + } + else if (ContainsOnlyNumbers(right->file)) + { + return 1; + } + + return strcmp(left->file, right->file); +} + +bool LoadFirmwareFiles() +{ + char fw_dir[128]; + memset(fw_dir, 0, sizeof(fw_dir)); + sprintf(fw_dir, "%s/firmware/isoldr", GetDefaultDir(menu_data.current_dev)); + + if (menu_data.firmware_array != NULL) + { + free(menu_data.firmware_array); + menu_data.firmware_array = NULL; + menu_data.firmware_array_count = 0; + } + + file_t fd = fs_open(fw_dir, O_RDONLY | O_DIR); + if (fd == FILEHND_INVALID) + return false; + + dirent_t *ent = NULL; + char *file_type = NULL; + + while ((ent = fs_readdir(fd)) != NULL) + { + if (ent->name[0] == '.') + continue; + + file_type = strrchr(ent->name, '.'); + + if (strcasecmp(file_type, ".bin") == 0 && (strlen(ent->name) - 4) <= FIRMWARE_SIZE) + { + menu_data.firmware_array_count++; + + if (menu_data.firmware_array == NULL) + { + menu_data.firmware_array = (FirmwareStruct *)malloc(sizeof(FirmwareStruct)); + } + else + { + menu_data.firmware_array = (FirmwareStruct *)realloc(menu_data.firmware_array, menu_data.firmware_array_count * sizeof(FirmwareStruct)); + } + + memset(&menu_data.firmware_array[menu_data.firmware_array_count-1], 0, sizeof(FirmwareStruct)); + memset(menu_data.firmware_array[menu_data.firmware_array_count-1].file, 0, FIRMWARE_SIZE+1); + strncpy(menu_data.firmware_array[menu_data.firmware_array_count-1].file, ent->name, strlen(ent->name) - 4); + } + } + fs_close(fd); + + if (menu_data.firmware_array_count > 0) + { + qsort(menu_data.firmware_array, menu_data.firmware_array_count, sizeof(FirmwareStruct), AppCompareFirmwares); + } + + return true; +} + +PresetStruct* GetDefaultPresetGame(const char* full_path_game, SectorDataStruct *sector_data) +{ + PresetStruct *preset = NULL; + + if (full_path_game) + { + preset = (PresetStruct *)malloc(sizeof(PresetStruct)); + memset(preset, 0, sizeof(PresetStruct)); + + bool free_sector_data = false; + if (sector_data == NULL) + { + if (fs_iso_mount("/iso_game", full_path_game) == 0) + { + sector_data = (SectorDataStruct *)malloc(sizeof(SectorDataStruct)); + memset(sector_data, 0, sizeof(SectorDataStruct)); + GetMD5HashISO("/iso_game", sector_data); + fs_iso_unmount("/iso_game"); + free_sector_data = true; + } + } + + if (CanUseTrueAsyncDMA(sector_data->sector_size, GetDeviceType(full_path_game), sector_data->image_type)) + { + preset->use_dma = 1; + preset->emu_async = 0; + sprintf(preset->memory, "0x%08lx", (unsigned long)ISOLDR_DEFAULT_ADDR_MIN_GINSU); + + } + else + { + preset->use_dma = 0; + preset->emu_async = 8; + sprintf(preset->memory, "0x%08lx", (unsigned long)ISOLDR_DEFAULT_ADDR_LOW); + } + + char title[32]; + memset(title, 0, sizeof(title)); + + ipbin_meta_t *ipbin = (ipbin_meta_t *)sector_data->boot_sector; + TrimSpaces(ipbin->title, title, sizeof(ipbin->title)); + + if (free_sector_data) + { + free(sector_data); + } + + preset->alt_read = 0; + strcpy(preset->device, "auto"); + preset->emu_cdda = CDDA_MODE_DISABLED; + preset->use_irq = 0; + preset->emu_vmu = 0; + preset->scr_hotkey = 0; + preset->boot_mode = BOOT_MODE_DIRECT; + strcpy(preset->title, title); + + // Enable CDDA if present + if (full_path_game[0] != '\0') + { + char *track_file_path = NULL; + size_t track_size = GetCDDATrackFilename(4, full_path_game, &track_file_path); + + if (track_size && track_size < 30 * 1024 * 1024) + { + track_size = GetCDDATrackFilename(6, full_path_game, &track_file_path); + } + + if (track_size > 0) + { + preset->use_irq = 1; + preset->emu_cdda = 1; + } + + if (track_file_path) + { + free(track_file_path); + } + } + + preset->heap = strtoul(preset->heap_memory, NULL, 16); + PatchParseText(preset); + } + + return preset; +} + +PresetStruct* LoadPresetGame(int game_index) +{ + PresetStruct *preset = NULL; + + if (game_index >= 0) + { + preset = (PresetStruct *)malloc(sizeof(PresetStruct)); + memset(preset, 0, sizeof(PresetStruct)); + + preset->game_index = game_index; + const char *full_path_game = GetFullGamePathByIndex(game_index); + + char *full_preset_file_name = NULL; + SectorDataStruct sector_data; + memset(§or_data, 0, sizeof(SectorDataStruct)); + + if (fs_iso_mount("/iso_game", full_path_game) == 0) + { + GetMD5HashISO("/iso_game", §or_data); + fs_iso_unmount("/iso_game"); + + int app_name_count = 0; + const char *app_name_array[3] = { "games_menu", "iso_loader", NULL }; + while (app_name_array[app_name_count] != NULL) + { + if (GetDeviceType(full_path_game) == APP_DEVICE_SD) + { + full_preset_file_name = MakePresetFilename(GetDefaultDir(APP_DEVICE_SD), GetDefaultDir(APP_DEVICE_SD), sector_data.md5, app_name_array[app_name_count]); + if (!FileExists(full_preset_file_name) && menu_data.ide) + { + full_preset_file_name = MakePresetFilename(GetDefaultDir(APP_DEVICE_IDE), GetDefaultDir(APP_DEVICE_SD), sector_data.md5, app_name_array[app_name_count]); + } + } + else if (GetDeviceType(full_path_game) == APP_DEVICE_IDE) + { + full_preset_file_name = MakePresetFilename(GetDefaultDir(APP_DEVICE_IDE), GetDefaultDir(APP_DEVICE_IDE), sector_data.md5, app_name_array[app_name_count]); + } + else + { + full_preset_file_name = MakePresetFilename(getenv("PATH"), GetDefaultDir(menu_data.current_dev), sector_data.md5, app_name_array[app_name_count]); + } + + if (FileSize(full_preset_file_name) >= 5) + { + break; + } + + ++app_name_count; + } + + // ds_printf("PresetFileName: %s\n", full_preset_file_name); + } + + if (full_preset_file_name != NULL) + { + memset(preset->preset_file_name, 0, sizeof(preset->preset_file_name)); + strcpy(preset->preset_file_name, strrchr(full_preset_file_name, '/') + 1); + } + + if (FileSize(full_preset_file_name) < 5) + { + full_preset_file_name = NULL; + } + + preset->emu_async = 16; + preset->boot_mode = BOOT_MODE_DIRECT; + preset->bin_type = BIN_TYPE_AUTO; + preset->cdda = CDDA_MODE_DISABLED; + strcpy(preset->title, ""); + strcpy(preset->device, ""); + sprintf(preset->memory, "0x%08lx", (unsigned long)ISOLDR_DEFAULT_ADDR_MIN); + + strcpy(preset->heap_memory, ""); + strcpy(preset->bin_file, ""); + + memset(preset->patch_a, 0, 2 * 10); + memset(preset->patch_v, 0, 2 * 10); + memset(preset->pa, 0, 2 * sizeof(uint32)); + memset(preset->pv, 0, 2 * sizeof(uint32)); + + char scr_hotkey[4]; + memset(scr_hotkey, 0, sizeof(scr_hotkey)); + + isoldr_conf options[] = { + {"dma", CONF_INT, (void *)&preset->use_dma}, + {"altread", CONF_INT, (void *)&preset->alt_read}, + {"cdda", CONF_ULONG, (void *)&preset->emu_cdda}, + {"irq", CONF_INT, (void *)&preset->use_irq}, + {"low", CONF_INT, (void *)&preset->low}, + {"vmu", CONF_INT, (void *)&preset->emu_vmu}, + {"scrhotkey", CONF_STR, (void *)scr_hotkey}, + {"heap", CONF_STR, (void *)&preset->heap_memory}, + {"memory", CONF_STR, (void *)preset->memory}, + {"async", CONF_INT, (void *)&preset->emu_async}, + {"mode", CONF_INT, (void *)&preset->boot_mode}, + {"type", CONF_INT, (void *)&preset->bin_type}, + {"file", CONF_STR, (void *)preset->bin_file}, + {"title", CONF_STR, (void *)preset->title}, + {"device", CONF_STR, (void *)preset->device}, + {"fastboot", CONF_INT, (void *)&preset->fastboot}, + {"pa1", CONF_STR, (void *)preset->patch_a[0]}, + {"pv1", CONF_STR, (void *)preset->patch_v[0]}, + {"pa2", CONF_STR, (void *)preset->patch_a[1]}, + {"pv2", CONF_STR, (void *)preset->patch_v[1]}, + {NULL, CONF_END, NULL}}; + + char preset_file_name[100]; + memset(preset_file_name, 0, sizeof(preset_file_name)); + strcpy(preset_file_name, preset->preset_file_name); + + if (full_preset_file_name != NULL) + { + if (ConfigParse(options, full_preset_file_name) == 0) + { + preset->heap = strtoul(preset->heap_memory, NULL, 16); + PatchParseText(preset); + + if (scr_hotkey[0] != '\0' && scr_hotkey[0] != ' ') + { + preset->scr_hotkey = strtol(scr_hotkey, NULL, 16); + } + } + else + { + free(preset); + preset = GetDefaultPresetGame(full_path_game, §or_data); + preset->game_index = game_index; + strcpy(preset->preset_file_name, preset_file_name); + } + + if (preset->scr_hotkey) + { + preset->screenshot = 1; + preset->use_irq = 1; + } + + if (preset->emu_vmu) + { + preset->use_irq = 1; + snprintf(preset->vmu_file, sizeof(preset->vmu_file), "vmu%03ld.vmd", preset->emu_vmu); + + char dst_path[NAME_MAX]; + snprintf(dst_path, NAME_MAX, "%s/%s", GetFolderPathFromFile(full_path_game), preset->vmu_file); + + int dst_size = FileSize(dst_path); + + menu_data.vmu_mode = 0; + if (dst_size <= 0) + { + preset->vmu_mode = 1; + } + else if(dst_size < 256 << 10) + { + preset->vmu_mode = 2; + } + else + { + preset->vmu_mode = 3; + } + menu_data.vmu_mode = preset->vmu_mode; + } + else + { + preset->vmu_mode = 0; + } + + if (preset->device[0] == '\0') + { + strcpy(preset->device, "auto"); + } + } + else + { + free(preset); + preset = GetDefaultPresetGame(full_path_game, §or_data); + preset->game_index = game_index; + strcpy(preset->preset_file_name, preset_file_name); + } + + if (preset->emu_cdda) + { + preset->cdda = 1; + } + } + + return preset; +} + +void PatchParseText(PresetStruct *preset) +{ + if (preset) + { + for(int i = 0; i < 2; ++i) + { + if (preset->patch_a[i][1] != '0' && strlen(preset->patch_a[i]) == 8 && strlen(preset->patch_v[i])) + { + preset->pa[i] = strtoul(preset->patch_a[i], NULL, 16); + preset->pv[i] = strtoul(preset->patch_v[i], NULL, 16); + } + else + { + preset->pa[i] = 0; + preset->pv[i] = 0; + } + } + } +} + +isoldr_info_t* ParsePresetToIsoldr(int game_index, PresetStruct *preset) +{ + isoldr_info_t *isoldr = NULL; + if (game_index >= 0 && preset != NULL && preset->game_index >= 0) + { + const char *full_path_game = GetFullGamePathByIndex(game_index); + + if ((isoldr = isoldr_get_info(full_path_game, 0)) == NULL) + { + return NULL; + } + + isoldr->use_dma = preset->use_dma; + isoldr->alt_read = preset->alt_read; + isoldr->emu_async = preset->emu_async; + isoldr->emu_cdda = preset->emu_cdda; + isoldr->use_irq = preset->use_irq; + isoldr->scr_hotkey = preset->scr_hotkey; + isoldr->heap = preset->heap; + isoldr->boot_mode = preset->boot_mode; + isoldr->fast_boot = preset->fastboot; + isoldr->emu_vmu = preset->emu_vmu; + + if (preset->low) + { + isoldr->syscalls = 1; + } + + if (preset->bin_type != BIN_TYPE_AUTO) + { + isoldr->exec.type = preset->bin_type; + } + + if (strlen(preset->device) > 0) + { + if (strncmp(preset->device, "auto", 4) != 0) + { + strcpy(isoldr->fs_dev, preset->device); + } + else + { + strcpy(isoldr->fs_dev, "auto"); + } + } + else + { + strcpy(isoldr->fs_dev, "auto"); + } + + for(int i = 0; i < sizeof(isoldr->patch_addr) >> 2; ++i) + { + if(preset->pa[i] & 0xffffff) + { + isoldr->patch_addr[i] = preset->pa[i]; + isoldr->patch_value[i] = preset->pa[i]; + } + } + + if(preset->alt_boot && menu_data.games_array[game_index].folder) + { + char game_path[NAME_MAX]; + memset(game_path, 0, NAME_MAX); + snprintf(game_path, NAME_MAX, "%s/%s", GetGamesPath(menu_data.games_array[game_index].device), menu_data.games_array[game_index].folder); + isoldr_set_boot_file(isoldr, game_path, ALT_BOOT_FILE); + } + } + + return isoldr; +} + +bool SavePresetGame(PresetStruct *preset) +{ + bool saved = false; + + if (menu_data.current_dev != APP_DEVICE_SD && menu_data.current_dev != APP_DEVICE_IDE) + { + return false; + } + + if (preset != NULL && preset->game_index >= 0) + { + file_t fd; + char result[1024]; + char memory[24]; + int async = 0, type = 0, mode = 0; + uint32 heap = HEAP_MODE_AUTO; + uint32 cdda_mode = CDDA_MODE_DISABLED; + + char preset_file_name[NAME_MAX]; + memset(preset_file_name, 0, NAME_MAX); + + snprintf(preset_file_name, sizeof(preset_file_name), + "%s/apps/%s/presets/%s", + GetDefaultDir(menu_data.current_dev), "games_menu", preset->preset_file_name); + + fd = fs_open(preset_file_name, O_CREAT | O_TRUNC | O_WRONLY); + + if(fd == FILEHND_INVALID) + { + return false; + } + + memset(result, 0, sizeof(result)); + memset(memory, 0, sizeof(memory)); + + async = preset->emu_async; + type = preset->bin_type; + mode = preset->boot_mode; + + if (preset->cdda) + { + cdda_mode = preset->emu_cdda; + } + + if (strcasecmp(preset->memory, "0x8c") == 0) + { + snprintf(memory, sizeof(memory), "%s%s", preset->memory, preset->custom_memory); + } + else + { + strcpy(memory, preset->memory); + } + + heap = preset->heap; + + if (preset->device[0] == '\0' || preset->device[0] == ' ') + { + strcpy(preset->device, "auto"); + } + + if (preset->emu_vmu > 0 || preset->scr_hotkey) + { + preset->use_irq = 1; + } + + snprintf(result, sizeof(result), + "title = %s\ndevice = %s\ndma = %d\nasync = %d\ncdda = %08lx\n" + "irq = %d\nlow = %d\nheap = %08lx\nfastboot = %d\ntype = %d\nmode = %d\nmemory = %s\n" + "vmu = %d\nscrhotkey = %lx\naltread = %d\n" + "pa1 = %08lx\npv1 = %08lx\npa2 = %08lx\npv2 = %08lx\n", + preset->title, preset->device, preset->use_dma, async, + cdda_mode, preset->use_irq, preset->low, heap, + preset->fastboot, type, mode, memory, + (int)preset->emu_vmu, (uint32)preset->scr_hotkey, + preset->alt_read, + preset->pa[0], preset->pv[0], preset->pa[1], preset->pv[1]); + + if (preset->alt_boot) + { + strcat(result, "file = " ALT_BOOT_FILE "\n"); + } + + fs_write(fd, result, strlen(result)); + fs_close(fd); + + char isoldr_preset_file_name[NAME_MAX]; + memset(isoldr_preset_file_name, 0, NAME_MAX); + + snprintf(isoldr_preset_file_name, sizeof(isoldr_preset_file_name), + "%s/apps/%s/presets/%s", + GetDefaultDir(menu_data.current_dev), "iso_loader", preset->preset_file_name); + + // COPY TO ISO LOADER FOLDER + CopyFile(preset_file_name, isoldr_preset_file_name, 0); + saved = true; + } + + return saved; +} + +void LoadDefaultMenuConfig() +{ + menu_data.app_config.initial_view = menu_data.menu_type = MT_PLANE_TEXT; + menu_data.app_config.save_preset = 1; + menu_data.app_config.cover_background = 1; + menu_data.app_config.change_page_with_pad = 0; +} + +bool LoadMenuConfig() +{ + char file_name[100]; + snprintf(file_name, sizeof(file_name), "%s/%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu", "menu_games.cfg"); + + GenericConfigStruct options[] = + { + { "initial_view", CONF_INT, (void *)&menu_data.app_config.initial_view }, + { "save_preset", CONF_INT, (void *)&menu_data.app_config.save_preset }, + { "cover_background", CONF_INT, (void *)&menu_data.app_config.cover_background }, + { "change_page_with_pad", CONF_INT, (void *)&menu_data.app_config.change_page_with_pad } + }; + + if (ConfigParse(options, file_name) == -1) + { + LoadDefaultMenuConfig(); + ParseMenuConfigToPresentation(); + + return false; + } + + ParseMenuConfigToPresentation(); + return true; +} + +void ParseMenuConfigToPresentation() +{ + if (menu_data.app_config.initial_view > 0) + { + menu_data.menu_type = menu_data.app_config.initial_view; + } + else + { + menu_data.app_config.initial_view = menu_data.menu_type = MT_PLANE_TEXT; + } + + menu_data.save_preset = (menu_data.app_config.save_preset == 1); + menu_data.cover_background = (menu_data.app_config.cover_background == 1); + menu_data.change_page_with_pad = (menu_data.app_config.change_page_with_pad == 1); +} + +void ParsePresentationToMenuConfig() +{ + menu_data.app_config.initial_view = menu_data.menu_type; + menu_data.app_config.save_preset = (menu_data.save_preset ? 1 : 0); + menu_data.app_config.cover_background = (menu_data.cover_background ? 1 : 0); + menu_data.app_config.change_page_with_pad = (menu_data.change_page_with_pad ? 1 : 0); +} + +bool SaveMenuConfig() +{ + if (menu_data.current_dev != APP_DEVICE_SD && menu_data.current_dev != APP_DEVICE_IDE) + { + return false; + } + + file_t fd; + char file_name[100]; + char result[1024]; + + ParsePresentationToMenuConfig(); + + snprintf(file_name, sizeof(file_name), "%s/%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu", "menu_games.cfg"); + fd = fs_open(file_name, O_CREAT | O_TRUNC | O_WRONLY); + + if(fd == FILEHND_INVALID) { + return false; + } + + snprintf(result, sizeof(result), + "initial_view = %d\nsave_preset = %d\ncover_background = %d\nchange_page_with_pad = %d", + menu_data.app_config.initial_view, menu_data.app_config.save_preset, menu_data.app_config.cover_background, + menu_data.app_config.change_page_with_pad); + + fs_write(fd, result, strlen(result)); + fs_close(fd); + + return true; +} + +bool SaveScannedCover() +{ + if (menu_data.current_dev != APP_DEVICE_SD && menu_data.current_dev != APP_DEVICE_IDE) + { + return false; + } + + char file_name[100]; + snprintf(file_name, sizeof(file_name), "%s/%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu", "scanned_cover.log"); + + file_t fd = fs_open(file_name, O_WRONLY | O_CREAT | O_TRUNC); + if (fd == FILEHND_INVALID) + { + return false; + } + + fs_write(fd, &menu_data.cover_scanned_app, sizeof(CoverScannedStruct)); + fs_close(fd); + + return true; +} + +bool LoadScannedCover() +{ + char file_name[100]; + snprintf(file_name, sizeof(file_name), "%s/%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu", "scanned_cover.log"); + + file_t fd = fs_open(file_name, O_RDONLY); + if (fd == FILEHND_INVALID) + { + return false; + } + + memset(&menu_data.cover_scanned_app, 0, sizeof(CoverScannedStruct)); + + size_t file_size = fs_total(fd); + if (file_size > 0) + { + unsigned char *buffer = (unsigned char *)malloc(sizeof(CoverScannedStruct)); + fs_read(fd, buffer, sizeof(CoverScannedStruct)); + memcpy(&menu_data.cover_scanned_app, buffer, sizeof(CoverScannedStruct)); + free(buffer); + } + fs_close(fd); + + if (file_size <= 0) + { + memset(&menu_data.cover_scanned_app, 0, sizeof(CoverScannedStruct)); + fs_unlink(file_name); + } + + if (!HasAnyCover()) + { + memset(&menu_data.cover_scanned_app, 0, sizeof(CoverScannedStruct)); + fs_unlink(file_name); + } + + return true; +} + +void CleanIncompleteCover() +{ + if (menu_data.cover_scanned_app.last_game_status == CSE_PROCESSING) + { + char game_cover_path[NAME_MAX]; + uint16 image_count = 1; + + while (image_count < IMAGE_TYPE_SUPPORTED) + { + if (IMAGE_TYPE_SUPPORTED & image_count) + { + memset(game_cover_path, 0, sizeof(game_cover_path)); + snprintf(game_cover_path, sizeof(game_cover_path), "%s/%s%s", GetCoversPath(menu_data.current_dev), menu_data.cover_scanned_app.last_game_scanned, GetCoverExtensionFromType(image_count)); + if (FileExists(game_cover_path)) + { + fs_unlink(game_cover_path); + } + + strcat(game_cover_path, ".tmp"); + if (FileExists(game_cover_path)) + { + fs_unlink(game_cover_path); + } + } + + image_count = image_count<<1; + } + } +} + +void OptimizeGameCovers() +{ + char *game_cover_path = NULL; + char *image_type = NULL; + char *game_without_extension = NULL; + kos_img_t kimg; + + for (int icount = 0; icount < menu_data.games_array_count; icount++) + { + if (menu_data.stop_optimize_game_cover || menu_data.finished_menu) break; + + if (CheckCover(icount, MT_PLANE_TEXT) == SC_EXISTS) + { + GetCoverName(icount, &game_without_extension); + menu_data.send_message_optimizer("Check COVER: %s", game_without_extension); + + if (CheckCover(icount, MT_IMAGE_TEXT_64_5X2) != SC_EXISTS || CheckCover(icount, MT_IMAGE_128_4X3) != SC_EXISTS) + { + if (GetGameCoverPath(icount, &game_cover_path, MT_PLANE_TEXT)) + { + image_type = strrchr(game_cover_path, '.'); + + if ((strcasecmp(image_type, ".png") == 0 && png_decode(game_cover_path, &kimg) == 0) + || (strcasecmp(image_type, ".jpg") == 0 && jpg_decode(game_cover_path, &kimg) == 0)) + { + menu_data.send_message_optimizer ("Optimizing cover: %s", game_without_extension); + OptimizeCover(icount, game_without_extension, &kimg, strcasecmp(image_type, ".png") == 0 ? true : false); + kos_img_free(&kimg, 0); + } + + free(game_cover_path); + game_cover_path = NULL; + } + } + + menu_data.send_message_optimizer("%s", ""); + if (game_without_extension != NULL) + { + free(game_without_extension); + game_without_extension = NULL; + } + } + } + + image_type = NULL; +} + +void OptimizeCover(int game_index, const char *game_name, kos_img_t *img, bool is_alpha) +{ + if (img != NULL && img->data) + { + bool optimize_to_128 = img->w > 128; + bool optimize_to_64 = img->w > 64; + char new_cover_path[NAME_MAX]; + char cover_folder[NAME_MAX]; + int image_size = 0; + + for (int16 imenu = 2; imenu <= MAX_MENU; imenu++) + { + if ((optimize_to_64 && imenu == MT_IMAGE_TEXT_64_5X2) || (optimize_to_128 && imenu == MT_IMAGE_128_4X3)) + { + memset(cover_folder, 0, NAME_MAX); + memset(new_cover_path, 0, NAME_MAX); + + image_size = (imenu == MT_IMAGE_TEXT_64_5X2 ? 64 : 128); + + snprintf(cover_folder, NAME_MAX, "%s%s", GetCoversPath(menu_data.current_dev), GetCoverFolder(imenu)); + if (DirExists(cover_folder) == 0) + { + fs_mkdir(cover_folder); + } + + snprintf(new_cover_path, NAME_MAX, "%s/%s%s", cover_folder, game_name, is_alpha ? GetCoverExtensionFromType(IT_PNG) : GetCoverExtensionFromType(IT_JPG)); + if (copy_image_memory_to_file(img, new_cover_path, true, image_size, image_size)) + { + CleanCoverType(game_index, imenu); + SetCoverType(game_index, imenu, is_alpha ? IT_PNG : IT_JPG); + menu_data.games_array[game_index].exists_cover[imenu - 1] = SC_EXISTS; + } + } + } + } +} + +bool ExtractPVRCover(int game_index) +{ + bool extracted_cover = false; + if (game_index >= 0 && (menu_data.current_dev == APP_DEVICE_SD || menu_data.current_dev == APP_DEVICE_IDE)) + { + if (!menu_data.games_array[game_index].check_pvr || menu_data.games_array[game_index].exists_cover[MT_PLANE_TEXT-1] != SC_DEFAULT) + { + return false; + } + + char *game_without_extension = NULL; + const char *full_game_path = GetFullGamePathByIndex(game_index); + menu_data.games_array[game_index].check_pvr = false; + + GetCoverName(game_index, &game_without_extension); + menu_data.send_message_scan("Mounting GAME: %s", game_without_extension); + + if(fs_iso_mount("/iso_cover", full_game_path) == 0) + { + char pvr_path[30]; + memset(pvr_path, 0, 30); + strcpy(pvr_path, "/iso_cover/0GDTEX.PVR"); + + menu_data.send_message_scan("Extracting PVR: %s", game_without_extension); + menu_data.cover_scanned_app.last_game_status = CSE_PROCESSING; + SaveScannedCover(); + + kos_img_t kimg; + if(pvr_decode(pvr_path, &kimg) == 0) + { + uint16 image_type = 0; + if (kimg.fmt != TFM_RGB565 && menu_data.convert_pvr_to_png) + { + image_type = IT_PNG; + } + else + { + image_type = IT_JPG; + } + + if (image_type > 0) + { + char game_cover_path[NAME_MAX]; + memset(game_cover_path, 0, NAME_MAX); + snprintf(game_cover_path, NAME_MAX, "%s/%s%s", GetCoversPath(menu_data.current_dev), game_without_extension, GetCoverExtensionFromType(image_type)); + + if ( (image_type == IT_PNG && img_to_png(&kimg, game_cover_path, 0, 0)) + || (image_type == IT_JPG && img_to_jpg(&kimg, game_cover_path, 0, 0, 100)) ) + { + menu_data.games_array[game_index].exists_cover[MT_PLANE_TEXT-1] = SC_EXISTS; + SetCoverType(game_index, MT_PLANE_TEXT, image_type); + + menu_data.send_message_scan("Optimizing cover: %s", game_without_extension); + OptimizeCover(game_index, game_without_extension, &kimg, (image_type == IT_PNG)); + + menu_data.games_array[game_index].is_pvr_cover = true; + extracted_cover = true; + } + } + + kos_img_free(&kimg, 0); + menu_data.cover_scanned_app.last_game_status = CSE_COMPLETED; + } + + fs_iso_unmount("/iso_cover"); + } + + if (game_without_extension) + { + free(game_without_extension); + game_without_extension = NULL; + } + } + + return extracted_cover; +} + +void* OptimizeCoverThread(void *param) +{ + OptimizeGameCovers(); + menu_data.post_optimizer_cover(); + + return NULL; +} + +void* LoadPVRCoverThread(void *params) +{ + menu_data.cover_scanned_app.scan_count++; + + bool new_cover = false; + if (menu_data.rescan_covers) + { + menu_data.rescan_covers = false; + menu_data.cover_scanned_app.last_game_index = 1; + } + + if (menu_data.cover_scanned_app.last_game_index == 0) + { + menu_data.cover_scanned_app.last_game_index = 1; + } + + char *game_without_extension = NULL; + for (int icount = menu_data.cover_scanned_app.last_game_index-1; icount < menu_data.games_array_count; icount++) + { + if (menu_data.stop_load_pvr_cover || menu_data.finished_menu) break; + + GetCoverName(icount, &game_without_extension); + + memset(menu_data.cover_scanned_app.last_game_scanned, 0, sizeof(menu_data.cover_scanned_app.last_game_scanned)); + strncpy(menu_data.cover_scanned_app.last_game_scanned, game_without_extension, strlen(game_without_extension)); + + menu_data.send_message_scan("Check game: %s", game_without_extension); + + if (CheckCover(icount, MT_PLANE_TEXT) == SC_DEFAULT) + { + // CHECK AGAIN TO SEE IF IT WAS NOT DOWNLOADED IN PVR + menu_data.games_array[icount].exists_cover[MT_PLANE_TEXT-1] = SC_WITHOUT_SEARCHING; + if (CheckCover(icount, MT_PLANE_TEXT) == SC_DEFAULT) + { + ExtractPVRCover(icount); + new_cover = true; + } + else + { + menu_data.games_array[icount].is_pvr_cover = true; + menu_data.cover_scanned_app.last_game_status = CSE_EXISTS; + } + } + else + { + menu_data.cover_scanned_app.last_game_status = CSE_EXISTS; + } + + menu_data.cover_scanned_app.last_game_index = (uint32)icount + 1; + SaveScannedCover(); + } + + if (game_without_extension != NULL) + { + free(game_without_extension); + game_without_extension = NULL; + } + + menu_data.post_pvr_cover(new_cover); + + return NULL; +} + +static int AppCompareGames(const void *a, const void *b) +{ + const GameItemStruct *left = (const GameItemStruct *)a; + const GameItemStruct *right = (const GameItemStruct *)b; + int cmp = 0; + + char *left_compare = malloc(NAME_MAX); + char *right_compare = malloc(NAME_MAX); + + memset(left_compare, 0, NAME_MAX); + memset(right_compare, 0, NAME_MAX); + + if (left->is_folder_name && right->is_folder_name) + { + cmp = strcmp(left->folder_name, right->folder_name); + } + else if (left->is_folder_name) + { + strncpy(right_compare, right->game, strlen(right->game) - 4); + cmp = strcmp(left->folder_name, right_compare); + } + else if (right->is_folder_name) + { + strncpy(left_compare, left->game, strlen(left->game) - 4); + cmp = strcmp(left_compare, right->folder_name); + } + else + { + strncpy(left_compare, left->game, strlen(left->game) - 4); + strncpy(right_compare, right->game, strlen(right->game) - 4); + + cmp = strcmp(left_compare, right_compare); + } + + if (cmp == 0) + { + int left_device = (left->device == APP_DEVICE_IDE ? 1 : 2); + int right_device = (right->device == APP_DEVICE_IDE ? 1 : 2); + cmp = left_device - right_device; + } + + free(left_compare); + free(right_compare); + + return cmp; +} + +bool CheckGdiOptimized(int game_index) +{ + bool optimized = false; + + if (game_index >= 0) + { + if (!menu_data.games_array[game_index].check_optimized) + { + menu_data.games_array[game_index].check_optimized = true; + optimized = menu_data.games_array[game_index].is_gdi_optimized = IsGdiOptimized(GetFullGamePathByIndex(game_index)); + } + else + { + optimized = menu_data.games_array[game_index].is_gdi_optimized; + } + } + + return optimized; +} + +bool IsUniqueFileGame(const char *full_path_folder) +{ + file_t fd = fs_open(full_path_folder, O_RDONLY | O_DIR); + if (fd == FILEHND_INVALID) + return false; + + int file_count = 0; + char *file_type = NULL; + dirent_t *ent = NULL; + + while ((ent = fs_readdir(fd)) != NULL) + { + if (ent->name[0] == '.') + continue; + + file_type = strrchr(ent->name, '.'); + if (strcasecmp(file_type, ".gdi") == 0 + || strcasecmp(file_type, ".cdi") == 0 + || (strcasecmp(file_type, ".iso") == 0 && !(strncasecmp(ent->name, "track", strlen("track")) == 0)) + || strcasecmp(file_type, ".cso") == 0) + { + file_count++; + + if (file_count > 1) + { + break; + } + } + } + ent = NULL; + fs_close(fd); + + return !(file_count > 1); +} + +bool HasAnyCover() +{ + bool any_cover = false; + file_t fd = fs_open(GetCoversPath(menu_data.current_dev), O_RDONLY | O_DIR); + if (fd == FILEHND_INVALID) + return false; + + char *file_type = NULL; + dirent_t *ent = NULL; + + while ((ent = fs_readdir(fd)) != NULL) + { + if (ent->name[0] == '.') + continue; + + file_type = strrchr(ent->name, '.'); + if (strcasecmp(file_type, ".jpg") == 0 + || strcasecmp(file_type, ".png") == 0 + || strcasecmp(file_type, ".pvr") == 0) + { + any_cover = true; + break; + } + } + ent = NULL; + fs_close(fd); + + return any_cover; +} + +static int AppCompareCovers(const void *a, const void *b) +{ + const CoverStruct *left = (const CoverStruct *)a; + const CoverStruct *right = (const CoverStruct *)b; + return strcasecmp(left->cover, right->cover); +} + +void RetrieveCovers(uint8 device, int menu_type) +{ + if (menu_type > 0) + { + char game_cover_path[NAME_MAX]; + snprintf(game_cover_path, NAME_MAX, "%s%s", GetCoversPath(device), GetCoverFolder(menu_type)); + file_t fd = fs_open(game_cover_path, O_RDONLY | O_DIR); + if (fd == FILEHND_INVALID) + return; + + dirent_t *ent = NULL; + char cover[NAME_MAX]; + char *file_type = NULL; + uint8 dw_image_type = 0; + CoverStruct cover_key; + CoverStruct *cover_found = NULL; + CoverStruct *covers_array = NULL; + + int covers_array_count = 0; + cover_key.cover = (char *)malloc(NAME_MAX); + + while ((ent = fs_readdir(fd)) != NULL) + { + if (ent->name[0] == '.') + continue; + + cover_found = NULL; + dw_image_type = 0; + memset(cover, 0, NAME_MAX); + file_type = strrchr(ent->name, '.'); + + if (strcasecmp(file_type, ".png") == 0) + { + dw_image_type = IT_PNG; + } + else if (strcasecmp(file_type, ".jpg") == 0) + { + dw_image_type = IT_JPG; + } + else if (strcasecmp(file_type, ".bpm") == 0) + { + dw_image_type = IT_BPM; + } + else if (strcasecmp(file_type, ".pvr") == 0) + { + dw_image_type = IT_PVR; + } + else if (strcasecmp(file_type, ".kmg") == 0) + { + dw_image_type = IT_KMG; + } + else + { + continue; + } + + strncpy(cover, ent->name, strlen(ent->name)); + + if (menu_data.covers_array_count > 0) + { + memset(cover_key.cover, 0, NAME_MAX); + strcpy(cover_key.cover, cover); + + cover_found = (CoverStruct *)bsearch(&cover_key, menu_data.covers_array, menu_data.covers_array_count, sizeof(CoverStruct), AppCompareCovers); + } + + if (cover_found != NULL) + { + cover_found->menu_type |= (1<<(menu_type-1)); + cover_found->image_type[menu_type-1] |= dw_image_type; + cover_found->device |= (1<<(device-1)); + continue; + } + else + { + covers_array_count++; + if (covers_array == NULL) + { + covers_array = (CoverStruct *)malloc(sizeof(CoverStruct)); + } + else + { + covers_array = (CoverStruct *)realloc(covers_array, covers_array_count * sizeof(CoverStruct)); + } + + memset(&covers_array[covers_array_count - 1], 0, sizeof(CoverStruct)); + + covers_array[covers_array_count-1].cover = (char *)malloc(strlen(cover)+1); + memset(covers_array[covers_array_count-1].cover, 0, strlen(cover)+1); + strncpy(covers_array[covers_array_count-1].cover, cover, strlen(cover)); + + covers_array[covers_array_count-1].menu_type = 0; + covers_array[covers_array_count-1].menu_type |= (1<<(menu_type-1)); + + // THIS VALUE IS TO AVOID SAVING EVERY GAME THAT CONTAINS MULTIPLE EXTENSIONS, NOT USED YET. + covers_array[covers_array_count-1].image_type[menu_type-1] = 0; + covers_array[covers_array_count-1].image_type[menu_type-1] |= dw_image_type; + + covers_array[covers_array_count-1].device = 0; + covers_array[covers_array_count-1].device |= (1<<(device-1)); + } + } + + if (covers_array_count > 0) + { + int current_pos = menu_data.covers_array_count; + menu_data.covers_array_count += covers_array_count; + + if (menu_data.covers_array == NULL) + { + menu_data.covers_array = (CoverStruct *)malloc(menu_data.covers_array_count * sizeof(CoverStruct)); + } + else + { + menu_data.covers_array = (CoverStruct *)realloc(menu_data.covers_array, menu_data.covers_array_count * sizeof(CoverStruct)); + } + + memcpy(&menu_data.covers_array[current_pos], covers_array, covers_array_count * sizeof(CoverStruct)); + free(covers_array); + + qsort(menu_data.covers_array, menu_data.covers_array_count, sizeof(CoverStruct), AppCompareCovers); + } + + free(cover_key.cover); + fs_close(fd); + } +} + +void RetrieveGamesRecursive(const char *full_path_folder, const char *folder, int level) +{ + file_t fd = fs_open(full_path_folder, O_RDONLY | O_DIR); + if (fd == FILEHND_INVALID) + return; + + dirent_t *ent = NULL; + char game[NAME_MAX]; + char new_folder[NAME_MAX]; + char *file_type = NULL; + bool is_folder_name = false; + int unique_file = -1; + int gdi_index = -1; + bool gdi_optimized = false; + int gdi_cdda = CCGE_NOT_CHECKED; + + while ((ent = fs_readdir(fd)) != NULL) + { + if (ent->name[0] == '.') + continue; + + // SKIP FULL NAMES WITH A LENGTH LONGER THAN NAMEMAX + if ((full_path_folder && (strlen(full_path_folder) + strlen(ent->name)) > NAME_MAX) + || strlen(ent->name) > NAME_MAX) + continue; + + is_folder_name = false; + file_type = strrchr(ent->name, '.'); + memset(game, '\0', sizeof(game)); + + if (strcasecmp(file_type, ".gdi") == 0) + { + strcpy(game, ent->name); + is_folder_name = level > 0; + } + else if (strcasecmp(file_type, ".cdi") == 0) + { + strcpy(game, ent->name); + + if (unique_file == -1) + { + unique_file = level > 0 ? IsUniqueFileGame(full_path_folder) : false; + } + is_folder_name = (unique_file == 1); + } + else if (strcasecmp(file_type, ".iso") == 0 && !(strncasecmp(ent->name, "track", strlen("track")) == 0)) + { + strcpy(game, ent->name); + + if (unique_file == -1) + { + unique_file = level > 0 ? IsUniqueFileGame(full_path_folder) : false; + } + is_folder_name = (unique_file == 1); + } + else if (strcasecmp(file_type, ".cso") == 0) + { + strcpy(game, ent->name); + if (unique_file == -1) + { + unique_file = level > 0 ? IsUniqueFileGame(full_path_folder) : false; + } + is_folder_name = (unique_file == 1); + } + else if (strncasecmp(ent->name, "track", strlen("track")) == 0) + { + if (strncasecmp(ent->name, "track03.iso", strlen("track03.iso")) == 0) + { + gdi_optimized = true; + } + + if (gdi_cdda != CCGE_CDDA) + { + if (strncasecmp(ent->name, "track06.wav", strlen("track06.wav")) == 0 + || strncasecmp(ent->name, "track06.raw", strlen("track06.raw")) == 0) + { + gdi_cdda = CCGE_CDDA; + } + else if (strncasecmp(ent->name, "track04.wav", strlen("track04.wav")) == 0 + || strncasecmp(ent->name, "track04.raw", strlen("track04.raw")) == 0) + { + gdi_cdda = CCGE_CANDIDATE; + } + } + } + else + { + if (ent->attr != O_DIR) + continue; + + memset(new_folder, 0, NAME_MAX); + snprintf(new_folder, NAME_MAX, "%s/%s", full_path_folder, ent->name); + RetrieveGamesRecursive(new_folder, ent->name, level+1); + } + + if (game[0] != '\0') + { + for (char *c = game; (*c = toupper(*c)); ++c) + { + if (*c == 'a') + *c = 'A'; // Maniac Vera: BUG toupper in the letter a, it does not convert it + } + + menu_data.games_array_count++; + if (menu_data.games_array == NULL) + { + menu_data.games_array = (GameItemStruct *)malloc(sizeof(GameItemStruct)); + } + else + { + menu_data.games_array = (GameItemStruct *)realloc(menu_data.games_array, menu_data.games_array_count * sizeof(GameItemStruct)); + } + + memset(&menu_data.games_array[menu_data.games_array_count - 1], 0, sizeof(GameItemStruct)); + + menu_data.games_array[menu_data.games_array_count - 1].game = (char *)malloc(strlen(game) + 1); + memset(menu_data.games_array[menu_data.games_array_count - 1].game, 0, strlen(game) + 1); + strncpy(menu_data.games_array[menu_data.games_array_count - 1].game, game, strlen(game)); + + if (folder) + { + char *folder_game = malloc(NAME_MAX); + memset(folder_game, 0, NAME_MAX); + strcpy(folder_game, full_path_folder + strlen(GetGamesPath(GetDeviceType(full_path_folder))) + 1); + + for (char *c = folder_game; (*c = toupper(*c)); ++c) + { + if (*c == 'a') + *c = 'A'; // Maniac Vera: BUG toupper in the letter a, it does not convert it + } + + menu_data.games_array[menu_data.games_array_count - 1].folder = (char *)malloc(strlen(folder_game) + 1); + memset(menu_data.games_array[menu_data.games_array_count - 1].folder, 0, strlen(folder_game) + 1); + strcpy(menu_data.games_array[menu_data.games_array_count - 1].folder, folder_game); + + if (is_folder_name) + { + menu_data.games_array[menu_data.games_array_count - 1].folder_name = (char *)malloc(strlen(GetLastPart(folder_game, '/', 0)) + 1); + memset(menu_data.games_array[menu_data.games_array_count - 1].folder_name, 0, strlen(GetLastPart(folder_game, '/', 0)) + 1); + strcpy(menu_data.games_array[menu_data.games_array_count - 1].folder_name, GetLastPart(folder_game, '/', 0)); + } + + free(folder_game); + } + + menu_data.games_array[menu_data.games_array_count - 1].is_folder_name = is_folder_name; + menu_data.games_array[menu_data.games_array_count - 1].exists_cover[MT_PLANE_TEXT-1] = SC_WITHOUT_SEARCHING; + menu_data.games_array[menu_data.games_array_count - 1].exists_cover[MT_IMAGE_TEXT_64_5X2-1] = SC_WITHOUT_SEARCHING; + menu_data.games_array[menu_data.games_array_count - 1].exists_cover[MT_IMAGE_128_4X3-1] = SC_WITHOUT_SEARCHING; + menu_data.games_array[menu_data.games_array_count - 1].check_optimized = false; + menu_data.games_array[menu_data.games_array_count - 1].is_cdda = CCGE_NOT_CHECKED; + menu_data.games_array[menu_data.games_array_count - 1].device = GetDeviceType(full_path_folder); + + if (strcasecmp(file_type, ".gdi") == 0) + { + gdi_index = menu_data.games_array_count - 1; + } + else + { + menu_data.games_array[menu_data.games_array_count - 1].is_cdda = CCGE_NOT_CDDA; + } + } + } + + if (gdi_index >= 0) + { + menu_data.games_array[gdi_index].check_optimized = menu_data.games_array[gdi_index].is_gdi_optimized = gdi_optimized; + + if (gdi_cdda == CCGE_CANDIDATE || gdi_cdda == CCGE_CDDA) + { + if (gdi_cdda == CCGE_CANDIDATE) + { + size_t track_size = 0; + char *track_file_path = (char *)malloc(NAME_MAX); + const char *full_path_game = GetFullGamePathByIndex(gdi_index); + track_size = GetCDDATrackFilename(4, full_path_game, &track_file_path); + free(track_file_path); + + if (track_size > 0) + { + if (track_size < 30 * 1024 * 1024) + { + gdi_cdda = CCGE_NOT_CDDA; + } + else + { + gdi_cdda = CCGE_CDDA; + } + } + else + { + gdi_cdda = CCGE_NOT_CDDA; + } + } + + menu_data.games_array[gdi_index].is_cdda = gdi_cdda; + } + else + { + menu_data.games_array[gdi_index].is_cdda = CCGE_NOT_CDDA; + } + } + + ent = NULL; + file_type = NULL; + fs_close(fd); +} + +bool RetrieveGames() +{ +#ifdef IN_CACHE_GAMES + if (menu_data.games_array_count > 0) + { + return true; + } +#endif + + FreeGames(); + RetrieveGamesRecursive(GetGamesPath(menu_data.current_dev), NULL, 0); + + if (menu_data.ide && menu_data.sd) + { + RetrieveGamesRecursive(GetGamesPath(APP_DEVICE_SD), NULL, 0); + } + + if (menu_data.games_array_count > 0) + { + qsort(menu_data.games_array, menu_data.games_array_count, sizeof(GameItemStruct), AppCompareGames); + + menu_data.rescan_covers = false; + if (menu_data.cover_scanned_app.games_count != menu_data.games_array_count) + { + menu_data.cover_scanned_app.games_count = menu_data.games_array_count; + menu_data.rescan_covers = true; + } + else if (menu_data.cover_scanned_app.games_count > 0 + && menu_data.cover_scanned_app.last_game_index > 0 + && menu_data.cover_scanned_app.last_game_index <= menu_data.games_array_count) + { + char *name = NULL; + if (GetCoverName(menu_data.cover_scanned_app.last_game_index-1, &name)) + { + if (strcasecmp(menu_data.cover_scanned_app.last_game_scanned, name) != 0) + { + menu_data.rescan_covers = true; + } + + if (name != NULL) + { + free(name); + } + } + } + + return true; + } + else + { + return false; + } +} \ No newline at end of file diff --git a/applications/games_menu/modules/module.c b/applications/games_menu/modules/module.c new file mode 100644 index 00000000..1c103413 --- /dev/null +++ b/applications/games_menu/modules/module.c @@ -0,0 +1,2317 @@ +/* DreamShell ##version## + + module.h - Games app module + Copyright (C) 2024 Maniac Vera + +*/ + +#include +#include "app_menu.h" +#include "app_system_menu.h" +#include "app_preset.h" +#include "app_utils.h" +#include "app_module.h" +#include "app_definition.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DEFAULT_MODULE_EXPORTS(app_games_menu); + +extern struct menu_structure menu_data; +static Event_t *do_menu_control_event; +static Event_t *do_menu_video_event; +static void* MenuExitHelper(void *params); +static mutex_t change_page_mutex = MUTEX_INITIALIZER; + +static struct +{ + App_t *app; + DSApp *dsapp_ptr; + Scene *scene_ptr; + + bool have_args; + int image_type; + int sector_size; + uint8 md5[16]; + uint8 boot_sector[2048]; + uint32 addr; + isoldr_info_t *isoldr; + + bool exit_app, wait_to_exit_app; + bool first_menu_load; + volatile bool game_changed; + volatile bool show_cover_game; + uint32 scan_covers_start_time; + uint32 scan_covers_end_time; + int pages; + int current_page; + int previous_page; + int game_count; + int menu_cursel; + int scan_count; + uint8 device_selected; + + int game_index_selected; + char item_value_selected[NAME_MAX]; + + LogXYMover *item_selector_animation; + Rectangle *item_selector; + Font *menu_font; + Font *message_font; + Death *exit_trigger_list[MAX_SIZE_ITEMS]; + LogXYMover *run_animation; + ExpXYMover *exit_animation_list[MAX_SIZE_ITEMS]; + LogXYMover *item_game_animation[MAX_SIZE_ITEMS]; + ItemMenu *item_game[MAX_SIZE_ITEMS]; + ItemMenu *item_button[MAX_BUTTONS]; + Banner *img_cover_game_background; + Banner *img_cover_game; + FadeTo *animation_cover_game; + FadeOut *fadeout_cover_animation; + Texture *texture_cover_game_background; + Texture *texture_cover_game; + Label *title, *title_back; + Label *title_type; + LogXYMover *title_animation; + LogXYMover *title_back_animation; + LogXYMover *title_type_animation; + Box *main_box; + Rectangle *title_rectangle; + Rectangle *title_type_rectangle; + Rectangle *game_list_rectangle; + Rectangle *img_cover_game_rectangle; + + kthread_t *show_cover_thread; +} self; + + +static void* ShowCoverThread(void *params) +{ + int game_index = (int)params; + if (game_index >= 0) + { + srand(time(NULL)); + uint time_elapsed = 0; + if (self.img_cover_game != NULL) + { + self.show_cover_game = false; + if (self.animation_cover_game != NULL) + { + TSU_AnimationComplete((Animation *)self.animation_cover_game, (Drawable *)self.img_cover_game); + thd_pass(); + TSU_FadeToDestroy(&self.animation_cover_game); + } + + if (self.fadeout_cover_animation == NULL) + { + self.fadeout_cover_animation = TSU_FadeOutCreate(10.0f); + TSU_DrawableAnimAdd((Drawable *)self.img_cover_game, (Animation *)self.fadeout_cover_animation); + } + + const uint max_time = 200; + while (!self.game_changed && menu_data.menu_type == MT_PLANE_TEXT && time_elapsed < max_time) + { + thd_sleep(50); + time_elapsed += 50; + } + + if (self.game_changed || menu_data.menu_type != MT_PLANE_TEXT) + return NULL; + + TSU_AnimationComplete((Animation *)self.fadeout_cover_animation, (Drawable *)self.img_cover_game); + thd_pass(); + TSU_FadeOutDestroy(&self.fadeout_cover_animation); + + TSU_DrawableSetFinished((Drawable *)self.img_cover_game); + TSU_AppSubRemoveBanner(self.dsapp_ptr, self.img_cover_game); + + thd_pass(); + TSU_BannerDestroy(&self.img_cover_game); + TSU_TextureDestroy(&self.texture_cover_game); + } + + const uint max_time = 400; + while (!self.game_changed && menu_data.menu_type == MT_PLANE_TEXT && time_elapsed < max_time) + { + thd_sleep(50); + time_elapsed += 50; + } + + if (self.game_changed || menu_data.menu_type != MT_PLANE_TEXT) + return NULL; + + uint16 checked_cover = CheckCover(game_index, MT_PLANE_TEXT); + if (checked_cover == SC_EXISTS || checked_cover == SC_DEFAULT) + { + self.show_cover_game = true; + + Vector vector_position = {490, 180, ML_ITEM + 2, 1}; + char *game_cover_path = NULL; + uint16 cover_type = GetCoverType(game_index, MT_PLANE_TEXT); + + if (checked_cover == SC_EXISTS) + { + GetGameCoverPath(game_index, &game_cover_path, MT_PLANE_TEXT); + } + else + { + game_cover_path = (char *)malloc(NAME_MAX); + snprintf(game_cover_path, NAME_MAX, "%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu/images/no_cover.png"); + cover_type = IT_PNG; + } + + self.texture_cover_game = TSU_TextureCreateFromFile(game_cover_path, cover_type != IT_JPG, false, 0); + self.img_cover_game = TSU_BannerCreate(cover_type == IT_JPG ? PVR_LIST_OP_POLY : PVR_LIST_TR_POLY, self.texture_cover_game); + free(game_cover_path); + + TSU_DrawableTranslate((Drawable *)self.img_cover_game, &vector_position); + TSU_BannerSetSize(self.img_cover_game, 256, 256); + + vector_position.x = 0.0f; + vector_position.y = 0.0f; + TSU_DrawableSetScale((Drawable *)self.img_cover_game, &vector_position); + + TSU_AppSubAddBanner(self.dsapp_ptr, self.img_cover_game); + self.animation_cover_game = TSU_FadeToCreate(0.0f, 1.0f, 7.5f); + TSU_DrawableAnimAdd((Drawable *)self.img_cover_game, (Animation *)self.animation_cover_game); + + self.show_cover_game = false; + } + } + + return NULL; +} + +static bool StopShowCover() +{ + if (self.show_cover_thread != NULL) + { + thd_join(self.show_cover_thread, NULL); + self.show_cover_thread = NULL; + } + + return (self.show_cover_thread == NULL); +} + +static void ShowCover(int game_index) +{ + if (menu_data.menu_type == MT_PLANE_TEXT && game_index >= 0) + { + if (StopShowCover()) + { + self.show_cover_thread = thd_create(0, ShowCoverThread, (void *)game_index); + thd_set_prio(self.show_cover_thread, PRIO_DEFAULT - 2); + } + } +} + +static bool LoadCover(ItemMenu *item_menu, int game_count) +{ + bool loaded_cover = false; + + if (item_menu != NULL) + { + int game_index = TSU_ItemMenuGetItemIndex(item_menu); + + if (menu_data.menu_type == MT_PLANE_TEXT) + { + if (self.menu_cursel == game_count) + { + ShowCover(game_index); + loaded_cover = true; + } + } + else + { + char *game_cover_path = NULL; + if (GetGameCoverPath(game_index, &game_cover_path, menu_data.menu_type)) + { + TSU_ItemMenuSetImage(item_menu, game_cover_path, ContainsCoverType(game_index, menu_data.menu_type, IT_JPG) ? PVR_LIST_OP_POLY : PVR_LIST_TR_POLY); + loaded_cover = true; + free(game_cover_path); + } + } + } + + return loaded_cover; +} + +static void SetTitle(int game_index, const char *text) +{ + char titleText[101]; + memset(titleText, 0, sizeof(titleText)); + + if (menu_data.ide && menu_data.sd && game_index >= 0) + { + switch (menu_data.games_array[game_index].device) + { + case APP_DEVICE_IDE: + strncpy(titleText, "IDE: ", sizeof("IDE: ")); + break; + + case APP_DEVICE_SD: + strncpy(titleText, "SD: ", sizeof("SD: ")); + break; + } + } + + if (strlen(text) > sizeof(titleText) - 6) + { + strncpy(&titleText[strlen(titleText)], text, sizeof(titleText) - 6); + } + else + { + strcpy(&titleText[strlen(titleText)], text); + } + + static Vector vectorInit = {-50, 32, ML_ITEM, 1}; + static Vector vector = {10, 32, ML_ITEM, 1}; + vectorInit.z = ML_ITEM + 1; + vector.z = ML_ITEM + 1; + + if(vid_check_cable() != CT_VGA) + { + vectorInit.y = 40; + + vector.x = 14; + vector.y = 40; + } + + if (self.title != NULL) + { + TSU_LabelSetText(self.title_back, titleText); + TSU_LabelSetText(self.title, titleText); + } + else + { + int font_size = 20; + Color color = {1, 1.0f, 1.0f, 1.0f}; + + self.title = TSU_LabelCreate(self.menu_font, titleText, font_size, false, false); + TSU_LabelSetTint(self.title, &color); + TSU_AppSubAddLabel(self.dsapp_ptr, self.title); + + Color color_back = {1, 0.0f, 0.0f, 0.0f}; + self.title_back = TSU_LabelCreate(self.menu_font, titleText, font_size, false, false); + TSU_LabelSetTint(self.title_back, &color_back); + TSU_AppSubAddLabel(self.dsapp_ptr, self.title_back); + } + + if (self.title_animation != NULL) + { + TSU_AnimationComplete((Animation *)self.title_animation, (Drawable *)self.title); + TSU_LogXYMoverDestroy(&self.title_animation); + + TSU_AnimationComplete((Animation *)self.title_back_animation, (Drawable *)self.title_back); + TSU_LogXYMoverDestroy(&self.title_back_animation); + } + + TSU_LabelSetTranslate(self.title, &vectorInit); + self.title_animation = TSU_LogXYMoverCreate(vector.x, vector.y); + TSU_DrawableAnimAdd((Drawable *)self.title, (Animation *)self.title_animation); + + vectorInit.z = ML_ITEM; + TSU_LabelSetTranslate(self.title_back, &vectorInit); + self.title_back_animation = TSU_LogXYMoverCreate(vector.x, vector.y); + TSU_DrawableAnimAdd((Drawable *)self.title_back, (Animation *)self.title_back_animation); +} + +static void SetTitleType(const char *full_path_game, bool is_gdi_optimized) +{ + if (full_path_game != NULL) + { + Color title_type_color = {1, 1.0f, 0.95f, 0.0f}; + char *file_type = strrchr(full_path_game, '.'); + + char title_text[4]; + memset(title_text, 0, sizeof(title_text)); + + if (is_gdi_optimized) + { + strncpy(title_text, "GDI", 3); + } + else if (strcasecmp(file_type, ".cdi") == 0) + { + strncpy(title_text, "CDI", 3); + } + else if (strcasecmp(file_type, ".iso") == 0) + { + strncpy(title_text, "ISO", 3); + } + else if (strcasecmp(file_type, ".cso") == 0) + { + strncpy(title_text, "CSO", 3); + } + else if (strcasecmp(file_type, ".gdi") == 0) + { + strncpy(title_text, "GDI", 3); + + title_type_color.r = 1.0f; + title_type_color.g = 0.54f; + title_type_color.b = 0.0f; + } + + TSU_DrawableSetTint((Drawable *)self.title_type_rectangle, &title_type_color); + + static Vector vectorInit = {700, 32, ML_ITEM + 6, 1}; + static Vector vector = {592, 32, ML_ITEM + 6, 1}; + + vectorInit.z = ML_ITEM + 6; + + if(vid_check_cable() != CT_VGA) + { + vectorInit.y = 40; + + vector.x = 592; + vector.y = 40; + } + + if (self.title_type != NULL) + { + TSU_LabelSetText(self.title_type, title_text); + } + else + { + int font_size = 20; + + static Color color = {1, 1.0f, 1.0f, 1.0f}; + self.title_type = TSU_LabelCreate(self.menu_font, title_text, font_size, false, true); + TSU_LabelSetTint(self.title_type, &color); + TSU_AppSubAddLabel(self.dsapp_ptr, self.title_type); + } + + if (self.title_type_animation != NULL) + { + TSU_AnimationComplete((Animation *)self.title_type_animation, (Drawable *)self.title_type); + TSU_LogXYMoverDestroy(&self.title_type_animation); + } + + TSU_LabelSetTranslate(self.title_type, &vectorInit); + self.title_type_animation = TSU_LogXYMoverCreate(vector.x, vector.y); + TSU_DrawableAnimAdd((Drawable *)self.title_type, (Animation *)self.title_type_animation); + } +} + +static void SetCursor() +{ + static Vector selector_translate = {0, 0, ML_CURSOR, 1}; + + if (self.item_selector_animation != NULL) + { + TSU_AnimationComplete((Animation *)self.item_selector_animation, (Drawable *)self.item_selector); + TSU_LogXYMoverDestroy(&self.item_selector_animation); + } + + if (self.item_selector != NULL) + { + TSU_AppSubRemoveRectangle(self.dsapp_ptr, self.item_selector); + TSU_RectangleDestroy(&self.item_selector); + } + + if (self.item_selector == NULL) + { + if (menu_data.menu_type == MT_IMAGE_TEXT_64_5X2) + { + Color color = {0, 0.0f, 0.0f, 0.0f}; + Color border_color = {1, 1.0f, 1.0f, 0.1f}; + + self.item_selector = TSU_RectangleCreateWithBorder(PVR_LIST_TR_POLY, 0, 0, 0, 0, &color, ML_CURSOR, 3, &border_color, 0); + TSU_DrawableSetTint((Drawable *)self.item_selector, &color); + } + else if (menu_data.menu_type == MT_IMAGE_128_4X3) + { + Color color = {0, 0.0f, 0.0f, 0.0f}; + Color border_color = {1, 1.0f, 1.0f, 0.1f}; + + self.item_selector = TSU_RectangleCreateWithBorder(PVR_LIST_TR_POLY, 0, 0, 0, 0, &color, ML_CURSOR, 6, &border_color, 0); + TSU_DrawableSetTint((Drawable *)self.item_selector, &color); + } + else if (menu_data.menu_type == MT_PLANE_TEXT) + { + Color color = {0.8f, 0.0f, 0.0f, 0.0f}; + Color border_color = {1, 1.0f, 1.0f, 0.1f}; + + self.item_selector = TSU_RectangleCreateWithBorder(PVR_LIST_TR_POLY, 0, 0, 0, 0, &color, ML_CURSOR, 2, &border_color, 0); + TSU_DrawableSetTint((Drawable *)self.item_selector, &color); + } + else + { + Color color = {0, 0.0f, 0.0f, 0.0f}; + Color border_color = {1, 1.0f, 1.0f, 0.1f}; + + self.item_selector = TSU_RectangleCreateWithBorder(PVR_LIST_TR_POLY, 0, 0, 0, 0, &color, ML_CURSOR, 4, &border_color, 0); + TSU_DrawableSetTint((Drawable *)self.item_selector, &color); + } + + if (selector_translate.x > 0 && selector_translate.y > 0) + { + TSU_DrawableSetTranslate((Drawable *)self.item_selector, &selector_translate); + } + + TSU_AppSubAddRectangle(self.dsapp_ptr, self.item_selector); + } + + static int init_position_x; + static int init_position_y; + + if (menu_data.menu_type == MT_IMAGE_TEXT_64_5X2) + { + TSU_RectangleSetSize(self.item_selector, 310, menu_data.menu_option.image_size + 8); + init_position_x = 5; + init_position_y = 34; + selector_translate.z = ML_CURSOR; + } + else if (menu_data.menu_type == MT_IMAGE_128_4X3) + { + TSU_RectangleSetSize(self.item_selector, menu_data.menu_option.image_size + 4, menu_data.menu_option.image_size + 4); + init_position_x = 9; + init_position_y = 41; + selector_translate.z = ML_CURSOR; + } + else if (menu_data.menu_type == MT_PLANE_TEXT) + { + TSU_RectangleSetSize(self.item_selector, 329, 30); + init_position_x = 18; + init_position_y = 52; + selector_translate.z = ML_SELECTED - 1; + } + else + { + TSU_RectangleSetSize(self.item_selector, menu_data.menu_option.image_size + 4, menu_data.menu_option.image_size + 4); + init_position_x = 8; + init_position_y = 40; + selector_translate.z = ML_CURSOR; + } + + TSU_DrawableSetTranslate((Drawable *)self.item_selector, &selector_translate); + selector_translate.x = self.menu_cursel / menu_data.menu_option.size_items_column * menu_data.menu_option.padding_x + init_position_x; + selector_translate.y = ((self.menu_cursel + 1) - menu_data.menu_option.size_items_column * (self.menu_cursel / menu_data.menu_option.size_items_column)) * (menu_data.menu_option.padding_y + menu_data.menu_option.image_size) + init_position_y; + + self.item_selector_animation = TSU_LogXYMoverCreate(selector_translate.x, selector_translate.y); + TSU_DrawableAnimAdd((Drawable *)self.item_selector, (Animation *)self.item_selector_animation); +} + +static void RemoveViewTextPlane() +{ + self.show_cover_game = false; + + if (self.img_cover_game_background != NULL) + { + TSU_DrawableSetFinished((Drawable *)self.img_cover_game_background); + } + + if (self.img_cover_game_rectangle != NULL) + { + TSU_DrawableSetFinished((Drawable *)self.img_cover_game_rectangle); + } + + for (int i = 0; i < MAX_BUTTONS; i++) + { + if (self.item_button[i] != NULL) + { + TSU_DrawableSetFinished((Drawable *)self.item_button[i]); + } + } + + if (self.img_cover_game != NULL) + { + if (self.animation_cover_game != NULL) + { + TSU_AnimationComplete((Animation *)self.animation_cover_game, (Drawable *)self.img_cover_game); + } + + if (self.fadeout_cover_animation != NULL) + { + TSU_AnimationComplete((Animation *)self.fadeout_cover_animation, (Drawable *)self.img_cover_game); + } + + TSU_DrawableSetFinished((Drawable *)self.img_cover_game); + } + + if (self.game_list_rectangle != NULL) + { + TSU_DrawableSetFinished((Drawable *)self.game_list_rectangle); + } + + TSU_DrawableSubRemoveFinished((Drawable *)self.scene_ptr); + thd_pass(); + + if (self.img_cover_game_background != NULL) + { + TSU_BannerDestroy(&self.img_cover_game_background); + TSU_TextureDestroy(&self.texture_cover_game_background); + } + + if (self.img_cover_game_rectangle != NULL) + { + TSU_RectangleDestroy(&self.img_cover_game_rectangle); + } + + for (int i = 0; i < MAX_BUTTONS; i++) + { + if (self.item_button[i] != NULL) + { + TSU_ItemMenuDestroy(&self.item_button[i]); + } + } + + if (self.img_cover_game != NULL) + { + if (self.animation_cover_game != NULL) + { + TSU_FadeToDestroy(&self.animation_cover_game); + } + + if (self.fadeout_cover_animation != NULL) + { + TSU_FadeOutDestroy(&self.fadeout_cover_animation); + } + + TSU_BannerDestroy(&self.img_cover_game); + TSU_TextureDestroy(&self.texture_cover_game); + } + + if (self.game_list_rectangle != NULL) + { + TSU_RectangleDestroy(&self.game_list_rectangle); + } +} + +static void CreateInfoButton(uint8 button_index, const char *button_file, const char *text, float x, float y) +{ + if(self.item_button[button_index] == NULL) + { + char *image_path = (char *)malloc(NAME_MAX); + snprintf(image_path, NAME_MAX, "%s/%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu/images", button_file); + + if (FileExists(image_path)) + { + Vector vector_translate = { x, y, ML_ITEM + 3, 1}; + self.item_button[button_index] = TSU_ItemMenuCreate(image_path, 32, 32, PVR_LIST_TR_POLY, text, self.menu_font, 14, false, 0); + TSU_ItemMenuSetTranslate(self.item_button[button_index], &vector_translate); + } + + free(image_path); + } +} + +static void AddInfoButtons() +{ + for (int i = 0; i < MAX_BUTTONS; i++) + { + if (self.item_button[i] != NULL) + { + TSU_AppSubAddItemMenu(self.dsapp_ptr, self.item_button[i]); + } + } +} + +static void CreateViewTextPlane() +{ + CreateInfoButton(0, "btn_x.png", "CHANGE VIEW", 359, 315); + CreateInfoButton(1, "btn_y.png", "SETTINGS", 512, 315); + CreateInfoButton(2, "btn_a.png", "PLAY", 359, 315 + (32 * 1) + (5 * 1)); + CreateInfoButton(3, "btn_b.png", "EXIT", 512, 315 + (32 * 1) + (5 * 1)); + CreateInfoButton(4, "btn_lt.png", "PREVIOUS", 359, 315 + (32 * 2) + (5 * 2)); + CreateInfoButton(5, "btn_rt.png", "NEXT", 512, 315 + (32 * 2) + (5 * 2)); + CreateInfoButton(6, "btn_start.png", "SYSTEM MENU", 359, 315 + (32 * 3) + (5 * 3)); + AddInfoButtons(); + + if (self.game_list_rectangle == NULL) + { + Color border_color = {1, 1.0f, 1.0f, 1.0f}; + Color background_color = {1, 0.22f, 0.06f, 0.25f}; + self.game_list_rectangle = TSU_RectangleCreateWithBorder(PVR_LIST_OP_POLY, 15, 460, 335, 405, &background_color, ML_ITEM, 3, &border_color, 0); + + TSU_AppSubAddRectangle(self.dsapp_ptr, self.game_list_rectangle); + } + + if (self.img_cover_game_background == NULL && menu_data.cover_background) + { + char *game_cover_path = (char *)malloc(NAME_MAX); + snprintf(game_cover_path, NAME_MAX, "%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu/images/cover.png"); + + if (FileExists(game_cover_path)) + { + self.texture_cover_game_background = TSU_TextureCreateFromFile(game_cover_path, true, false, 0); + self.img_cover_game_background = TSU_BannerCreate(PVR_LIST_TR_POLY, self.texture_cover_game_background); + + Vector vector_position = {490, 180, ML_ITEM + 3, 1}; + TSU_DrawableTranslate((Drawable *)self.img_cover_game_background, &vector_position); + TSU_BannerSetSize(self.img_cover_game_background, 256, 256); + TSU_AppSubAddBanner(self.dsapp_ptr, self.img_cover_game_background); + + Color background_color = {1, 0.88f, 0.88f, 0.88f}; + self.img_cover_game_rectangle = TSU_RectangleCreate(PVR_LIST_OP_POLY, vector_position.x - 128, vector_position.y + 128, 256, 256, &background_color, ML_ITEM + 1, 0); + TSU_AppSubAddRectangle(self.dsapp_ptr, self.img_cover_game_rectangle); + } + + free(game_cover_path); + } +} + +static bool LoadPage(bool change_view, uint8 direction) +{ + const int LENGTH_NAME_TEXT_PLANE = 26; + const int LENGTH_NAME_TEXT_64_5X2 = 19; + bool loaded = false; + char name_truncated[LENGTH_NAME_TEXT_PLANE]; + char game_cover_path[NAME_MAX]; + char name[NAME_MAX]; + char *game_cover_path_tmp = NULL; + + if (self.pages == -1) + { + for (int imenu = 1; imenu <= MAX_MENU; imenu++) + { + RetrieveCovers(menu_data.current_dev, imenu); + } + + if (RetrieveGames()) + { + timer_ms_gettime(&self.scan_covers_start_time, NULL); + self.pages = (menu_data.games_array_count > 0 ? ceil((float)menu_data.games_array_count / (float)menu_data.menu_option.max_page_size) : 0); + } + } + else + { + if (change_view) + { + self.pages = (menu_data.games_array_count > 0 ? ceil((float)menu_data.games_array_count / (float)menu_data.menu_option.max_page_size) : 0); + } + } + + if (self.pages > 0) + { + if (self.current_page < 1) + { + self.current_page = self.pages; + } + else if (self.current_page > self.pages) + { + self.current_page = 1; + } + + if (self.current_page != self.previous_page || change_view) + { + self.previous_page = self.current_page; + + for (int i = 0; i < MAX_SIZE_ITEMS; i++) + { + if (self.item_game_animation[i] != NULL) + { + TSU_AnimationComplete((Animation *)self.item_game_animation[i], (Drawable *)self.item_game[i]); + } + + if (self.item_game[i] != NULL) + { + TSU_DrawableSetFinished((Drawable *)self.item_game[i]); + } + } + + TSU_DrawableSubRemoveFinished((Drawable *)self.scene_ptr); + thd_pass(); + + for (int i = 0; i < MAX_SIZE_ITEMS; i++) + { + if (self.item_game_animation[i] != NULL) + { + TSU_LogXYMoverDestroy(&self.item_game_animation[i]); + } + + if (self.item_game[i] != NULL) + { + TSU_ItemMenuDestroy(&self.item_game[i]); + } + } + + if (change_view || self.first_menu_load) + { + if (menu_data.menu_type == MT_PLANE_TEXT) + { + CreateViewTextPlane(); + } + else + { + RemoveViewTextPlane(); + } + } + + int cover_menu_type = 0; + int column = 0; + int page = 0; + self.game_count = 0; + Vector vectorTranslate = {0, 480, ML_ITEM, 1}; + + for (int icount = ((self.current_page - 1) * menu_data.menu_option.max_page_size); icount < menu_data.games_array_count; icount++) + { + page = ceil((float)(icount + 1) / (float)menu_data.menu_option.max_page_size); + + if (page < self.current_page) + continue; + else if (page > self.current_page) + break; + + self.game_count++; + + column = floor((float)(self.game_count - 1) / (float)menu_data.menu_option.size_items_column); + + if (menu_data.menu_type != MT_PLANE_TEXT) + { + game_cover_path_tmp = NULL; + if (CheckCover(icount, menu_data.menu_type) == SC_EXISTS) + { + cover_menu_type = menu_data.menu_type; + } + else + { + CheckCover(icount, MT_PLANE_TEXT); + cover_menu_type = MT_PLANE_TEXT; + } + + if (GetGameCoverPath(icount, &game_cover_path_tmp, cover_menu_type)) + { + strcpy(game_cover_path, game_cover_path_tmp); + free(game_cover_path_tmp); + } + } + + memset(name, 0, sizeof(name)); + if (menu_data.games_array[icount].is_folder_name) + { + strcpy(name, menu_data.games_array[icount].folder_name); + } + else + { + strncpy(name, menu_data.games_array[icount].game, strlen(menu_data.games_array[icount].game) - 4); + } + + if (menu_data.menu_type == MT_IMAGE_TEXT_64_5X2) + { + memset(name_truncated, 0, LENGTH_NAME_TEXT_PLANE); + if (strlen(name) > LENGTH_NAME_TEXT_64_5X2 - 1) + { + strncpy(name_truncated, name, LENGTH_NAME_TEXT_64_5X2 - 1); + } + else + { + strcpy(name_truncated, name); + } + + self.item_game[self.game_count - 1] = TSU_ItemMenuCreate(game_cover_path, menu_data.menu_option.image_size, menu_data.menu_option.image_size + , ContainsCoverType(icount, cover_menu_type, IT_JPG) ? PVR_LIST_OP_POLY : PVR_LIST_TR_POLY + , name_truncated, self.menu_font, 18, false, 0); + + } + else if (menu_data.menu_type == MT_PLANE_TEXT) + { + memset(name_truncated, 0, LENGTH_NAME_TEXT_PLANE); + if (strlen(name) > LENGTH_NAME_TEXT_PLANE - 1) + { + strncpy(name_truncated, name, LENGTH_NAME_TEXT_PLANE - 1); + } + else + { + strcpy(name_truncated, name); + } + + self.item_game[self.game_count - 1] = TSU_ItemMenuCreateLabel(name_truncated, self.menu_font, 18); + Color color_unselected = { 1, 0.95f, 0.95f, 0.95f }; + TSU_ItemMenuSetColorUnselected(self.item_game[self.game_count - 1], &color_unselected); + } + else + { + self.item_game[self.game_count - 1] = TSU_ItemMenuCreateImage(game_cover_path + , menu_data.menu_option.image_size + , menu_data.menu_option.image_size + , ContainsCoverType(icount, cover_menu_type, IT_JPG) ? PVR_LIST_OP_POLY : PVR_LIST_TR_POLY + , false + , PVR_TXRLOAD_SQ); + } + + ItemMenu *item_menu = self.item_game[self.game_count - 1]; + + TSU_ItemMenuSetItemIndex(item_menu, icount); + TSU_ItemMenuSetItemValue(item_menu, name); + + if (menu_data.menu_type == MT_PLANE_TEXT) + { + switch (direction) + { + case DMD_UP: + case DMD_LEFT: + vectorTranslate.x = 50; + vectorTranslate.y = (menu_data.menu_option.image_size + menu_data.menu_option.padding_y) * (self.game_count - menu_data.menu_option.size_items_column * column) + menu_data.menu_option.init_position_y; + break; + + case DMD_DOWN: + case DMD_RIGHT: + vectorTranslate.x = -50; + vectorTranslate.y = (menu_data.menu_option.image_size + menu_data.menu_option.padding_y) * (self.game_count - menu_data.menu_option.size_items_column * column) + menu_data.menu_option.init_position_y; + break; + + default: + vectorTranslate.x = 50; + break; + } + + TSU_ItemMenuSetTranslate(item_menu, &vectorTranslate); + } + else + { + TSU_ItemMenuSetTranslate(item_menu, &vectorTranslate); + } + + self.item_game_animation[self.game_count - 1] = TSU_LogXYMoverCreate(menu_data.menu_option.init_position_x + (column * menu_data.menu_option.padding_x), + (menu_data.menu_option.image_size + menu_data.menu_option.padding_y) * (self.game_count - menu_data.menu_option.size_items_column * column) + menu_data.menu_option.init_position_y); + + if (menu_data.menu_type != MT_PLANE_TEXT) + { + if (menu_data.menu_type == MT_IMAGE_TEXT_64_5X2) + { + TSU_LogXYMoverSetFactor(self.item_game_animation[self.game_count - 1], 6.5f); + } + else + { + TSU_LogXYMoverSetFactor(self.item_game_animation[self.game_count - 1], 6.0f); + } + } + + TSU_ItemMenuAnimAdd(item_menu, (Animation *)self.item_game_animation[self.game_count - 1]); + + if (self.first_menu_load) + { + if (self.game_count == 1) + { + TSU_ItemMenuSetSelected(item_menu, true, false); + SetTitle(TSU_ItemMenuGetItemIndex(item_menu), name); + SetTitleType(GetFullGamePathByIndex(TSU_ItemMenuGetItemIndex(item_menu)) + , CheckGdiOptimized(TSU_ItemMenuGetItemIndex(item_menu))); + + SetCursor(); + ShowCover(TSU_ItemMenuGetItemIndex(item_menu)); + PlayCDDA(TSU_ItemMenuGetItemIndex(item_menu)); + } + else + { + TSU_ItemMenuSetSelected(item_menu, false, false); + } + } + + TSU_AppSubAddItemMenu(self.dsapp_ptr, item_menu); + } + + loaded = true; + self.first_menu_load = false; + FreeGames(); + } + } + else + { + static bool draw_message = true; + if (draw_message) + { + draw_message = false; + memset(game_cover_path, 0, sizeof(game_cover_path)); + + char font_path[NAME_MAX]; + memset(font_path, 0, sizeof(font_path)); + snprintf(font_path, sizeof(font_path), "%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu/fonts/message.txf"); + + TSU_LabelDestroy(&self.title); + TSU_FontDestroy(&self.menu_font); + self.menu_font = TSU_FontCreate(font_path, PVR_LIST_TR_POLY); + + snprintf(game_cover_path, sizeof(game_cover_path), "You need put the games here:\n\n%s\n\n\n\nand images here:\n\n%s", GetGamesPath(menu_data.current_dev), GetCoversPath(menu_data.current_dev)); + ds_printf(game_cover_path); + SetTitle(-1, game_cover_path); + } + } + + return loaded; +} + +static void InitMenu() +{ + menu_data.state_app = SA_GAMES_MENU; + self.scene_ptr = TSU_AppGetScene(self.dsapp_ptr); + + char font_path[NAME_MAX]; + memset(font_path, 0, sizeof(font_path)); + snprintf(font_path, sizeof(font_path), "%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu/fonts/default.txf"); + self.menu_font = TSU_FontCreate(font_path, PVR_LIST_TR_POLY); + + memset(font_path, 0, sizeof(font_path)); + snprintf(font_path, sizeof(font_path), "%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu/fonts/message.txf"); + self.message_font = TSU_FontCreate(font_path, PVR_LIST_TR_POLY); + + SetMenuType(menu_data.menu_type); + self.exit_app = false; + self.title = NULL; + self.title_type = NULL; + self.title_back = NULL; + self.game_index_selected = -1; + + Vector vector = {0, 0, 10, 1}; + TSU_AppSetTranslate(self.dsapp_ptr, &vector); + + if (LoadScannedCover()) + { + CleanIncompleteCover(); + } + else + { + memset(&menu_data.cover_scanned_app, 0, sizeof(CoverScannedStruct)); + } + + self.current_page = 1; + LoadPage(false, DMD_NONE); + + self.menu_cursel = 0; +} + +static void RemoveAll() +{ + TSU_AnimationComplete((Animation *)self.title_animation, (Drawable *)self.title); + TSU_AnimationComplete((Animation *)self.title_back_animation, (Drawable *)self.title_back); + TSU_AnimationComplete((Animation *)self.title_type_animation, (Drawable *)self.title_type); + TSU_AnimationComplete((Animation *)self.item_selector_animation, (Drawable *)self.item_selector); + + if (self.animation_cover_game != NULL && self.img_cover_game != NULL) + { + TSU_AnimationComplete((Animation *)self.animation_cover_game, (Drawable *)self.img_cover_game); + } + + + TSU_DrawableSetFinished((Drawable *)self.main_box); + TSU_DrawableSetFinished((Drawable *)self.title_rectangle); + TSU_DrawableSetFinished((Drawable *)self.title_type_rectangle); + TSU_DrawableSetFinished((Drawable *)self.game_list_rectangle); + TSU_DrawableSetFinished((Drawable *)self.item_selector); + TSU_DrawableSetFinished((Drawable *)self.title); + TSU_DrawableSetFinished((Drawable *)self.title_type); + TSU_DrawableSetFinished((Drawable *)self.title_back); + + if (self.img_cover_game != NULL) + { + TSU_DrawableSetFinished((Drawable *)self.img_cover_game); + } + + if (self.img_cover_game_background != NULL) + { + TSU_DrawableSetFinished((Drawable *)self.img_cover_game_background); + } + + if (self.img_cover_game_rectangle != NULL) + { + TSU_DrawableSetFinished((Drawable *)self.img_cover_game_rectangle); + } + + for (int i = 0; i < MAX_BUTTONS; i++) + { + if (self.item_button[i] != NULL) + { + TSU_DrawableSetFinished((Drawable *)self.item_button[i]); + } + } + + for (int i = 0; i < MAX_SIZE_ITEMS; i++) + { + if (self.item_game_animation[i] != NULL) + { + TSU_AnimationComplete((Animation *)self.item_game_animation[i], (Drawable *)self.item_game[i]); + } + } + + SystemMenuRemoveAll(); + TSU_DrawableSubRemoveFinished((Drawable *)self.scene_ptr); + thd_pass(); +} + +static void StartExit() +{ + StopCDDA(); + StopShowCover(); + RemoveAll(); + + float y = 1.0f; + self.game_index_selected = -1; + + for (int i = 0; i < MAX_SIZE_ITEMS; i++) + { + if (self.item_game[i] != NULL) + { + if (!self.exit_app && TSU_ItemMenuIsSelected(self.item_game[i])) + { + self.game_index_selected = TSU_ItemMenuGetItemIndex(self.item_game[i]); + strcpy(self.item_value_selected, GetFullGamePathByIndex(self.game_index_selected)); + self.device_selected = menu_data.games_array[TSU_ItemMenuGetItemIndex(self.item_game[i])].device; + + if (menu_data.menu_type == MT_PLANE_TEXT) + { + float w, h; + char *label_text = (char *)malloc(NAME_MAX); + memset(label_text, 0, NAME_MAX); + + strcpy(label_text, TSU_ItemMenuGetLabelText(self.item_game[i])); + label_text = Trim(label_text); + + TSU_FontSetSize(self.menu_font, 18); + TSU_FontGetTextSize(self.menu_font, label_text, &w, &h); + self.run_animation = TSU_LogXYMoverCreate((640/2 - w/2), (480 - 84) / 2); + + free(label_text); + } + else if (TSU_ItemMenuHasTextAndImage(self.item_game[i])) + { + self.run_animation = TSU_LogXYMoverCreate((640 - (84 + 64)) / 2, (480 - 84) / 2); + } + else + { + self.run_animation = TSU_LogXYMoverCreate((640 - 84) / 2, (480 - 84) / 2); + } + + self.exit_trigger_list[i] = TSU_DeathCreate(NULL); + TSU_TriggerAdd((Animation *)self.run_animation, (Trigger *)self.exit_trigger_list[i]); + TSU_DrawableAnimAdd((Drawable *)self.item_game[i], (Animation *)self.run_animation); + } + else + { + self.exit_animation_list[i] = TSU_ExpXYMoverCreate(0, y + .10, 0, 500); + self.exit_trigger_list[i] = TSU_DeathCreate(NULL); + + TSU_TriggerAdd((Animation *)self.exit_animation_list[i], (Trigger *)self.exit_trigger_list[i]); + TSU_DrawableAnimAdd((Drawable *)self.item_game[i], (Animation *)self.exit_animation_list[i]); + } + } + } + TSU_AppStartExit(self.dsapp_ptr); + + self.wait_to_exit_app = true; + while (!menu_data.finished_menu) { thd_pass(); } + + RemoveEvent(do_menu_video_event); + RemoveEvent(do_menu_control_event); + + MenuExitHelper(NULL); +} + +static void GamesApp_SystemMenuInputEvent(int type, int key) +{ + if (type != EvtKeypress) + return; + + mutex_lock(&change_page_mutex); + + switch (key) + { + case KeyStart: + { + if (StateSystemMenu()) + { + menu_data.state_app = SA_GAMES_MENU; + HideSystemMenu(); + } + } + break; + + default: + { + if (StateSystemMenu()) + { + SystemMenuInputEvent(type, key); + } + } + break; + } + + mutex_unlock(&change_page_mutex); +} + +static void GamesApp_PresetMenuInputEvent(int type, int key) +{ + if (type != EvtKeypress) + return; + + mutex_lock(&change_page_mutex); + + switch (key) + { + case KeyStart: + case KeyMiscY: + { + if (StatePresetMenu()) + { + HidePresetMenu(); + menu_data.state_app = SA_GAMES_MENU; + } + } + break; + + default: + { + if (StatePresetMenu()) + { + PresetMenuInputEvent(type, key); + } + } + break; + } + + mutex_unlock(&change_page_mutex); +} + +static void GamesApp_ScanCoverInputEvent(int type, int key) +{ + if (type != EvtKeypress) + return; + + mutex_lock(&change_page_mutex); + + StopScanCovers(); + HideCoverScan(); + menu_data.state_app = SA_GAMES_MENU; + timer_ms_gettime(&self.scan_covers_start_time, NULL); + + mutex_unlock(&change_page_mutex); +} + +static void GamesApp_OptimizeCoverInputEvent(int type, int key) +{ + if (type != EvtKeypress) + return; + + mutex_lock(&change_page_mutex); + + StopOptimizeCovers(); + HideOptimizeCoverPopup(); + menu_data.state_app = SA_GAMES_MENU; + timer_ms_gettime(&self.scan_covers_start_time, NULL); + + mutex_unlock(&change_page_mutex); +} + +static void GamesApp_ControlInputEvent(int type, int key, int state_app) +{ + if (type != EvtKeypress) + return; + + switch (state_app) + { + case SA_CONTROL + ASYNC_CONTROL_ID: //ASYNC + { + AsyncInputEvent(type, key); + break; + } + + case SA_CONTROL + OS_CONTROL_ID: //OS + { + OSInputEvent(type, key); + break; + } + + case SA_CONTROL + LOADER_CONTROL_ID: //LOADER + { + LoaderInputEvent(type, key); + break; + } + + case SA_CONTROL + BOOT_CONTROL_ID: //BOOT + { + BootInputEvent(type, key); + break; + } + + case SA_CONTROL + MEMORY_CONTROL_ID: //MEMORY + { + MemoryInputEvent(type, key); + break; + } + + case SA_CONTROL + CUSTOM_MEMORY_CONTROL_ID: //CUSTOM MEMORY + { + CustomMemoryInputEvent(type, key); + break; + } + + case SA_CONTROL + HEAP_CONTROL_ID: //HEAP + { + HeapInputEvent(type, key); + break; + } + + case SA_CONTROL + CDDA_SOURCE_CONTROL_ID: //CDDA SOURCE + { + CDDASourceInputEvent(type, key); + break; + } + + case SA_CONTROL + CDDA_DESTINATION_CONTROL_ID: //CDDA DESTINATION + { + CDDADestinationInputEvent(type, key); + break; + } + + case SA_CONTROL + CDDA_POSITION_CONTROL_ID: //CDDA POSITION + { + CDDAPositionInputEvent(type, key); + break; + } + + case SA_CONTROL + CDDA_CHANNEL_CONTROL_ID: //CDDA CHANNEL + { + CDDAChannelInputEvent(type, key); + break; + } + + case SA_CONTROL + PATCH_ADDRESS1_CONTROL_ID: //PATCH ADDRESS 1 + { + PatchAddress1InputEvent(type, key); + break; + } + + case SA_CONTROL + PATCH_VALUE1_CONTROL_ID: //PATCH VALUE 1 + { + PatchValue1InputEvent(type, key); + break; + } + + case SA_CONTROL + PATCH_ADDRESS2_CONTROL_ID: //PATCH ADDRESS 2 + { + PatchAddress2InputEvent(type, key); + break; + } + + case SA_CONTROL + PATCH_VALUE2_CONTROL_ID: //PATCH VALUE 2 + { + PatchValue2InputEvent(type, key); + break; + } + + case SA_CONTROL + ALTERBOOT_CONTROL_ID: //ALTER BOOT + { + AlterBootInputEvent(type, key); + break; + } + + case SA_CONTROL + SCREENSHOT_CONTROL_ID: //SCREENSHOT + { + ScreenshotInputEvent(type, key); + break; + } + + case SA_CONTROL + VMU_CONTROL_ID: //VMU + { + VMUInputEvent(type, key); + break; + } + + case SA_CONTROL + VMUSELECTOR_CONTROL_ID: //VMU OPTION + { + VMUSelectorInputEvent(type, key); + break; + } + + case SA_CONTROL + SHORTCUT_SIZE_CONTROL_ID: //SHORTCUT SIZE + { + ShortcutSizeInputEvent(type, key); + break; + } + + case SA_CONTROL + SHORTCUT_ROTATE_CONTROL_ID: //SHORTCUT ROTATE + { + ShortcutRotateInputEvent(type, key); + break; + } + + case SA_CONTROL + SHORTCUT_NAME_CONTROL_ID: //SHORTCUT NAME + { + ShortcutNameInputEvent(type, key); + break; + } + + case SA_CONTROL + SHORTCUT_DONTSHOWNAME_CONTROL_ID: //SHORTCUT DONT SHOW NAME + { + ShortcutDontShowNameInputEvent(type, key); + break; + } + + default: + break; + } +} + +static void GamesApp_InputEvent(int type, int key) +{ + if (type != EvtKeypress) + return; + + mutex_lock(&change_page_mutex); + + bool skip_cursor = false; + bool fast_cursor = false; + timer_ms_gettime(&self.scan_covers_start_time, NULL); + + switch (key) + { + case KeyStart: + { + menu_data.state_app = SA_SYSTEM_MENU; + ShowSystemMenu(); + skip_cursor = true; + } + break; + + case KeyMiscY: + { + if (menu_data.games_array_count > 0) + { + menu_data.state_app = SA_PRESET_MENU; + ShowPresetMenu(TSU_ItemMenuGetItemIndex(self.item_game[self.menu_cursel])); + } + skip_cursor = true; + } + break; + + // X: CHANGE VISUALIZATION + case KeyMiscX: + { + StopShowCover(); + + int real_cursel = (self.current_page - 1) * menu_data.menu_option.max_page_size + self.menu_cursel + 1; + SetMenuType(menu_data.menu_type >= MT_IMAGE_128_4X3 ? MT_PLANE_TEXT : menu_data.menu_type + 1); + self.current_page = ceil((float)real_cursel / (float)menu_data.menu_option.max_page_size); + self.menu_cursel = real_cursel - (self.current_page - 1) * menu_data.menu_option.max_page_size - 1; + + if (LoadPage(true, DMD_NONE)) + { + } + } + break; + + case KeyCancel: + { + self.exit_app = true; + skip_cursor = true; + StartExit(); + } + break; + + // LEFT TRIGGER + case KeyPgup: + { + if (menu_data.menu_type == MT_PLANE_TEXT) + { + fast_cursor = true; + } + + self.current_page--; + self.menu_cursel = 0; + if (LoadPage(false, DMD_RIGHT)) + { + } + } + break; + + // RIGHT TRIGGER + case KeyPgdn: + { + if (menu_data.menu_type == MT_PLANE_TEXT) + { + fast_cursor = true; + } + + self.current_page++; + self.menu_cursel = 0; + if (LoadPage(false, DMD_LEFT)) + { + } + } + break; + + case KeyUp: + { + self.menu_cursel--; + + if (menu_data.menu_type == MT_IMAGE_TEXT_64_5X2) + { + if (self.menu_cursel < 0) + { + self.menu_cursel += self.game_count; + } + } + else if (menu_data.menu_type == MT_PLANE_TEXT) + { + if (self.menu_cursel < 0) + { + fast_cursor = true; + self.current_page--; + LoadPage(false, DMD_DOWN); + self.menu_cursel = self.game_count - 1; + } + } + else + { + if ((self.menu_cursel + 1) % menu_data.menu_option.size_items_column == 0) + { + self.menu_cursel += menu_data.menu_option.size_items_column; + } + + if (self.menu_cursel < 0) + { + self.menu_cursel = 0; + } + else if (self.menu_cursel >= self.game_count) + { + self.menu_cursel = self.game_count - 1; + } + } + } + + break; + + case KeyDown: + { + self.menu_cursel++; + + if (menu_data.menu_type == MT_IMAGE_TEXT_64_5X2) + { + if (self.menu_cursel >= self.game_count) + { + self.menu_cursel -= self.game_count; + } + } + else if (menu_data.menu_type == MT_PLANE_TEXT) + { + if (self.menu_cursel >= self.game_count) + { + fast_cursor = true; + self.current_page++; + self.menu_cursel = 0; + LoadPage(false, DMD_UP); + } + } + else + { + if (self.menu_cursel % menu_data.menu_option.size_items_column == 0) + { + self.menu_cursel -= menu_data.menu_option.size_items_column; + } + + if (self.menu_cursel >= self.game_count) + { + self.menu_cursel = self.game_count - 1; + } + } + } + + break; + + case KeyLeft: + + if (menu_data.menu_type == MT_PLANE_TEXT) + { + fast_cursor = true; + self.current_page--; + self.menu_cursel = 0; + if (LoadPage(false, DMD_RIGHT)) + { + } + } + else + { + if (self.game_count <= menu_data.menu_option.size_items_column) + { + self.menu_cursel = 0; + + if (menu_data.app_config.change_page_with_pad) + { + self.current_page--; + self.menu_cursel = 0; + if (LoadPage(false, DMD_RIGHT)) + { + } + } + } + else + { + self.menu_cursel -= menu_data.menu_option.size_items_column; + + if (self.menu_cursel < 0) + { + if (menu_data.app_config.change_page_with_pad) + { + self.current_page--; + self.menu_cursel = 0; + if (LoadPage(false, DMD_RIGHT)) + { + } + } + else + { + self.menu_cursel += self.game_count; + } + } + } + } + + break; + + case KeyRight: + if (menu_data.menu_type == MT_PLANE_TEXT) + { + fast_cursor = true; + self.current_page++; + self.menu_cursel = 0; + if (LoadPage(false, DMD_LEFT)) + { + } + } + else + { + if (self.game_count <= menu_data.menu_option.size_items_column) + { + self.menu_cursel = self.game_count - 1; + + if (menu_data.app_config.change_page_with_pad) + { + self.current_page++; + self.menu_cursel = 0; + if (LoadPage(false, DMD_LEFT)) + { + } + } + } + else + { + self.menu_cursel += menu_data.menu_option.size_items_column; + + if (self.menu_cursel >= self.game_count) + { + if (menu_data.app_config.change_page_with_pad) + { + self.current_page++; + self.menu_cursel = 0; + if (LoadPage(false, DMD_LEFT)) + { + } + } + else + { + self.menu_cursel -= self.game_count; + } + } + } + } + + break; + + case KeySelect: + { + StartExit(); + skip_cursor = true; + } + break; + + default: + { + } + break; + } + + if (!skip_cursor) + { + if (key != KeyMiscX) + StopCDDA(); + + self.game_changed = true; + for (int i = 0; i < self.game_count; i++) + { + if (self.item_game[i] != NULL) + { + if (i == self.menu_cursel) + { + TSU_ItemMenuSetSelected(self.item_game[i], true, false); + SetTitle(TSU_ItemMenuGetItemIndex(self.item_game[i]), TSU_ItemMenuGetItemValue(self.item_game[i])); + + SetTitleType(GetFullGamePathByIndex(TSU_ItemMenuGetItemIndex(self.item_game[i])) + , CheckGdiOptimized(TSU_ItemMenuGetItemIndex(self.item_game[i]))); + + SetCursor(); + + if (fast_cursor) + { + TSU_LogXYMoverSetFactor(self.item_selector_animation, 5.5f); + } + else + { + TSU_LogXYMoverSetFactor(self.item_selector_animation, 7.0f); + } + + ShowCover(TSU_ItemMenuGetItemIndex(self.item_game[i])); + + if (key != KeyMiscX) + PlayCDDA(TSU_ItemMenuGetItemIndex(self.item_game[i])); + } + else + { + TSU_ItemMenuSetSelected(self.item_game[i], false, false); + } + } + } + self.game_changed = false; + } + + mutex_unlock(&change_page_mutex); +} + +static int LoadPreset() +{ + if (menu_data.preset == NULL || menu_data.preset->game_index != self.game_index_selected) + { + if (menu_data.preset != NULL) + { + free(menu_data.preset); + } + + menu_data.preset = LoadPresetGame(self.game_index_selected); + } + + if ((self.isoldr = ParsePresetToIsoldr(self.game_index_selected, menu_data.preset)) == NULL) + { + return 0; + } + + char memory[12]; + memset(memory, 0, sizeof(memory)); + + if (strcasecmp(menu_data.preset->memory, "0x8c") == 0) + { + snprintf(memory, sizeof(memory), "%s%s", menu_data.preset->memory, menu_data.preset->custom_memory); + } + else + { + strcpy(memory, menu_data.preset->memory); + } + + self.addr = strtoul(memory, NULL, 16); + + if (menu_data.preset->emu_vmu) + { + GenerateVMUFile(self.item_value_selected, menu_data.preset->vmu_mode, menu_data.preset->emu_vmu); + } + + if (strncmp(self.isoldr->fs_dev, "auto", 4) == 0) + { + if (self.device_selected == APP_DEVICE_SD) + { + strcpy(self.isoldr->fs_dev, "sd"); + } + else + { + if (!strncasecmp(GetDefaultDir(menu_data.current_dev), "/cd", 3)) + { + strcpy(self.isoldr->fs_dev, "cd"); + } + else if (!strncasecmp(GetDefaultDir(menu_data.current_dev), "/sd", 3)) + { + strcpy(self.isoldr->fs_dev, "sd"); + } + else if (!strncasecmp(GetDefaultDir(menu_data.current_dev), "/ide", 4)) + { + strcpy(self.isoldr->fs_dev, "ide"); + } + } + } + + return 1; +} + +static void FreeAppData() +{ + mutex_destroy(&change_page_mutex); + + for (int i = 0; i < MAX_SIZE_ITEMS; i++) + { + if (self.item_game[i] != NULL) + { + if (!self.exit_app && TSU_ItemMenuIsSelected(self.item_game[i]) && self.run_animation != NULL) + { + TSU_AnimationComplete((Animation *)self.run_animation, (Drawable *)self.item_game[i]); + } + else if (self.exit_animation_list[i] != NULL) + { + TSU_AnimationComplete((Animation *)self.exit_animation_list[i], (Drawable *)self.item_game[i]); + } + + TSU_DrawableSetFinished((Drawable *)self.item_game[i]); + } + } + + TSU_DrawableSubRemoveFinished((Drawable *)self.scene_ptr); + thd_pass(); + + for (int i = 0; i < MAX_SIZE_ITEMS; i++) + { + if (self.item_game[i] != NULL) + { + if (TSU_ItemMenuIsSelected(self.item_game[i]) && self.run_animation != NULL) + { + TSU_LogXYMoverDestroy(&self.run_animation); + } + else if (self.exit_animation_list[i] != NULL) + { + TSU_ExpXYMoverDestroy(&self.exit_animation_list[i]); + } + + if (self.item_game_animation[i] != NULL) + { + TSU_LogXYMoverDestroy(&self.item_game_animation[i]); + } + + if (self.exit_trigger_list[i] != NULL) + { + TSU_DeathDestroy(&self.exit_trigger_list[i]); + } + + if (self.item_game[i] != NULL) + { + TSU_ItemMenuDestroy(&self.item_game[i]); + } + } + } + + if (self.main_box != NULL) + { + TSU_BoxDestroy(&self.main_box); + } + + if (self.title_rectangle != NULL) + { + TSU_RectangleDestroy(&self.title_rectangle); + } + + if (self.title_type_rectangle != NULL) + { + TSU_RectangleDestroy(&self.title_type_rectangle); + } + + if (self.game_list_rectangle != NULL) + { + TSU_RectangleDestroy(&self.game_list_rectangle); + } + + if (self.item_selector != NULL) + { + TSU_RectangleDestroy(&self.item_selector); + } + + if (self.title != NULL) + { + TSU_LabelDestroy(&self.title); + } + + if (self.title_back != NULL) + { + TSU_LabelDestroy(&self.title_back); + } + + if (self.title_animation != NULL) + { + TSU_LogXYMoverDestroy(&self.title_animation); + } + + if (self.title_back_animation != NULL) + { + TSU_LogXYMoverDestroy(&self.title_back_animation); + } + + if (self.title_type != NULL) + { + TSU_LabelDestroy(&self.title_type); + } + + if (self.title_type_animation != NULL) + { + TSU_LogXYMoverDestroy(&self.title_type_animation); + } + + if (self.item_selector_animation != NULL) + { + TSU_LogXYMoverDestroy(&self.item_selector_animation); + } + + if (self.animation_cover_game != NULL) + { + TSU_FadeToDestroy(&self.animation_cover_game); + } + + if (self.fadeout_cover_animation != NULL) + { + TSU_FadeOutDestroy(&self.fadeout_cover_animation); + } + + if (self.img_cover_game != NULL) + { + TSU_BannerDestroy(&self.img_cover_game); + TSU_TextureDestroy(&self.texture_cover_game); + } + + if (self.img_cover_game_background != NULL) + { + TSU_BannerDestroy(&self.img_cover_game_background); + TSU_TextureDestroy(&self.texture_cover_game_background); + } + + for (int i = 0; i < MAX_BUTTONS; i++) + { + if (self.item_button[i] != NULL) + { + TSU_ItemMenuDestroy(&self.item_button[i]); + } + } + + if (self.img_cover_game_rectangle != NULL) + { + TSU_RectangleDestroy(&self.img_cover_game_rectangle); + } + + DestroySystemMenu(); + DestroyPresetMenu(); + + TSU_FontDestroy(&self.menu_font); + TSU_FontDestroy(&self.message_font); + TSU_AppDestroy(&self.dsapp_ptr); + self.scene_ptr = NULL; + + DestroyMenuData(); +} + +static bool PlayGame() +{ + bool is_running = false; + + if (self.item_value_selected[0] != '\0') + { + ds_printf("DS_GAMES: Run: %s", self.item_value_selected); + + if (LoadPreset() == 1) + { + ds_printf("LoadPresset: %s", "OK"); + SaveMenuConfig(); + FreeAppData(); + + isoldr_exec(self.isoldr, self.addr); + is_running = true; + } + } + + return is_running; +} + +static void PostOptimizer() +{ + HideOptimizeCoverPopup(); + menu_data.state_app = SA_GAMES_MENU; + menu_data.optimize_game_cover_thread = NULL; +} + +static void PostLoadPVRCover(bool new_cover) +{ + if (new_cover) + { + int game_index = 0; + for (int i = 0; i < self.game_count; i++) + { + if (menu_data.finished_menu) break; + + if (self.item_game[i] != NULL) + { + game_index = TSU_ItemMenuGetItemIndex(self.item_game[i]); + + // ONLY PVR AT THIS TIME + if (menu_data.games_array[game_index].is_pvr_cover) + { + if (menu_data.finished_menu) break; + + thd_pass(); + LoadCover(self.item_game[i], i); + } + } + } + } + + HideCoverScan(); + menu_data.state_app = SA_GAMES_MENU; + menu_data.load_pvr_cover_thread = NULL; +} + +static void StateAppInpuEvent(int state_app, int type, int key) +{ + switch (state_app) + { + case SA_GAMES_MENU: + GamesApp_InputEvent(type, key); + break; + + case SA_SYSTEM_MENU: + GamesApp_SystemMenuInputEvent(type, key); + break; + + case SA_PRESET_MENU: + GamesApp_PresetMenuInputEvent(type, key); + break; + + case SA_SCAN_COVER: + GamesApp_ScanCoverInputEvent(type, key); + break; + + case SA_OPTIMIZE_COVER: + GamesApp_OptimizeCoverInputEvent(type, key); + break; + + default: + if (state_app >= SA_CONTROL) { + GamesApp_ControlInputEvent(type, key, state_app); + } + break; + } +} + +static void DoMenuControlHandler(void *ds_event, void *param, int action) +{ + if (self.show_cover_game) thd_pass(); + + SDL_Event *event = (SDL_Event *) param; + + switch(event->type) { + case SDL_JOYBUTTONDOWN: { + switch(event->jbutton.button) { + case 1: // B + StateAppInpuEvent(menu_data.state_app, EvtKeypress, KeyCancel); + break; + + case 2: // A + StateAppInpuEvent(menu_data.state_app, EvtKeypress, KeySelect); + break; + + case 5: // Y + StateAppInpuEvent(menu_data.state_app, EvtKeypress, KeyMiscY); + break; + + case 6: // X + StateAppInpuEvent(menu_data.state_app, EvtKeypress, KeyMiscX); + break; + + case 4: // Z + StateAppInpuEvent(menu_data.state_app, EvtKeypress, KeyPgup); + break; + + case 0: // C + StateAppInpuEvent(menu_data.state_app, EvtKeypress, KeyPgdn); + break; + + case 3: // START + StateAppInpuEvent(menu_data.state_app, EvtKeypress, KeyStart); + break; + + default: + break; + } + } + break; + + case SDL_JOYAXISMOTION: { + switch(event->jaxis.axis) { + case 2: // RIGHT TRIGGER + { + static bool right_trigger_down = false; + if (!right_trigger_down && (event->jaxis.value >= MAX_TRIGGER_VALUE/3)) { + right_trigger_down = true; + StateAppInpuEvent(menu_data.state_app, EvtKeypress, KeyPgdn); + } + else if (event->jaxis.value < MAX_TRIGGER_VALUE/3) { + right_trigger_down = false; + } + } + break; + + case 3: // LEFT TRIGGER + { + static bool left_trigger_down = false; + if (!left_trigger_down && (event->jaxis.value >= MAX_TRIGGER_VALUE/3)) { + left_trigger_down = true; + StateAppInpuEvent(menu_data.state_app, EvtKeypress, KeyPgup); + } + else if (event->jaxis.value < MAX_TRIGGER_VALUE/3) { + left_trigger_down = false; + } + } + break; + } + } + + case SDL_JOYHATMOTION: { + switch(event->jhat.value) { + case 0x0E: // KEY UP + StateAppInpuEvent(menu_data.state_app, EvtKeypress, KeyUp); + break; + + case 0x0B: // KEY DOWN + StateAppInpuEvent(menu_data.state_app, EvtKeypress, KeyDown); + break; + + case 0x07: // KEY LEFT + StateAppInpuEvent(menu_data.state_app, EvtKeypress, KeyLeft); + break; + + case 0x0D: // KEY RIGHT + StateAppInpuEvent(menu_data.state_app, EvtKeypress, KeyRight); + break; + + default: + break; + } + } + break; + + default: + break; + } +} + +static void DoMenuVideoHandler(void *ds_event, void *param, int action) +{ + if (!self.wait_to_exit_app) + { + if (self.show_cover_game) thd_pass(); + + if ((menu_data.current_dev == APP_DEVICE_SD || menu_data.current_dev == APP_DEVICE_IDE) + && menu_data.state_app == SA_GAMES_MENU) + { + if (menu_data.cover_scanned_app.scan_count < MAX_SCAN_COUNT + && (self.scan_count < MAX_SCAN_COUNT) + && menu_data.cover_scanned_app.last_game_index < menu_data.games_array_count + && menu_data.load_pvr_cover_thread == NULL + && menu_data.optimize_game_cover_thread == NULL) + { + timer_ms_gettime(&self.scan_covers_end_time, NULL); + if ((self.scan_covers_end_time - self.scan_covers_start_time) >= 5) + { + self.scan_covers_start_time = self.scan_covers_end_time; + menu_data.stop_load_pvr_cover = false; + menu_data.load_pvr_cover_thread = thd_create(0, LoadPVRCoverThread, NULL); + StopCDDA(); + ShowCoverScan(); + } + } + } + } + + switch(action) + { + case EVENT_ACTION_RENDER: + { + if (!self.wait_to_exit_app) + { + TSU_AppDoFrame(self.dsapp_ptr); + } + else + { + if (!menu_data.finished_menu && TSU_AppEnd(self.dsapp_ptr)) + { + menu_data.finished_menu = true; + } + } + } + break; + case EVENT_ACTION_RENDER_POST: + break; + case EVENT_ACTION_UPDATE: + break; + default: + break; + } +} + +static void* MenuExitHelper(void *params) +{ + if (self.dsapp_ptr != NULL) + { + if (!self.exit_app) + { + if (PlayGame()) + { + SDL_DC_EmulateMouse(SDL_TRUE); + EnableScreen(); + GUI_Enable(); + ShutdownDS(); + } + else + { + FreeAppData(); + SDL_DC_EmulateMouse(SDL_TRUE); + EnableScreen(); + GUI_Enable(); + + GamesApp_Exit(NULL); + } + } + else + { + FreeAppData(); + SDL_DC_EmulateMouse(SDL_TRUE); + EnableScreen(); + GUI_Enable(); + + GamesApp_Exit(NULL); + } + } + + return NULL; +} + +void GamesApp_Init(App_t *app) +{ + srand(time(NULL)); + mutex_init((mutex_t *)&change_page_mutex, MUTEX_TYPE_NORMAL); + + memset(&self, 0, sizeof(self)); + + if (app->args != NULL) + { + self.have_args = true; + } + else + { + self.have_args = false; + } + + self.app = app; + self.app->thd = NULL; + self.sector_size = 2048; + self.pages = -1; + self.first_menu_load = true; + + memset(self.item_value_selected, 0, sizeof(self.item_value_selected)); + + for (int i = 0; i < MAX_BUTTONS; i++) + { + self.item_button[i] = NULL; + } + + self.run_animation = NULL; + for (int i = 0; i < MAX_SIZE_ITEMS; i++) + { + self.item_game[i] = NULL; + self.item_game_animation[i] = NULL; + self.exit_animation_list[i] = NULL; + self.exit_trigger_list[i] = NULL; + } + + if ((self.dsapp_ptr = TSU_AppCreate(GamesApp_InputEvent)) != NULL) + { + Color main_color = {1, 0.22f, 0.06f, 0.25f}; + self.main_box = TSU_BoxCreate(PVR_LIST_OP_POLY, 3, 480 - 12, 640 - 6, 480 - 9, 12, &main_color, ML_BACKGROUND, 0); + + Color title_color = {1, 0.22f, 0.06f, 0.25f}; + Color title_type_color = {1, 1.0f, 1.0f, 0.1f}; + + if(vid_check_cable() == CT_VGA) + { + self.title_rectangle = TSU_RectangleCreate(PVR_LIST_OP_POLY, 0, 40, 640, 40, &title_color, ML_BACKGROUND, 0); + self.title_type_rectangle = TSU_RectangleCreateWithBorder(PVR_LIST_OP_POLY, 584, 37, 60, 34, &title_type_color, ML_ITEM + 2, 3, &title_color, 0); + } + else + { + self.title_rectangle = TSU_RectangleCreate(PVR_LIST_OP_POLY, 0, 46, 640, 46, &title_color, ML_BACKGROUND, 0); + self.title_type_rectangle = TSU_RectangleCreateWithBorder(PVR_LIST_OP_POLY, 584, 43, 60, 40, &title_type_color, ML_ITEM + 2, 3, &title_color, 0); + } + + TSU_AppSubAddBox(self.dsapp_ptr, self.main_box); + TSU_AppSubAddRectangle(self.dsapp_ptr, self.title_rectangle); + TSU_AppSubAddRectangle(self.dsapp_ptr, self.title_type_rectangle); + + CreateMenuData(&SetMessageScan, &SetMessageOptimizer, &PostLoadPVRCover, &PostOptimizer); + InitMenu(); + + CreateSystemMenu(self.dsapp_ptr, self.scene_ptr, self.menu_font, self.message_font); + CreatePresetMenu(self.dsapp_ptr, self.scene_ptr, self.menu_font, self.message_font); + } +} + +void GamesApp_Open(App_t *app) +{ + (void)app; + + if (self.dsapp_ptr != NULL) + { + int mx = 0; + int my = 0; + SDL_GetMouseState(&mx, &my); + SDL_DC_EmulateMouse(SDL_FALSE); + SDL_DC_EmulateKeyboard(SDL_FALSE); + + DisableScreen(); + GUI_Disable(); + + TSU_AppBegin(self.dsapp_ptr); + + do_menu_control_event = AddEvent + ( + "GamesControl_Event", + EVENT_TYPE_INPUT, + EVENT_PRIO_DEFAULT, + DoMenuControlHandler, + NULL + ); + + do_menu_video_event = AddEvent + ( + "GamesVideo_Event", + EVENT_TYPE_VIDEO, + EVENT_PRIO_DEFAULT, + DoMenuVideoHandler, + NULL + ); + } +} + +void GamesApp_Shutdown(App_t *app) +{ + (void)app; + + if (self.isoldr) + { + free(self.isoldr); + } +} + +void GamesApp_Exit(GUI_Widget *widget) +{ + (void)widget; + App_t *app = GetAppByName("Main"); + OpenApp(app, NULL); +} \ No newline at end of file diff --git a/applications/games_menu/modules/preset.c b/applications/games_menu/modules/preset.c new file mode 100644 index 00000000..3110df71 --- /dev/null +++ b/applications/games_menu/modules/preset.c @@ -0,0 +1,2301 @@ +/* DreamShell ##version## + + preset.h + Copyright (C) 2024 Maniac Vera + +*/ + +#include "app_preset.h" +#include "app_definition.h" +#include "app_menu.h" +#include "app_utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct menu_structure menu_data; + +enum PatchEnum +{ + PATCH_ADDRESS_TYPE = 1, + PATCH_VALUE_TYPE = 2 +}; + +enum ViewEnum +{ + GENERAL_VIEW = 0, + CDDA_VIEW, + PATCH_VIEW, + EXTENSIONS_VIEW, + SHORTCUT_VIEW +}; + +static struct +{ + DSApp *dsapp_ptr; + Scene *scene_ptr; + Form *preset_menu_form; + + bool save; + int body_letter_size; + int game_index; + uint body_height_size; + char full_path_game[NAME_MAX]; + char *game_cover_path; + int changed_yflip; + bool button_start; + bool button_x; + bool button_y; + bool button_z; + bool button_lt; + bool button_a; + bool button_b; + bool button_c; + bool button_rt; + + LogXYMover *menu_init_animation; + ExpXYMover *menu_end_animation; + + Font *message_font; + Font *menu_font; + Font *textbox_font; + + CheckBox *save_preset_option; + CheckBox *dma_option; + OptionGroup *async_option; + CheckBox *bypass_option; + CheckBox *cdda_option; + CheckBox *irq_option; + OptionGroup *os_option; + OptionGroup *loader_option; + OptionGroup *boot_option; + CheckBox *fast_option; + CheckBox *lowlevel_option; + OptionGroup *memory_option; + TextBox *custom_memory_option; + OptionGroup *heap_option; + + OptionGroup *cdda_source_option; + OptionGroup *cdda_destination_option; + OptionGroup *cdda_position_option; + OptionGroup *cdda_channel_option; + + TextBox *patch_address1_option; + TextBox *patch_value1_option; + TextBox *patch_address2_option; + TextBox *patchvalue2_option; + + CheckBox *altboot_option; + CheckBox *screenshot_option; + CheckBox *button_start_option; + CheckBox *button_x_option; + CheckBox *button_y_option; + CheckBox *button_z_option; + CheckBox *button_rt_option; + CheckBox *button_a_option; + CheckBox *button_b_option; + CheckBox *button_c_option; + CheckBox *button_lt_option; + TextBox *vmu_option; + OptionGroup *vmu_selector_option; + + OptionGroup *shortcut_size_option; + CheckBox *shortcut_rotate_option; + TextBox *shortcut_name_option; + CheckBox *shortcut_dontshowname_option; + ItemMenu *shortcut_create_option; + + Texture *cover_texture; + Banner *cover_banner; +} self; + + +void CreatePresetMenu(DSApp *dsapp_ptr, Scene *scene_ptr, Font *menu_font, Font *message_font) +{ + self.dsapp_ptr = dsapp_ptr; + self.scene_ptr = scene_ptr; + self.message_font = message_font; + self.menu_font = menu_font; + self.preset_menu_form = NULL; + self.menu_init_animation = NULL; + self.menu_end_animation = NULL; + self.body_letter_size = 16; + self.body_height_size = 22; + + char font_path[NAME_MAX]; + memset(font_path, 0, sizeof(font_path)); + snprintf(font_path, sizeof(font_path), "%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu/fonts/default.txf"); + + self.textbox_font = TSU_FontCreate(font_path, PVR_LIST_TR_POLY); + + memset(self.full_path_game, 0, NAME_MAX); + self.cover_texture = NULL; + self.cover_banner = NULL; + self.game_cover_path = NULL; + self.changed_yflip = -1; +} + +void DestroyPresetMenu() +{ + HidePresetMenu(); + + self.dsapp_ptr = NULL; + self.scene_ptr = NULL; + self.message_font = NULL; + self.menu_font = NULL; + + TSU_FontDestroy(&self.textbox_font); + + if (menu_data.preset != NULL) + { + free(menu_data.preset); + menu_data.preset = NULL; + } + + self.cover_texture = NULL; +} + +void PresetMenuRemoveAll() +{ +} + +void PresetMenuInputEvent(int type, int key) +{ + TSU_FormInputEvent(self.preset_menu_form, type, key); +} + +int StatePresetMenu() +{ + return (self.preset_menu_form != NULL ? 1 : 0); +} + +void OnViewIndexChangedEvent(Drawable *drawable, int view_index) +{ + switch (view_index) + { + case GENERAL_VIEW: + CreateGeneralView((Form *)drawable); + break; + + case CDDA_VIEW: + CreateCDDAView((Form *)drawable); + break; + + case PATCH_VIEW: + CreatePatchView((Form *)drawable); + break; + + case EXTENSIONS_VIEW: + CreateExtensionsView((Form *)drawable); + break; + + case SHORTCUT_VIEW: + CreateShortcutView((Form *)drawable); + break; + } +} + +void OnGetObjectsCurrentViewEvent(uint loop_index, int id, Drawable *drawable, uint type, uint row, uint column, int view_index) +{ + if (loop_index == 0 && view_index == CDDA_VIEW) + { + menu_data.preset->emu_cdda = CDDA_MODE_DISABLED; + } + + switch (id) + { + case SAVE_CONTROL_ID: + { + self.save = TSU_CheckBoxGetValue((CheckBox *)drawable); + } + break; + + case DMA_CONTROL_ID: + { + menu_data.preset->use_dma = TSU_CheckBoxGetValue((CheckBox *)drawable); + } + break; + + case ASYNC_CONTROL_ID: + { + menu_data.preset->emu_async = TSU_OptionGroupGetKeySelected((OptionGroup *)drawable); + } + break; + + case BYPASS_CONTROL_ID: + { + menu_data.preset->alt_read = TSU_CheckBoxGetValue((CheckBox *)drawable); + } + break; + + case IRQ_CONTROL_ID: + { + menu_data.preset->use_irq = TSU_CheckBoxGetValue((CheckBox *)drawable); + } + break; + + case OS_CONTROL_ID: + { + menu_data.preset->bin_type = TSU_OptionGroupGetKeySelected((OptionGroup *)drawable); + } + break; + + case LOADER_CONTROL_ID: + { + memset(menu_data.preset->device, 0, FIRMWARE_SIZE); + strcpy(menu_data.preset->device, TSU_OptionGroupGetTextSelected((OptionGroup *)drawable)); + } + break; + + case BOOT_CONTROL_ID: + { + menu_data.preset->boot_mode = TSU_OptionGroupGetKeySelected((OptionGroup *)drawable); + } + break; + + case FASTBOOT_CONTROL_ID: + { + menu_data.preset->fastboot = TSU_CheckBoxGetValue((CheckBox *)drawable); + } + break; + + case LOWLEVEL_CONTROL_ID: + { + menu_data.preset->low = TSU_CheckBoxGetValue((CheckBox *)drawable); + } + break; + + case MEMORY_CONTROL_ID: + { + memset(menu_data.preset->memory, 0, sizeof(menu_data.preset->memory)); + strcpy(menu_data.preset->memory, TSU_OptionGroupGetTextSelected((OptionGroup *)drawable)); + } + break; + + case CUSTOM_MEMORY_CONTROL_ID: + { + + } + break; + + case HEAP_CONTROL_ID: + { + memset(menu_data.preset->heap_memory, 0, sizeof(menu_data.preset->heap_memory)); + strcpy(menu_data.preset->heap_memory, TSU_OptionGroupGetTextSelected((OptionGroup *)drawable)); + menu_data.preset->heap = TSU_OptionGroupGetKeySelected((OptionGroup *)drawable); + } + break; + + case CDDA_CONTROL_ID: + { + menu_data.preset->cdda = TSU_CheckBoxGetValue((CheckBox *)drawable); + } + break; + + case CDDA_SOURCE_CONTROL_ID: + { + menu_data.preset->emu_cdda |= TSU_OptionGroupGetKeySelected((OptionGroup *)drawable); + } + break; + + case CDDA_DESTINATION_CONTROL_ID: + { + menu_data.preset->emu_cdda |= TSU_OptionGroupGetKeySelected((OptionGroup *)drawable); + } + break; + + case CDDA_POSITION_CONTROL_ID: + { + menu_data.preset->emu_cdda |= TSU_OptionGroupGetKeySelected((OptionGroup *)drawable); + } + break; + + case CDDA_CHANNEL_CONTROL_ID: + { + menu_data.preset->emu_cdda |= TSU_OptionGroupGetKeySelected((OptionGroup *)drawable); + } + break; + + case PATCH_ADDRESS1_CONTROL_ID: + { + } + break; + + case PATCH_VALUE1_CONTROL_ID: + { + } + break; + + case PATCH_ADDRESS2_CONTROL_ID: + { + } + break; + + case PATCH_VALUE2_CONTROL_ID: + { + } + break; + + case ALTERBOOT_CONTROL_ID: + { + menu_data.preset->alt_boot = TSU_CheckBoxGetValue((CheckBox *)drawable); + } + break; + + case SCREENSHOT_CONTROL_ID: + { + menu_data.preset->screenshot = TSU_CheckBoxGetValue((CheckBox *)drawable); + } + break; + + case VMU_CONTROL_ID: + { + } + break; + + case VMUSELECTOR_CONTROL_ID: + { + menu_data.preset->vmu_mode = TSU_OptionGroupGetKeySelected((OptionGroup *)drawable); + } + break; + + case SHORTCUT_SIZE_CONTROL_ID: + { + menu_data.preset->icon_size = TSU_OptionGroupGetKeySelected((OptionGroup *)drawable); + } + break; + + case SHORTCUT_ROTATE_CONTROL_ID: + { + menu_data.preset->rotate_image = TSU_CheckBoxGetValue((CheckBox *)drawable); + } + break; + + case SHORTCUT_NAME_CONTROL_ID: + { + } + break; + + case SHORTCUT_DONTSHOWNAME_CONTROL_ID: + { + menu_data.preset->dont_show_name = TSU_CheckBoxGetValue((CheckBox *)drawable); + } + break; + + case SHORTCUT_CREATE_CONTROL_ID: + { + } + break; + } +} + +void CreateGeneralView(Form *form_ptr) +{ + TSU_FormtSetAttributes(form_ptr, 4, 9, 100, 34); + + Font* form_font = TSU_FormGetTitleFont(form_ptr); + TSU_FormSetColumnSize(form_ptr, 1, 120); + TSU_FormSetColumnSize(form_ptr, 2, 170); + TSU_FormSetColumnSize(form_ptr, 3, 140); + TSU_FormSetColumnSize(form_ptr, 4, 170); + TSU_FormSetRowSize(form_ptr, 7, 34); + TSU_FormSetTitle(form_ptr, "SETTINGS - PRESET"); + + int body_letter_size = self.body_letter_size - 2; + uint body_height_size = self.body_height_size - 4; + + { + // SAVE + Label *save_label = TSU_LabelCreate(form_font, "SAVE AS PRESET:", body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)save_label, true); + TSU_FormAddBodyLabel(form_ptr, save_label, 1, 1); + + self.save_preset_option = TSU_CheckBoxCreate(form_font, (uint)body_letter_size, 50, body_height_size); + TSU_DrawableSetId((Drawable *)self.save_preset_option, SAVE_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.save_preset_option, 2, 1); + TSU_DrawableEventSetClick((Drawable *)self.save_preset_option, &SavePresetOptionClick); + + if (self.save) + { + TSU_CheckBoxSetOn(self.save_preset_option); + } + + Vector save_vector = TSU_DrawableGetPosition((Drawable *)self.save_preset_option); + save_vector.x += 80; + TSU_DrawableSetTranslate((Drawable *)self.save_preset_option, &save_vector); + TSU_FormSetCursor(form_ptr, (Drawable *)self.save_preset_option); + + save_label = NULL; + } + + { + // DMA + Label *dma_label = TSU_LabelCreate(form_font, "DMA:", body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)dma_label, true); + TSU_FormAddBodyLabel(form_ptr, dma_label, 1, 2); + + self.dma_option = TSU_CheckBoxCreate(form_font, (uint)body_letter_size, 50, body_height_size); + TSU_DrawableSetId((Drawable *)self.dma_option, DMA_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.dma_option, 2, 2); + TSU_DrawableEventSetClick((Drawable *)self.dma_option, &DMAOptionClick); + + if (menu_data.preset->use_dma) + { + TSU_CheckBoxSetOn(self.dma_option); + } + + dma_label = NULL; + } + + { + // ASYNC + Label *async_label = TSU_LabelCreate(form_font, "ASYNC:", body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)async_label, true); + TSU_FormAddBodyLabel(form_ptr, async_label, 1, 3); + + self.async_option = TSU_OptionGroupCreate(form_font, (uint)body_letter_size, 80, body_height_size); + TSU_DrawableSetId((Drawable *)self.async_option, ASYNC_CONTROL_ID); + + if (TSU_CheckBoxGetValue(self.dma_option)) + { + TSU_OptionGroupAdd(self.async_option, 0, "TRUE"); + } + else + { + TSU_OptionGroupAdd(self.async_option, 0, "NONE"); + } + + TSU_OptionGroupAdd(self.async_option, 1, "1"); + TSU_OptionGroupAdd(self.async_option, 2, "2"); + TSU_OptionGroupAdd(self.async_option, 3, "3"); + TSU_OptionGroupAdd(self.async_option, 4, "4"); + TSU_OptionGroupAdd(self.async_option, 5, "5"); + TSU_OptionGroupAdd(self.async_option, 6, "6"); + TSU_OptionGroupAdd(self.async_option, 7, "7"); + TSU_OptionGroupAdd(self.async_option, 8, "8"); + TSU_OptionGroupAdd(self.async_option, 16, "16"); + TSU_OptionGroupSetStates(self.async_option, SA_CONTROL + ASYNC_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyOptionGroup(form_ptr, self.async_option, 2, 3); + TSU_DrawableEventSetClick((Drawable *)self.async_option, &AsyncOptionClick); + + if (menu_data.preset->emu_async) + { + TSU_OptionGroupSelectOptionByKey(self.async_option, menu_data.preset->emu_async); + } + + async_label = NULL; + } + + { + // BY PASS + Label *bypass_label = TSU_LabelCreate(form_font, "BY PASS:", body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)bypass_label, true); + TSU_FormAddBodyLabel(form_ptr, bypass_label, 1, 4); + + self.bypass_option = TSU_CheckBoxCreate(form_font, (uint)body_letter_size, 50, body_height_size); + TSU_DrawableSetId((Drawable *)self.bypass_option, BYPASS_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.bypass_option, 2, 4); + TSU_DrawableEventSetClick((Drawable *)self.bypass_option, &ByPassOptionClick); + + if (menu_data.preset->alt_read) + { + TSU_CheckBoxSetOn(self.bypass_option); + } + + bypass_label = NULL; + } + + { + // IRQ + Label *irq_label = TSU_LabelCreate(form_font, "IRQ:", body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)irq_label, true); + TSU_FormAddBodyLabel(form_ptr, irq_label, 1, 5); + + self.irq_option = TSU_CheckBoxCreate(form_font, (uint)body_letter_size, 50, body_height_size); + TSU_DrawableSetId((Drawable *)self.irq_option, IRQ_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.irq_option, 2, 5); + TSU_DrawableEventSetClick((Drawable *)self.irq_option, &IRQOptionClick); + + if (menu_data.preset->use_irq) + { + TSU_CheckBoxSetOn(self.irq_option); + } + + irq_label = NULL; + } + + { + // LOADER + Label *loader_label = TSU_LabelCreate(form_font, "LOADER FW:", body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)loader_label, true); + TSU_FormAddBodyLabel(form_ptr, loader_label, 1, 6); + + self.loader_option = TSU_OptionGroupCreate(form_font, (uint)body_letter_size, 130, body_height_size); + TSU_DrawableSetId((Drawable *)self.loader_option, LOADER_CONTROL_ID); + + TSU_OptionGroupSetStates(self.loader_option, SA_CONTROL + LOADER_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_OptionGroupAdd(self.loader_option, 0, "auto"); + + for (int i = 0; i < menu_data.firmware_array_count; i++) + { + TSU_OptionGroupAdd(self.loader_option, i+1, menu_data.firmware_array[i].file); + } + + TSU_FormAddBodyOptionGroup(form_ptr, self.loader_option, 2, 6); + TSU_DrawableEventSetClick((Drawable *)self.loader_option, &LoaderOptionClick); + + if (menu_data.preset->device[0] != '\0') + { + TSU_OptionGroupSelectOptionByText(self.loader_option, menu_data.preset->device); + } + + loader_label = NULL; + } + + { + // OS + Label *os_label = TSU_LabelCreate(form_font, "OS:", body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)os_label, true); + TSU_FormAddBodyLabel(form_ptr, os_label, 3, 2); + + self.os_option = TSU_OptionGroupCreate(form_font, (uint)body_letter_size, 130, body_height_size); + TSU_DrawableSetId((Drawable *)self.os_option, OS_CONTROL_ID); + + TSU_OptionGroupAdd(self.os_option, 0, "AUTO"); + TSU_OptionGroupAdd(self.os_option, 1, "KATANA"); + TSU_OptionGroupAdd(self.os_option, 2, "HOMEBREW"); + TSU_OptionGroupAdd(self.os_option, 3, "WinCE"); + TSU_OptionGroupSetStates(self.os_option, SA_CONTROL + OS_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyOptionGroup(form_ptr, self.os_option, 4, 2); + TSU_DrawableEventSetClick((Drawable *)self.os_option, &OSOptionClick); + + if (menu_data.preset->bin_type) + { + TSU_OptionGroupSelectOptionByKey(self.os_option, menu_data.preset->bin_type); + } + + os_label = NULL; + } + + { + // BOOT MODE + Label *boot_label = TSU_LabelCreate(form_font, "BOOT MODE:", body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)boot_label, true); + TSU_FormAddBodyLabel(form_ptr, boot_label, 3, 3); + + self.boot_option = TSU_OptionGroupCreate(form_font, (uint)body_letter_size, 120, body_height_size); + TSU_DrawableSetId((Drawable *)self.boot_option, BOOT_CONTROL_ID); + + TSU_OptionGroupAdd(self.boot_option, BOOT_MODE_DIRECT, "DIRECT"); + TSU_OptionGroupAdd(self.boot_option, BOOT_MODE_IPBIN, "IP.BIN"); + TSU_OptionGroupAdd(self.boot_option, BOOT_MODE_IPBIN_TRUNC, "IP.BIN CUT"); + TSU_OptionGroupSetStates(self.boot_option, SA_CONTROL + BOOT_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyOptionGroup(form_ptr, self.boot_option, 4, 3); + TSU_DrawableEventSetClick((Drawable *)self.boot_option, &BootOptionClick); + + if (menu_data.preset->boot_mode) + { + TSU_OptionGroupSelectOptionByKey(self.boot_option, menu_data.preset->boot_mode); + } + + boot_label = NULL; + } + + { + // FAST BOOT + Label *fast_label = TSU_LabelCreate(form_font, "FAST BOOT:", body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)fast_label, true); + TSU_FormAddBodyLabel(form_ptr, fast_label, 3, 4); + + self.fast_option = TSU_CheckBoxCreate(form_font, (uint)body_letter_size, 50, body_height_size); + TSU_DrawableSetId((Drawable *)self.fast_option, FASTBOOT_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.fast_option, 4, 4); + TSU_DrawableEventSetClick((Drawable *)self.fast_option, &FastOptionClick); + + if (menu_data.preset->fastboot) + { + TSU_CheckBoxSetOn(self.fast_option); + } + + fast_label = NULL; + } + + { + // LOW LEVEL + Label *lowlevel_label = TSU_LabelCreate(form_font, "LOW LEVEL:", body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)lowlevel_label, true); + TSU_FormAddBodyLabel(form_ptr, lowlevel_label, 3, 5); + + self.lowlevel_option = TSU_CheckBoxCreate(form_font, (uint)body_letter_size, 50, body_height_size); + TSU_DrawableSetId((Drawable *)self.lowlevel_option, LOWLEVEL_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.lowlevel_option, 4, 5); + TSU_DrawableEventSetClick((Drawable *)self.lowlevel_option, &LowLevelOptionClick); + + if (menu_data.preset->low) + { + TSU_CheckBoxSetOn(self.lowlevel_option); + } + + lowlevel_label = NULL; + } + + { + // MEMORY + Label *memory_label = TSU_LabelCreate(form_font, "LOADER :\n\nMEMORY", body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)memory_label, true); + TSU_FormAddBodyLabel(form_ptr, memory_label, 1, 7); + + self.memory_option = TSU_OptionGroupCreate(form_font, (uint)body_letter_size, 130, body_height_size); + TSU_DrawableSetId((Drawable *)self.memory_option, MEMORY_CONTROL_ID); + + TSU_OptionGroupAdd(self.memory_option, 1, "0x8c000100"); + TSU_OptionGroupAdd(self.memory_option, 2, "0x8c001100"); + TSU_OptionGroupAdd(self.memory_option, 3, "0x8c004000"); + TSU_OptionGroupAdd(self.memory_option, 4, "0x8c004800"); + TSU_OptionGroupAdd(self.memory_option, 5, "0x8c008000"); + TSU_OptionGroupAdd(self.memory_option, 6, "0x8ce00000"); + TSU_OptionGroupAdd(self.memory_option, 7, "0x8cef8000"); + TSU_OptionGroupAdd(self.memory_option, 8, "0x8cf80000"); + TSU_OptionGroupAdd(self.memory_option, 9, "0x8cfc0000"); + TSU_OptionGroupAdd(self.memory_option, 10, "0x8cfd0000"); + TSU_OptionGroupAdd(self.memory_option, 11, "0x8cfe0000"); + TSU_OptionGroupAdd(self.memory_option, 12, "0x8cfe8000"); + TSU_OptionGroupAdd(self.memory_option, 13, "0x8cff0000"); + TSU_OptionGroupAdd(self.memory_option, 14, "0x8cff4800"); + TSU_OptionGroupAdd(self.memory_option, 15, "0x8d000000"); + TSU_OptionGroupAdd(self.memory_option, 16, "0x8c"); + TSU_OptionGroupSetStates(self.memory_option, SA_CONTROL + MEMORY_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyOptionGroup(form_ptr, self.memory_option, 2, 7); + TSU_DrawableEventSetClick((Drawable *)self.memory_option, &MemoryOptionClick); + + if (menu_data.preset->memory[0] != 0) + { + if (TSU_OptionGroupGetOptionByText(self.memory_option, menu_data.preset->memory) != NULL) + { + TSU_OptionGroupSelectOptionByText(self.memory_option, menu_data.preset->memory); + } + else + { + TSU_OptionGroupSelectOptionByText(self.memory_option, "0x8c"); + memset(menu_data.preset->custom_memory, 0, sizeof(menu_data.preset->custom_memory)); + + if (strlen(menu_data.preset->memory) == 10) + { + strcpy(menu_data.preset->custom_memory, &menu_data.preset->memory[4]); + memset(menu_data.preset->memory, 0, sizeof(menu_data.preset->memory)); + strcpy(menu_data.preset->memory, "0x8c"); + } + else + { + TSU_OptionGroupSelectOptionByText(self.memory_option, "0x8c000100"); + } + } + } + + memory_label = NULL; + } + + { + // CUSTOM MEMORY + self.custom_memory_option = TSU_TextBoxCreate(self.textbox_font, (uint)body_letter_size, false, 130, body_height_size, true, false, true, false); + TSU_DrawableSetId((Drawable *)self.custom_memory_option, CUSTOM_MEMORY_CONTROL_ID); + + TSU_TextBoxSetStates(self.custom_memory_option, SA_CONTROL + CUSTOM_MEMORY_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyTextBox(form_ptr, self.custom_memory_option, 3, 7); + TSU_DrawableEventSetClick((Drawable *)self.custom_memory_option, &CustomMemoryOptionClick); + + TSU_TextBoxSetText(self.custom_memory_option, menu_data.preset->custom_memory); + } + + { + // HEAP + Label *heap_label = TSU_LabelCreate(form_font, "HEAP :\n\nMEMORY", body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)heap_label, true); + TSU_FormAddBodyLabel(form_ptr, heap_label, 1, 9); + + self.heap_option = TSU_OptionGroupCreate(form_font, (uint)body_letter_size, 320, body_height_size); + TSU_DrawableSetId((Drawable *)self.heap_option, HEAP_CONTROL_ID); + + TSU_OptionGroupAdd(self.heap_option, HEAP_MODE_AUTO, "AUTO"); + TSU_OptionGroupAdd(self.heap_option, HEAP_MODE_BEHIND, "BEHIND THE LOADER"); + TSU_OptionGroupAdd(self.heap_option, HEAP_MODE_INGAME, "INGAME HEAP (KATANA ONLY)"); + TSU_OptionGroupAdd(self.heap_option, HEAP_MODE_MAPLE, "MAPPLE DMA BUFFER"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8c000100, "0x8c000100"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8c001100, "0x8c001100"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8c004000, "0x8c004000"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8c004800, "0x8c004800"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8c008000, "0x8c008000"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8ce00000, "0x8ce00000"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8cef8000, "0x8cef8000"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8cf80000, "0x8cf80000"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8cfc0000, "0x8cfc0000"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8cfd0000, "0x8cfd0000"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8cfe0000, "0x8cfe0000"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8cfe8000, "0x8cfe8000"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8cff0000, "0x8cff0000"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8cff4800, "0x8cff4800"); + TSU_OptionGroupAdd(self.heap_option, (int32)0x8d000000, "0x8d000000"); + TSU_OptionGroupSetStates(self.heap_option, SA_CONTROL + HEAP_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyOptionGroup(form_ptr, self.heap_option, 2, 9); + TSU_DrawableEventSetClick((Drawable *)self.heap_option, &HeapOptionClick); + + if (menu_data.preset->heap) + { + TSU_OptionGroupSelectOptionByKey(self.heap_option, (int32)menu_data.preset->heap); + } + + heap_label = NULL; + } +} + +void CreateCDDAView(Form *form_ptr) +{ + TSU_FormtSetAttributes(form_ptr, 4, 3, 100, 42); + + Font* form_font = TSU_FormGetTitleFont(form_ptr); + TSU_FormSetColumnSize(form_ptr, 1, 110); + TSU_FormSetColumnSize(form_ptr, 2, 165); + TSU_FormSetColumnSize(form_ptr, 3, 150); + TSU_FormSetColumnSize(form_ptr, 4, 165); + TSU_FormSetTitle(form_ptr, "SETTINGS - PRESET"); + + { + // CDDA + Label *cdda_label = TSU_LabelCreate(form_font, "CDDA:", self.body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)cdda_label, true); + TSU_FormAddBodyLabel(form_ptr, cdda_label, 1, 1); + + self.cdda_option = TSU_CheckBoxCreate(form_font, (uint)self.body_letter_size, 50, self.body_height_size); + TSU_DrawableSetId((Drawable *)self.cdda_option, CDDA_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.cdda_option, 2, 1); + TSU_DrawableEventSetClick((Drawable *)self.cdda_option, &CDDAOptionClick); + + if (menu_data.preset->cdda) + { + TSU_CheckBoxSetOn(self.cdda_option); + } + + cdda_label = NULL; + } + + { + // CDDA SOURCE + Label *cdda_source_label = TSU_LabelCreate(form_font, "Source:", self.body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)cdda_source_label, true); + TSU_FormAddBodyLabel(form_ptr, cdda_source_label, 1, 2); + + self.cdda_source_option = TSU_OptionGroupCreate(form_font, (uint)self.body_letter_size, 120, self.body_height_size); + TSU_DrawableSetId((Drawable *)self.cdda_source_option, CDDA_SOURCE_CONTROL_ID); + + TSU_OptionGroupAdd(self.cdda_source_option, CDDA_MODE_SRC_PIO, "PIO"); + TSU_OptionGroupAdd(self.cdda_source_option, CDDA_MODE_SRC_DMA, "DMA"); + TSU_OptionGroupSetStates(self.cdda_source_option, SA_CONTROL + CDDA_SOURCE_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyOptionGroup(form_ptr, self.cdda_source_option, 2, 2); + TSU_DrawableEventSetClick((Drawable *)self.cdda_source_option, &CDDASourceOptionClick); + + if (menu_data.preset->emu_cdda == CDDA_MODE_DISABLED) + { + TSU_OptionGroupSelectOptionByKey(self.cdda_source_option, (int32)CDDA_MODE_SRC_PIO); + } + else if (menu_data.preset->emu_cdda & CDDA_MODE_SRC_PIO) + { + TSU_OptionGroupSelectOptionByKey(self.cdda_source_option, (int32)CDDA_MODE_SRC_PIO); + } + else + { + TSU_OptionGroupSelectOptionByKey(self.cdda_source_option, (int32)CDDA_MODE_SRC_DMA); + } + + cdda_source_label = NULL; + } + + { + // CDDA DESTINATION + Label *cdda_destination_label = TSU_LabelCreate(form_font, "Destination:", self.body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)cdda_destination_label, true); + TSU_FormAddBodyLabel(form_ptr, cdda_destination_label, 3, 2); + + self.cdda_destination_option = TSU_OptionGroupCreate(form_font, (uint)self.body_letter_size, 120, self.body_height_size); + TSU_DrawableSetId((Drawable *)self.cdda_destination_option, CDDA_DESTINATION_CONTROL_ID); + + TSU_OptionGroupAdd(self.cdda_destination_option, 1, "PIO"); + TSU_OptionGroupAdd(self.cdda_destination_option, CDDA_MODE_DST_SQ, "SQ"); + TSU_OptionGroupAdd(self.cdda_destination_option, CDDA_MODE_DST_DMA, "DMA"); + TSU_OptionGroupSetStates(self.cdda_destination_option, SA_CONTROL + CDDA_DESTINATION_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyOptionGroup(form_ptr, self.cdda_destination_option, 4, 2); + TSU_DrawableEventSetClick((Drawable *)self.cdda_destination_option, &CDDADestinationOptionClick); + + if (menu_data.preset->emu_cdda == CDDA_MODE_DISABLED) + { + TSU_OptionGroupSelectOptionByKey(self.cdda_destination_option, (int32)CDDA_MODE_DST_SQ); + } + else if (menu_data.preset->emu_cdda & CDDA_MODE_DST_SQ) + { + TSU_OptionGroupSelectOptionByKey(self.cdda_destination_option, (int32)CDDA_MODE_DST_SQ); + } + else + { + TSU_OptionGroupSelectOptionByKey(self.cdda_destination_option, (int32)CDDA_MODE_DST_DMA); + } + + cdda_destination_label = NULL; + } + + { + // CDDA POSITION + Label *cdda_position_label = TSU_LabelCreate(form_font, "Position:", self.body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)cdda_position_label, true); + TSU_FormAddBodyLabel(form_ptr, cdda_position_label, 1, 3); + + self.cdda_position_option = TSU_OptionGroupCreate(form_font, (uint)self.body_letter_size, 120, self.body_height_size); + TSU_DrawableSetId((Drawable *)self.cdda_position_option, CDDA_POSITION_CONTROL_ID); + + TSU_OptionGroupAdd(self.cdda_position_option, CDDA_MODE_POS_TMU1, "TMU1"); + TSU_OptionGroupAdd(self.cdda_position_option, CDDA_MODE_POS_TMU2, "TMU2"); + TSU_OptionGroupSetStates(self.cdda_position_option, SA_CONTROL + CDDA_POSITION_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyOptionGroup(form_ptr, self.cdda_position_option, 2, 3); + TSU_DrawableEventSetClick((Drawable *)self.cdda_position_option, &CDDAPositionOptionClick); + + if (menu_data.preset->emu_cdda == CDDA_MODE_DISABLED) + { + TSU_OptionGroupSelectOptionByKey(self.cdda_position_option, (int32)CDDA_MODE_POS_TMU1); + } + else if (menu_data.preset->emu_cdda & CDDA_MODE_POS_TMU1) + { + TSU_OptionGroupSelectOptionByKey(self.cdda_position_option, (int32)CDDA_MODE_POS_TMU1); + } + else + { + TSU_OptionGroupSelectOptionByKey(self.cdda_position_option, (int32)CDDA_MODE_POS_TMU2); + } + + cdda_position_label = NULL; + } + + { + // CDDA CHANNEL + Label *cdda_channel_label = TSU_LabelCreate(form_font, "Channel:", self.body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)cdda_channel_label, true); + TSU_FormAddBodyLabel(form_ptr, cdda_channel_label, 3, 3); + + self.cdda_channel_option = TSU_OptionGroupCreate(form_font, (uint)self.body_letter_size, 120, self.body_height_size); + TSU_DrawableSetId((Drawable *)self.cdda_channel_option, CDDA_CHANNEL_CONTROL_ID); + + TSU_OptionGroupAdd(self.cdda_channel_option, CDDA_MODE_CH_ADAPT, "Fixed"); + TSU_OptionGroupAdd(self.cdda_channel_option, CDDA_MODE_CH_FIXED, "Adaptive"); + TSU_OptionGroupSetStates(self.cdda_channel_option, SA_CONTROL + CDDA_CHANNEL_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyOptionGroup(form_ptr, self.cdda_channel_option, 4, 3); + TSU_DrawableEventSetClick((Drawable *)self.cdda_channel_option, &CDDAChannelOptionClick); + + if (menu_data.preset->emu_cdda == CDDA_MODE_DISABLED) + { + TSU_OptionGroupSelectOptionByKey(self.cdda_channel_option, (int32)CDDA_MODE_CH_ADAPT); + } + else if (menu_data.preset->emu_cdda & CDDA_MODE_CH_ADAPT) + { + TSU_OptionGroupSelectOptionByKey(self.cdda_channel_option, (int32)CDDA_MODE_CH_ADAPT); + } + else + { + TSU_OptionGroupSelectOptionByKey(self.cdda_channel_option, (int32)CDDA_MODE_CH_FIXED); + } + + cdda_channel_label = NULL; + } +} + +void CreatePatchView(Form *form_ptr) +{ + TSU_FormtSetAttributes(form_ptr, 2, 4, 100, 42); + + Font* form_font = TSU_FormGetTitleFont(form_ptr); + TSU_FormSetColumnSize(form_ptr, 1, 220); + TSU_FormSetColumnSize(form_ptr, 2, 170); + TSU_FormSetTitle(form_ptr, "SETTINGS - PRESET"); + + { + // PATCH ADDRESS 1 + Label *patch_addres1_label = TSU_LabelCreate(form_font, "PATCH ADDRESS 1:", self.body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)patch_addres1_label, true); + TSU_FormAddBodyLabel(form_ptr, patch_addres1_label, 1, 1); + + self.patch_address1_option = TSU_TextBoxCreate(self.textbox_font, (uint)self.body_letter_size, false, 155, self.body_height_size, true, false, true, true); + TSU_DrawableSetId((Drawable *)self.patch_address1_option, PATCH_ADDRESS1_CONTROL_ID); + + TSU_TextBoxSetStates(self.patch_address1_option, SA_CONTROL + PATCH_ADDRESS1_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyTextBox(form_ptr, self.patch_address1_option, 2, 1); + TSU_DrawableEventSetClick((Drawable *)self.patch_address1_option, &PatchAddress1OptionClick); + + if (menu_data.preset->pa[0] == 0) { + TSU_TextBoxSetText(self.patch_address1_option, "0c000000"); + } + else { + TSU_TextBoxSetText(self.patch_address1_option, menu_data.preset->patch_a[0]); + } + + patch_addres1_label = NULL; + } + + { + // PATCH VALUE 1 + Label *patch_value1_label = TSU_LabelCreate(form_font, "PATCH VALUE 1:", self.body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)patch_value1_label, true); + TSU_FormAddBodyLabel(form_ptr, patch_value1_label, 1, 2); + + self.patch_value1_option = TSU_TextBoxCreate(self.textbox_font, (uint)self.body_letter_size, false, 155, self.body_height_size, true, false, true, true); + TSU_DrawableSetId((Drawable *)self.patch_value1_option, PATCH_VALUE1_CONTROL_ID); + + TSU_TextBoxSetStates(self.patch_value1_option, SA_CONTROL + PATCH_VALUE1_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyTextBox(form_ptr, self.patch_value1_option, 2, 2); + TSU_DrawableEventSetClick((Drawable *)self.patch_value1_option, &PatchValue1OptionClick); + + if (menu_data.preset->pv[0] == 0) { + TSU_TextBoxSetText(self.patch_value1_option, "00000000"); + } + else { + TSU_TextBoxSetText(self.patch_value1_option, menu_data.preset->patch_v[0]); + } + + patch_value1_label = NULL; + } + + { + // PATCH ADDRESS 2 + Label *patch_addres2_label = TSU_LabelCreate(form_font, "PATCH ADDRESS 2:", self.body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)patch_addres2_label, true); + TSU_FormAddBodyLabel(form_ptr, patch_addres2_label, 1, 3); + + self.patch_address2_option = TSU_TextBoxCreate(self.textbox_font, (uint)self.body_letter_size, false, 155, self.body_height_size, true, false, true, true); + TSU_DrawableSetId((Drawable *)self.patch_address2_option, PATCH_ADDRESS2_CONTROL_ID); + + TSU_TextBoxSetStates(self.patch_address2_option, SA_CONTROL + PATCH_ADDRESS2_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyTextBox(form_ptr, self.patch_address2_option, 2, 3); + TSU_DrawableEventSetClick((Drawable *)self.patch_address2_option, &PatchAddress2OptionClick); + + if (menu_data.preset->pa[1] == 0) { + TSU_TextBoxSetText(self.patch_address2_option, "0c000000"); + } + else { + TSU_TextBoxSetText(self.patch_address2_option, menu_data.preset->patch_a[1]); + } + + patch_addres2_label = NULL; + } + + { + // PATCH VALUE 2 + Label *patch_value2_label = TSU_LabelCreate(form_font, "PATCH VALUE 2:", self.body_letter_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)patch_value2_label, true); + TSU_FormAddBodyLabel(form_ptr, patch_value2_label, 1, 4); + + self.patchvalue2_option = TSU_TextBoxCreate(self.textbox_font, (uint)self.body_letter_size, false, 155, self.body_height_size, true, false, true, true); + TSU_DrawableSetId((Drawable *)self.patchvalue2_option, PATCH_VALUE2_CONTROL_ID); + + TSU_TextBoxSetStates(self.patchvalue2_option, SA_CONTROL + PATCH_VALUE2_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyTextBox(form_ptr, self.patchvalue2_option, 2, 4); + TSU_DrawableEventSetClick((Drawable *)self.patchvalue2_option, &PatchValue2OptionClick); + + if (menu_data.preset->pv[1] == 0) { + TSU_TextBoxSetText(self.patchvalue2_option, "00000000"); + } + else { + TSU_TextBoxSetText(self.patchvalue2_option, menu_data.preset->patch_v[1]); + } + + patch_value2_label = NULL; + } +} + +void CreateExtensionsView(Form *form_ptr) +{ + TSU_FormtSetAttributes(form_ptr, 5, 6, 100, 42); + + Font* form_font = TSU_FormGetTitleFont(form_ptr); + TSU_FormSetColumnSize(form_ptr, 1, 250); + TSU_FormSetColumnSize(form_ptr, 2, 320); + TSU_FormSetRowSize(form_ptr, 3, 34); + TSU_FormSetRowSize(form_ptr, 4, 34); + TSU_FormSetTitle(form_ptr, "SETTINGS - EXTENSIONS"); + + { + // ALTER BOOT + Label *alterboot_label = TSU_LabelCreate(form_font, "Boot from 2ND_READ.BIN:", self.body_letter_size - 2, false, false); + TSU_DrawableSetReadOnly((Drawable*)alterboot_label, true); + TSU_FormAddBodyLabel(form_ptr, alterboot_label, 1, 1); + + self.altboot_option = TSU_CheckBoxCreate(form_font, (uint)self.body_letter_size - 2, 50, self.body_height_size); + TSU_DrawableSetId((Drawable *)self.altboot_option, ALTERBOOT_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.altboot_option, 2, 1); + TSU_DrawableEventSetClick((Drawable *)self.altboot_option, &AlterBootOptionClick); + + Vector altboot_vector = TSU_DrawableGetPosition((Drawable *)self.altboot_option); + altboot_vector.x += 50; + TSU_DrawableSetTranslate((Drawable *)self.altboot_option, &altboot_vector); + + if (menu_data.preset->alt_boot) + { + TSU_CheckBoxSetOn(self.altboot_option); + } + + alterboot_label = NULL; + } + + { + // SCREENSHOT + Label *screenshot_label = TSU_LabelCreate(form_font, "Enable screenshots:", self.body_letter_size - 2, false, false); + TSU_DrawableSetReadOnly((Drawable*)screenshot_label, true); + TSU_FormAddBodyLabel(form_ptr, screenshot_label, 1, 2); + + self.screenshot_option = TSU_CheckBoxCreate(form_font, (uint)self.body_letter_size - 2, 50, self.body_height_size); + TSU_DrawableSetId((Drawable *)self.screenshot_option, SCREENSHOT_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.screenshot_option, 2, 2); + TSU_DrawableEventSetClick((Drawable *)self.screenshot_option, &ScreenshotOptionClick); + + Vector screenshot_vector = TSU_DrawableGetPosition((Drawable *)self.screenshot_option); + screenshot_vector.x += 50; + TSU_DrawableSetTranslate((Drawable *)self.screenshot_option, &screenshot_vector); + + if (menu_data.preset->screenshot) + { + TSU_CheckBoxSetOn(self.screenshot_option); + } + + screenshot_label = NULL; + } + + Vector init_button_position, start_button_vector, a_button_vector; + { + self.button_start_option = TSU_CheckBoxCreateWithCustomText(form_font, (uint)self.body_letter_size - 3, 52, self.body_height_size - 6, "START", "START"); + TSU_DrawableSetId((Drawable *)self.button_start_option, BUTTON_START_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.button_start_option, 1, 3); + TSU_DrawableEventSetClick((Drawable *)self.button_start_option, &ButtonStartOptionClick); + + init_button_position = start_button_vector = TSU_DrawableGetPosition((Drawable *)self.button_start_option); + start_button_vector.x += 40; + TSU_DrawableSetTranslate((Drawable *)self.button_start_option, &start_button_vector); + + if (self.button_start) + { + TSU_CheckBoxSetOn(self.button_start_option); + } + } + + { + self.button_x_option = TSU_CheckBoxCreateWithCustomText(form_font, (uint)self.body_letter_size - 2, 36, self.body_height_size - 6, "x", "x"); + TSU_DrawableSetId((Drawable *)self.button_x_option, BUTTON_X_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.button_x_option, 2, 3); + TSU_DrawableEventSetClick((Drawable *)self.button_x_option, &ButtonXOptionClick); + + start_button_vector.x += 120; + TSU_DrawableSetTranslate((Drawable *)self.button_x_option, &start_button_vector); + + if (self.button_x) + { + TSU_CheckBoxSetOn(self.button_x_option); + } + } + + { + self.button_y_option = TSU_CheckBoxCreateWithCustomText(form_font, (uint)self.body_letter_size - 2, 36, self.body_height_size - 6, "y", "y"); + TSU_DrawableSetId((Drawable *)self.button_y_option, BUTTON_Y_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.button_y_option, 3, 3); + TSU_DrawableEventSetClick((Drawable *)self.button_y_option, &ButtonYOptionClick); + + start_button_vector.x += 100; + TSU_DrawableSetTranslate((Drawable *)self.button_y_option, &start_button_vector); + + if (self.button_y) + { + TSU_CheckBoxSetOn(self.button_y_option); + } + } + + { + self.button_z_option = TSU_CheckBoxCreateWithCustomText(form_font, (uint)self.body_letter_size - 2, 36, self.body_height_size - 6, "z", "z"); + TSU_DrawableSetId((Drawable *)self.button_z_option, BUTTON_Z_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.button_z_option, 4, 3); + TSU_DrawableEventSetClick((Drawable *)self.button_z_option, &ButtonZOptionClick); + + start_button_vector.x += 100; + TSU_DrawableSetTranslate((Drawable *)self.button_z_option, &start_button_vector); + + if (self.button_z) + { + TSU_CheckBoxSetOn(self.button_z_option); + } + } + + if (1 != 1) // DISABLED + { + self.button_lt_option = TSU_CheckBoxCreateWithCustomText(form_font, (uint)self.body_letter_size - 2, 36, self.body_height_size - 6, "lt", "lt"); + TSU_DrawableSetId((Drawable *)self.button_lt_option, BUTTON_LT_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.button_lt_option, 5, 3); + TSU_DrawableEventSetClick((Drawable *)self.button_lt_option, &ButtonLTOptionClick); + + start_button_vector.x += 100; + TSU_DrawableSetTranslate((Drawable *)self.button_lt_option, &start_button_vector); + + if (self.button_lt) + { + TSU_CheckBoxSetOn(self.button_lt_option); + } + } + + init_button_position.x += 160; + { + self.button_a_option = TSU_CheckBoxCreateWithCustomText(form_font, (uint)self.body_letter_size - 2, 36, self.body_height_size - 6, "a", "a"); + TSU_DrawableSetId((Drawable *)self.button_a_option, BUTTON_A_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.button_a_option, 2, 4); + TSU_DrawableEventSetClick((Drawable *)self.button_a_option, &ButtonAOptionClick); + + a_button_vector = TSU_DrawableGetPosition((Drawable *)self.button_a_option); + a_button_vector.x = init_button_position.x; + TSU_DrawableSetTranslate((Drawable *)self.button_a_option, &a_button_vector); + + if (self.button_a) + { + TSU_CheckBoxSetOn(self.button_a_option); + } + } + + { + self.button_b_option = TSU_CheckBoxCreateWithCustomText(form_font, (uint)self.body_letter_size - 2, 36, self.body_height_size - 6, "b", "b"); + TSU_DrawableSetId((Drawable *)self.button_b_option, BUTTON_B_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.button_b_option, 3, 4); + TSU_DrawableEventSetClick((Drawable *)self.button_b_option, &ButtonBOptionClick); + + a_button_vector.x += 100; + TSU_DrawableSetTranslate((Drawable *)self.button_b_option, &a_button_vector); + + if (self.button_b) + { + TSU_CheckBoxSetOn(self.button_b_option); + } + } + + { + self.button_c_option = TSU_CheckBoxCreateWithCustomText(form_font, (uint)self.body_letter_size - 2, 36, self.body_height_size - 6, "c", "c"); + TSU_DrawableSetId((Drawable *)self.button_c_option, BUTTON_C_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.button_c_option, 4, 4); + TSU_DrawableEventSetClick((Drawable *)self.button_c_option, &ButtonCOptionClick); + + a_button_vector.x += 100; + TSU_DrawableSetTranslate((Drawable *)self.button_c_option, &a_button_vector); + + if (self.button_c) + { + TSU_CheckBoxSetOn(self.button_c_option); + } + } + + if (1 != 1) // DISABLED + { + self.button_rt_option = TSU_CheckBoxCreateWithCustomText(form_font, (uint)self.body_letter_size - 2, 36, self.body_height_size - 6, "rt", "rt"); + TSU_DrawableSetId((Drawable *)self.button_rt_option, BUTTON_RT_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.button_rt_option, 5, 4); + TSU_DrawableEventSetClick((Drawable *)self.button_rt_option, &ButtonRTOptionClick); + + a_button_vector.x += 100; + TSU_DrawableSetTranslate((Drawable *)self.button_rt_option, &a_button_vector); + + if (self.button_rt) + { + TSU_CheckBoxSetOn(self.button_rt_option); + } + } + + { + // VMU EMULATION + Label *vmu_label = TSU_LabelCreate(form_font, "VMU EMULATION:", self.body_letter_size - 2, false, false); + TSU_DrawableSetReadOnly((Drawable*)vmu_label, true); + TSU_FormAddBodyLabel(form_ptr, vmu_label, 1, 5); + + self.vmu_option = TSU_TextBoxCreate(self.textbox_font, (uint)self.body_letter_size - 2, false, 130, self.body_height_size, false, false, true, false); + TSU_DrawableSetId((Drawable *)self.vmu_option, VMU_CONTROL_ID); + + TSU_TextBoxSetStates(self.vmu_option, SA_CONTROL + VMU_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyTextBox(form_ptr, self.vmu_option, 2, 5); + TSU_DrawableEventSetClick((Drawable *)self.vmu_option, &VMUOptionClick); + + memset(menu_data.preset->vmu_file, 0, sizeof(menu_data.preset->vmu_file)); + if (menu_data.preset->emu_vmu) + { + snprintf(menu_data.preset->vmu_file, sizeof(menu_data.preset->vmu_file), "%03ld", menu_data.preset->emu_vmu); + TSU_TextBoxSetText(self.vmu_option, menu_data.preset->vmu_file); + } + else + { + menu_data.preset->emu_vmu = atoi(DEFAULT_VMU_NUMBER); + strcpy(menu_data.preset->vmu_file, DEFAULT_VMU_NUMBER); + TSU_TextBoxSetText(self.vmu_option, menu_data.preset->vmu_file); + } + + vmu_label = NULL; + } + + { + // VMU SELECTOR MODE + self.vmu_selector_option = TSU_OptionGroupCreate(form_font, (uint)self.body_letter_size - 2, 320, self.body_height_size); + TSU_DrawableSetId((Drawable *)self.vmu_selector_option, VMUSELECTOR_CONTROL_ID); + + TSU_OptionGroupAdd(self.vmu_selector_option, 0, "Disable"); + TSU_OptionGroupAdd(self.vmu_selector_option, 1, "Shared dump from root"); + TSU_OptionGroupAdd(self.vmu_selector_option, 2, "Private dump 200 blocks (128 KB)"); + TSU_OptionGroupAdd(self.vmu_selector_option, 3, "Private dump 1800 blocks (1024 KB)"); + TSU_OptionGroupSetStates(self.vmu_selector_option, SA_CONTROL + VMUSELECTOR_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyOptionGroup(form_ptr, self.vmu_selector_option, 2, 6); + TSU_DrawableEventSetClick((Drawable *)self.vmu_selector_option, &VMUSelectorOptionClick); + + if (menu_data.preset->vmu_mode) + { + TSU_OptionGroupSelectOptionByKey(self.vmu_selector_option, menu_data.preset->vmu_mode); + } + else + { + menu_data.preset->vmu_mode = 0; + TSU_OptionGroupSelectOptionByKey(self.vmu_selector_option, 0); + } + } +} + +void CreateShortcutView(Form *form_ptr) +{ + TSU_FormtSetAttributes(form_ptr, 3, 6, 100, 42); + + Font* form_font = TSU_FormGetTitleFont(form_ptr); + TSU_FormSetColumnSize(form_ptr, 1, 170); + TSU_FormSetColumnSize(form_ptr, 2, 170); + TSU_FormSetColumnSize(form_ptr, 3, 260); + TSU_FormSetRowSize(form_ptr, 6, 60); + TSU_FormSetTitle(form_ptr, "SETTINGS - SHORTCUT"); + + { + // SHORTCUT ICON SIZE + Label *shortcut_size_label = TSU_LabelCreate(form_font, "Icon size:", self.body_letter_size - 2, false, false); + TSU_DrawableSetReadOnly((Drawable*)shortcut_size_label, true); + TSU_FormAddBodyLabel(form_ptr, shortcut_size_label, 1, 1); + + self.shortcut_size_option = TSU_OptionGroupCreate(form_font, (uint)self.body_letter_size - 2, 170, self.body_height_size); + TSU_DrawableSetId((Drawable *)self.shortcut_size_option, SHORTCUT_SIZE_CONTROL_ID); + + TSU_OptionGroupAdd(self.shortcut_size_option, 1, "48x48"); + TSU_OptionGroupAdd(self.shortcut_size_option, 2, "64x64 (as apps)"); + TSU_OptionGroupAdd(self.shortcut_size_option, 3, "96x96"); + TSU_OptionGroupAdd(self.shortcut_size_option, 4, "128x128 (Default)"); + TSU_OptionGroupAdd(self.shortcut_size_option, 5, "256x256"); + TSU_OptionGroupSetStates(self.shortcut_size_option, SA_CONTROL + SHORTCUT_SIZE_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyOptionGroup(form_ptr, self.shortcut_size_option, 2, 1); + TSU_DrawableEventSetClick((Drawable *)self.shortcut_size_option, &ShortcutSizeOptionClick); + + if (menu_data.preset->icon_size) + { + TSU_OptionGroupSelectOptionByKey(self.shortcut_size_option, menu_data.preset->icon_size); + } + else + { + menu_data.preset->icon_size = 0; + TSU_OptionGroupSelectOptionByKey(self.shortcut_size_option, 4); + } + + shortcut_size_label = NULL; + } + + { + // SHORTCUT ROTATE IMAGE + Label *screenshot_rotateimage_label = TSU_LabelCreate(form_font, "Rotate image 180:", self.body_letter_size - 2, false, false); + TSU_DrawableSetReadOnly((Drawable*)screenshot_rotateimage_label, true); + TSU_FormAddBodyLabel(form_ptr, screenshot_rotateimage_label, 1, 2); + + self.shortcut_rotate_option = TSU_CheckBoxCreate(form_font, (uint)self.body_letter_size - 2, 50, self.body_height_size); + TSU_DrawableSetId((Drawable *)self.shortcut_rotate_option, SHORTCUT_ROTATE_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.shortcut_rotate_option, 2, 2); + TSU_DrawableEventSetClick((Drawable *)self.shortcut_rotate_option, &ShortcutRotateOptionClick); + + if (menu_data.preset->rotate_image) + { + TSU_CheckBoxSetOn(self.shortcut_rotate_option); + } + + screenshot_rotateimage_label = NULL; + } + + Vector name_label_vector; + { + // SHORTCUT NAME + Label *shortcut_name_label = TSU_LabelCreate(form_font, "Icon name:", self.body_letter_size - 2, false, false); + TSU_DrawableSetReadOnly((Drawable*)shortcut_name_label, true); + TSU_FormAddBodyLabel(form_ptr, shortcut_name_label, 1, 3); + name_label_vector = TSU_DrawableGetPosition((Drawable *)shortcut_name_label); + + self.shortcut_name_option = TSU_TextBoxCreate(self.textbox_font, (uint)self.body_letter_size - 2, false, 300, self.body_height_size, true, false, true, false); + TSU_DrawableSetId((Drawable *)self.shortcut_name_option, SHORTCUT_NAME_CONTROL_ID); + + TSU_TextBoxSetStates(self.shortcut_name_option, SA_CONTROL + SHORTCUT_NAME_CONTROL_ID, SA_PRESET_MENU, &menu_data.state_app); + TSU_FormAddBodyTextBox(form_ptr, self.shortcut_name_option, 2, 4); + TSU_DrawableEventSetClick((Drawable *)self.shortcut_name_option, &ShortcutNameOptionClick); + + Vector shortcut_name_vector = TSU_DrawableGetPosition((Drawable *)self.shortcut_name_option); + shortcut_name_vector.y -= 18; + shortcut_name_vector.x = name_label_vector.x; + TSU_DrawableSetTranslate((Drawable *)self.shortcut_name_option, &shortcut_name_vector); + + if (menu_data.preset->shortcut_name[0] == '\0' || menu_data.preset->shortcut_name[0] == ' ') + { + char *game_without_extension = NULL; + GetCoverName(self.game_index, &game_without_extension); + + memset(menu_data.preset->shortcut_name, 0, sizeof(menu_data.preset->shortcut_name)); + strncpy(menu_data.preset->shortcut_name, game_without_extension, sizeof(menu_data.preset->shortcut_name) - 4); + free(game_without_extension); + } + + TSU_TextBoxSetText(self.shortcut_name_option, menu_data.preset->shortcut_name); + shortcut_name_label = NULL; + } + + { + // SHORTCUT DONT SHOW NAME + Label *shortcut_dontshowname_label = TSU_LabelCreate(form_font, "Don't show name:", self.body_letter_size - 2, false, false); + TSU_DrawableSetReadOnly((Drawable*)shortcut_dontshowname_label, true); + TSU_FormAddBodyLabel(form_ptr, shortcut_dontshowname_label, 1, 5); + + self.shortcut_dontshowname_option = TSU_CheckBoxCreate(form_font, (uint)self.body_letter_size - 2, 50, self.body_height_size); + TSU_DrawableSetId((Drawable *)self.shortcut_dontshowname_option, SHORTCUT_DONTSHOWNAME_CONTROL_ID); + + TSU_FormAddBodyCheckBox(form_ptr, self.shortcut_dontshowname_option, 2, 5); + TSU_DrawableEventSetClick((Drawable *)self.shortcut_dontshowname_option, &ShortcutDontShowNameOptionClick); + + if (menu_data.preset->dont_show_name) + { + TSU_CheckBoxSetOn(self.shortcut_dontshowname_option); + } + + shortcut_dontshowname_label = NULL; + } + + { + // CREATE SHORTCUT + char *image_path = (char *)malloc(NAME_MAX); + snprintf(image_path, NAME_MAX, "%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu/images/save.png"); + self.shortcut_create_option = TSU_ItemMenuCreate(image_path, 32, 32, PVR_LIST_TR_POLY, "Create shortcut", self.menu_font, (uint)self.body_letter_size - 2, false, 0); + free(image_path); + + TSU_DrawableSetId((Drawable *)self.shortcut_create_option, SHORTCUT_CREATE_CONTROL_ID); + TSU_FormAddBodyItemMenu(form_ptr, self.shortcut_create_option, 2, 6); + TSU_DrawableEventSetClick((Drawable *)self.shortcut_create_option, &ShortcutCreateOptionClick); + + Vector shortcut_vector = TSU_DrawableGetPosition((Drawable *)self.shortcut_create_option); + shortcut_vector.y -= 22; + shortcut_vector.x = name_label_vector.x; + TSU_DrawableSetTranslate((Drawable *)self.shortcut_create_option, &shortcut_vector); + } + + { + // COVER BANNER + ShortcutLoadTextureCover(); + if (self.cover_texture != NULL) + { + uint16 cover_type = GetCoverType(self.game_index, MT_PLANE_TEXT); + self.cover_banner = TSU_BannerCreate(cover_type == IT_JPG ? PVR_LIST_OP_POLY : PVR_LIST_TR_POLY, self.cover_texture); + TSU_DrawableSetReadOnly((Drawable*)self.cover_banner, true); + TSU_BannerSetSize(self.cover_banner, 128, 128); + TSU_DrawableSetId((Drawable *)self.cover_banner, SHORTCUT_COVER_CONTROL_ID); + TSU_FormAddBodyBanner(form_ptr, self.cover_banner, 3, 5); + + Vector banner_vector = TSU_DrawableGetPosition((Drawable *)self.cover_banner); + banner_vector.x += 256/2; + TSU_DrawableSetTranslate((Drawable *)self.cover_banner, &banner_vector); + } + } + + CheckIfShorcutExits(); +} + +void ShortcutLoadTextureCover() +{ + if (TSU_CheckBoxGetValue(self.shortcut_rotate_option) != self.changed_yflip) + { + self.changed_yflip = TSU_CheckBoxGetValue(self.shortcut_rotate_option); + uint16 cover_type = GetCoverType(self.game_index, MT_PLANE_TEXT); + + if (self.game_cover_path != NULL) + { + Texture *texture_tmp = TSU_TextureCreateFromFile(self.game_cover_path, cover_type != IT_JPG, self.changed_yflip, 0); + if (texture_tmp != NULL) + { + if (self.cover_banner != NULL) + { + TSU_BannerSetTexture(self.cover_banner, texture_tmp); + } + + if (self.cover_texture != NULL) + { + TSU_TextureDestroy(&self.cover_texture); + } + + self.cover_texture = texture_tmp; + texture_tmp = NULL; + } + } + else + { + // READ PVR + } + } +} + +void ShowPresetMenu(int game_index) +{ + if (game_index >= 0) + { + self.changed_yflip = -1; + self.game_index = game_index; + memset(self.full_path_game, 0, sizeof(NAME_MAX)); + strcpy(self.full_path_game, GetFullGamePathByIndex(self.game_index)); + + HidePresetMenu(); + + CheckCover(self.game_index, MT_PLANE_TEXT); + GetGameCoverPath(self.game_index, &self.game_cover_path, MT_PLANE_TEXT); + + if (self.preset_menu_form == NULL) + { + self.save = menu_data.save_preset; + if (menu_data.preset == NULL || menu_data.preset->game_index != self.game_index) + { + if (menu_data.preset != NULL) + { + free(menu_data.preset); + } + + menu_data.preset = LoadPresetGame(self.game_index); + } + + SetModeScreenshot(); + + char font_path[NAME_MAX]; + memset(font_path, 0, sizeof(font_path)); + snprintf(font_path, sizeof(font_path), "%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu/fonts/default.txf"); + + Font *form_font = TSU_FontCreate(font_path, PVR_LIST_TR_POLY); + + self.preset_menu_form = TSU_FormCreate(640/2 - 618/2, 480/2 + 450/2, 618, 455, true, 3, true, true, form_font, &OnViewIndexChangedEvent); + TSU_FormGetObjectsCurrentViewEvent(self.preset_menu_form, &OnGetObjectsCurrentViewEvent); + + { + Label *general_label = TSU_LabelCreate(form_font, "GENERAL", 12, false, false); + TSU_FormAddBottomLabel(self.preset_menu_form, general_label); + general_label = NULL; + } + + { + Label *cdda_label = TSU_LabelCreate(form_font, "CDDA", 12, false, false); + TSU_FormAddBottomLabel(self.preset_menu_form, cdda_label); + cdda_label = NULL; + } + + { + Label *patch_label = TSU_LabelCreate(form_font, "PATCH", 12, false, false); + TSU_FormAddBottomLabel(self.preset_menu_form, patch_label); + patch_label = NULL; + } + + { + Label *extensions_label = TSU_LabelCreate(form_font, "EXTS.", 12, false, false); + TSU_FormAddBottomLabel(self.preset_menu_form, extensions_label); + extensions_label = NULL; + } + + { + Label *shortcut_label = TSU_LabelCreate(form_font, "SHORTCUT", 11, false, false); + TSU_FormAddBottomLabel(self.preset_menu_form, shortcut_label); + shortcut_label = NULL; + } + + TSU_DrawableSubAdd((Drawable *)self.scene_ptr, (Drawable *)self.preset_menu_form); + + Vector menu_position = TSU_DrawableGetPosition((Drawable *)self.preset_menu_form); + menu_position.y -= 200; + TSU_DrawableSetTranslate((Drawable *)self.preset_menu_form, &menu_position); + + self.menu_init_animation = TSU_LogXYMoverCreate(0, 0); + TSU_DrawableAnimAdd((Drawable *)self.preset_menu_form, (Animation *)self.menu_init_animation); + + form_font = NULL; + } + } +} + +void HidePresetMenu() +{ + if (self.preset_menu_form != NULL) + { + TSU_FormOnGetObjectsCurrentView(self.preset_menu_form); + + if (menu_data.preset->vmu_mode == 0) + { + menu_data.preset->emu_vmu = 0; + } + + if (menu_data.preset->cdda == 0) + { + menu_data.preset->emu_cdda = CDDA_MODE_DISABLED; + } + + if (menu_data.preset->vmu_mode || menu_data.preset->screenshot) + { + menu_data.preset->use_irq = 1; + } + + if (strlen(menu_data.preset->memory) == 10) + { + memset(menu_data.preset->custom_memory, 0, sizeof(menu_data.preset->custom_memory)); + } + + if (self.save) + { + SavePresetGame(menu_data.preset); + } + + if (self.menu_init_animation != NULL) + { + TSU_AnimationComplete((Animation *)self.menu_init_animation, (Drawable *)self.preset_menu_form); + TSU_LogXYMoverDestroy(&self.menu_init_animation); + } + + TSU_FormDestroy(&self.preset_menu_form); + self.cover_banner = NULL; + + if (self.cover_texture != NULL) + { + TSU_TextureDestroy(&self.cover_texture); + } + + if (self.game_cover_path != NULL) + { + free(self.game_cover_path); + self.game_cover_path = NULL; + } + } +} + +void SavePresetOptionClick(Drawable *drawable) +{ + SavePresetInputEvent(0, KeySelect); +} + +void DMAOptionClick(Drawable *drawable) +{ + DMAInputEvent(0, KeySelect); + + if (TSU_CheckBoxGetValue(self.dma_option)) + { + TSU_OptionGroupSetOptionByKey(self.async_option, 0, "TRUE"); + TSU_OptionGroupSelectOptionByIndex(self.async_option, 0); + } + else + { + TSU_OptionGroupSetOptionByKey(self.async_option, 0, "NONE"); + + if (TSU_OptionGroupGetKeySelected(self.async_option) == 0) + { + TSU_OptionGroupSelectOptionByIndex(self.async_option, 8); + } + } +} + +void AsyncOptionClick(Drawable *drawable) +{ + TSU_OptionGroupSetFocus(self.async_option, true); +} + +void ByPassOptionClick(Drawable *drawable) +{ + ByPassInputEvent(0, KeySelect); +} + +void CDDAOptionClick(Drawable *drawable) +{ + CDDAInputEvent(0, KeySelect); +} + +void IRQOptionClick(Drawable *drawable) +{ + IRQInputEvent(0, KeySelect); +} + +void OSOptionClick(Drawable *drawable) +{ + TSU_OptionGroupSetFocus(self.os_option, true); +} + +void LoaderOptionClick(Drawable *drawable) +{ + TSU_OptionGroupSetFocus(self.loader_option, true); +} + +void BootOptionClick(Drawable *drawable) +{ + TSU_OptionGroupSetFocus(self.boot_option, true); +} + +void FastOptionClick(Drawable *drawable) +{ + FastInputEvent(0, KeySelect); +} + +void LowLevelOptionClick(Drawable *drawable) +{ + LowLevelInputEvent(0, KeySelect); +} + +void MemoryOptionClick(Drawable *drawable) +{ + TSU_OptionGroupSetFocus(self.memory_option, true); +} + +void CustomMemoryOptionClick(Drawable *drawable) +{ + TSU_TextBoxSetFocus(self.custom_memory_option, true); +} + +void HeapOptionClick(Drawable *drawable) +{ + TSU_OptionGroupSetFocus(self.heap_option, true); +} + +void CDDASourceOptionClick(Drawable *drawable) +{ + TSU_OptionGroupSetFocus(self.cdda_source_option, true); +} + +void CDDADestinationOptionClick(Drawable *drawable) +{ + TSU_OptionGroupSetFocus(self.cdda_destination_option, true); +} + +void CDDAPositionOptionClick(Drawable *drawable) +{ + TSU_OptionGroupSetFocus(self.cdda_position_option, true); +} + +void CDDAChannelOptionClick(Drawable *drawable) +{ + TSU_OptionGroupSetFocus(self.cdda_channel_option, true); +} + +void PatchAddress1OptionClick(Drawable *drawable) +{ + TSU_TextBoxSetFocus(self.patch_address1_option, true); +} + +void PatchValue1OptionClick(Drawable *drawable) +{ + TSU_TextBoxSetFocus(self.patch_value1_option, true); +} + +void PatchAddress2OptionClick(Drawable *drawable) +{ + TSU_TextBoxSetFocus(self.patch_address2_option, true); +} + +void PatchValue2OptionClick(Drawable *drawable) +{ + TSU_TextBoxSetFocus(self.patchvalue2_option, true); +} + +void AlterBootOptionClick(Drawable *drawable) +{ + AlterBootInputEvent(0, KeySelect); +} + +bool AllButtonsDisabled() +{ + return (!self.button_x && !self.button_y && !self.button_z && !self.button_rt + && !self.button_a && !self.button_b && !self.button_c && !self.button_lt + && !self.button_start); +} + +void ScreenshotOptionClick(Drawable *drawable) +{ + ScreenshotInputEvent(0, KeySelect); + + if (TSU_CheckBoxGetValue(self.screenshot_option)) + { + if (AllButtonsDisabled()) + { + TSU_CheckBoxSetOn(self.button_start_option); + TSU_CheckBoxSetOn(self.button_a_option); + TSU_CheckBoxSetOn(self.button_b_option); + + self.button_a = self.button_b = self.button_start = true; + menu_data.preset->scr_hotkey = GetModeScreenshot(); + } + } + else + { + TSU_CheckBoxSetOff(self.button_start_option); + TSU_CheckBoxSetOff(self.button_x_option); + TSU_CheckBoxSetOff(self.button_y_option); + TSU_CheckBoxSetOff(self.button_z_option); + TSU_CheckBoxSetOff(self.button_lt_option); + TSU_CheckBoxSetOff(self.button_a_option); + TSU_CheckBoxSetOff(self.button_b_option); + TSU_CheckBoxSetOff(self.button_c_option); + TSU_CheckBoxSetOff(self.button_rt_option); + + self.button_x = self.button_y = self.button_z = self.button_rt = self.button_a = self.button_b = self.button_c = self.button_lt = self.button_start = false; + menu_data.preset->scr_hotkey = GetModeScreenshot(); + } +} + +void SetModeScreenshot() +{ + self.button_x = (menu_data.preset->scr_hotkey & CONT_X); + self.button_y = (menu_data.preset->scr_hotkey & CONT_Y); + self.button_z = (menu_data.preset->scr_hotkey & CONT_Z); + self.button_lt = false; + self.button_a = (menu_data.preset->scr_hotkey & CONT_A); + self.button_b = (menu_data.preset->scr_hotkey & CONT_B); + self.button_c = (menu_data.preset->scr_hotkey & CONT_C); + self.button_start = (menu_data.preset->scr_hotkey & CONT_START); + self.button_rt = false; +} + +int GetModeScreenshot() +{ + int screenshot_mode = 0; + + if (self.button_x) + { + screenshot_mode |= CONT_X; + } + + if (self.button_y) + { + screenshot_mode |= CONT_Y; + } + + if (self.button_z) + { + screenshot_mode |= CONT_Z; + } + + if (self.button_rt) + { + } + + if (self.button_a) + { + screenshot_mode |= CONT_A; + } + + if (self.button_b) + { + screenshot_mode |= CONT_B; + } + + if (self.button_c) + { + screenshot_mode |= CONT_C; + } + + if (self.button_lt) + { + } + + if (self.button_start) + { + screenshot_mode |= CONT_START; + } + + return screenshot_mode; +} + +void CheckScreenshot() +{ + if (AllButtonsDisabled()) + { + TSU_CheckBoxSetOff(self.screenshot_option); + menu_data.preset->scr_hotkey = menu_data.preset->screenshot = 0; + } + else + { + TSU_CheckBoxSetOn(self.screenshot_option); + menu_data.preset->screenshot = 1; + menu_data.preset->scr_hotkey = GetModeScreenshot(); + } +} + +void ButtonStartOptionClick(Drawable *drawable) +{ + TSU_CheckBoxInputEvent(self.button_start_option, 0, KeySelect); + self.button_start = TSU_CheckBoxGetValue(self.button_start_option); + CheckScreenshot(); +} + +void ButtonXOptionClick(Drawable *drawable) +{ + TSU_CheckBoxInputEvent(self.button_x_option, 0, KeySelect); + self.button_x = TSU_CheckBoxGetValue(self.button_x_option); + CheckScreenshot(); +} + +void ButtonYOptionClick(Drawable *drawable) +{ + TSU_CheckBoxInputEvent(self.button_y_option, 0, KeySelect); + self.button_y = TSU_CheckBoxGetValue(self.button_y_option); + CheckScreenshot(); +} + +void ButtonZOptionClick(Drawable *drawable) +{ + TSU_CheckBoxInputEvent(self.button_z_option, 0, KeySelect); + self.button_z = TSU_CheckBoxGetValue(self.button_z_option); + CheckScreenshot(); +} + +void ButtonLTOptionClick(Drawable *drawable) +{ + TSU_CheckBoxInputEvent(self.button_lt_option, 0, KeySelect); + self.button_lt = TSU_CheckBoxGetValue(self.button_lt_option); + CheckScreenshot(); +} + +void ButtonAOptionClick(Drawable *drawable) +{ + TSU_CheckBoxInputEvent(self.button_a_option, 0, KeySelect); + self.button_a = TSU_CheckBoxGetValue(self.button_a_option); + CheckScreenshot(); +} + +void ButtonBOptionClick(Drawable *drawable) +{ + TSU_CheckBoxInputEvent(self.button_b_option, 0, KeySelect); + self.button_b = TSU_CheckBoxGetValue(self.button_b_option); + CheckScreenshot(); +} + +void ButtonCOptionClick(Drawable *drawable) +{ + TSU_CheckBoxInputEvent(self.button_c_option, 0, KeySelect); + self.button_c = TSU_CheckBoxGetValue(self.button_c_option); + CheckScreenshot(); +} + +void ButtonRTOptionClick(Drawable *drawable) +{ + TSU_CheckBoxInputEvent(self.button_rt_option, 0, KeySelect); + self.button_rt = TSU_CheckBoxGetValue(self.button_rt_option); + CheckScreenshot(); +} + +void VMUOptionClick(Drawable *drawable) +{ + TSU_TextBoxSetFocus(self.vmu_option, true); +} + +void VMUSelectorOptionClick(Drawable *drawable) +{ + TSU_OptionGroupSetFocus(self.vmu_selector_option, true); +} + +void ShortcutSizeOptionClick(Drawable *drawable) +{ + TSU_OptionGroupSetFocus(self.shortcut_size_option, true); +} + +void ShortcutRotateOptionClick(Drawable *drawable) +{ + ShortcutRotateInputEvent(0, KeySelect); +} + +void ShortcutNameOptionClick(Drawable *drawable) +{ + TSU_TextBoxSetFocus(self.shortcut_name_option, true); +} + +void ShortcutDontShowNameOptionClick(Drawable *drawable) +{ + ShortcutDontShowNameInputEvent(0, KeySelect); +} + +void ShortcutCreateOptionClick(Drawable *drawable) +{ + Color press_color = {1, 1.0f, 1.0f, 0.1f}; + Color drop_color = {1, 1.0f, 1.0f, 1.0f}; + + TSU_ItemMenuSetTint(self.shortcut_create_option, press_color, press_color); + TSU_ItemMenuSetLabelText(self.shortcut_create_option, "Creating..."); + + int width = 0; + int height = 0; + if (self.cover_banner != NULL) + { + switch (TSU_OptionGroupGetKeySelected(self.shortcut_size_option)) + { + case 1: + width = height = 48; + break; + + case 2: + width = height = 64; + break; + + case 3: + width = height = 96; + break; + + case 5: + width = height = 256; + break; + + default: + width = height = 128; + break; + } + } + + MakeShortcut(menu_data.preset, GetDefaultDir(menu_data.current_dev), self.full_path_game + , TSU_CheckBoxGetValue(self.shortcut_dontshowname_option) ? false : true, self.game_cover_path + , width, height, TSU_CheckBoxGetValue(self.shortcut_rotate_option) ? true : false); + + TSU_ItemMenuSetTint(self.shortcut_create_option, drop_color, drop_color); + TSU_ItemMenuSetLabelText(self.shortcut_create_option, "Rewrite shortcut"); +} + +void ExitPresetMenuClick(Drawable *drawable) +{ + HidePresetMenu(); + menu_data.state_app = SA_GAMES_MENU; +} + +void SavePresetInputEvent(int type, int key) +{ + TSU_CheckBoxInputEvent(self.save_preset_option, type, key); +} + +void DMAInputEvent(int type, int key) +{ + TSU_CheckBoxInputEvent(self.dma_option, type, key); +} + +void AsyncInputEvent(int type, int key) +{ + TSU_OptionGroupInputEvent(self.async_option, type, key); +} + +void ByPassInputEvent(int type, int key) +{ + TSU_CheckBoxInputEvent(self.bypass_option, type, key); +} + +void CDDAInputEvent(int type, int key) +{ + TSU_CheckBoxInputEvent(self.cdda_option, type, key); +} + +void IRQInputEvent(int type, int key) +{ + TSU_CheckBoxInputEvent(self.irq_option, type, key); +} + +void OSInputEvent(int type, int key) +{ + TSU_OptionGroupInputEvent(self.os_option, type, key); +} + +void LoaderInputEvent(int type, int key) +{ + TSU_OptionGroupInputEvent(self.loader_option, type, key); +} + +void BootInputEvent(int type, int key) +{ + TSU_OptionGroupInputEvent(self.boot_option, type, key); +} + +void FastInputEvent(int type, int key) +{ + TSU_CheckBoxInputEvent(self.fast_option, type, key); +} + +void LowLevelInputEvent(int type, int key) +{ + TSU_CheckBoxInputEvent(self.lowlevel_option, type, key); +} + +void MemoryInputEvent(int type, int key) +{ + TSU_OptionGroupInputEvent(self.memory_option, type, key); +} + +void CustomMemoryInputEvent(int type, int key) +{ + if (key == KeyCancel) + { + memset(menu_data.preset->custom_memory, 0, sizeof(menu_data.preset->custom_memory)); + strcpy(menu_data.preset->custom_memory, TSU_TextBoxGetText(self.custom_memory_option)); + + if (strlen(menu_data.preset->custom_memory) != 6) + { + memset(menu_data.preset->custom_memory, 0, sizeof(menu_data.preset->custom_memory)); + TSU_TextBoxSetText(self.custom_memory_option, menu_data.preset->custom_memory); + } + } + + TSU_TextBoxInputEvent(self.custom_memory_option, type, key); +} + +void HeapInputEvent(int type, int key) +{ + TSU_OptionGroupInputEvent(self.heap_option, type, key); +} + +void CDDASourceInputEvent(int type, int key) +{ + TSU_OptionGroupInputEvent(self.cdda_source_option, type, key); +} + +void CDDADestinationInputEvent(int type, int key) +{ + TSU_OptionGroupInputEvent(self.cdda_destination_option, type, key); +} + +void CDDAPositionInputEvent(int type, int key) +{ + TSU_OptionGroupInputEvent(self.cdda_position_option, type, key); +} + +void CDDAChannelInputEvent(int type, int key) +{ + TSU_OptionGroupInputEvent(self.cdda_channel_option, type, key); +} + +void SetPatchByType(TextBox *patch_textbox, int control_type, char *patch_text_variable_ptr, uint32 *patch_variable_ptr) +{ + char text[10]; + memset(text, 0, sizeof(text)); + strcpy(text, TSU_TextBoxGetText(patch_textbox)); + uint32_t text_value = strtoul(text, NULL, 16); + + if( (strlen(text) != 8) || (control_type == PATCH_ADDRESS_TYPE && (!(text_value & 0xffffff) + || ( + (text_value >> 24 != 0x0c) + && (text_value >> 24 != 0x8c) + && (text_value >> 24 != 0xac) + ) + )) + || (control_type == PATCH_VALUE_TYPE && !text_value)) + { + TSU_TextBoxSetText(patch_textbox, (control_type == PATCH_ADDRESS_TYPE ? "0c000000" : "00000000")); + + memset(patch_text_variable_ptr, 0, 10); + strcpy(patch_text_variable_ptr, (control_type == PATCH_ADDRESS_TYPE ? "0c000000" : "00000000")); + *patch_variable_ptr = 0; + } + else + { + memset(patch_text_variable_ptr, 0, 10); + strcpy(patch_text_variable_ptr, text); + *patch_variable_ptr = strtoul(text, NULL, 16); + } +} + +void PatchAddress1InputEvent(int type, int key) +{ + if (key == KeyCancel) + { + SetPatchByType(self.patch_address1_option, PATCH_ADDRESS_TYPE, menu_data.preset->patch_a[0], &menu_data.preset->pa[0]); + } + + TSU_TextBoxInputEvent(self.patch_address1_option , type, key); +} + +void PatchValue1InputEvent(int type, int key) +{ + if (key == KeyCancel) + { + SetPatchByType(self.patch_value1_option, PATCH_VALUE_TYPE, menu_data.preset->patch_v[0], &menu_data.preset->pv[0]); + } + + TSU_TextBoxInputEvent(self.patch_value1_option, type, key); +} + +void PatchAddress2InputEvent(int type, int key) +{ + if (key == KeyCancel) + { + SetPatchByType(self.patch_address2_option, PATCH_ADDRESS_TYPE, menu_data.preset->patch_a[1], &menu_data.preset->pa[1]); + } + + TSU_TextBoxInputEvent(self.patch_address2_option, type, key); +} + +void PatchValue2InputEvent(int type, int key) +{ + if (key == KeyCancel) + { + SetPatchByType(self.patchvalue2_option, PATCH_VALUE_TYPE, menu_data.preset->patch_v[1], &menu_data.preset->pv[1]); + } + + TSU_TextBoxInputEvent(self.patchvalue2_option, type, key); +} + +void AlterBootInputEvent(int type, int key) +{ + TSU_CheckBoxInputEvent(self.altboot_option, type, key); +} + +void ScreenshotInputEvent(int type, int key) +{ + TSU_CheckBoxInputEvent(self.screenshot_option, type, key); +} + +void VMUInputEvent(int type, int key) +{ + if (key == KeyCancel) + { + memset(menu_data.preset->vmu_file, 0, sizeof(menu_data.preset->vmu_file)); + strcpy(menu_data.preset->vmu_file, TSU_TextBoxGetText(self.vmu_option)); + + if (menu_data.preset->vmu_file[0] != '\0' && ContainsOnlyNumbers(menu_data.preset->vmu_file)) + { + menu_data.preset->emu_vmu = atoi(menu_data.preset->vmu_file); + if (menu_data.preset->emu_vmu > MAX_VMU) + { + memset(menu_data.preset->vmu_file, 0, sizeof(menu_data.preset->vmu_file)); + snprintf(menu_data.preset->vmu_file, sizeof(menu_data.preset->vmu_file), "%d", MAX_VMU); + menu_data.preset->emu_vmu = MAX_VMU; + } + } + else + { + strcpy(menu_data.preset->vmu_file, DEFAULT_VMU_NUMBER); + menu_data.preset->emu_vmu = 1; + } + + TSU_TextBoxSetText(self.vmu_option, menu_data.preset->vmu_file); + } + + TSU_TextBoxInputEvent(self.vmu_option, type, key); +} + +void VMUSelectorInputEvent(int type, int key) +{ + TSU_OptionGroupInputEvent(self.vmu_selector_option, type, key); +} + +void ShortcutChangeCoverSize() +{ + if (self.cover_banner != NULL) + { + switch (TSU_OptionGroupGetKeySelected(self.shortcut_size_option)) + { + case 1: + TSU_BannerSetSize(self.cover_banner, 48, 48); + break; + + case 2: + TSU_BannerSetSize(self.cover_banner, 64, 64); + break; + + case 3: + TSU_BannerSetSize(self.cover_banner, 96, 96); + break; + + case 5: + TSU_BannerSetSize(self.cover_banner, 256, 256); + break; + + default: + TSU_BannerSetSize(self.cover_banner, 128, 128); + break; + } + } +} + +void ShortcutSizeInputEvent(int type, int key) +{ + TSU_OptionGroupInputEvent(self.shortcut_size_option, type, key); + ShortcutChangeCoverSize(); +} + +void ShortcutRotateInputEvent(int type, int key) +{ + TSU_CheckBoxInputEvent(self.shortcut_rotate_option, type, key); + ShortcutLoadTextureCover(); + ShortcutChangeCoverSize(); +} + +void ShortcutNameInputEvent(int type, int key) +{ + TSU_TextBoxInputEvent(self.shortcut_name_option, type, key); + + if (key == KeyCancel) + { + memset(menu_data.preset->shortcut_name, 0, sizeof(menu_data.preset->shortcut_name)); + strncpy(menu_data.preset->shortcut_name, TSU_TextBoxGetText(self.shortcut_name_option), sizeof(menu_data.preset->shortcut_name) - 4); + + CheckIfShorcutExits(); + } +} + +void ShortcutDontShowNameInputEvent(int type, int key) +{ + TSU_CheckBoxInputEvent(self.shortcut_dontshowname_option, type, key); + CheckIfShorcutExits(); +} + +void ShortcutCreateInputEvent(int type, int key) +{ +} + +void CheckIfShorcutExits() +{ + char shortcut_file[NAME_MAX]; + + if (TSU_CheckBoxGetValue(self.shortcut_dontshowname_option) == 0) + { + snprintf(shortcut_file, NAME_MAX, "%s/apps/main/scripts/%s.dsc", GetDefaultDir(menu_data.current_dev), menu_data.preset->shortcut_name); + } + else + { + snprintf(shortcut_file, NAME_MAX, "%s/apps/main/scripts/_%s.dsc", GetDefaultDir(menu_data.current_dev), menu_data.preset->shortcut_name); + } + + if (FileExists(shortcut_file)) + { + TSU_ItemMenuSetLabelText(self.shortcut_create_option, "Rewrite shortcut"); + } + else + { + TSU_ItemMenuSetLabelText(self.shortcut_create_option, "Create shortcut"); + } +} \ No newline at end of file diff --git a/applications/games_menu/modules/system_menu.c b/applications/games_menu/modules/system_menu.c new file mode 100644 index 00000000..7a1727de --- /dev/null +++ b/applications/games_menu/modules/system_menu.c @@ -0,0 +1,501 @@ +/* DreamShell ##version## + + app_system_menu.h - ISO Loader app utils + Copyright (C) 2024 Maniac Vera + +*/ + +#include "app_system_menu.h" +#include "app_definition.h" +#include "app_menu.h" +#include "app_utils.h" +#include +#include +#include +#include +#include + +extern struct menu_structure menu_data; + +enum ViewEnum +{ + SYSTEM_MENU_VIEW = 0 +}; + +static struct +{ + bool first_scan_cover; + DSApp *dsapp_ptr; + Scene *scene_ptr; + Form *system_menu_form; + Form *optimize_cover_popup; + + Label *optimize_covers_label; + Label *title_cover_scan; + Label *note_cover_scan; + Label *message_cover_scan; + Rectangle *modal_cover_scan; + + CheckBox *save_preset_option; + CheckBox *cover_background_option; + CheckBox *change_page_with_page_option; + + Font *message_font; + Font *menu_font; +} self; + +void CreateSystemMenu(DSApp *dsapp_ptr, Scene *scene_ptr, Font *menu_font, Font *message_font) +{ + self.dsapp_ptr = dsapp_ptr; + self.scene_ptr = scene_ptr; + self.message_font = message_font; + self.menu_font = menu_font; + self.system_menu_form = NULL; + self.optimize_cover_popup = NULL; + self.optimize_covers_label = NULL; + self.first_scan_cover = false; +} + +void DestroySystemMenu() +{ + HideSystemMenu(); + HideOptimizeCoverPopup(); + HideCoverScan(); + + self.dsapp_ptr = NULL; + self.scene_ptr = NULL; + self.message_font = NULL; + self.menu_font = NULL; +} + +void SystemMenuRemoveAll() +{ +} + +void SystemMenuInputEvent(int type, int key) +{ + TSU_FormInputEvent(self.system_menu_form, type, key); +} + +int StateSystemMenu() +{ + return (self.system_menu_form != NULL ? 1 : 0); +} + +void ShowSystemMenu() +{ + HideSystemMenu(); + + if (self.system_menu_form == NULL) + { + char font_path[NAME_MAX]; + memset(font_path, 0, sizeof(font_path)); + snprintf(font_path, sizeof(font_path), "%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu/fonts/default.txf"); + + Font *form_font = TSU_FontCreate(font_path, PVR_LIST_TR_POLY); + + uint width = 300; + uint height = 390; + self.system_menu_form = TSU_FormCreate(640 / 2 - 150, 480 / 2 + (height - 10) / 2, width, height, true, 3, true, false, form_font, &OnSystemViewIndexChangedEvent); + TSU_DrawableSubAdd((Drawable *)self.scene_ptr, (Drawable *)self.system_menu_form); + TSU_FormSelectedEvent(self.system_menu_form, &SystemMenuSelectedEvent); + } +} + +void OnSystemViewIndexChangedEvent(Drawable *drawable, int view_index) +{ + switch (view_index) + { + case SYSTEM_MENU_VIEW: + CreateSystemMenuView((Form *)drawable); + break; + } +} + +void CreateSystemMenuView(Form *form_ptr) +{ + Font* form_font = TSU_FormGetTitleFont(form_ptr); + TSU_FormtSetAttributes(form_ptr, 1, 10, 296, 38); + TSU_FormSetRowSize(form_ptr, 4, 30); + TSU_FormSetRowSize(form_ptr, 6, 30); + TSU_FormSetRowSize(form_ptr, 8, 30); + TSU_FormSetRowSize(form_ptr, 9, 20); + TSU_FormSetTitle(form_ptr, "SYSTEM MENU"); + + int font_size = 17; + + // THE ALIGN SHOULD BE IN FORM CLASS + { + // SCAN MISSING COVERS + Label *missing_covers_label = TSU_LabelCreate(form_font, "Scan missing covers", font_size, false, false); + TSU_FormAddBodyLabel(form_ptr, missing_covers_label, 1, 1); + TSU_DrawableEventSetClick((Drawable *)missing_covers_label, &ScanMissingCoversClick); + + Vector option_vector = TSU_DrawableGetPosition((Drawable *)missing_covers_label); + option_vector.x += 24; + TSU_DrawableSetTranslate((Drawable *)missing_covers_label, &option_vector); + TSU_FormSetCursor(form_ptr, (Drawable *)missing_covers_label); + + missing_covers_label = NULL; + } + + { + // OPTIMIZE COVERS + Label *optimize_covers_label = TSU_LabelCreate(form_font, "Optimize covers", font_size, false, false); + TSU_FormAddBodyLabel(form_ptr, optimize_covers_label, 1, 2); + TSU_DrawableEventSetClick((Drawable *)optimize_covers_label, &OptimizeCoversClick); + + Vector option_vector = TSU_DrawableGetPosition((Drawable *)optimize_covers_label); + option_vector.x += 48; + TSU_DrawableSetTranslate((Drawable *)optimize_covers_label, &option_vector); + + optimize_covers_label = NULL; + } + + { + // DEFAULT SAVE PRESET + Label *save_preset_label = TSU_LabelCreate(form_font, "Default save preset:", font_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)save_preset_label, true); + TSU_FormAddBodyLabel(form_ptr, save_preset_label, 1, 3); + + self.save_preset_option = TSU_CheckBoxCreate(form_font, font_size, 50, 22); + TSU_FormAddBodyCheckBox(form_ptr, self.save_preset_option, 1, 4); + + Vector option_vector = TSU_DrawableGetPosition((Drawable *)self.save_preset_option); + option_vector.y -= 5; + TSU_DrawableSetTranslate((Drawable *)self.save_preset_option, &option_vector); + + TSU_DrawableEventSetClick((Drawable *)self.save_preset_option, &DefaultSavePresetOptionClick); + + if (menu_data.app_config.save_preset) + { + TSU_CheckBoxSetOn(self.save_preset_option); + } + + save_preset_label = NULL; + } + + { + // COVER BACKGROUND + Label *cover_background_label = TSU_LabelCreate(form_font, "Cover background:", font_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)cover_background_label, true); + TSU_FormAddBodyLabel(form_ptr, cover_background_label, 1, 5); + + self.cover_background_option = TSU_CheckBoxCreate(form_font, font_size, 50, 22); + TSU_FormAddBodyCheckBox(form_ptr, self.cover_background_option, 1, 6); + + Vector option_vector = TSU_DrawableGetPosition((Drawable *)self.cover_background_option); + option_vector.y -= 5; + TSU_DrawableSetTranslate((Drawable *)self.cover_background_option, &option_vector); + + TSU_DrawableEventSetClick((Drawable *)self.cover_background_option, &CoverBackgroundOptionClick); + + if (menu_data.app_config.cover_background) + { + TSU_CheckBoxSetOn(self.cover_background_option); + } + + cover_background_label = NULL; + } + + { + // CHANGE PAGE WITH PAD + Label *change_page_with_page_label = TSU_LabelCreate(form_font, "Change page with pad:", font_size, false, false); + TSU_DrawableSetReadOnly((Drawable*)change_page_with_page_label, true); + TSU_FormAddBodyLabel(form_ptr, change_page_with_page_label, 1, 7); + + self.change_page_with_page_option = TSU_CheckBoxCreate(form_font, font_size, 50, 22); + TSU_FormAddBodyCheckBox(form_ptr, self.change_page_with_page_option, 1, 8); + + Vector option_vector = TSU_DrawableGetPosition((Drawable *)self.change_page_with_page_option); + option_vector.y -= 5; + TSU_DrawableSetTranslate((Drawable *)self.change_page_with_page_option, &option_vector); + + TSU_DrawableEventSetClick((Drawable *)self.change_page_with_page_option, &ChangePageWithPadOptionClick); + + if (menu_data.app_config.change_page_with_pad) + { + TSU_CheckBoxSetOn(self.change_page_with_page_option); + } + + change_page_with_page_label = NULL; + } + + { + // EXIT TO MAIN MENU + Label *exit_covers_label = TSU_LabelCreate(form_font, "Return", font_size, false, false); + TSU_FormAddBodyLabel(form_ptr, exit_covers_label, 1, 10); + TSU_DrawableEventSetClick((Drawable *)exit_covers_label, &ExitSystemMenuClick); + + Vector option_vector = TSU_DrawableGetPosition((Drawable *)exit_covers_label); + option_vector.x += 110; + TSU_DrawableSetTranslate((Drawable *)exit_covers_label, &option_vector); + + exit_covers_label = NULL; + } + + form_font = NULL; +} + +void HideSystemMenu() +{ + if (self.system_menu_form != NULL) + { + TSU_FormDestroy(&self.system_menu_form); + } +} + +void SystemMenuSelectedEvent(Drawable *drawable, uint bottom_index, uint column, uint row) +{ +} + +void ScanMissingCoversClick(Drawable *drawable) +{ + HideSystemMenu(); + + if (menu_data.current_dev == APP_DEVICE_SD || menu_data.current_dev == APP_DEVICE_IDE) + { + if (menu_data.load_pvr_cover_thread == NULL && menu_data.optimize_game_cover_thread == NULL) + { + menu_data.stop_load_pvr_cover = false; + + if (!self.first_scan_cover) + { + menu_data.rescan_covers = true; + } + + self.first_scan_cover = true; + menu_data.load_pvr_cover_thread = thd_create(0, LoadPVRCoverThread, NULL); + ShowCoverScan(); + } + } + else + { + menu_data.state_app = SA_GAMES_MENU; + } +} + +void OptimizeCoversClick(Drawable *drawable) +{ + HideSystemMenu(); + + if (menu_data.current_dev == APP_DEVICE_SD || menu_data.current_dev == APP_DEVICE_IDE) + { + if (menu_data.load_pvr_cover_thread == NULL && menu_data.optimize_game_cover_thread == NULL) + { + menu_data.stop_optimize_game_cover = false; + menu_data.optimize_game_cover_thread = thd_create(0, OptimizeCoverThread, NULL); + ShowOptimizeCoverPopup(); + menu_data.state_app = SA_OPTIMIZE_COVER; + } + } + else + { + menu_data.state_app = SA_GAMES_MENU; + } +} + +void DefaultSavePresetOptionClick(Drawable *drawable) +{ + TSU_CheckBoxInputEvent(self.save_preset_option, 0, KeySelect); + menu_data.save_preset = (TSU_CheckBoxGetValue(self.save_preset_option) ? 1 : 0); +} + +void CoverBackgroundOptionClick(Drawable *drawable) +{ + TSU_CheckBoxInputEvent(self.cover_background_option, 0, KeySelect); + menu_data.cover_background = (TSU_CheckBoxGetValue(self.cover_background_option) ? 1 : 0); +} + +void ChangePageWithPadOptionClick(Drawable *drawable) +{ + TSU_CheckBoxInputEvent(self.change_page_with_page_option, 0, KeySelect); + menu_data.change_page_with_pad = (TSU_CheckBoxGetValue(self.change_page_with_page_option) ? 1 : 0); +} + +void ExitSystemMenuClick(Drawable *drawable) +{ + HideSystemMenu(); + + int menu_type = menu_data.menu_type; + menu_data.menu_type = menu_data.app_config.initial_view; + SaveMenuConfig(); + menu_data.menu_type = menu_type; + + menu_data.state_app = SA_GAMES_MENU; +} + +void ShowOptimizeCoverPopup() +{ + StopCDDA(); + HideOptimizeCoverPopup(); + + if (self.optimize_cover_popup == NULL) + { + char font_path[NAME_MAX]; + memset(font_path, 0, sizeof(font_path)); + snprintf(font_path, sizeof(font_path), "%s/%s", GetDefaultDir(menu_data.current_dev), "apps/games_menu/fonts/default.txf"); + + Font *form_font = TSU_FontCreate(font_path, PVR_LIST_TR_POLY); + + self.optimize_cover_popup = TSU_FormCreate(640 / 2 - 500 / 2, 480 / 2 + 230 / 2 - 30, 500, 230, true, 3, true, false, form_font, NULL); + TSU_FormtSetAttributes(self.optimize_cover_popup, 1, 2, 500, 32); + + self.optimize_covers_label = TSU_LabelCreate(form_font, "", 16, false, false); + Label *exit_covers_label = TSU_LabelCreate(form_font, " Press any key to exit", 16, false, false); + + TSU_DrawableSetReadOnly((Drawable *)self.optimize_covers_label, true); + TSU_DrawableSetReadOnly((Drawable *)exit_covers_label, true); + + TSU_FormSetRowSize(self.optimize_cover_popup, 1, 50); + TSU_FormSetRowSize(self.optimize_cover_popup, 2, 100); + TSU_FormSetTitle(self.optimize_cover_popup, "OPTIMIZING COVERS"); + + TSU_FormAddBodyLabel(self.optimize_cover_popup, self.optimize_covers_label, 1, 1); + TSU_FormAddBodyLabel(self.optimize_cover_popup, exit_covers_label, 1, 2); + + TSU_DrawableSubAdd((Drawable *)self.scene_ptr, (Drawable *)self.optimize_cover_popup); + + form_font = NULL; + exit_covers_label = NULL; + } +} + +void HideOptimizeCoverPopup() +{ + if (self.optimize_cover_popup != NULL) + { + TSU_FormDestroy(&self.optimize_cover_popup); + self.optimize_covers_label = NULL; + } +} + +void SetMessageOptimizer(const char *fmt, const char *message) +{ + char message_optimizer[NAME_MAX]; + memset(message_optimizer, 0, sizeof(message_optimizer)); + snprintf(message_optimizer, sizeof(message_optimizer), fmt, message); + message_optimizer[44] = '\0'; + + if (self.optimize_covers_label != NULL) + { + TSU_LabelSetText(self.optimize_covers_label, message_optimizer); + } +} + +int StopOptimizeCovers() +{ + if (menu_data.optimize_game_cover_thread != NULL) + { + bool skip_return = self.optimize_cover_popup == NULL; + menu_data.stop_optimize_game_cover = true; + thd_join(menu_data.optimize_game_cover_thread, NULL); + menu_data.optimize_game_cover_thread = NULL; + HideOptimizeCoverPopup(); + + if (!skip_return) + return 1; + } + + return (menu_data.optimize_game_cover_thread == NULL ? 0 : -1); +} + +void ShowCoverScan() +{ + menu_data.state_app = SA_SCAN_COVER; + + StopCDDA(); + HideCoverScan(); + + Vector vector_init_title = {640 / 2, 400 / 2 - 50, ML_CURSOR + 6, 1}; + Color modal_color = {1, 0.0f, 0.0f, 0.0f}; + Color modal_border_color = {1, 1.0f, 1.0f, 1.0f}; + + self.modal_cover_scan = TSU_RectangleCreateWithBorder(PVR_LIST_OP_POLY, 40, 350, 560, 230, &modal_color, ML_CURSOR + 5, 3, &modal_border_color, 0); + TSU_AppSubAddRectangle(self.dsapp_ptr, self.modal_cover_scan); + + static Color color = {1, 1.0f, 1.0f, 1.0f}; + self.title_cover_scan = TSU_LabelCreate(self.menu_font, "SCANNING MISSING COVER", 26, true, true); + TSU_LabelSetTint(self.title_cover_scan, &color); + TSU_AppSubAddLabel(self.dsapp_ptr, self.title_cover_scan); + TSU_LabelSetTranslate(self.title_cover_scan, &vector_init_title); + + vector_init_title.x = 640 / 2; + vector_init_title.y = 400 / 2 + 100; + self.note_cover_scan = TSU_LabelCreate(self.menu_font, "\n\nPress any key to exit", 14, true, true); + TSU_LabelSetTint(self.note_cover_scan, &color); + TSU_AppSubAddLabel(self.dsapp_ptr, self.note_cover_scan); + TSU_LabelSetTranslate(self.note_cover_scan, &vector_init_title); +} + +void HideCoverScan() +{ + if (self.modal_cover_scan != NULL) + { + TSU_AppSubRemoveRectangle(self.dsapp_ptr, self.modal_cover_scan); + thd_pass(); + TSU_RectangleDestroy(&self.modal_cover_scan); + } + + if (self.title_cover_scan != NULL) + { + TSU_AppSubRemoveLabel(self.dsapp_ptr, self.title_cover_scan); + thd_pass(); + TSU_LabelDestroy(&self.title_cover_scan); + } + + if (self.note_cover_scan != NULL) + { + TSU_AppSubRemoveLabel(self.dsapp_ptr, self.note_cover_scan); + thd_pass(); + TSU_LabelDestroy(&self.note_cover_scan); + } + + if (self.message_cover_scan != NULL) + { + TSU_AppSubRemoveLabel(self.dsapp_ptr, self.message_cover_scan); + thd_pass(); + TSU_LabelDestroy(&self.message_cover_scan); + } +} + +void SetMessageScan(const char *fmt, const char *message) +{ + char message_scan[NAME_MAX]; + memset(message_scan, 0, sizeof(message_scan)); + snprintf(message_scan, sizeof(message_scan), fmt, message); + message_scan[49] = '\0'; + + if (self.message_cover_scan != NULL) + { + TSU_LabelSetText(self.message_cover_scan, message_scan); + } + else + { + Color color = {1, 1.0f, 1.0f, 1.0f}; + Vector vector_init_title = {640/2, 400/2, ML_CURSOR + 6, 1}; + vector_init_title.x = 50; + vector_init_title.y += 40; + + self.message_cover_scan = TSU_LabelCreate(self.message_font, message_scan, 16, false, true); + TSU_LabelSetTint(self.message_cover_scan, &color); + TSU_AppSubAddLabel(self.dsapp_ptr, self.message_cover_scan); + TSU_LabelSetTranslate(self.message_cover_scan, &vector_init_title); + } +} + +int StopScanCovers() +{ + if (menu_data.load_pvr_cover_thread != NULL) + { + bool skip_return = self.modal_cover_scan == NULL; + menu_data.stop_load_pvr_cover = true; + thd_join(menu_data.load_pvr_cover_thread, NULL); + menu_data.load_pvr_cover_thread = NULL; + + if (!skip_return) + return 1; + } + + return (menu_data.load_pvr_cover_thread == NULL ? 0 : -1); +} \ No newline at end of file diff --git a/applications/games_menu/modules/utils.c b/applications/games_menu/modules/utils.c new file mode 100644 index 00000000..d3d68d43 --- /dev/null +++ b/applications/games_menu/modules/utils.c @@ -0,0 +1,612 @@ +/* DreamShell ##version## + + utils.c - ISO Loader app utils + Copyright (C) 2022-2024 SWAT + Copyright (C) 2024 Maniac Vera + +*/ + +#include "ds.h" +#include +#include +#include +#include "app_utils.h" +#include "audio/wav.h" + +/* Trim begin/end spaces and copy into output buffer */ +void TrimSpaces(char *input, char *output, int size) +{ + char *p; + char *o; + int s = 0; + size--; + + p = input; + o = output; + + if (*p == '\0') + { + *o = '\0'; + return; + } + + while (*p == ' ' && size > 0) + { + p++; + size--; + } + + if (!size) + { + *o = '\0'; + return; + } + + while (size--) + { + *o++ = *p++; + s++; + } + + *o = '\0'; + o--; + + while (*o == ' ' && s > 0) + { + *o = '\0'; + o--; + s--; + } +} + +char *Trim(char *string) +{ + // trim prefix + while ((*string) == ' ' ) { + string ++; + } + + // find end of original string + char *c = string; + while (*c) { + c ++; + } + c--; + + // trim suffix + while ((*c) == ' ' ) { + *c = '\0'; + c--; + } + return string; +} + +char *TrimSpaces2(char *txt) +{ + int32_t i; + + while (txt[0] == ' ') + { + txt++; + } + + int32_t len = strlen(txt); + + for (i = len; i; i--) + { + if (txt[i] > ' ') + break; + txt[i] = '\0'; + } + + return txt; +} + +char *FixSpaces(char *str) +{ + if (!str) + return NULL; + + int i, len = (int)strlen(str); + + for (i = 0; i < len; i++) + { + if (str[i] == ' ') + str[i] = '\\'; + } + + return str; +} + +int ConfigParse(isoldr_conf *cfg, const char *filename) +{ + file_t fd; + int i; + + fd = fs_open(filename, O_RDONLY); + + if (fd == FILEHND_INVALID) + { + ds_printf("DS_ERROR: Can't open %s\n", filename); + return -1; + } + + size_t size = fs_total(fd); + + char buf[512]; + char *optname = NULL, *value = NULL; + + if (fs_read(fd, buf, size) != size) + { + fs_close(fd); + ds_printf("DS_ERROR: Can't read %s\n", filename); + return -1; + } + + fs_close(fd); + + while (1) + { + if (optname == NULL) + optname = strtok(buf, "="); + else + optname = strtok('\0', "="); + + value = strtok('\0', "\n"); + + if (optname == NULL || value == NULL) + break; + + for (i = 0; cfg[i].conf_type; i++) + { + if (strncasecmp(cfg[i].name, TrimSpaces2(optname), 32)) + continue; + + switch (cfg[i].conf_type) + { + case CONF_INT: + *(int *)cfg[i].pointer = atoi(value); + break; + + case CONF_STR: + strcpy((char *)cfg[i].pointer, TrimSpaces2(value)); + break; + + case CONF_ULONG: + *(uint32 *)cfg[i].pointer = strtoul(value, NULL, 16); + } + break; + } + } + + return 0; +} + +bool IsGdiOptimized(const char *full_path_game) +{ + bool is_optimized = false; + if (full_path_game) + { + char result[NAME_MAX]; + char path[NAME_MAX]; + + char game[NAME_MAX]; + memset(game, 0, NAME_MAX); + strcpy(game, GetLastPart(full_path_game, '/', 0)); + + memset(result, 0, NAME_MAX); + memset(path, 0, NAME_MAX); + + strncpy(path, full_path_game, strlen(full_path_game) - (strlen(game) + 1)); + snprintf(result, NAME_MAX, "%s/track03.iso", path); + + is_optimized = (FileExists(result) == 1); + } + return is_optimized; +} + +const char* GetLastPart(const char *source, const char separator, int option_path) +{ + static char path[NAME_MAX]; + memset(path, 0, NAME_MAX); + + char *last_folder = strrchr(source, separator); + if (last_folder != NULL) + { + strcpy(path, last_folder + 1); + } + else + { + strcpy(path, source); + } + + if (option_path == 2) + { + for (char *c = path; (*c = toupper(*c)); ++c) + { + if (*c == 'a') + *c = 'A'; // Maniac Vera: BUG toupper in the letter a, it does not convert it + } + } + else if (option_path == 1) + { + for (char *c = path; (*c = tolower(*c)); ++c) + { + if (*c == 'A') + *c = 'a'; // Maniac Vera: BUG toupper in the letter a, it does not convert it + } + } + + return path; +} + +bool ContainsOnlyNumbers(const char *string) +{ + volatile char c; + + if (string == NULL) + return false; + + if (*string == 0) + return false; + + while ((c=*(string++))!=0) + { + if (c<'0' || c>'9') + return false; + } + + return true; +} + +int GetDeviceType(const char *dir) +{ + if (!strncasecmp(dir, "/cd", 3)) + { + return APP_DEVICE_CD; + } + else if (!strncasecmp(dir, "/sd", 3)) + { + return APP_DEVICE_SD; + } + else if (!strncasecmp(dir, "/ide", 4)) + { + return APP_DEVICE_IDE; + } + else if (!strncasecmp(dir, "/pc", 3)) + { + return APP_DEVICE_PC; + // } else if(!strncasecmp(dir, "/???", 5)) { + // return APP_DEVICE_NET; + } + else + { + return -1; + } +} + +int CanUseTrueAsyncDMA(int sector_size, int current_dev, int image_type) +{ + return (sector_size == 2048 && + (current_dev == APP_DEVICE_IDE || current_dev == APP_DEVICE_CD) && + (image_type == ISOFS_IMAGE_TYPE_ISO || image_type == ISOFS_IMAGE_TYPE_GDI)); +} + +void GetMD5HashISO(const char *file_mount_point, SectorDataStruct *sector_data) +{ + file_t fd; + fd = fs_iso_first_file(file_mount_point); + + if (fd != FILEHND_INVALID) + { + if (fs_ioctl(fd, ISOFS_IOCTL_GET_BOOT_SECTOR_DATA, (int)sector_data->boot_sector) < 0) + { + memset(sector_data->md5, 0, sizeof(sector_data->md5)); + memset(sector_data->boot_sector, 0, sizeof(sector_data->boot_sector)); + } + else + { + kos_md5(sector_data->boot_sector, sizeof(sector_data->boot_sector), sector_data->md5); + } + + // Also get image type and sector size + if (fs_ioctl(fd, ISOFS_IOCTL_GET_IMAGE_TYPE, (int)§or_data->image_type) < 0) + { + ds_printf("Can't get image type\n"); + } + + if (fs_ioctl(fd, ISOFS_IOCTL_GET_DATA_TRACK_SECTOR_SIZE, (int)§or_data->sector_size) < 0) + { + ds_printf("Can't get sector size\n"); + } + + fs_close(fd); + } +} + +bool MakeShortcut(PresetStruct *preset, const char* device_dir, const char* full_path_game, bool show_name, const char* game_cover_path, int width, int height, bool yflip) +{ + FILE *fd; + char save_file[NAME_MAX]; + char cmd[NAME_MAX * 2]; + int i; + + if (show_name) + { + snprintf(save_file, NAME_MAX, "%s/apps/main/scripts/%s.dsc", device_dir, preset->shortcut_name); + } + else + { + snprintf(save_file, NAME_MAX, "%s/apps/main/scripts/_%s.dsc", device_dir, preset->shortcut_name); + } + + fd = fopen(save_file, "w"); + + if(!fd) + { + ds_printf("DS_ERROR: Can't save shortcut\n"); + return false; + } + + fprintf(fd, "module -o -f %s/modules/minilzo.klf\n", device_dir); + fprintf(fd, "module -o -f %s/modules/isofs.klf\n", device_dir); + fprintf(fd, "module -o -f %s/modules/isoldr.klf\n", device_dir); + + strcpy(cmd, "isoldr"); + strcat(cmd, preset->fastboot ? " -s" : " -i" ); + + if (preset->use_dma) + { + strcat(cmd, " -a"); + } + + if (preset->alt_read) + { + strcat(cmd, " -y"); + } + + if (preset->use_irq) + { + strcat(cmd, " -q"); + } + + if (preset->low) + { + strcat(cmd, " -l"); + } + + if (preset->emu_async) + { + char async[8]; + snprintf(async, sizeof(async), " -e %d", preset->emu_async); + strcat(cmd, async); + } + + if(strncmp(preset->device, "auto", 4) != 0) + { + strcat(cmd, " -d "); + strcat(cmd, preset->device); + } + + const char *memory_tmp; + + if(strlen(preset->memory) < 8) + { + char text[24]; + memset(text, 0, sizeof(text)); + strncpy(text, preset->memory, 10); + memory_tmp = strncat(text, preset->custom_memory, 10); + + strcat(cmd, " -x "); + strcat(cmd, memory_tmp); + } + else + { + strcat(cmd, " -x "); + strcat(cmd, preset->memory); + } + + char game[NAME_MAX]; + memset(game, 0, NAME_MAX); + strcpy(game, full_path_game); + + strcat(cmd, " -f "); + strcat(cmd, FixSpaces(game)); + + char boot_mode[8]; + sprintf(boot_mode, "%d", preset->boot_mode); + strcat(cmd, " -j "); + strcat(cmd, boot_mode); + + char os[8]; + sprintf(os, "%d", preset->bin_type); + strcat(cmd, " -o "); + strcat(cmd, os); + + char patchstr[24]; + for(i = 0; i < 2 ; ++i) + { + if(preset->pa[i] & 0xffffff) { + sprintf(patchstr," --pa%d 0x%s", i + 1, preset->patch_a[i]); + strcat(cmd, patchstr); + sprintf(patchstr," --pv%d 0x%s", i + 1, preset->patch_v[i]); + strcat(cmd, patchstr); + } + } + + if (preset->emu_cdda) + { + char cdda_mode[12]; + sprintf(cdda_mode, "0x%08lx", preset->emu_cdda); + strcat(cmd, " -g "); + strcat(cmd, cdda_mode); + } + + if (preset->heap <= HEAP_MODE_MAPLE) + { + char mode[24]; + sprintf(mode, " -h %d", i); + strcat(cmd, mode); + } + else + { + char *addr = preset->heap_memory; + strcat(cmd, " -h "); + strcat(cmd, addr); + } + + if (preset->vmu_mode > 0) + { + char number[12]; + sprintf(number, " -v %d", atoi(preset->vmu_file)); + strcat(cmd, number); + } + + if (preset->screenshot) + { + char hotkey[24]; + sprintf(hotkey, " -k 0x%lx", (uint32)SCREENSHOT_HOTKEY); + strcat(cmd, hotkey); + } + + if(preset->alt_boot) + { + char boot_file[24]; + sprintf(boot_file, " -b %s", ALT_BOOT_FILE); + strcat(cmd, boot_file); + } + + fprintf(fd, "%s\n", cmd); + fprintf(fd, "console --show\n"); + fclose(fd); + + if (show_name) + { + snprintf(save_file, NAME_MAX, "%s/apps/main/images/%s.png", device_dir, preset->shortcut_name); + } + else + { + snprintf(save_file, NAME_MAX, "%s/apps/main/images/_%s.png", device_dir, preset->shortcut_name); + } + + if(FileExists(save_file)) + { + fs_unlink(save_file); + } + + if (game_cover_path != NULL) + { + copy_image(game_cover_path, save_file, strcasecmp(strrchr(game_cover_path, '.'), ".png") == 0 ? true : false, true, 256, 256, width, height, yflip); + } + + return true; +} + +char* MakePresetFilename(const char *default_dir, const char *device_dir, uint8 *md5, const char *app_name) +{ + char dev[8]; + static char filename[NAME_MAX]; + + memset(filename, 0, sizeof(filename)); + strncpy(dev, &device_dir[1], 3); + + if (dev[2] == '/') + { + dev[2] = '\0'; + } + else + { + dev[3] = '\0'; + } + + snprintf(filename, sizeof(filename), + "%s/apps/%s/presets/%s_%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x.cfg", + default_dir, app_name, dev, md5[0], + md5[1], md5[2], md5[3], md5[4], md5[5], + md5[6], md5[7], md5[8], md5[9], md5[10], + md5[11], md5[12], md5[13], md5[14], md5[15]); + + return filename; +} + +const char *GetFolderPathFromFile(const char *full_path_file) +{ + static char path[NAME_MAX]; + + char *filename = (char *)malloc(NAME_MAX); + memset(filename, 0, NAME_MAX); + strcpy(filename, GetLastPart(full_path_file, '/', 0)); + + memset(path, 0, NAME_MAX); + + strncpy(path, full_path_file, strlen(full_path_file) - (strlen(filename) + 1)); + free(filename); + + return path; +} + +size_t GetCDDATrackFilename(int num, const char *full_path_game, char **result) +{ + char *path = (char *)malloc(NAME_MAX); + int size = 0; + + char *game = (char *)malloc(NAME_MAX); + memset(game, 0, NAME_MAX); + strcpy(game, GetLastPart(full_path_game, '/', 0)); + + if (*result == NULL) + { + *result = (char *)malloc(NAME_MAX); + } + + memset(*result, 0, NAME_MAX); + memset(path, 0, NAME_MAX); + + strncpy(path, full_path_game, strlen(full_path_game) - (strlen(game) + 1)); + snprintf(*result, NAME_MAX, "%s/track%02d.raw", path, num); + free(game); + free(path); + size = FileSize(*result); + + if (size > 0) + { + return size; + } + + int len = strlen(*result); + (*result)[len - 3] = 'w'; + (*result)[len - 1] = 'v'; + + return FileSize(*result); +} + +static wav_stream_hnd_t wav_hnd = SND_STREAM_INVALID; +static int wav_inited = 0; + +void StopCDDATrack() +{ + if (wav_inited) + { + wav_inited = 0; + wav_shutdown(); + } +} + +void PlayCDDATrack(const char *file, int loop) +{ + StopCDDATrack(); + wav_inited = wav_init(); + + if (wav_inited) + { + wav_hnd = wav_create(file, loop); + + if (wav_hnd == SND_STREAM_INVALID) + { + ds_printf("DS_ERROR: Can't play file: %s\n", file); + return; + } + ds_printf("DS_OK: Start playing: %s\n", file); + wav_play(wav_hnd); + } +} diff --git a/applications/games_menu/resources/empty_vmu_1024kb.vmd b/applications/games_menu/resources/empty_vmu_1024kb.vmd new file mode 100644 index 00000000..f7a72c9c Binary files /dev/null and b/applications/games_menu/resources/empty_vmu_1024kb.vmd differ diff --git a/applications/games_menu/resources/empty_vmu_128kb.vmd b/applications/games_menu/resources/empty_vmu_128kb.vmd new file mode 100755 index 00000000..90f4f872 Binary files /dev/null and b/applications/games_menu/resources/empty_vmu_128kb.vmd differ diff --git a/applications/iso_loader/app.xml b/applications/iso_loader/app.xml index 8920e888..1622ca63 100644 --- a/applications/iso_loader/app.xml +++ b/applications/iso_loader/app.xml @@ -1,10 +1,10 @@ - + - + @@ -15,19 +15,19 @@ - + - + - + @@ -42,15 +42,15 @@ - + - + - + @@ -78,6 +78,13 @@ + + + + + + + @@ -149,6 +156,23 @@ + + + + + + + + + + + + + + + + + @@ -255,12 +279,12 @@