diff --git a/.github/workflows/compilation.yml b/.github/workflows/compilation.yml
index 7c4dc376..fe13b989 100644
--- a/.github/workflows/compilation.yml
+++ b/.github/workflows/compilation.yml
@@ -13,7 +13,7 @@ permissions:
jobs:
build:
runs-on: ubuntu-22.04
- container: ghcr.io/ps2homebrew/ps2homebrew:main
+ container: ghcr.io/ps2homebrew/ps2homebrew@sha256:207523ad98f17fd406afa7dd0c6cfe10730627545cbe45247f0bd73c8a66c376
steps:
- name: Install required dependencies
run: |
@@ -59,8 +59,8 @@ jobs:
cp -f DETAILED_CHANGELOG "$TMP"
cat > DETAILED_CHANGELOG << 'EOF'
- [](https://github.com/ps2homebrew/wOPL/actions/workflows/compilation.yml)
- [](https://app.codacy.com/gh/ps2homebrew/wOPL/dashboard)
+ [](https://github.com/ps2homebrew/wOPL/actions/workflows/compilation.yml)
+ [](https://app.codacy.com/gh/ps2homebrew/wOPL/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[](https://tinyurl.com/PS2SPACE)
[](https://github.com/ps2homebrew/wOPL/releases)
@@ -100,7 +100,7 @@ jobs:
cheat: [CHEAT=0, CHEAT=1]
pademu: [PADEMU=0, PADEMU=1]
runs-on: ubuntu-22.04
- container: ghcr.io/ps2homebrew/ps2homebrew:main
+ container: ghcr.io/ps2homebrew/ps2homebrew@sha256:207523ad98f17fd406afa7dd0c6cfe10730627545cbe45247f0bd73c8a66c376
steps:
- name: Install required dependencies
run: |
@@ -160,7 +160,7 @@ jobs:
build-lang:
runs-on: ubuntu-22.04
- container: ghcr.io/ps2homebrew/ps2homebrew:main
+ container: ghcr.io/ps2homebrew/ps2homebrew@sha256:207523ad98f17fd406afa7dd0c6cfe10730627545cbe45247f0bd73c8a66c376
steps:
- name: Install required dependencies
run: |
@@ -209,7 +209,7 @@ jobs:
debug: [iopcore_debug, ingame_debug, eesio_debug, DTL_T10000=1]
container_commit: [":main"]
runs-on: ubuntu-22.04
- container: ghcr.io/ps2homebrew/ps2homebrew:main
+ container: ghcr.io/ps2homebrew/ps2homebrew@sha256:207523ad98f17fd406afa7dd0c6cfe10730627545cbe45247f0bd73c8a66c376
steps:
- name: Install required dependencies
run: |
@@ -269,26 +269,27 @@ jobs:
release:
needs: [build, merge-variants, build-lang, merge-debug]
runs-on: ubuntu-22.04
- # Create a NEW prerelease + NEW tag on every run (never on PRs).
- if: github.event_name != 'pull_request'
+
+ # Publish one rolling beta from wOPL-base, or a permanent release from v* tags.
+ # Other branches, pull requests and non-version tags still build artifacts,
+ # but they do not create GitHub releases.
+ if: github.ref == 'refs/heads/wOPL-base' || startsWith(github.ref, 'refs/tags/v')
+
+ env:
+ USERNAME: ${{ secrets.MEGA_USERNAME }}
+ PASSWORD: ${{ secrets.MEGA_PASSWORD }}
+
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- - name: Generate version + unique prerelease tag
- id: meta
+ - name: Get version
shell: bash
run: |
set -euo pipefail
- wOPL_VERSION="$(make woplversion)"
- SHORT_SHA="${GITHUB_SHA::7}"
- TS="$(date -u +'%Y%m%d-%H%M%S')"
- TAG="${wOPL_VERSION}-${TS}-${GITHUB_RUN_NUMBER}-${SHORT_SHA}"
- echo "wOPL_VERSION=${wOPL_VERSION}" >> "$GITHUB_ENV"
- echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
- echo "name=Double Unofficial OPL ${wOPL_VERSION} (${TS} ${SHORT_SHA})" >> "$GITHUB_OUTPUT"
+ echo "wOPL_VERSION=$(make woplversion)" >> "$GITHUB_ENV"
- name: Download all artifacts
uses: actions/download-artifact@v4
@@ -302,14 +303,14 @@ jobs:
shell: bash
run: |
set -euo pipefail
- echo "Preparing prerelease ${wOPL_VERSION}"
+ echo "Preparing release ${wOPL_VERSION}"
- # Copy INFO into folders where it exists
+ # Copy project information into the packaged artifact folders.
cp -v INFO/* WOPNPS2LD/ || true
cp -v INFO/* WOPNPS2LD-VARIANTS/ || true
cp -v INFO/* WOPNPS2LD-LANGS/ || true
- # Put raw main ELF on the release as WOPNPS2LD.ELF
+ # Put the raw main ELF on the release as WOPNPS2LD.ELF.
MAIN_ELF="$(find WOPNPS2LD -maxdepth 1 -type f -name 'WOPNPS2LD-*.ELF' | head -n 1)"
if [ -z "${MAIN_ELF}" ]; then
echo "ERROR: Could not find main ELF in WOPNPS2LD/ (expected WOPNPS2LD-*.ELF)"
@@ -318,34 +319,75 @@ jobs:
fi
cp -f "${MAIN_ELF}" WOPNPS2LD.ELF
- # Create archives
+ # Create versioned archives for permanent releases.
7z a -t7z "WOPNPS2LD-${wOPL_VERSION}.7z" WOPNPS2LD/*
- 7z a -t7z "WOPNPS2LD-VARIANTS-${wOPL_VERSION}.7z" WOPNPS2LD-VARIANTS/* || true
- 7z a -t7z "WOPNPS2LD-LANGS-${wOPL_VERSION}.7z" WOPNPS2LD-LANGS/* || true
- 7z a -t7z "WOPNPS2LD-DEBUG-${wOPL_VERSION}.7z" WOPNPS2LD-DEBUG/* || true
+ 7z a -t7z "WOPNPS2LD-VARIANTS-${wOPL_VERSION}.7z" WOPNPS2LD-VARIANTS/*
+ 7z a -t7z "WOPNPS2LD-LANGS-${wOPL_VERSION}.7z" WOPNPS2LD-LANGS/*
+ 7z a -t7z "WOPNPS2LD-DEBUG-${wOPL_VERSION}.7z" WOPNPS2LD-DEBUG/*
- # Stable filenames
+ # Fixed filenames are replaced whenever the rolling beta is updated.
cp -f "WOPNPS2LD-${wOPL_VERSION}.7z" WOPNPS2LD.7z
- cp -f "WOPNPS2LD-VARIANTS-${wOPL_VERSION}.7z" WOPNPS2LD-VARIANTS.7z || true
- cp -f "WOPNPS2LD-LANGS-${wOPL_VERSION}.7z" WOPNPS2LD-LANGS.7z || true
- cp -f "WOPNPS2LD-DEBUG-${wOPL_VERSION}.7z" WOPNPS2LD-DEBUG.7z || true
+ cp -f "WOPNPS2LD-VARIANTS-${wOPL_VERSION}.7z" WOPNPS2LD-VARIANTS.7z
+ cp -f "WOPNPS2LD-LANGS-${wOPL_VERSION}.7z" WOPNPS2LD-LANGS.7z
+ cp -f "WOPNPS2LD-DEBUG-${wOPL_VERSION}.7z" WOPNPS2LD-DEBUG.7z
+
+ # Move the single "latest" tag to the newest successful wOPL-base build.
+ - name: Update rolling beta tag
+ if: github.ref == 'refs/heads/wOPL-base'
+ shell: bash
+ run: |
+ set -euo pipefail
+ git tag --force latest "$GITHUB_SHA"
+ git push --force origin refs/tags/latest
- - name: Create GitHub prerelease (new tag every run)
- uses: softprops/action-gh-release@v2
+ # Reuse the same prerelease and replace its assets on every wOPL-base build.
+ - name: Create or update latest beta prerelease
+ if: github.ref == 'refs/heads/wOPL-base'
+ uses: softprops/action-gh-release@v3
with:
- tag_name: ${{ steps.meta.outputs.tag }}
- name: ${{ steps.meta.outputs.name }}
+ tag_name: latest
+ name: Latest beta - Double Unofficial OPL ${{ env.wOPL_VERSION }}
prerelease: true
- generate_release_notes: true
+ make_latest: false
+ generate_release_notes: false
+ overwrite_files: true
target_commitish: ${{ github.sha }}
body: |
- [](https://github.com/ps2homebrew/wOPL/actions/workflows/compilation.yml)
- [](https://app.codacy.com/gh/ps2homebrew/wOPL/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
+ This is the latest automatically compiled wOPL beta. Its files are replaced after each successful build of `wOPL-base`.
+
+ [](https://github.com/ps2homebrew/wOPL/actions/workflows/compilation.yml)
+ [](https://app.codacy.com/gh/ps2homebrew/wOPL/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[](https://tinyurl.com/PS2SPACE)
[](https://github.com/ps2homebrew/wOPL/releases)
+ files: |
+ WOPNPS2LD.ELF
+ WOPNPS2LD.7z
+ WOPNPS2LD-VARIANTS.7z
+ WOPNPS2LD-LANGS.7z
+ WOPNPS2LD-DEBUG.7z
+ INFO/DETAILED_CHANGELOG
+ INFO/README.md
+ INFO/LICENSE
+ INFO/CREDITS
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ # Version tags such as v1.2.0 create permanent, separately listed releases.
+ - name: Create permanent versioned release
+ if: startsWith(github.ref, 'refs/tags/v')
+ uses: softprops/action-gh-release@v3
+ with:
+ tag_name: ${{ github.ref_name }}
+ name: Double Unofficial OPL ${{ github.ref_name }}
+ prerelease: ${{ contains(github.ref_name, '-rc') }}
+ generate_release_notes: true
+ overwrite_files: true
files: |
WOPNPS2LD.ELF
+ WOPNPS2LD-${{ env.wOPL_VERSION }}.7z
+ WOPNPS2LD-VARIANTS-${{ env.wOPL_VERSION }}.7z
+ WOPNPS2LD-LANGS-${{ env.wOPL_VERSION }}.7z
+ WOPNPS2LD-DEBUG-${{ env.wOPL_VERSION }}.7z
WOPNPS2LD.7z
WOPNPS2LD-VARIANTS.7z
WOPNPS2LD-LANGS.7z
@@ -356,3 +398,18 @@ jobs:
INFO/CREDITS
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ # Keep every successful wOPL-base beta permanently in MEGA. The GitHub
+ # `latest` prerelease can be replaced each build without losing old binaries.
+ - name: Archive beta build to MEGA
+ if: github.ref == 'refs/heads/wOPL-base' && env.USERNAME != '' && env.PASSWORD != ''
+ uses: Difegue/action-megacmd@1.4.0
+ with:
+ args: put -c WOPNPS2LD.7z /wOPL/Pre-Release/${{ env.wOPL_VERSION }}/run_${{ github.run_number }}/
+
+ # Tagged versions are archived separately from beta builds.
+ - name: Archive permanent release to MEGA
+ if: startsWith(github.ref, 'refs/tags/v') && env.USERNAME != '' && env.PASSWORD != ''
+ uses: Difegue/action-megacmd@1.4.0
+ with:
+ args: put -c WOPNPS2LD.7z /wOPL/Release/${{ env.wOPL_VERSION }}/
diff --git a/Makefile b/Makefile
index 96c208d2..15a063f5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 1
-SUBVERSION = 1
-EXTRAVERSION =
+SUBVERSION = 2
+EXTRAVERSION = beta
# How to DEBUG?
# Simply type "make " to build wOPL with the necessary debugging functionality.
@@ -83,9 +83,9 @@ ifneq ($(GIT_TAG),latest)
endif
endif
-FRONTEND_OBJS = pad.o xparam.o fntsys.o renderman.o menusys.o OSDHistory.o system.o lang.o lang_internal.o config.o dialogs.o tetris.o \
+FRONTEND_OBJS = pad.o xparam.o fntsys.o renderman.o menusys.o OSDHistory.o system.o lang.o lang_internal.o config_wopl.o pathsupport.o config_migration.o dialogs.o tetris.o \
dia.o ioman.o texcache.o themes.o supportbase.o bdmsupport.o ethsupport.o hddsupport.o lwnbd.o iosupport.o initializer.o zso.o lz4.o \
- appsupport.o mmcesupport.o favsupport.o gui.o guigame.o vmc_groups.o textures.o art_tar.o common.o main.o module.o atlas.o nbns.o sound.o ps2cnf.o
+ appsupport.o mmcesupport.o favsupport.o gui.o guigame.o vmc_groups.o textures.o tar.o common.o main.o module.o atlas.o nbns.o sound.o ps2cnf.o
IOP_OBJS = iomanx.o filexio.o ps2fs.o usbd.o bdmevent.o \
bdm.o bdmfs_fatfs.o usbmass_bd.o usbmass_bd_single.o iLinkman.o IEEE1394_bd.o mx4sio_bd.o \
@@ -101,25 +101,23 @@ EECORE_OBJS = ee_core.o ioprp.o util.o \
udnl.o imgdrv.o eesync.o \
bdm_cdvdman.o bdm_ata_cdvdman.o IOPRP_img.o smb_cdvdman.o \
hdd_cdvdman.o hdd_gamestar_cdvdman.o mmce_cdvdman.o hdd_hdpro_cdvdman.o cdvdfsv.o \
- ingame_smstcpip.o smap_ingame.o smbman.o smbinit.o
-
-PNG_ASSETS = logo_01 logo_02 logo_03 logo_04 logo_05 logo_06 logo_07 logo_08 logo_09 logo_10 logo_11 logo_12 logo_13 logo_14 logo_15 \
- logo_16 logo_17 logo_18 logo_19 logo_20 logo_21 loading_1 loading_2 loading_3 loading_4 loading_5 loading_6 loading_7 loading_8 \
- category_empty_bdm category_usb category_ilink category_mx4sio category_hdd_bdm \
- category_hdd_apa category_net_smb category_apps category_fav category_mmce mark_star button_symbol_cross button_symbol_triangle button_symbol_circle button_symbol_square button_select button_start button_dpad_left button_dpad_right \
- bg_main bg_info cover_app cover_game disc screenshot badge_exec_elf badge_disc_hdl badge_disc_iso badge_disc_zso badge_disc_ul badge_exec_app badge_disc_cd badge_disc_dvd badge_vmode_43 badge_vmode_169 badge_vmode_169_ps2rd badge_vmode_169_hexiso \
- dev_1 dev_2 dev_3 dev_4 dev_5 dev_6 dev_7 dev_8 rating_0 \
- rating_1 rating_2 rating_3 rating_4 rating_5 badge_res_240p badge_res_240p_hexiso badge_res_480i badge_res_480p \
- badge_res_480p_xt badge_res_480p_xc badge_res_480p_gsm badge_res_480p_ps2rd badge_res_480p_hexiso \
- badge_res_576i badge_res_576p_gsm badge_res_720p_gsm \
- badge_res_1080i badge_res_1080i_gsm badge_res_1080p_gsm badge_region_multi badge_region_ntsc badge_region_pal case apps_case \
- plank discbox_list_games discbox_list_apps discbox_list_shadow \
- bdm_index_1 bdm_index_2 bdm_index_3 bdm_index_4 bdm_index_5 button_stick_r3 button_dpad_up button_dpad_down
+ ingame_smstcpip.o smap_ingame.o smbman.o smbinit.o neutrino_loader.o
+
+PNG_ASSETS = load0 load1 load2 load3 load4 load5 load6 load7 usb usb_bd ilk_bd \
+ m4s_bd hdd_bd hdd eth app fav mmce fav_mark cross triangle circle square select start left right \
+ settings_bg info cover cover_app disc screen ELF HDL ISO ZSO UL APPS CD DVD Aspect_s Aspect_w Aspect_w1 \
+ Aspect_w2 Rating_0 Rating_1 Rating_2 Rating_3 Rating_4 Rating_5 \
+ Region_pal Region_ntsc pademu_on pademu_off gsm_off gsm_on cht_off cht_on \
+ logo_01 logo_02 logo_03 logo_04 logo_05 logo_06 logo_07 logo_08 logo_09 logo_10 \
+ logo_11 logo_12 logo_13 logo_14 logo_15 logo_16 logo_17 logo_18 logo_19 logo_20 logo_21 \
+ case apps_case \
+ plank lm_case lm_apps_case lm_case_shadow \
+ Index_0 Index_1 Index_2 Index_3 Index_4 R3 up down
# unused icons - l1 l2 l3 r1 r2
GFX_OBJS = $(PNG_ASSETS:%=%_png.o) poeveticanew.o icon_sys.o icon_icn.o icon_cpy_icn.o icon_del_icn.o
-AUDIO_OBJS = boot.o cancel.o confirm.o cursor.o message.o transition.o bd_connect.o bd_disconnect.o
+AUDIO_OBJS = boot.o cancel.o confirm.o coverflow.o cursor.o message.o transition.o bd_connect.o bd_disconnect.o
MISC_OBJS = icon_sys_A.o icon_sys_J.o icon_sys_C.o theme_list.o theme_coverflow.o
@@ -142,7 +140,7 @@ PNG_ASSETS_DIR = gfx/
MAPFILE = wopl.map
EE_LDFLAGS += -Wl,-Map,$(MAPFILE)
-EE_LIBS = -L$(PS2SDK)/ports/lib -L$(GSKIT)/lib -L./lib -lgskit -ldmakit -lpoweroff -lfileXio -lpatches -lpng -lz -lmc -lfreetype -lvux -lcdvd -lnetman -lps2ips -laudsrv -lvorbisfile -lvorbis -logg -lpadx -lelf-loader-nocolour -lc -lkernel
+EE_LIBS = -L$(PS2SDK)/ports/lib -L$(GSKIT)/lib -L./lib -lgskit -ldmakit -lpoweroff -lfileXio -lpatches -lpng -lz -lconfig -lmc -lfreetype -lvux -lcdvd -lnetman -lps2ips -laudsrv -lvorbisfile -lvorbis -logg -lpadx -lelf-loader-nocolour -lc -lkernel -lbz2_static
EE_INCS += -I$(PS2SDK)/ports/include -I$(PS2SDK)/ports/include/freetype2 -I$(GSKIT)/include -I$(GSKIT)/ee/dma/include -I$(GSKIT)/ee/gs/include -Imodules/iopcore/common -Imodules/network/common -Imodules/hdd/common -Iinclude
BIN2C = $(PS2SDK)/bin/bin2c
@@ -236,7 +234,7 @@ else
SMSTCPIP_INGAME_CFLAGS = INGAME_DRIVER=1
endif
-EE_CFLAGS += -fsingle-precision-constant -DWOPL_VERSION=\"$(wOPL_VERSION)\"
+EE_CFLAGS += -fsingle-precision-constant -DWOPL_VERSION=\"$(wOPL_VERSION)\" -DWOPL_CONFIG_STRING=\"wOPL_$(VERSION)_$(SUBVERSION)\"
# There are a few places where the config key/value are truncated, so disable these warnings
EE_CFLAGS += -Wno-format-truncation -Wno-stringop-truncation
@@ -297,6 +295,8 @@ clean: download_lwNBD
rm -fr $(MAPFILE) $(EE_BIN) $(EE_BIN_PACKED) $(EE_BIN_STRIPPED) $(EE_VPKD).* $(EE_OBJS_DIR) $(EE_ASM_DIR)
echo "-EE core"
$(MAKE) -C ee_core clean
+ echo "-NEUTRINO LOADER"
+ $(MAKE) -C neutrino_loader clean
echo "-IOP core"
echo " -imgdrv"
$(MAKE) -C modules/iopcore/imgdrv clean
@@ -422,6 +422,12 @@ ee_core/ee_core.elf: ee_core
$(EE_ASM_DIR)ee_core.c: ee_core/ee_core.elf | $(EE_ASM_DIR)
$(BIN2C) $< $@ eecore_elf
+neutrino_loader/loader.elf: neutrino_loader/loader.c neutrino_loader/linkfile
+ $(MAKE) -C neutrino_loader
+
+$(EE_ASM_DIR)neutrino_loader.c: neutrino_loader/loader.elf | $(EE_ASM_DIR)
+ $(BIN2C) $< $@ neutrino_loader_elf
+
$(EE_ASM_DIR)udnl.c: $(UDNL_OUT) | $(EE_ASM_DIR)
$(BIN2C) $(UDNL_OUT) $@ udnl_irx
@@ -792,6 +798,9 @@ $(EE_ASM_DIR)cancel.c: audio/cancel.adp | $(EE_ASM_DIR)
$(EE_ASM_DIR)confirm.c: audio/confirm.adp | $(EE_ASM_DIR)
$(BIN2C) $< $@ $(*F)_adp
+$(EE_ASM_DIR)coverflow.c: audio/coverflow.adp | $(EE_ASM_DIR)
+ $(BIN2C) $< $@ $(*F)_adp
+
$(EE_ASM_DIR)cursor.c: audio/cursor.adp | $(EE_ASM_DIR)
$(BIN2C) $< $@ $(*F)_adp
diff --git a/README.md b/README.md
index 94f1d294..bc578d16 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
+
# Double Unofficial-Open-PS2-Loader
-
+
Copyright 2013, Ifcaro & jimmikaelkael
Copyright 2024, KrahJohilto
@@ -9,9 +10,10 @@ Copyright 2025-Present, Wolf3s and Ripto
Licensed under Academic Free License version 3.0
Review the LICENSE file for further details.
-[](https://github.com/ps2homebrew/wOPL/actions/workflows/compilation.yml)
-[](https://app.codacy.com/gh/ps2homebree/wOPL/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
+[](https://github.com/ps2homebrew/wOPL/actions/workflows/compilation.yml)
+[](https://app.codacy.com/gh/ps2homebrew/wOPL/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[](https://tinyurl.com/PS2SPACE)
+[](https://mega.nz/folder/bCplmQSZ#jbsgYTkqpsPiBQ23DDnSbw)
[](https://github.com/ps2homebrew/wOPL/releases)
## Introduction
@@ -24,6 +26,7 @@ the PS2 and PS3 units. This version uses features that at the moment are not mer
v1.0
+
* Removal of UDPBD since there are not much users using this.
* Add a easter egg when booting on wOPL.
* Code cleanups/fixes and compiler warning fixes.
@@ -36,6 +39,7 @@ v1.0
* Disc cover enable.
v1.1
+
* Added USB option in BDM
* Reworked the mmce option(Suggestion by Hardlevel)
* Enabled sfx/boot sounds by standard
@@ -45,20 +49,60 @@ v1.1
* Fix main background on models eg. 90000s
* Modularized the art loading unifying art .tar
* Enable by standard the favourites mode
-* Added a persistence while the game it´s being selected
+* Added a persistence while the game it´s being selected on favourites
+
+v1.2
+
+* Added Code Inject for games support(Credits: fniq5)
+* Enable by standard USB on BDM
+* Add a name to wOPL folders the actual version being used(Suggestion by Canal do Bis)
+* Add neutrino for MMCE
+* Readded info about the format of the game or app
+* Added back the old background configuration
+* Added a hint to hide R3 while is being selected
+* Added a lazy tar loader for ART/CFG/CHT thanks to mystyq
+* Coverflow improvements thanks to KrahJohlito
+* Support grayscale and grayscale+alpha images(DarthMotzkus)
+* App support to the favourites tab
+* Pademu fixes on frontend thanks to krahjohilito
+* BDM USB fixes
+* Fix send game id on mmce thanks to mystq
+* Add unimplmemented icons for pademu, gsm and cheats
+* Migrated from old internal config api to libconfig thanks to krahjohilito
+* Added splash boot notifications and version text thanks to krahjohilito
+* Updated neutrino path support to BDM, MMCE and HDD (APA) thanks to krahjohilito
+
+### ART/CFG/CHT .tar compact support
-### Art Tar
-Your all cover arts should be stored using .tar format, stored on ART/art.tar
+You can now store your ART/CFG/CHT files using tar archive.
+All you need it´s to do it´s pack all the files into a `.tar` file name after the folder like this:
+
+`ART/art.tar`
+
+`CFG/cfg.tar`
+
+`CHT/cht.tar`
### wOPL Easter egg
+
This easter egg will need to be activated using R1+R2+L1+L2+Start+Select
-### Theme renamed files
-This variant is incompatible with OPL themes as the structure follows up diffrently than traditional OPL
+### Modularized art
-# Modularized art
You can see game list with both normal art and art.tar
+### Code Inject
+
+Image Linker module allows patch files up to 4KiB in size to be linked as the game launches. IMG directory contains the GAMEID.img file the same as PS2RD CHT. This feature can be activated with "Enable Linker" under the game's "Cheat Settings."
+
+The binary format of img file can be discovered in ee_core/src/cheat_api.c LinkImage subroutine.
+
+### Neutrino´s new path support
+
+The folder name needs to be stored with name: `neutrino`(uppercased or lowercased) on the root of `USB` `MX4SIO` `HDD (ATA)` `iLink` `mmce` `HDD (APA)` with the usual setup of `config` and `modules` subfolders.
+In game settings you will find an option `Loader Core` with the options of `` or `Neutrino`
+Get [Neutrino](https://github.com/rickgaiser/neutrino/releases)
+
@@ -389,6 +433,17 @@ Useful CMake targets: `release`, `clean`, `rebuild`, `debug`, `iopcore_debug`, `
+
Frequent Issues
diff --git a/audio/coverflow.adp b/audio/coverflow.adp
new file mode 100644
index 00000000..6cd86eb0
Binary files /dev/null and b/audio/coverflow.adp differ
diff --git a/ee_core/include/cheat_api.h b/ee_core/include/cheat_api.h
index f261bd1a..b167b696 100644
--- a/ee_core/include/cheat_api.h
+++ b/ee_core/include/cheat_api.h
@@ -6,6 +6,8 @@
void EnableCheats(void);
void DisableCheats(void);
+void LinkImage(void);
+void EnableImage(int Enable);
/**
* code_t - a code object
diff --git a/ee_core/include/coreconfig.h b/ee_core/include/coreconfig.h
index df5ec346..5d1800f7 100644
--- a/ee_core/include/coreconfig.h
+++ b/ee_core/include/coreconfig.h
@@ -47,6 +47,7 @@ struct EECoreConfig_t
#ifdef CHEAT
u32 *gCheatList; // Store hooks/codes addr+val pairs
+ u32 *gImage;
#endif
void *eeloadCopy;
diff --git a/ee_core/src/cheat_api.c b/ee_core/src/cheat_api.c
index e6d826b8..98871a48 100644
--- a/ee_core/src/cheat_api.c
+++ b/ee_core/src/cheat_api.c
@@ -27,6 +27,46 @@
#include "include/cheat_api.h"
#include "coreconfig.h"
+#define MAX_IMAGEWORDS 1024
+static unsigned gImage[MAX_IMAGEWORDS];
+
+void LinkImage()
+{
+ unsigned CountEntries, CountWords, Offset;
+ unsigned *p = gImage;
+
+ Offset = *p++;
+ if (!Offset)
+ return;
+ CountWords = *p++;
+ if (*(u32 *)(Offset) != CountWords)
+ return;
+ CountEntries = *p++;
+ while (CountEntries--) {
+ Offset = *p++;
+ CountWords = *p++;
+ for (; CountWords--; Offset += 4) {
+ *(u32 *)(Offset) = *p++;
+ }
+ }
+}
+
+static void CopyImage()
+{
+ USE_LOCAL_EECORE_CONFIG;
+ for (unsigned i = 0; i < MAX_IMAGEWORDS; ++i) {
+ gImage[i] = config->gImage[i];
+ }
+}
+
+void EnableImage(int e)
+{
+ if (e) {
+ CopyImage();
+ } else
+ gImage[0] = 0;
+}
+
/*---------------------------------*/
/* Setup PS2RD Cheat Engine params */
/*---------------------------------*/
diff --git a/ee_core/src/main.c b/ee_core/src/main.c
index 89ad2c75..85790d21 100644
--- a/ee_core/src/main.c
+++ b/ee_core/src/main.c
@@ -103,6 +103,7 @@ static int eecoreInit(int argc, char **argv)
if (config->gCheatList) {
EnableCheats();
}
+ EnableImage((int)config->gImage);
#endif
#ifdef GSM
if (config->EnableGSMOp) {
diff --git a/ee_core/src/syshook.c b/ee_core/src/syshook.c
index 54b44464..f4bb6e3f 100644
--- a/ee_core/src/syshook.c
+++ b/ee_core/src/syshook.c
@@ -62,6 +62,10 @@ u32 New_SifSetDma(SifDmaTransfer_t *sdd, s32 len)
return 1;
}
+#ifdef CHEAT
+void LinkImage(void);
+#endif
+
// ------------------------------------------------------------------------
void sysLoadElf(char *filename, int argc, char **argv)
{
@@ -103,6 +107,10 @@ void sysLoadElf(char *filename, int argc, char **argv)
DPRINTF("t_loadElf: loading elf...");
r = LoadElf(filename, &elf);
+#ifdef CHEAT
+ LinkImage();
+#endif
+
if (!r) {
DPRINTF(" done\n");
diff --git a/gfx/APPS.png b/gfx/APPS.png
new file mode 100644
index 00000000..12815da3
Binary files /dev/null and b/gfx/APPS.png differ
diff --git a/gfx/Aspect_s.png b/gfx/Aspect_s.png
new file mode 100644
index 00000000..b8f972d3
Binary files /dev/null and b/gfx/Aspect_s.png differ
diff --git a/gfx/Aspect_w.png b/gfx/Aspect_w.png
new file mode 100644
index 00000000..9eed7f07
Binary files /dev/null and b/gfx/Aspect_w.png differ
diff --git a/gfx/Aspect_w1.png b/gfx/Aspect_w1.png
new file mode 100644
index 00000000..03a90132
Binary files /dev/null and b/gfx/Aspect_w1.png differ
diff --git a/gfx/Aspect_w2.png b/gfx/Aspect_w2.png
new file mode 100644
index 00000000..59cd60de
Binary files /dev/null and b/gfx/Aspect_w2.png differ
diff --git a/gfx/CD.png b/gfx/CD.png
new file mode 100644
index 00000000..9619386d
Binary files /dev/null and b/gfx/CD.png differ
diff --git a/gfx/DVD.png b/gfx/DVD.png
new file mode 100644
index 00000000..a740ac4b
Binary files /dev/null and b/gfx/DVD.png differ
diff --git a/gfx/ELF.png b/gfx/ELF.png
new file mode 100644
index 00000000..66fd01b1
Binary files /dev/null and b/gfx/ELF.png differ
diff --git a/gfx/HDL.png b/gfx/HDL.png
new file mode 100644
index 00000000..dfb5c118
Binary files /dev/null and b/gfx/HDL.png differ
diff --git a/gfx/ISO.png b/gfx/ISO.png
new file mode 100644
index 00000000..6bab7cb3
Binary files /dev/null and b/gfx/ISO.png differ
diff --git a/gfx/bdm_index_1.png b/gfx/Index_0.png
similarity index 100%
rename from gfx/bdm_index_1.png
rename to gfx/Index_0.png
diff --git a/gfx/bdm_index_2.png b/gfx/Index_1.png
similarity index 100%
rename from gfx/bdm_index_2.png
rename to gfx/Index_1.png
diff --git a/gfx/bdm_index_3.png b/gfx/Index_2.png
similarity index 100%
rename from gfx/bdm_index_3.png
rename to gfx/Index_2.png
diff --git a/gfx/bdm_index_4.png b/gfx/Index_3.png
similarity index 100%
rename from gfx/bdm_index_4.png
rename to gfx/Index_3.png
diff --git a/gfx/bdm_index_5.png b/gfx/Index_4.png
similarity index 100%
rename from gfx/bdm_index_5.png
rename to gfx/Index_4.png
diff --git a/gfx/button_trigger_l1.png b/gfx/L1.png
similarity index 100%
rename from gfx/button_trigger_l1.png
rename to gfx/L1.png
diff --git a/gfx/button_trigger_l2.png b/gfx/L2.png
similarity index 100%
rename from gfx/button_trigger_l2.png
rename to gfx/L2.png
diff --git a/gfx/button_stick_l3.png b/gfx/L3.png
similarity index 100%
rename from gfx/button_stick_l3.png
rename to gfx/L3.png
diff --git a/gfx/button_stick_r3.png b/gfx/R3.png
similarity index 100%
rename from gfx/button_stick_r3.png
rename to gfx/R3.png
diff --git a/gfx/rating_0.png b/gfx/Rating_0.png
similarity index 100%
rename from gfx/rating_0.png
rename to gfx/Rating_0.png
diff --git a/gfx/rating_1.png b/gfx/Rating_1.png
similarity index 100%
rename from gfx/rating_1.png
rename to gfx/Rating_1.png
diff --git a/gfx/rating_2.png b/gfx/Rating_2.png
similarity index 100%
rename from gfx/rating_2.png
rename to gfx/Rating_2.png
diff --git a/gfx/rating_3.png b/gfx/Rating_3.png
similarity index 100%
rename from gfx/rating_3.png
rename to gfx/Rating_3.png
diff --git a/gfx/rating_4.png b/gfx/Rating_4.png
similarity index 100%
rename from gfx/rating_4.png
rename to gfx/Rating_4.png
diff --git a/gfx/rating_5.png b/gfx/Rating_5.png
similarity index 100%
rename from gfx/rating_5.png
rename to gfx/Rating_5.png
diff --git a/gfx/Region_ntsc.png b/gfx/Region_ntsc.png
new file mode 100644
index 00000000..18a7ef57
Binary files /dev/null and b/gfx/Region_ntsc.png differ
diff --git a/gfx/Region_pal.png b/gfx/Region_pal.png
new file mode 100644
index 00000000..9d827ab3
Binary files /dev/null and b/gfx/Region_pal.png differ
diff --git a/gfx/UL.png b/gfx/UL.png
new file mode 100644
index 00000000..d2265b5f
Binary files /dev/null and b/gfx/UL.png differ
diff --git a/gfx/ZSO.png b/gfx/ZSO.png
new file mode 100644
index 00000000..51b43f12
Binary files /dev/null and b/gfx/ZSO.png differ
diff --git a/gfx/category_apps.png b/gfx/app.png
similarity index 100%
rename from gfx/category_apps.png
rename to gfx/app.png
diff --git a/gfx/badge_disc_cd.png b/gfx/badge_disc_cd.png
deleted file mode 100644
index 5e7cbfa4..00000000
Binary files a/gfx/badge_disc_cd.png and /dev/null differ
diff --git a/gfx/badge_disc_dvd.png b/gfx/badge_disc_dvd.png
deleted file mode 100644
index 000eec19..00000000
Binary files a/gfx/badge_disc_dvd.png and /dev/null differ
diff --git a/gfx/badge_disc_hdl.png b/gfx/badge_disc_hdl.png
deleted file mode 100644
index 1af5d169..00000000
Binary files a/gfx/badge_disc_hdl.png and /dev/null differ
diff --git a/gfx/badge_disc_iso.png b/gfx/badge_disc_iso.png
deleted file mode 100644
index 31c8f1ab..00000000
Binary files a/gfx/badge_disc_iso.png and /dev/null differ
diff --git a/gfx/badge_disc_ul.png b/gfx/badge_disc_ul.png
deleted file mode 100644
index 39c69b4f..00000000
Binary files a/gfx/badge_disc_ul.png and /dev/null differ
diff --git a/gfx/badge_disc_zso.png b/gfx/badge_disc_zso.png
deleted file mode 100644
index ff525b75..00000000
Binary files a/gfx/badge_disc_zso.png and /dev/null differ
diff --git a/gfx/badge_exec_app.png b/gfx/badge_exec_app.png
deleted file mode 100644
index 0f80a8b1..00000000
Binary files a/gfx/badge_exec_app.png and /dev/null differ
diff --git a/gfx/badge_exec_elf.png b/gfx/badge_exec_elf.png
deleted file mode 100644
index 19991179..00000000
Binary files a/gfx/badge_exec_elf.png and /dev/null differ
diff --git a/gfx/badge_region_multi.png b/gfx/badge_region_multi.png
deleted file mode 100644
index 6f63762b..00000000
Binary files a/gfx/badge_region_multi.png and /dev/null differ
diff --git a/gfx/badge_region_ntsc.png b/gfx/badge_region_ntsc.png
deleted file mode 100644
index 759f1dd2..00000000
Binary files a/gfx/badge_region_ntsc.png and /dev/null differ
diff --git a/gfx/badge_region_pal.png b/gfx/badge_region_pal.png
deleted file mode 100644
index 144c92a2..00000000
Binary files a/gfx/badge_region_pal.png and /dev/null differ
diff --git a/gfx/badge_res_1080i.png b/gfx/badge_res_1080i.png
deleted file mode 100644
index 1e80c9ac..00000000
Binary files a/gfx/badge_res_1080i.png and /dev/null differ
diff --git a/gfx/badge_res_1080i_gsm.png b/gfx/badge_res_1080i_gsm.png
deleted file mode 100644
index c413b70c..00000000
Binary files a/gfx/badge_res_1080i_gsm.png and /dev/null differ
diff --git a/gfx/badge_res_1080p_gsm.png b/gfx/badge_res_1080p_gsm.png
deleted file mode 100644
index 7e00dea0..00000000
Binary files a/gfx/badge_res_1080p_gsm.png and /dev/null differ
diff --git a/gfx/badge_res_240p.png b/gfx/badge_res_240p.png
deleted file mode 100644
index 26825671..00000000
Binary files a/gfx/badge_res_240p.png and /dev/null differ
diff --git a/gfx/badge_res_240p_hexiso.png b/gfx/badge_res_240p_hexiso.png
deleted file mode 100644
index f614cc3b..00000000
Binary files a/gfx/badge_res_240p_hexiso.png and /dev/null differ
diff --git a/gfx/badge_res_288p_gsm.png b/gfx/badge_res_288p_gsm.png
deleted file mode 100644
index 46c22745..00000000
Binary files a/gfx/badge_res_288p_gsm.png and /dev/null differ
diff --git a/gfx/badge_res_480i.png b/gfx/badge_res_480i.png
deleted file mode 100644
index a99e648b..00000000
Binary files a/gfx/badge_res_480i.png and /dev/null differ
diff --git a/gfx/badge_res_480p.png b/gfx/badge_res_480p.png
deleted file mode 100644
index 12771f9b..00000000
Binary files a/gfx/badge_res_480p.png and /dev/null differ
diff --git a/gfx/badge_res_480p_gsm.png b/gfx/badge_res_480p_gsm.png
deleted file mode 100644
index 4ade9a92..00000000
Binary files a/gfx/badge_res_480p_gsm.png and /dev/null differ
diff --git a/gfx/badge_res_480p_hexiso.png b/gfx/badge_res_480p_hexiso.png
deleted file mode 100644
index 164b8e9a..00000000
Binary files a/gfx/badge_res_480p_hexiso.png and /dev/null differ
diff --git a/gfx/badge_res_480p_ps2rd.png b/gfx/badge_res_480p_ps2rd.png
deleted file mode 100644
index 99ba225a..00000000
Binary files a/gfx/badge_res_480p_ps2rd.png and /dev/null differ
diff --git a/gfx/badge_res_480p_xc.png b/gfx/badge_res_480p_xc.png
deleted file mode 100644
index 78728f9a..00000000
Binary files a/gfx/badge_res_480p_xc.png and /dev/null differ
diff --git a/gfx/badge_res_480p_xt.png b/gfx/badge_res_480p_xt.png
deleted file mode 100644
index 07788db3..00000000
Binary files a/gfx/badge_res_480p_xt.png and /dev/null differ
diff --git a/gfx/badge_res_576i.png b/gfx/badge_res_576i.png
deleted file mode 100644
index 1acadbc0..00000000
Binary files a/gfx/badge_res_576i.png and /dev/null differ
diff --git a/gfx/badge_res_576p_gsm.png b/gfx/badge_res_576p_gsm.png
deleted file mode 100644
index 93129e8f..00000000
Binary files a/gfx/badge_res_576p_gsm.png and /dev/null differ
diff --git a/gfx/badge_res_720p_gsm.png b/gfx/badge_res_720p_gsm.png
deleted file mode 100644
index e57f57b3..00000000
Binary files a/gfx/badge_res_720p_gsm.png and /dev/null differ
diff --git a/gfx/badge_vmode_169.png b/gfx/badge_vmode_169.png
deleted file mode 100644
index cd7e6574..00000000
Binary files a/gfx/badge_vmode_169.png and /dev/null differ
diff --git a/gfx/badge_vmode_169_hexiso.png b/gfx/badge_vmode_169_hexiso.png
deleted file mode 100644
index dac7b110..00000000
Binary files a/gfx/badge_vmode_169_hexiso.png and /dev/null differ
diff --git a/gfx/badge_vmode_169_ps2rd.png b/gfx/badge_vmode_169_ps2rd.png
deleted file mode 100644
index b2777dd0..00000000
Binary files a/gfx/badge_vmode_169_ps2rd.png and /dev/null differ
diff --git a/gfx/badge_vmode_43.png b/gfx/badge_vmode_43.png
deleted file mode 100644
index 39b84d10..00000000
Binary files a/gfx/badge_vmode_43.png and /dev/null differ
diff --git a/gfx/button_trigger_r1.png b/gfx/button_trigger_r1.png
deleted file mode 100644
index bcc707cf..00000000
Binary files a/gfx/button_trigger_r1.png and /dev/null differ
diff --git a/gfx/button_trigger_r2.png b/gfx/button_trigger_r2.png
deleted file mode 100644
index f34e4927..00000000
Binary files a/gfx/button_trigger_r2.png and /dev/null differ
diff --git a/gfx/cht_off.png b/gfx/cht_off.png
new file mode 100644
index 00000000..e1963fc1
Binary files /dev/null and b/gfx/cht_off.png differ
diff --git a/gfx/cht_on.png b/gfx/cht_on.png
new file mode 100644
index 00000000..3f2cd15a
Binary files /dev/null and b/gfx/cht_on.png differ
diff --git a/gfx/button_symbol_circle.png b/gfx/circle.png
similarity index 100%
rename from gfx/button_symbol_circle.png
rename to gfx/circle.png
diff --git a/gfx/cover_game.png b/gfx/cover.png
similarity index 100%
rename from gfx/cover_game.png
rename to gfx/cover.png
diff --git a/gfx/button_symbol_cross.png b/gfx/cross.png
similarity index 100%
rename from gfx/button_symbol_cross.png
rename to gfx/cross.png
diff --git a/gfx/dev_1.png b/gfx/dev_1.png
deleted file mode 100644
index 4eb5d256..00000000
Binary files a/gfx/dev_1.png and /dev/null differ
diff --git a/gfx/dev_2.png b/gfx/dev_2.png
deleted file mode 100644
index 4fb63c1a..00000000
Binary files a/gfx/dev_2.png and /dev/null differ
diff --git a/gfx/dev_3.png b/gfx/dev_3.png
deleted file mode 100644
index 76608b3c..00000000
Binary files a/gfx/dev_3.png and /dev/null differ
diff --git a/gfx/dev_4.png b/gfx/dev_4.png
deleted file mode 100644
index ec4d370e..00000000
Binary files a/gfx/dev_4.png and /dev/null differ
diff --git a/gfx/dev_5.png b/gfx/dev_5.png
deleted file mode 100644
index 7ef133d3..00000000
Binary files a/gfx/dev_5.png and /dev/null differ
diff --git a/gfx/dev_6.png b/gfx/dev_6.png
deleted file mode 100644
index 18f095f9..00000000
Binary files a/gfx/dev_6.png and /dev/null differ
diff --git a/gfx/dev_7.png b/gfx/dev_7.png
deleted file mode 100644
index e2b3352b..00000000
Binary files a/gfx/dev_7.png and /dev/null differ
diff --git a/gfx/dev_8.png b/gfx/dev_8.png
deleted file mode 100644
index ad891e12..00000000
Binary files a/gfx/dev_8.png and /dev/null differ
diff --git a/gfx/button_dpad_down.png b/gfx/down.png
similarity index 100%
rename from gfx/button_dpad_down.png
rename to gfx/down.png
diff --git a/gfx/category_net_smb.png b/gfx/eth.png
similarity index 100%
rename from gfx/category_net_smb.png
rename to gfx/eth.png
diff --git a/gfx/category_fav.png b/gfx/fav.png
similarity index 100%
rename from gfx/category_fav.png
rename to gfx/fav.png
diff --git a/gfx/mark_star.png b/gfx/fav_mark.png
similarity index 100%
rename from gfx/mark_star.png
rename to gfx/fav_mark.png
diff --git a/gfx/gsm_off.png b/gfx/gsm_off.png
new file mode 100644
index 00000000..a2b3ec36
Binary files /dev/null and b/gfx/gsm_off.png differ
diff --git a/gfx/gsm_on.png b/gfx/gsm_on.png
new file mode 100644
index 00000000..b2a88d13
Binary files /dev/null and b/gfx/gsm_on.png differ
diff --git a/gfx/category_hdd_apa.png b/gfx/hdd.png
similarity index 100%
rename from gfx/category_hdd_apa.png
rename to gfx/hdd.png
diff --git a/gfx/category_hdd_bdm.png b/gfx/hdd_bd.png
similarity index 100%
rename from gfx/category_hdd_bdm.png
rename to gfx/hdd_bd.png
diff --git a/gfx/category_ilink.png b/gfx/ilk_bd.png
similarity index 100%
rename from gfx/category_ilink.png
rename to gfx/ilk_bd.png
diff --git a/gfx/bg_info.png b/gfx/info.png
similarity index 100%
rename from gfx/bg_info.png
rename to gfx/info.png
diff --git a/gfx/button_dpad_left.png b/gfx/left.png
similarity index 100%
rename from gfx/button_dpad_left.png
rename to gfx/left.png
diff --git a/gfx/discbox_list_apps.png b/gfx/lm_apps_case.png
similarity index 100%
rename from gfx/discbox_list_apps.png
rename to gfx/lm_apps_case.png
diff --git a/gfx/discbox_list_games.png b/gfx/lm_case.png
similarity index 100%
rename from gfx/discbox_list_games.png
rename to gfx/lm_case.png
diff --git a/gfx/discbox_list_shadow.png b/gfx/lm_case_shadow.png
similarity index 100%
rename from gfx/discbox_list_shadow.png
rename to gfx/lm_case_shadow.png
diff --git a/gfx/loading_1.png b/gfx/load0.png
similarity index 100%
rename from gfx/loading_1.png
rename to gfx/load0.png
diff --git a/gfx/loading_2.png b/gfx/load1.png
similarity index 100%
rename from gfx/loading_2.png
rename to gfx/load1.png
diff --git a/gfx/loading_3.png b/gfx/load2.png
similarity index 100%
rename from gfx/loading_3.png
rename to gfx/load2.png
diff --git a/gfx/loading_4.png b/gfx/load3.png
similarity index 100%
rename from gfx/loading_4.png
rename to gfx/load3.png
diff --git a/gfx/loading_5.png b/gfx/load4.png
similarity index 100%
rename from gfx/loading_5.png
rename to gfx/load4.png
diff --git a/gfx/loading_6.png b/gfx/load5.png
similarity index 100%
rename from gfx/loading_6.png
rename to gfx/load5.png
diff --git a/gfx/loading_7.png b/gfx/load6.png
similarity index 100%
rename from gfx/loading_7.png
rename to gfx/load6.png
diff --git a/gfx/loading_8.png b/gfx/load7.png
similarity index 100%
rename from gfx/loading_8.png
rename to gfx/load7.png
diff --git a/gfx/category_mx4sio.png b/gfx/m4s_bd.png
similarity index 100%
rename from gfx/category_mx4sio.png
rename to gfx/m4s_bd.png
diff --git a/gfx/category_mmce.png b/gfx/mmce.png
similarity index 100%
rename from gfx/category_mmce.png
rename to gfx/mmce.png
diff --git a/gfx/pademu_off.png b/gfx/pademu_off.png
new file mode 100644
index 00000000..0a436db5
Binary files /dev/null and b/gfx/pademu_off.png differ
diff --git a/gfx/pademu_on.png b/gfx/pademu_on.png
new file mode 100644
index 00000000..bf67d2f1
Binary files /dev/null and b/gfx/pademu_on.png differ
diff --git a/gfx/button_dpad_right.png b/gfx/right.png
similarity index 100%
rename from gfx/button_dpad_right.png
rename to gfx/right.png
diff --git a/gfx/screenshot.png b/gfx/screen.png
similarity index 100%
rename from gfx/screenshot.png
rename to gfx/screen.png
diff --git a/gfx/button_select.png b/gfx/select.png
similarity index 100%
rename from gfx/button_select.png
rename to gfx/select.png
diff --git a/gfx/bg_main.png b/gfx/settings_bg.png
similarity index 100%
rename from gfx/bg_main.png
rename to gfx/settings_bg.png
diff --git a/gfx/button_symbol_square.png b/gfx/square.png
similarity index 100%
rename from gfx/button_symbol_square.png
rename to gfx/square.png
diff --git a/gfx/button_start.png b/gfx/start.png
similarity index 100%
rename from gfx/button_start.png
rename to gfx/start.png
diff --git a/gfx/button_symbol_triangle.png b/gfx/triangle.png
similarity index 100%
rename from gfx/button_symbol_triangle.png
rename to gfx/triangle.png
diff --git a/gfx/button_dpad_up.png b/gfx/up.png
similarity index 100%
rename from gfx/button_dpad_up.png
rename to gfx/up.png
diff --git a/gfx/category_empty_bdm.png b/gfx/usb.png
similarity index 100%
rename from gfx/category_empty_bdm.png
rename to gfx/usb.png
diff --git a/gfx/category_usb.png b/gfx/usb_bd.png
similarity index 100%
rename from gfx/category_usb.png
rename to gfx/usb_bd.png
diff --git a/include/art_tar.h b/include/art_tar.h
deleted file mode 100644
index c5f55f79..00000000
--- a/include/art_tar.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef __TAR_H
-#define __TAR_H
-
-#define TAR_BLOCK_SIZE 512
-#define TAR_NAME_FIELD_SIZE 100
-#define MAX_FILE_SIZE (4 * 1024 * 1024) // 4 MiB
-#define ARC_MAGIC "ARC\0"
-#define ARC_VERSION 2
-
-typedef struct ArtCacheHeader
-{
- char magic[4];
- u32 version;
- u64 tarSize;
- u32 entryCount;
-} ArtCacheHeader;
-
-typedef struct
-{
- u64 offset; // offset of file data in archive
- u32 rawSize; // actual file size from header (octal), capped by MAX_FILE_SIZE
- u32 paddedSize; // file size rounded up to 512-byte TAR blocks
- char filename[TAR_NAME_FIELD_SIZE]; // TAR filename field
-} ArtTarEntry;
-
-int loadTarFile(const char *path);
-int closeTarFile(void);
-ArtTarEntry *findTarEntry(const char *filename);
-int hasTarEntry(const char *filename);
-void *getFileFromTar(const char *filename);
-u32 readFileFromTar(const ArtTarEntry *entry, void *dst, u32 dstSize);
-
-#endif
\ No newline at end of file
diff --git a/include/bdmsupport.h b/include/bdmsupport.h
index f26ce2cc..145c836c 100644
--- a/include/bdmsupport.h
+++ b/include/bdmsupport.h
@@ -4,10 +4,13 @@
#include "include/supportbase.h"
#include "include/iosupport.h"
+#include
+
typedef struct
{
- int massDeviceIndex; // Underlying device index backing the mass fs partition, ex: usb0 = 0, usb1 = 1, etc.
- char bdmPrefix[40]; // Contains the full path to the folder where all the games are.
+ int massDeviceIndex; // Underlying BDM device index, ex: usb0 = 0, usb1 = 1, etc.
+ char bdmPrefix[40]; // Contains the full path to the folder where all the games are.
+ char bdmTruePrefix[16]; // Stable device identity.. ex: usb0:, mx4sio0:, ilink0:, ata0:
int bdmULSizePrev;
time_t bdmModifiedCDPrev;
time_t bdmModifiedDVDPrev;
@@ -20,7 +23,6 @@ typedef struct
int ataHighestUDMAMode; // Highest UDMA mode supported by the HDD
unsigned char ThemesLoaded;
unsigned char LanguagesLoaded;
- unsigned char ArtArchivedLoaded;
unsigned char ForceRefresh;
} bdm_device_data_t;
@@ -44,10 +46,11 @@ extern int gEnableBdmHDD;
extern base_game_info_t *gAutoLaunchBDMGame;
extern bdm_device_data_t *gAutoLaunchDeviceData;
-
-int bdmFindPartition(char *target, const char *name, int write);
void bdmLoadModules(void);
-void bdmLaunchGame(item_list_t *itemList, int id, config_set_t *configSet);
+void bdmLoadModulesForPath(const char *path);
+void bdmLoadModulesForUsbMassCompat(void);
+void bdmLoadEnabledDeviceModules(void);
+void bdmLaunchGame(item_list_t *itemList, int id, per_game_cfg_t *pgcfg);
void bdmInitSemaphore();
void bdmEnumerateDevices();
@@ -57,4 +60,6 @@ int bdmHDDIsPresent(u32 timeoutMs);
void autoLaunchBDMGame(char *argv[]);
+int bdmResolveUsbMassCompatPath(char *out, size_t out_len, const char *path);
+
#endif
diff --git a/include/cheatman.h b/include/cheatman.h
index bf608a83..f5e82074 100644
--- a/include/cheatman.h
+++ b/include/cheatman.h
@@ -26,8 +26,6 @@
#define _CHEATMAN_H_
#include
-#include "config.h"
-
#define CHEAT_VERSION "0.5.3.7"
@@ -35,6 +33,7 @@
#define MAX_CODES 250
#define MAX_CHEATLIST (MAX_HOOKS * 2 + MAX_CODES * 2)
#define CHEAT_NAME_MAX 128
+#define MAX_IMAGEWORDS 1024
/* Some character defines */
#define NUL 0x00
@@ -64,11 +63,16 @@ typedef struct
extern cheat_entry_t gCheats[MAX_CODES];
extern int gCheatSource;
+extern int gEnableCheat; // Enables PS2RD Cheat Engine - 0 for Off, 1 for On
-void InitCheatsConfig(config_set_t *configSet);
+void InitCheatsConfig(int enable, int mode, int enable_image);
int GetCheatsEnabled(void);
u32 *GetCheatsList(void);
int load_cheats(const char *cheatfile);
+int load_cheats_buf(const char *buf, int size);
void set_cheats_list(void);
+int GetImageEnabled(void);
+const u32 *GetImage(void);
+int LoadImage(const char *filename);
#endif /* _CHEATMAN_H_ */
diff --git a/include/common.h b/include/common.h
index 9ab3d52a..a827ff6c 100644
--- a/include/common.h
+++ b/include/common.h
@@ -3,6 +3,7 @@
#include "include/iosupport.h"
+#include "include/config_wopl.h"
#include
#include
@@ -81,4 +82,6 @@ BLURT output char blurttext[128];
#endif
+#define WOPL_CONFIG_NAME WOPL_CONFIG_STRING // String to indicate what version of opl is used to avoid conflicts
+
#endif
diff --git a/include/config.h b/include/config_migration.h
similarity index 69%
rename from include/config.h
rename to include/config_migration.h
index 3ddfbd46..4065a6b4 100644
--- a/include/config.h
+++ b/include/config_migration.h
@@ -1,53 +1,74 @@
-#ifndef __CONFIG_H
-#define __CONFIG_H
+#ifndef CONFIG_MIGRATION_H
+#define CONFIG_MIGRATION_H
-#include
-#include
+#include "include/iosupport.h"
+#include "include/config_wopl.h"
-// Enum for the different types of config files. Game-specific config files (.cfg) will always have an ID of 0.
+// ---------------------------------------------------------------------------
+// DELETE_WITH_MIGRATION.. legacy key=value config infrastructure
+// Everything in this file is only needed during the migration period
+// ---------------------------------------------------------------------------
+
+// Each returns 1 on success.. 0 if not found or failed
+int cfgMigrateLegacyOPL(const char *path, int *out_theme_id, int *out_lang_id);
+int cfgMigrateLegacyNet(const char *path);
+int cfgMigrateLegacyGlobalGame(const char *path);
+int cfgMigrateLegacyAppTitleCfg(const char *path);
+int cfgMigrateLegacyTheme(const char *oldPath, const char *newPath);
+
+int cfgBatchMigratePerGame(const char *inputPrefix, const char *outputPrefix, int keepOriginals, void (*progressCb)(int done, int total));
+
+// Config type bits
enum CONFIG_INDEX {
CONFIG_INDEX_OPL = 0,
CONFIG_INDEX_LAST,
CONFIG_INDEX_APPS,
CONFIG_INDEX_NETWORK,
CONFIG_INDEX_GAME,
-
CONFIG_INDEX_COUNT
};
-// Config type bits
-#define CONFIG_OPL (1 << CONFIG_INDEX_OPL)
-#define CONFIG_LAST (1 << CONFIG_INDEX_LAST)
-#define CONFIG_APPS (1 << CONFIG_INDEX_APPS)
-#define CONFIG_NETWORK (1 << CONFIG_INDEX_NETWORK)
-#define CONFIG_GAME (1 << CONFIG_INDEX_GAME)
-#define CONFIG_ALL 0xFF
-
-#define CONFIG_SOURCE_DEFAULT 0
-#define CONFIG_SOURCE_USER 1
-#define CONFIG_SOURCE_DLOAD 2 // Downloaded from the network
-
-// Items for per-game config files.
-#define CONFIG_ITEM_NAME "#Name"
-#define CONFIG_ITEM_LONGNAME "#LongName"
-#define CONFIG_ITEM_SIZE "#Size"
-#define CONFIG_ITEM_FORMAT "#Format"
-#define CONFIG_ITEM_MEDIA "#Media"
-#define CONFIG_ITEM_STARTUP "#Startup"
-#define CONFIG_ITEM_ALTSTARTUP "$AltStartup"
-#define CONFIG_ITEM_VMC "$VMC"
-#define CONFIG_ITEM_COMPAT "$Compatibility"
-#define CONFIG_ITEM_DMA "$DMA"
-#define CONFIG_ITEM_DNAS "$DNAS"
-#define CONFIG_ITEM_CONFIGSOURCE "$ConfigSource"
-#define CONFIG_ITEM_CORE_LOADER "$CoreLoader"
+#define CONFIG_LAST (1 << CONFIG_INDEX_LAST)
+#define CONFIG_APPS (1 << CONFIG_INDEX_APPS)
+#define CONFIG_KEY_NAME_LEN 32
+#define CONFIG_KEY_VALUE_LEN 256
+
+struct config_kv_t
+{
+ char key[CONFIG_KEY_NAME_LEN];
+ char val[CONFIG_KEY_VALUE_LEN];
+ struct config_kv_t *next;
+};
+
+typedef struct
+{
+ int type;
+ struct config_kv_t *head;
+ struct config_kv_t *tail;
+ char *filename;
+ int modified;
+ u32 uid;
+} config_set_t;
+
+// Per-game config keys
+#define CONFIG_ITEM_NAME "#Name"
+#define CONFIG_ITEM_LONGNAME "#LongName"
+#define CONFIG_ITEM_SIZE "#Size"
+#define CONFIG_ITEM_FORMAT "#Format"
+#define CONFIG_ITEM_MEDIA "#Media"
+#define CONFIG_ITEM_STARTUP "#Startup"
+#define CONFIG_ITEM_ALTSTARTUP "$AltStartup"
+#define CONFIG_ITEM_VMC "$VMC"
+#define CONFIG_ITEM_COMPAT "$Compatibility"
+#define CONFIG_ITEM_DMA "$DMA"
+#define CONFIG_ITEM_DNAS "$DNAS"
+#define CONFIG_ITEM_CORE_LOADER "$CoreLoader"
#define CONFIG_ITEM_OSD_SETTINGS_LANGID "$CustomLanguageValue"
#define CONFIG_ITEM_OSD_SETTINGS_SOURCE "$CustomLanguageSource"
#define CONFIG_ITEM_OSD_SETTINGS_ENABLE "$OSDSettingsEnable"
#define CONFIG_ITEM_OSD_SETTINGS_TV_ASP "$OSDAspectRatio"
#define CONFIG_ITEM_OSD_SETTINGS_VMODE "$OSDVideoMode"
-// Per-Game GSM keys. -Bat-
#ifdef GSM
#define CONFIG_ITEM_GSMSOURCE "$GSMSource"
#define CONFIG_ITEM_ENABLEGSM "$EnableGSM"
@@ -56,14 +77,12 @@ enum CONFIG_INDEX {
#define CONFIG_ITEM_GSMYOFFSET "$GSMYOffset"
#define CONFIG_ITEM_GSMFIELDFIX "$GSMFIELDFix"
#endif
-
-// Per-Game CHEAT keys. -Bat-
#ifdef CHEAT
#define CONFIG_ITEM_CHEATSSOURCE "$CheatsSource"
#define CONFIG_ITEM_ENABLECHEAT "$EnableCheat"
#define CONFIG_ITEM_CHEATMODE "$CheatMode"
+#define CONFIG_ITEM_ENABLEIMAGE "$EnableImage"
#endif
-
#define CONFIG_ITEM_PADEMUSOURCE "$PADEMUSource"
#define CONFIG_ITEM_ENABLEPADEMU "$EnablePadEmu"
#define CONFIG_ITEM_PADEMUSETTINGS "$PadEmuSettings"
@@ -96,11 +115,11 @@ enum CONFIG_INDEX {
#define CONFIG_OPL_DEFAULT_DEVICE "default_device"
#define CONFIG_OPL_ENABLE_WRITE "enable_delete_rename"
#define CONFIG_OPL_HDD_SPINDOWN "hdd_spindown"
-#define CONFIG_OPL_BDM_PREFIX "usb_prefix" // Leave this "usb" for compatibility
+#define CONFIG_OPL_BDM_PREFIX "usb_prefix"
#define CONFIG_OPL_ETH_PREFIX "eth_prefix"
#define CONFIG_OPL_REMEMBER_LAST "remember_last"
#define CONFIG_OPL_AUTOSTART_LAST "autostart_last"
-#define CONFIG_OPL_BDM_MODE "usb_mode" // Leave this "usb" for compatibility
+#define CONFIG_OPL_BDM_MODE "usb_mode"
#define CONFIG_OPL_HDD_MODE "hdd_mode"
#define CONFIG_OPL_ETH_MODE "eth_mode"
#define CONFIG_OPL_APP_MODE "app_mode"
@@ -120,18 +139,21 @@ enum CONFIG_INDEX {
#define CONFIG_OPL_ENABLE_MX4SIO "enable_mx4sio"
#define CONFIG_OPL_ENABLE_BDMHDD "enable_bdm_hdd"
#define CONFIG_OPL_ENABLE_MMCE "enable_mmce"
-
-#define CONFIG_OPL_SWAP_SEL_BUTTON "swap_select_btn"
-#define CONFIG_OPL_PARENTAL_LOCK_PWD "parental_lock_password"
-#define CONFIG_OPL_SFX "enable_sfx"
-#define CONFIG_OPL_BOOT_SND "enable_boot_snd"
-#define CONFIG_OPL_BGM "enable_bgm"
-#define CONFIG_OPL_SFX_VOLUME "sfx_volume"
-#define CONFIG_OPL_BOOT_SND_VOLUME "boot_snd_volume"
-#define CONFIG_OPL_BGM_VOLUME "bgm_volume"
-#define CONFIG_OPL_DEFAULT_BGM_PATH "default_bgm_path"
-#define CONFIG_OPL_XSENSITIVITY "x_sensitivity"
-#define CONFIG_OPL_YSENSITIVITY "y_sensitivity"
+#define CONFIG_OPL_COVERFLOW_COUNT "coverflow_count"
+#define CONFIG_OPL_COVERFLOW_SCALE "coverflow_scale"
+#define CONFIG_OPL_COVERFLOW_ANIM "coverflow_anim"
+#define CONFIG_OPL_COVERFLOW_DIM "coverflow_dim"
+#define CONFIG_OPL_SWAP_SEL_BUTTON "swap_select_btn"
+#define CONFIG_OPL_PARENTAL_LOCK_PWD "parental_lock_password"
+#define CONFIG_OPL_SFX "enable_sfx"
+#define CONFIG_OPL_BOOT_SND "enable_boot_snd"
+#define CONFIG_OPL_BGM "enable_bgm"
+#define CONFIG_OPL_SFX_VOLUME "sfx_volume"
+#define CONFIG_OPL_BOOT_SND_VOLUME "boot_snd_volume"
+#define CONFIG_OPL_BGM_VOLUME "bgm_volume"
+#define CONFIG_OPL_DEFAULT_BGM_PATH "default_bgm_path"
+#define CONFIG_OPL_XSENSITIVITY "x_sensitivity"
+#define CONFIG_OPL_YSENSITIVITY "y_sensitivity"
// Network config keys
#define CONFIG_NET_ETH_LINKM "eth_linkmode"
@@ -149,37 +171,17 @@ enum CONFIG_INDEX {
#define CONFIG_NET_SMB_PORT "smb_port"
#define CONFIG_NET_NBD_DEFAULT_EXPORT "nbd_default_export"
-#define CONFIG_KEY_NAME_LEN 32
-#define CONFIG_KEY_VALUE_LEN 256
-
-struct config_value_t
-{
- // Including the NULL terminator
- char key[CONFIG_KEY_NAME_LEN];
- char val[CONFIG_KEY_VALUE_LEN];
-
- struct config_value_t *next;
-};
-
-typedef struct
-{
- int type;
- struct config_value_t *head;
- struct config_value_t *tail;
- char *filename;
- int modified;
- u32 uid;
-} config_set_t;
-
-extern char *gBaseMCDir;
-
-void configInit(char *prefix);
-void configSetMove(char *prefix);
-void configMove(config_set_t *configSet, const char *fileName);
-void configEnd();
+// Function declarations
config_set_t *configAlloc(int type, config_set_t *configSet, char *fileName);
void configFree(config_set_t *configSet);
config_set_t *configGetByType(int type);
+void configMove(config_set_t *configSet, const char *fileName);
+void configClear(config_set_t *configSet);
+
+void configInit(char *prefix);
+void configSetMove(char *prefix);
+void configEnd(void);
+
int configSetStr(config_set_t *configSet, const char *key, const char *value);
int configGetStr(config_set_t *configSet, const char *key, const char **value);
int configGetStrCopy(config_set_t *configSet, const char *key, char *value, int length);
@@ -189,14 +191,11 @@ int configSetColor(config_set_t *configSet, const char *key, unsigned char *colo
int configGetColor(config_set_t *configSet, const char *key, unsigned char *color);
int configRemoveKey(config_set_t *configSet, const char *key);
-void configGetDiscIDBinary(config_set_t *configSet, void *dst);
-
int configRead(config_set_t *configSet);
int configReadBuffer(config_set_t *configSet, const void *buffer, int size);
int configReadMulti(int types);
int configWrite(config_set_t *configSet);
int configWriteMulti(int types);
-void configClear(config_set_t *configSet);
void configGetVMC(config_set_t *configSet, char *vmc, int length, int slot);
void configSetVMC(config_set_t *configSet, const char *vmc, int slot);
@@ -205,14 +204,4 @@ void configRemoveVMC(config_set_t *configSet, int slot);
char *configGetDir(void);
void configPrepareNotifications(char *prefix);
-void loadConfig();
-
-int configCheckLoadConfigBDM(int types);
-
-int configCheckLoadConfigHDD(int types);
-
-int configLoad(int types);
-int configSave(int types, int showUI);
-void configApply(int themeID, int langID, int skipDeviceRefresh);
-
#endif
diff --git a/include/config_wopl.h b/include/config_wopl.h
new file mode 100644
index 00000000..54d56c0a
--- /dev/null
+++ b/include/config_wopl.h
@@ -0,0 +1,61 @@
+#ifndef CONFIG_WOPL_H
+#define CONFIG_WOPL_H
+
+#include "include/iosupport.h"
+#include
+
+#define CONFIG_OPL 1
+#define CONFIG_NETWORK 8
+#define CONFIG_GAME 16
+#define CONFIG_ALL (CONFIG_OPL | CONFIG_NETWORK | CONFIG_GAME)
+
+#define CORE_LOADER_WOPL 0
+#define CORE_LOADER_NEUTRINO 1
+
+extern int gBDMFramesDelay;
+extern int gETHFramesDelay;
+extern int gHDDFramesDelay;
+extern int gMMCEFramesDelay;
+extern int gAPPFramesDelay;
+extern int gFAVFramesDelay;
+
+void log_config_error(const char *path, const config_t *cfg);
+void cfgValidateBegin(const char *label);
+void cfgValidateEnd(void);
+int cfgGetInt(const config_t *cfg, const char *path, int *out);
+int cfgGetStr(const config_t *cfg, const char *path, const char **out);
+void cfgCheckExists(const config_t *cfg, const char *path, const char *expected);
+
+void dnas_to_binary(const char *dnas, char *out, int out_size);
+
+const char *wOPLGetDir(void);
+const char *wOPLGetThemeName(void);
+const char *wOPLGetLanguageName(void);
+
+int wOPLLoad(int *out_theme_id, int *out_lang_id);
+int wOPLSave(void);
+
+int wOPLNetLoad(void);
+int wOPLNetSave(void);
+
+int wOPLLastLoad(void);
+int wOPLLastSave(const char *startup);
+const char *wOPLLastGet(void);
+
+int wOPLGlobalGameLoad(void);
+int wOPLGlobalGameSave(void);
+int wOPLPerGameLoad(const char *path, per_game_cfg_t *cfg);
+int wOPLPerGameSave(const char *path, const per_game_cfg_t *cfg);
+
+int wOPLGameInfoLoad(const char *path, game_info_t *gi);
+int wOPLGameInfoSave(const char *path, const game_info_t *gi);
+
+extern char gParentalLockPassword[256];
+extern char *gBaseMCDir;
+
+void _loadConfig();
+void configApply(int themeID, int langID, int skipDeviceRefresh);
+int configLoad(int types);
+int configSave(int types, int showUI);
+
+#endif
diff --git a/include/dialogs.h b/include/dialogs.h
index ca9cd9a0..97b1618c 100644
--- a/include/dialogs.h
+++ b/include/dialogs.h
@@ -26,6 +26,7 @@ enum UI_ITEMS {
UICFG_YOFF,
UICFG_OVERSCAN,
UICFG_NOTIFICATIONS,
+ UICFG_COVERFLOW_BUTTON,
CFG_DEBUG,
CFG_BDM_DEBUG,
@@ -118,6 +119,7 @@ enum UI_ITEMS {
CHTCFG_CHEATCFG,
CHTCFG_ENABLECHEAT,
CHTCFG_CHEATMODE,
+ CHTCFG_ENABLEIMAGE,
#endif
#ifdef GSM
GSMCFG_GSMSOURCE,
@@ -209,6 +211,23 @@ enum UI_ITEMS {
#endif
};
+enum COVERFLOW_CFG_IDs {
+ CFG_COVERFLOW_COUNT = 1,
+ CFG_COVERFLOW_SCALE,
+ CFG_COVERFLOW_ANIM,
+ CFG_COVERFLOW_DIM,
+};
+
+// DELETE_WITH_MIGRATION v
+enum CFG_MIGRATION_IDs {
+ CFG_MIG_INPUT = 1,
+ CFG_MIG_OUTPUT,
+ CFG_MIG_KEEP_ORIGINALS,
+ CFG_MIG_CONVERT,
+};
+extern struct UIItem diaCfgMigration[];
+// DELETE_WITH_MIGRATION ^
+
#define COMPAT_NOEXIT 0x70000000
#define COMPAT_LOADFROMDISC (COMPAT_LOADFROMDISC_ID | COMPAT_NOEXIT)
@@ -247,4 +266,6 @@ extern struct UIItem diaBlockDevicesConfig[];
extern struct UIItem diaOSDConfig[];
extern struct UIItem diaMMCEConfig[];
+extern struct UIItem diaCoverflowConfig[];
+
#endif
diff --git a/include/fntsys.h b/include/fntsys.h
index 2706fe30..8818b5b4 100644
--- a/include/fntsys.h
+++ b/include/fntsys.h
@@ -19,6 +19,13 @@ void fntEnd();
* @return font slot id (negative value means error happened) */
int fntLoadFile(char *path, int fontSize);
+/** Loads a font from a memory buffer
+ * @param buf The buffer containing the font data
+ * @param bufSize The size of the buffer
+ * @param fontSize The font size to load
+ * @return font slot id (negative value means error happened) */
+int fntLoadFromBuffer(void *buf, int bufSize, int fontSize);
+
/** Reloads the default font */
int fntLoadDefault(char *path);
diff --git a/include/gui.h b/include/gui.h
index b88309d0..dddec7e7 100644
--- a/include/gui.h
+++ b/include/gui.h
@@ -135,15 +135,13 @@ void guiUpdateScrollSpeed(void);
void guiUpdateScreenScale(void);
void guiDrawBGPlasma();
-int guiDrawBGMain(void);
+int guiDrawBGSettings(void);
int guiDrawIconAndText(int iconId, int textId, int font, int x, int y, u64 color);
void guiDrawSubMenuHints(void);
int guiAlignMenuHints(menu_hint_item_t *hint, int font, int width);
int guiAlignSubMenuHints(int hintCount, int *textID, int *iconID, int font, int width, int align);
-void guiShowNetCompatUpdate(void);
-void guiShowNetCompatUpdateSingle(int id, item_list_t *support, config_set_t *configSet);
void guiShowAbout();
void guiShowConfig();
void guiShowUIConfig();
@@ -151,9 +149,15 @@ void guiShowAudioConfig();
void guiShowControllerConfig();
void guiShowNetConfig();
void guiShowParentalLockConfig();
+void guiShowCoverflowConfig(void);
void guiCheckNotifications(int checkTheme, int checkLang);
+void guiShowCfgMigration(void); // DELETE_WITH_MIGRATION
+
+void guiSetBootStatusIfActive(const char *status);
+void guiShowBootStatus(const char *status);
+
/** Renders the given string on screen for the given function until it's io finishes
* @note The ptr pointer is watched for it's value. The IO is considered finished when the value becomes zero.
* @param ptr The finished state pointer (1 unfinished, 0 finished)
@@ -178,7 +182,7 @@ void guiWarning(const char *text, int count);
int guiConfirmVideoMode(void);
-int guiGameShowRemoveSettings(config_set_t *configSet, config_set_t *configGame);
+int guiGameShowRemoveSettings(per_game_cfg_t *pgcfg);
#ifdef CHEAT
void guiManageCheats(void);
diff --git a/include/guigame.h b/include/guigame.h
index 6648be7e..6b3ffcaf 100644
--- a/include/guigame.h
+++ b/include/guigame.h
@@ -6,11 +6,9 @@
int guiGameAltStartupNameHandler(char *text, int maxLen);
-char *gameConfigSource(void);
-
int guiGameVmcNameHandler(char *text, int maxLen);
void guiGameShowVMCMenu(int id, item_list_t *support);
-void guiGameShowCompatConfig(int id, item_list_t *support, config_set_t *configSet);
+void guiGameShowCompatConfig(int id, item_list_t *support);
#ifdef GSM
void guiGameShowGSConfig(void);
#endif
@@ -22,17 +20,18 @@ void guiGameShowCheatConfig(void);
#ifdef PADEMU
void guiGameShowPadEmuConfig(int forceGlobal);
void guiGameShowPadMacroConfig(int forceGlobal);
-void guiGameSavePadEmuGlobalConfig(config_set_t *configGame);
-void guiGameSavePadMacroGlobalConfig(config_set_t *configGame);
+void guiGameSavePadEmuGlobalConfig(void);
+void guiGameSavePadMacroGlobalConfig(void);
#endif
void guiGameShowOSDLanguageConfig(int forceGlobal);
-void guiGameSaveOSDLanguageGlobalConfig(config_set_t *configGame);
+void guiGameSaveOSDLanguageGlobalConfig(void);
+
+void guiGameLoadConfig(item_list_t *support, per_game_cfg_t *pg);
+int guiGameSaveConfig(per_game_cfg_t *pg, item_list_t *support);
+void guiGameTestSettings(int id, item_list_t *support, per_game_cfg_t *pg);
-void guiGameLoadConfig(item_list_t *support, config_set_t *configSet);
-int guiGameSaveConfig(config_set_t *configSet, item_list_t *support);
-void guiGameTestSettings(int id, item_list_t *support, config_set_t *configSet);
+void guiGameRemoveSettings(per_game_cfg_t *pg);
+void guiGameRemoveGlobalSettings(void);
-void guiGameRemoveSettings(config_set_t *configSet);
-void guiGameRemoveGlobalSettings(config_set_t *configGame);
#endif
diff --git a/include/hddsupport.h b/include/hddsupport.h
index 4f33700a..2824129c 100644
--- a/include/hddsupport.h
+++ b/include/hddsupport.h
@@ -44,7 +44,7 @@ void hddSetIdleTimeout(int timeout);
item_list_t *hddGetObject(int initOnly);
int hddLoadModules(void);
void hddLoadSupportModules(void);
-void hddLaunchGame(item_list_t *itemList, int id, config_set_t *configSet);
+void hddLaunchGame(item_list_t *itemList, int id, per_game_cfg_t *pgcfg);
int hddIsPresent();
void autoLaunchHDDGame(char *argv[]);
diff --git a/include/initializer.h b/include/initializer.h
index 7bb426fd..b8a17993 100644
--- a/include/initializer.h
+++ b/include/initializer.h
@@ -10,7 +10,7 @@ void deinitAllSupport(int exception, int modeSelected);
void deinit(int exception, int modeSelected);
// Shutdown minimal services initiated for auto loading.
-void miniDeinit(config_set_t *configSet);
+void miniDeinit();
void init(void);
diff --git a/include/iosupport.h b/include/iosupport.h
index 673a129c..96a75b4a 100644
--- a/include/iosupport.h
+++ b/include/iosupport.h
@@ -1,7 +1,6 @@
#ifndef __IOSUPPORT_H
#define __IOSUPPORT_H
-#include "include/config.h"
#include
struct menu_item;
@@ -14,6 +13,13 @@ struct menu_item;
#define IO_MENU_UPDATE_DEFFERED 2
#define IO_CACHE_LOAD_ART 3 // io call to handle the loading of covers
+#define UL_GAME_NAME_MAX 32
+#define ISO_GAME_NAME_MAX 160
+#define ISO_GAME_EXTENSION_MAX 4
+#define GAME_STARTUP_MAX 12
+
+#define ISO_GAME_FNAME_MAX (ISO_GAME_NAME_MAX + ISO_GAME_EXTENSION_MAX)
+
enum IO_MODES {
BDM_MODE = 0,
BDM_MODE1,
@@ -89,6 +95,99 @@ enum ERROR_CODE {
#define MENU_UPD_DELAY_NOUPDATE -1 // Auto refresh is disabled for the item. The refresh button may be used to manually refresh the item.
#define MENU_UPD_DELAY_GENREFRESH 0 // The item will be refreshed every MENU_GENERAL_UPDATE_DELAY frames, regardless of whether automatic refresh is enabled or not.
+typedef struct
+{
+#ifdef GSM
+ int gsm_enable;
+ int gsm_vmode;
+ int gsm_xoffset;
+ int gsm_yoffset;
+ int gsm_fieldfix;
+#endif
+#ifdef CHEAT
+ int cheat_enable;
+ int cheat_mode;
+ int cheat_enable_image;
+#endif
+#ifdef PADEMU
+ int pademu_enable;
+ int pademu_settings;
+ int padmacro_settings;
+#endif
+ int osd_enable;
+ int osd_langid;
+ int osd_tv_aspect;
+ int osd_vmode;
+} global_game_cfg_t;
+
+typedef struct
+{
+ int size_mb;
+ int compat;
+ int dma; // 7 = not set (device default)
+ int core_loader;
+ char format[8];
+ char media[4];
+ char dnas[32];
+ char alt_startup[32];
+ char vmc1[32];
+ char vmc2[32];
+#ifdef GSM
+ int gsm_source;
+ int gsm_enable;
+ int gsm_vmode;
+ int gsm_xoffset;
+ int gsm_yoffset;
+ int gsm_fieldfix;
+#endif
+#ifdef CHEAT
+ int cheat_source;
+ int cheat_enable;
+ int cheat_mode;
+ int cheat_enable_image;
+#endif
+#ifdef PADEMU
+ int pademu_source;
+ int pademu_enable;
+ int pademu_settings;
+ int padmacro_source;
+ int padmacro_settings;
+#endif
+ int osd_source;
+ int osd_enable;
+ int osd_langid;
+ int osd_tv_aspect;
+ int osd_vmode;
+} per_game_cfg_t;
+
+typedef struct
+{
+ char title[ISO_GAME_NAME_MAX + 1];
+ char description[256];
+ char developer[64];
+ char genre[64];
+ char publisher[64];
+ char serial[16];
+ char release[32];
+ char aspect[32];
+ char parental[32];
+ char region[32];
+ int players;
+ int user_rating;
+ char version[32];
+ char package[64];
+ char source[128];
+} game_info_t;
+
+typedef struct
+{
+ game_info_t *gi;
+ per_game_cfg_t *pg;
+ int uid;
+} render_ctx_t;
+
+extern global_game_cfg_t gGlobalGameCfg;
+
typedef struct _item_list_t item_list_t;
typedef struct _item_list_t
@@ -143,9 +242,13 @@ typedef struct _item_list_t
void (*itemRename)(item_list_t *itemList, int id, char *newName);
- void (*itemLaunch)(item_list_t *itemList, int id, config_set_t *configSet);
+ void (*itemLaunch)(item_list_t *itemList, int id, per_game_cfg_t *pgcfg);
+
+ void (*itemGetInfo)(item_list_t *itemList, int id, game_info_t *gi);
- config_set_t *(*itemGetConfig)(item_list_t *itemList, int id);
+ void (*itemGetPgCfg)(item_list_t *itemList, int id, per_game_cfg_t *cfg);
+
+ int (*itemSavePgCfg)(item_list_t *itemList, int id, const per_game_cfg_t *cfg);
int (*itemGetImage)(item_list_t *itemList, char *folder, int isRelative, char *value, char *suffix, GSTEXTURE *resultTex, short psm);
@@ -174,5 +277,4 @@ void itemExecTriangle(struct menu_item *curMenu);
void itemExecFav(struct menu_item *curMenu);
-
#endif
diff --git a/include/lwnbd.h b/include/lwnbd.h
index f6378367..a8e4707a 100644
--- a/include/lwnbd.h
+++ b/include/lwnbd.h
@@ -1,6 +1,8 @@
#ifndef INCLUDE_LWNBDSUPPORT_H_
#define INCLUDE_LWNBDSUPPORT_H_
+#include
+
#define LWNBD_SID 0x2A39;
enum LWNBD_SERVER_CMD {
diff --git a/include/menusys.h b/include/menusys.h
index ff595cfa..8a363c63 100644
--- a/include/menusys.h
+++ b/include/menusys.h
@@ -1,8 +1,11 @@
#ifndef __MENUSYS_H
#define __MENUSYS_H
-#include "include/config.h"
#include "include/dia.h"
+#include "include/iosupport.h"
+#include "include/config_wopl.h"
+
+#include
/// a single submenu item
typedef struct submenu_item
@@ -61,7 +64,7 @@ typedef struct menu_item
void *userdata;
/// submenu, selection and page start (only used in static mode)
- struct submenu_list *submenu, *current, *pagestart;
+ struct submenu_list *submenu, *current, *pagestart, *last;
short remindLast;
@@ -112,8 +115,8 @@ void submenuSort(submenu_list_t **submenu);
char *submenuItemGetText(submenu_item_t *it);
char *menuItemGetText(menu_item_t *it);
-config_set_t *menuLoadConfig();
-config_set_t *gameMenuLoadConfig(struct UIItem *ui);
+per_game_cfg_t *menuLoadConfig();
+per_game_cfg_t *gameMenuLoadConfig(struct UIItem *ui);
void menuSaveConfig();
void menuRenderMain();
@@ -139,4 +142,6 @@ void menuDeferredUpdate(void *data);
void menuUpdateHook();
+int menuGetDevicePaths(char paths[][64], char labels[][80], int maxCount); // DELETE_WITH_MIGRATION
+
#endif
diff --git a/include/mmcesupport.h b/include/mmcesupport.h
index 4493c861..0061b289 100644
--- a/include/mmcesupport.h
+++ b/include/mmcesupport.h
@@ -24,8 +24,9 @@ extern char gMMCEPrefix[32];
void mmceInit(item_list_t *itemList);
item_list_t *mmceGetObject(int initOnly);
+void mmceSendGameId(const char *gameId);
+void mmceSendGameIdToDevice(int device, const char *gameId);
void mmceLoadModules(void);
-void mmceLaunchGame(item_list_t *itemList, int fd, config_set_t *configSet);
+void mmceLaunchGame(item_list_t *itemList, int id, per_game_cfg_t *pgcfg);
-
-#endif
\ No newline at end of file
+#endif
diff --git a/include/pathsupport.h b/include/pathsupport.h
new file mode 100644
index 00000000..21a005c1
--- /dev/null
+++ b/include/pathsupport.h
@@ -0,0 +1,17 @@
+#ifndef PATHSUPPORT_H
+#define PATHSUPPORT_H
+
+#include
+
+void pathSetLaunchPath(const char *path);
+const char *pathGetLaunchPath(void);
+
+int pathHasDevicePrefix(const char *path, const char *device);
+int pathParseDevicePrefix(const char *path, const char *device, int *index, const char **tail, int requireIndex);
+int pathIsDevicePath(const char *path);
+int pathIsUsbMassCompatPath(const char *path);
+void pathNormaliseDir(char *dir, size_t dir_len);
+int pathGetBootDir(char *dir_out, size_t dir_len);
+int pathJoin(char *out, size_t out_len, const char *dir, const char *name);
+
+#endif
diff --git a/include/pggsm.h b/include/pggsm.h
index 6779c20d..0c109b87 100644
--- a/include/pggsm.h
+++ b/include/pggsm.h
@@ -49,10 +49,16 @@ typedef struct predef_vmode_struct
u64 syncv;
} predef_vmode_struct;
+extern int gEnableGSM; // Enables GSM - 0 for Off, 1 for On
extern int gGSMSource;
+extern int gGSMVMode;
+extern int gGSMXOffset;
+extern int gGSMYOffset;
+extern int gGSMFIELDFix;
-void InitGSMConfig(config_set_t *configSet);
int GetGSMEnabled(void);
-void PrepareGSM(char *cmdline, struct GsmConfig_t *config);
+
+void InitGSMConfig(const per_game_cfg_t *pgcfg);
+void PrepareGSM(char *cmdline, struct GsmConfig_t *config, int vmode, int xoff, int yoff, int fieldfix);
#endif
diff --git a/include/renderman.h b/include/renderman.h
index 49665523..b598283c 100644
--- a/include/renderman.h
+++ b/include/renderman.h
@@ -60,7 +60,6 @@ extern const u64 gColBlack;
extern const u64 gColDarker;
extern const u64 gColFocus;
extern const u64 gDefaultCol;
-extern const u64 gDefaultBGCol;
extern const u64 gDefaultAlpha;
enum rm_aratio {
@@ -100,15 +99,10 @@ void rmDrawQuad(rm_quad_t *q);
void rmSetBackground(GSTEXTURE *txt);
/** Queues a specified pixmap (tinted with colour) to be rendered on specified position */
-void rmDrawPixmap(GSTEXTURE *txt, int x, int y, short aligned, int w, int h, short scaled, u64 color);
+void rmDrawPixmap(GSTEXTURE *txt, int x, int y, short aligned, int w, int h, short scaled, u64 color, int reflection);
void rmDrawOverlayPixmap(GSTEXTURE *overlay, int x, int y, short aligned, int w, int h, short scaled, u64 color,
- GSTEXTURE *inlay, int ulx, int uly, int urx, int ury, int blx, int bly, int brx, int bry);
-
-void rmDrawPixmapWithReflection(GSTEXTURE *txt, int x, int y, short aligned, int w, int h, short scaled, u64 color);
-
-void rmDrawOverlayPixmapWithReflection(GSTEXTURE *overlay, int x, int y, short aligned, int w, int h, short scaled, u64 color,
- GSTEXTURE *inlay, int ulx, int uly, int urx, int ury, int blx, int bly, int brx, int bry);
+ GSTEXTURE *inlay, int ulx, int uly, int urx, int ury, int blx, int bly, int brx, int bry, int reflection);
/** Queues a opaque rectangle to be rendered */
void rmDrawRect(int x, int y, int w, int h, u64 color);
diff --git a/include/sound.h b/include/sound.h
index 5fb8abe2..a2ca251f 100644
--- a/include/sound.h
+++ b/include/sound.h
@@ -5,6 +5,7 @@ enum SFX {
SFX_BOOT = 0,
SFX_CANCEL,
SFX_CONFIRM,
+ SFX_COVERFLOW,
SFX_CURSOR,
SFX_MESSAGE,
SFX_TRANSITION,
@@ -33,7 +34,5 @@ void sfxPlay(int id);
void bgmStart(void);
void bgmStop(void);
int isBgmPlaying(void);
-void bgmMute(void);
-void bgmUnMute(void);
#endif
diff --git a/include/supportbase.h b/include/supportbase.h
index 588d3847..3e48e350 100644
--- a/include/supportbase.h
+++ b/include/supportbase.h
@@ -1,13 +1,6 @@
#ifndef __SUPPORT_BASE_H
#define __SUPPORT_BASE_H
-#define UL_GAME_NAME_MAX 32
-#define ISO_GAME_NAME_MAX 160
-#define ISO_GAME_EXTENSION_MAX 4
-#define GAME_STARTUP_MAX 12
-
-#define ISO_GAME_FNAME_MAX (ISO_GAME_NAME_MAX + ISO_GAME_EXTENSION_MAX)
-
enum GAME_FORMAT {
GAME_FORMAT_USBLD = 0,
GAME_FORMAT_OLD_ISO,
@@ -49,6 +42,12 @@ typedef struct
short allocResult;
} file_buffer_t;
+typedef struct
+{
+ char elf[256];
+ char cwd[256];
+} neutrino_path_t;
+
#ifdef PADEMU
extern int gEnablePadEmu;
extern int gPadEmuSettings;
@@ -59,8 +58,9 @@ extern int gPadEmuSource;
int isValidIsoName(char *name, int *pNameLen);
int sbGetmcID(void);
+int sbCheckMC(void);
+int sbEnsureMCConfigFolder(const char *dir);
int sbGetFileSize(int fd);
-void sbCheckMCFolder(void);
int sbOpenFile(char *path, int mode);
void *sbReadFile(char *path, int align, int *size);
int sbIsSameSize(const char *prefix, int prevSize);
@@ -69,19 +69,22 @@ int sbCreateSemaphore(void);
int sbListDir(char *path, const char *separator, int maxElem,
int (*readEntry)(int index, const char *path, const char *separator, const char *name, unsigned char d_type));
int sbReadList(base_game_info_t **list, const char *prefix, int *fsize, int *gamecount);
-int sbPrepare(base_game_info_t *game, config_set_t *configSet, int size_cdvdman, void **cdvdman_irx, int *patchindex);
+int sbPrepare(base_game_info_t *game, const per_game_cfg_t *pgcfg, int size_cdvdman, void **cdvdman_irx, int *patchindex);
void sbUnprepare(void *pCommon);
void sbRebuildULCfg(base_game_info_t **list, const char *prefix, int gamecount, int excludeID);
void sbCreatePath(const base_game_info_t *game, char *path, const char *prefix, const char *sep, int part);
void sbDelete(base_game_info_t **list, const char *prefix, const char *sep, int gamecount, int id);
void sbRename(base_game_info_t **list, const char *prefix, const char *sep, int gamecount, int id, char *newname);
-config_set_t *sbPopulateConfig(base_game_info_t *game, const char *prefix, const char *sep);
+void sbPopulateConfig(base_game_info_t *game, const char *prefix, const char *sep, game_info_t *gi, per_game_cfg_t *pgcfg);
void sbCreateFolders(const char *path, int createDiscImgFolders);
file_buffer_t *sbOpenFileBufferBuffer(short allocResult, const void *buffer, unsigned int size);
file_buffer_t *sbOpenFileBuffer(char *fpath, int mode, short allocResult, unsigned int size);
int sbReadFileBuffer(file_buffer_t *readContext, char **outBuf);
void sbWriteFileBuffer(file_buffer_t *fileBuffer, char *inBuf, int size);
void sbCloseFileBuffer(file_buffer_t *fileBuffer);
+void sbMMCESendGameId(const char *gameId);
+
+int sbSaveConfig(base_game_info_t *game, const char *prefix, const char *sep, const per_game_cfg_t *cfg);
// ISO9660 filesystem management functions.
u32 sbGetISO9660MaxLBA(const char *path);
@@ -90,6 +93,14 @@ int sbProbeISO9660_64(const char *path, base_game_info_t *game, u32 layer1_offse
#ifdef CHEAT
int sbLoadCheats(const char *path, const char *file);
+int sbLoadImage(const char *path, const char *file);
#endif
+int sbFindNeutrino(neutrino_path_t *path, const char *preferredPrefix);
+void sbCreateNeutrinoVMCPath(char *path, int length, const char *prefix, const char *vmc);
+
+int sbGetPathModeAndDevice(const char *path, int *device);
+int sbGetPathMode(const char *path);
+int sbPathIsMC(const char *path);
+
#endif
diff --git a/include/system.h b/include/system.h
index 76e3b1e8..43cc4d72 100644
--- a/include/system.h
+++ b/include/system.h
@@ -3,9 +3,6 @@
#include "include/mcemu.h"
-#define NEUTRINO_PATH "mc0:NEUTRINO/neutrino.elf"
-#define NEUTRINO_ALT_PATH "mc1:NEUTRINO/neutrino.elf"
-
extern int gOSDLanguageValue;
extern int gOSDTVAspectRatio;
extern int gOSDVideOutput;
@@ -29,8 +26,12 @@ void sysPowerOff(void);
int sysInitDECI2(void);
#endif
+#ifdef PADEMU
+void sysInitPadEmu(void);
+#endif
+
void sysLaunchLoaderElf(const char *filename, const char *mode_str, int size_cdvdman_irx, void **cdvdman_irx, int size_mcemu_irx, void **mcemu_irx, int EnablePS2Logo, unsigned int compatflags);
-void sysLaunchNeutrino(const char *driver, const char *path, int compatmask, int EnablePS2Logo, const char *neutrinoPath);
+void sysLaunchNeutrino(const char *driver, const char *path, int compatmask, int EnablePS2Logo, const char *neutrinoPath, const char *neutrinoCwd, const char *vmc0, const char *vmc1);
int sysLoadModuleBuffer(void *buffer, int size, int argc, char *argv);
int sysCheckMC(void);
diff --git a/include/tar.h b/include/tar.h
new file mode 100644
index 00000000..437082a7
--- /dev/null
+++ b/include/tar.h
@@ -0,0 +1,70 @@
+#ifndef __TAR_H
+#define __TAR_H
+
+#define TAR_BLOCK_SIZE 512
+#define MAX_FILE_SIZE (4 * 1024 * 1024) // 4 MiB
+#define ARC_MAGIC "ARC\0"
+#define ARC_VERSION 3
+
+typedef struct
+{
+ char magic[4];
+ u32 version;
+ u64 tarSize;
+ u32 entryCount;
+} TarCacheHeader;
+
+typedef struct
+{
+ u64 offset; // offset of file data in archive
+ u32 rawSize; // actual file size from header (octal), capped by MAX_FILE_SIZE
+ u32 paddedSize; // file size rounded up to 512-byte TAR blocks
+} TarEntryBase;
+
+typedef struct
+{
+ TarEntryBase base;
+ char filename[41]; // ART: filename up to 40 chars + null
+} TarEntryArt;
+
+typedef struct
+{
+ TarEntryBase base;
+ char filename[20]; // CFG: "SLUS_203.70.cfg"
+} TarEntryCfg;
+
+typedef struct
+{
+ TarEntryBase base;
+ char filename[20]; // CHT: "SLUS_203.70.cht"
+} TarEntryCht;
+
+typedef struct
+{
+ TarEntryBase base;
+ char filename[128]; // THM: "OPL Master/aspect/s_Aspect.png", "OPL Master/case.png", etc.
+} TarEntryThm;
+
+typedef enum {
+ TAR_KIND_ART = 0,
+ TAR_KIND_CFG = 1,
+ TAR_KIND_CHT = 2,
+ TAR_KIND_THM = 3,
+ TAR_KIND_MAX
+} TarKind;
+
+int tarLoadFromAnyDevice(TarKind kind);
+int tarLoadFile(TarKind kind, const char *path);
+int tarClose(TarKind kind);
+
+TarEntryBase *tarFind(TarKind kind, const char *filename);
+void *tarGet(TarKind kind, const char *filename);
+u32 tarRead(TarKind kind, const TarEntryBase *entry, void *dst, u32 dstSize);
+
+int tarEnsureLoaded(TarKind kind);
+void tarInvalidate(TarKind kind);
+
+const char *tarGetDevicePrefix(TarKind kind);
+char **tarGetThemeNames(int *count);
+
+#endif
diff --git a/include/textures.h b/include/textures.h
index c8baba5d..3e3ff097 100644
--- a/include/textures.h
+++ b/include/textures.h
@@ -2,124 +2,100 @@
#define __TEXTURES_H
enum INTERNAL_TEXTURE {
- LOGO_1_ICON = 0,
- LOGO_2_ICON,
- LOGO_3_ICON,
- LOGO_4_ICON,
- LOGO_5_ICON,
- LOGO_6_ICON,
- LOGO_7_ICON,
- LOGO_8_ICON,
- LOGO_9_ICON,
- LOGO_10_ICON,
- LOGO_11_ICON,
- LOGO_12_ICON,
- LOGO_13_ICON,
- LOGO_14_ICON,
- LOGO_15_ICON,
- LOGO_16_ICON,
- LOGO_17_ICON,
- LOGO_18_ICON,
- LOGO_19_ICON,
- LOGO_20_ICON,
- LOGO_21_ICON,
- LOADING_1_ICON,
- LOADING_2_ICON,
- LOADING_3_ICON,
- LOADING_4_ICON,
- LOADING_5_ICON,
- LOADING_6_ICON,
- LOADING_7_ICON,
- LOADING_8_ICON,
- CATEGORY_EMPTY_BDM_ICON,
- CATEGORY_USB_ICON,
- CATEGORY_ILINK_ICON,
- CATEGORY_MX4SIO_ICON,
- CATEGORY_HDD_BDM_ICON,
- CATEGORY_HDD_APA_ICON,
- CATEGORY_NET_SMB_ICON,
- CATEGORY_APPS_ICON,
- CATEGORY_FAV_ICON,
- MARK_STAR,
- CATEGORY_MMCE_ICON,
- BDM_INDEX_1,
- BDM_INDEX_2,
- BDM_INDEX_3,
- BDM_INDEX_4,
- BDM_INDEX_5,
- BUTTON_STICK_R3_ICON,
- BUTTON_DPAD_LEFT_ICON,
- BUTTON_DPAD_RIGHT_ICON,
- BUTTON_SYMBOL_CROSS_ICON,
- BUTTON_SYMBOL_TRIANGLE_ICON,
- BUTTON_SYMBOL_CIRCLE_ICON,
- BUTTON_SYMBOL_SQUARE_ICON,
- BUTTON_SELECT_ICON,
- BUTTON_START_ICON,
- BUTTON_DPAD_UP_ICON,
- BUTTON_DPAD_DOWN_ICON,
- /* currently unused.
- L1_ICON,
- L2_ICON,
- L3_ICON,
- R1_ICON,
- R2_ICON, */
- BG_MAIN,
- BG_INFO,
- COVER_GAMES,
- COVER_APPS,
+ LOAD0_ICON = 0,
+ LOAD1_ICON,
+ LOAD2_ICON,
+ LOAD3_ICON,
+ LOAD4_ICON,
+ LOAD5_ICON,
+ LOAD6_ICON,
+ LOAD7_ICON,
+ BDM_ICON,
+ USB_ICON,
+ ILINK_ICON,
+ MX4SIO_ICON,
+ HDD_BD_ICON,
+ HDD_ICON,
+ ETH_ICON,
+ APP_ICON,
+ FAV_ICON,
+ FAV_MARK,
+ MMCE_ICON,
+ INDEX_0,
+ INDEX_1,
+ INDEX_2,
+ INDEX_3,
+ INDEX_4,
+ R3_ICON,
+ LEFT_ICON,
+ RIGHT_ICON,
+ CROSS_ICON,
+ TRIANGLE_ICON,
+ CIRCLE_ICON,
+ SQUARE_ICON,
+ SELECT_ICON,
+ START_ICON,
+ UP_ICON,
+ DOWN_ICON,
+ SETTINGS_BG,
+ INFO_BG,
+ COVER_DEFAULT,
+ APP_COVER_DEFAULT,
DISC_DEFAULT,
- SCREENSHOT_DEFAULT,
- BADGE_EXEC_ELF_FORMAT,
- BADGE_DISC_HDL_FORMAT,
- BADGE_DISC_ISO_FORMAT,
- BADGE_DISC_ZSO_FORMAT,
- BADGE_DISC_UL_FORMAT,
- BADGE_EXEC_APP_MEDIA,
- BADGE_DISC_CD_MEDIA,
- BADGE_DISC_DVD_MEDIA,
- BADGE_VMODE_43,
- BADGE_VMODE_169,
- BADGE_VMODE_169_PS2RD,
- BADGE_VMODE_169_HEXISO,
- DEV_1,
- DEV_2,
- DEV_3,
- DEV_4,
- DEV_5,
- DEV_6,
- DEV_7,
- DEV_8,
+ SCREEN_DEFAULT,
+ ELF_FORMAT,
+ HDL_FORMAT,
+ ISO_FORMAT,
+ ZSO_FORMAT,
+ UL_FORMAT,
+ APP_MEDIA,
+ CD_MEDIA,
+ DVD_MEDIA,
+ ASPECT_STD,
+ ASPECT_WIDE,
+ ASPECT_WIDE1,
+ ASPECT_WIDE2,
+ REGION_PAL,
+ REGION_NTSC,
+ PADEMU_ON,
+ PADEMU_OFF,
+ GSM_ON,
+ GSM_OFF,
+ CHT_ON,
+ CHT_OFF,
RATING_0,
RATING_1,
RATING_2,
RATING_3,
RATING_4,
RATING_5,
- BADGE_RES_240P,
- BADGE_RES_240_HEXISO,
- BADGE_RES_480I,
- BADGE_RES_480P,
- BADGE_RES_480P_XT,
- BADGE_RES_480P_XC,
- BADGE_RES_480P_GSM,
- BADGE_RES_480P_PS2RD,
- BADGE_RES_480P_HEXISO,
- BADGE_RES_576I,
- BADGE_RES_576P_GSM,
- BADGE_RES_720P_GSM,
- BADGE_RES_1080I,
- BADGE_RES_1080I_GSM,
- BADGE_RES_1080P_GSM,
- BADGE_REGION_MULTI,
- BADGE_REGION_NTSC,
- BADGE_REGION_PAL,
+ LOGO_01,
+ LOGO_02,
+ LOGO_03,
+ LOGO_04,
+ LOGO_05,
+ LOGO_06,
+ LOGO_07,
+ LOGO_08,
+ LOGO_09,
+ LOGO_10,
+ LOGO_11,
+ LOGO_12,
+ LOGO_13,
+ LOGO_14,
+ LOGO_15,
+ LOGO_16,
+ LOGO_17,
+ LOGO_18,
+ LOGO_19,
+ LOGO_20,
+ LOGO_21,
CASE_OVERLAY,
APPS_CASE_OVERLAY,
PLANK,
- DISCBOX_LIST_GAMES_OVERLAY,
- DISCBOX_LIST_APPS_OVERLAY,
- DISCBOX_LIST_SHADOW_OVERLAY,
+ LM_CASE_OVERLAY,
+ LM_APPS_CASE_OVERLAY,
+ LM_CASE_SHADOW,
TEXTURES_COUNT
};
@@ -132,6 +108,10 @@ enum INTERNAL_TEXTURE {
#define ERR_MISSING_ALPHA -6
#define ERR_BAD_DEPTH -7
+#define RES_FILESYSTEM 0
+#define RES_TAR_ART 1
+#define RES_TAR_THM 2
+
int texLookupInternalTexId(const char *name);
int texLoadInternal(GSTEXTURE *texture, int texId);
int texDiscoverLoad(GSTEXTURE *texture, const char *path, int texId, int archived);
diff --git a/include/themes.h b/include/themes.h
index 2857721c..37ab2862 100644
--- a/include/themes.h
+++ b/include/themes.h
@@ -31,7 +31,7 @@ typedef struct
{
// Attributes for: AttributeImage
int currentUid;
- u32 currentConfigId;
+ int currentConfigId;
char *currentValue;
// Attributes for: AttributeImage & GameImage
@@ -56,7 +56,7 @@ typedef struct
char *alias;
int displayMode;
- u32 currentConfigId;
+ int currentConfigId;
char *currentValue;
} mutable_text_t;
@@ -85,7 +85,7 @@ typedef struct theme_element
void *extended;
- void (*drawElem)(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem);
+ void (*drawElem)(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem);
void (*endElem)(struct theme_element *elem);
struct theme_element *next;
@@ -130,10 +130,6 @@ typedef struct theme
theme_element_t *itemsList;
- // Icon Logo
- theme_element_t *logoIcon;
- int logoIconCount;
-
// Icon Loading
theme_element_t *loadingIcon;
int loadingIconCount;
@@ -142,12 +138,18 @@ typedef struct theme
int fonts[THM_MAX_FONTS]; //!< Storage of font handles for removal once not needed
theme_element_t *coverflow;
+ int coverflowCoverOffset; // used to compensate for asymmetric overlay transparency, in texture pixels (half the transparent padding). e.g. overlay 256px wide with 29px right padding = 14 (29/2)
} theme_t;
extern theme_t *gTheme;
extern int gDiscEnableArt;
+extern int gCoverflowCount;
+extern int gCoverflowCenterScale;
+extern int gCoverflowAnimSpeed;
+extern int gCoverflowDimCovers;
+
void thmInit(void);
void thmReinit(const char *path);
void thmReloadScreenExtents(void);
@@ -163,7 +165,6 @@ int thmFindGuiID(const char *theme);
const char **thmGetGuiList(void);
char *thmGetFilePath(int themeID);
-extern int isAnimating;
-extern int animationDirection;
+extern void thmTriggerCoverflowAnim(int direction);
#endif
diff --git a/lng_tmpl/_base.yml b/lng_tmpl/_base.yml
index deb94b6b..76486212 100644
--- a/lng_tmpl/_base.yml
+++ b/lng_tmpl/_base.yml
@@ -148,8 +148,8 @@ gui_strings:
string: No controller detected, waiting...
- label: DISCART
string: Enable Disc Art
-- label: HINT_DISCART
- string: To apply this you will need to restart wOPL
+- label: HINT_RESTART
+ string: To apply, you will need to restart wOPL
- label: WIDE_SCREEN
string: Widescreen
- label: POWEROFF
@@ -414,6 +414,12 @@ gui_strings:
string: 'Error: failed to load cheat file.'
- label: NO_CHEATS_FOUND
string: No cheats found.
+- label: ENABLEIMAGE
+ string: Image Linker
+- label: HINT_ENABLEIMAGE
+ string: Patch game from IMG directory.
+- label: ERR_IMAGE_LOAD_FAILED
+ string: 'Error: failed to load image file.'
- label: AUTO_START_IN_N_SECS
string: Auto start in %i s...
- label: AUTOSTARTLAST
diff --git a/misc/theme_coverflow.cfg b/misc/theme_coverflow.cfg
index 8c745219..c39cf2ad 100644
--- a/misc/theme_coverflow.cfg
+++ b/misc/theme_coverflow.cfg
@@ -1,211 +1,289 @@
-font1=builtin
-font1_size=20
-plasma_blend_color=#FFFFFF
-main0:
- type=Background
- default=bg_main
- aligned=0
- scaled=0
- width=DIM_INF
- height=DIM_INF
-main1:
- type=ItemsList
- x=POS_MID
- y=45
- width=400
- height=24
- aligned=1
- font=1
-main2:
- type=StaticImage
- default=plank
- aligned=0
- scaled=0
- y=315
- width=DIM_INF
- height=124
-main3:
- type=Coverflow
- default=cover_game
- reflection=1
- y=197
- width=200
- height=255
- overlay=case
- overlay_ulx=0
- overlay_uly=12
- overlay_urx=175
- overlay_ury=12
- overlay_llx=0
- overlay_lly=253
- overlay_lrx=175
- overlay_lry=253
-appsMain3:
- type=Coverflow
- default=cover_app
- reflection=1
- y=239
- width=200
- height=190
- overlay=apps_case
- overlay_ulx=0
- overlay_uly=8
- overlay_urx=174
- overlay_ury=8
- overlay_llx=0
- overlay_lly=189
- overlay_lrx=174
- overlay_lry=189
-main4:
- type=MenuIcon
- width=180
- height=120
- x=-1
- y=334
- aligned=2
-main5:
- type=HintText
- aligned=1
- x=POS_MID
- color=#FFFFFF
-main6:
- type=BdmIndex
- width=24
- height=24
- y=375
- x=-150
- wsX=-115
- aligned=2
-main7:
- type=LoadingIcon
- y=-80
- width=32
- height=32
-info0:
- type=Background
- pattern=bg
-info1:
- type=StaticImage
- default=bg_info
- aligned=0
- scaled=0
- width=DIM_INF
- height=DIM_INF
-info2:
- type=AttributeText
- attribute=Title
- display=0
- aligned=0
- x=33
- y=28
- color=#FFFFFF
-info3:
- type=AttributeText
- attribute=Genre
- display=0
- aligned=0
- x=33
- y=47
- color=#FFFFFF
-info4:
- type=AttributeText
- attribute=Release
- display=0
- aligned=0
- x=33
- y=66
- color=#FFFFFF
-info5:
- type=AttributeText
- attribute=Developer
- display=0
- aligned=0
- x=33
- y=85
- color=#FFFFFF
-info6:
- type=AttributeText
- attribute=#Size
- display=0
- aligned=0
- x=33
- y=104
- color=#FFFFFF
-info7:
- type=AttributeText
- attribute=Description
- display=0
- aligned=0
- x=33
- y=123
- width=574
- height=80
- wrap=1
- color=#FFFFFF
-info8:
- type=AttributeImage
- attribute=#Media
- width=42
- height=22
- x=55
- y=-242
-info9:
- type=AttributeImage
- attribute=#Format
- width=42
- height=22
- x=110
- y=-242
-info10:
- type=AttributeImage
- attribute=Vmode
- width=42
- height=22
- x=165
- y=-242
-info11:
- type=AttributeImage
- attribute=Aspect
- width=42
- height=22
- x=220
- y=-242
-info12:
- type=AttributeImage
- attribute=Scan
- width=42
- height=22
- x=275
- y=-242
-info13:
- type=AttributeImage
- attribute=Rating
- default=rating_0
- x=-74
- y=-242
-info14:
- type=AttributeImage
- attribute=Device
- x=-74
- y=38
-info15:
- type=GameImage
- pattern=SCR
- default=screenshot
- x=184
- y=-133
- width=173
- height=148
-info16:
- type=GameImage
- pattern=SCR2
- default=screenshot
- x=-186
- y=-133
- width=173
- height=148
-info17:
- type=InfoHintText
- aligned=1
- x=POS_MID
- color=#FFFFFF
+font1 = "builtin";
+font1_size = 20;
+plasma_blend_color = "#FFFFFF";
+coverflow_cover_offset = 14;
+
+main0: {
+ type = "Background";
+ default = "settings_bg";
+ aligned = 0;
+ scaled = 0;
+ width = "DIM_INF";
+ height = "DIM_INF";
+};
+main1: {
+ type = "ItemsList";
+ x = "POS_MID";
+ y = 45;
+ width = 400;
+ height = 24;
+ aligned = 1;
+ font = 1;
+};
+main2: {
+ type = "StaticImage";
+ default = "plank";
+ aligned = 0;
+ scaled = 0;
+ y = 315;
+ width = "DIM_INF";
+ height = 124;
+};
+main3: {
+ type = "Coverflow";
+ default = "cover";
+ reflection = 1;
+ y = 197;
+ width = 200;
+ height = 255;
+ overlay = "case";
+ overlay_ulx = 0;
+ overlay_uly = 13;
+ overlay_urx = 176;
+ overlay_ury = 13;
+ overlay_llx = 0;
+ overlay_lly = 254;
+ overlay_lrx = 176;
+ overlay_lry = 254;
+};
+appsMain3: {
+ type = "Coverflow";
+ default = "cover";
+ reflection = 1;
+ y = 239;
+ width = 200;
+ height = 190;
+ overlay = "apps_case";
+ overlay_ulx = 0;
+ overlay_uly = 9;
+ overlay_urx = 175;
+ overlay_ury = 9;
+ overlay_llx = 0;
+ overlay_lly = 190;
+ overlay_lrx = 175;
+ overlay_lry = 190;
+};
+main4: {
+ type = "MenuIcon";
+ width = 180;
+ height = 120;
+ x = -1;
+ y = 334;
+ aligned = 2;
+};
+main5: {
+ type = "HintText";
+ aligned = 1;
+ x = "POS_MID";
+ color = "#FFFFFF";
+};
+main6: {
+ type = "BdmIndex";
+ width = 24;
+ height = 24;
+ y = 375;
+ x = -150;
+ wsX = -115;
+ aligned = 2;
+};
+main7: {
+ type = "LoadingIcon";
+ y = -80;
+ width = 32;
+ height = 32;
+};
+info0: {
+ type = "Background";
+ pattern = "BG";
+};
+info1: {
+ type = "StaticImage";
+ default = "info";
+ aligned = 0;
+ scaled = 0;
+ width = "DIM_INF";
+ height = "DIM_INF";
+};
+info2: {
+ type = "AttributeText";
+ attribute = "Title";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 28;
+ color = "#FFFFFF";
+};
+info3: {
+ type = "AttributeText";
+ attribute = "Genre";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 47;
+ color = "#FFFFFF";
+};
+info4: {
+ type = "AttributeText";
+ attribute = "Release";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 66;
+ color = "#FFFFFF";
+};
+info5: {
+ type = "AttributeText";
+ attribute = "Developer";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 85;
+ color = "#FFFFFF";
+};
+info6: {
+ type = "AttributeText";
+ attribute = "#Size";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 104;
+ color = "#FFFFFF";
+};
+info7: {
+ type = "AttributeText";
+ attribute = "Description";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 123;
+ width = 574;
+ height = 80;
+ wrap = 1;
+ color = "#FFFFFF";
+};
+info8: {
+ type = "AttributeImage";
+ attribute = "#Media";
+ width = 42;
+ height = 22;
+ x = 55;
+ y = -242;
+};
+info9: {
+ type = "AttributeImage";
+ attribute = "#Format";
+ width = 42;
+ height = 22;
+ x = 110;
+ y = -242;
+};
+info10: {
+ type = "AttributeImage";
+ attribute = "Aspect";
+ width = 42;
+ height = 22;
+ x = 220;
+ y = -242;
+};
+info11: {
+ type = "AttributeImage";
+ attribute = "Rating";
+ default = "Rating_0";
+ x = -74;
+ y = -242;
+};
+info12: {
+ type = "GameImage";
+ pattern = "SCR";
+ default = "screen";
+ x = 184;
+ y = -133;
+ width = 173;
+ height = 148;
+};
+info13: {
+ type = "GameImage";
+ pattern = "SCR2";
+ default = "screen";
+ x = -186;
+ y = -133;
+ width = 173;
+ height = 148;
+};
+info14: {
+ type = "InfoHintText";
+ aligned = 1;
+ x = "POS_MID";
+ color = "#FFFFFF";
+};
+info15: {
+ type = "AttributeImage";
+ attribute = "$PadEmu";
+ width = 42;
+ height = 22;
+ x = 275;
+ y = -242;
+};
+info16: {
+ type = "AttributeImage";
+ attribute = "$Cheat";
+ width = 42;
+ height = 22;
+ x = 330;
+ y = -242;
+};
+info17: {
+ type = "AttributeImage";
+ attribute = "$GSM";
+ width = 42;
+ height = 22;
+ x = 385;
+ y = -242;
+};
+info18: {
+ type = "AttributeImage";
+ attribute = "Region";
+ width = 42;
+ height = 22;
+ x = 165;
+ y = -242;
+};
+appsInfo3: {
+ type = "AttributeText";
+ attribute = "Version";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 47;
+ color = "#FFFFFF";
+};
+appsInfo7: {
+ type = "AttributeText";
+ attribute = "Description";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 161;
+ width = 574;
+ height = 61;
+ wrap = 1;
+ color = "#FFFFFF";
+};
+appsInfo10: {
+ type = "AttributeText";
+ attribute = "Package";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 123;
+ color = "#FFFFFF";
+};
+appsInfo11: {
+ type = "AttributeText";
+ attribute = "Source";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 142;
+ color = "#FFFFFF";
+};
diff --git a/misc/theme_list.cfg b/misc/theme_list.cfg
index ac14c3ee..22d4fb19 100644
--- a/misc/theme_list.cfg
+++ b/misc/theme_list.cfg
@@ -1,224 +1,293 @@
-plasma_blend_color=#FFFFFF
-main0:
- type=Background
- default=bg_main
- aligned=0
- scaled=0
- width=DIM_INF
- height=DIM_INF
-main1:
- type=ItemsList
- x=40
- y=38
- width=400
- height=380
- color=#FFFFFF
-main2:
- type=StaticImage
- default=discbox_list_shadow
- x=-106
- y=-165
- skip=1
-main3:
- type=ItemCoverGames
- default=cover_game
- x=-106
- y=-296
- overlay=discbox_list_games
- overlay_ulx=0
- overlay_uly=9
- overlay_urx=168
- overlay_ury=19
- overlay_llx=0
- overlay_lly=244
- overlay_lrx=168
- overlay_lry=254
-appsMain3:
- type=ItemCoverApps
- default=cover_app
- x=-106
- y=-224
- overlay=discbox_list_apps
- overlay_ulx=0
- overlay_uly=9
- overlay_urx=168
- overlay_ury=19
- overlay_llx=0
- overlay_lly=170
- overlay_lrx=168
- overlay_lry=180
-main4:
- type=ItemIcon
- default=disc
- x=-240
- y=-210
- wsX=-205
- width=128
- height=128
- reflection=1
-appsMain4:
- type=ItemIcon
- width=0
- height=0
-main5:
- type=MenuIcon
- width=180
- height=120
- x=-1
- y=324
- aligned=2
-main6:
- type=HintText
- aligned=1
- x=POS_MID
- color=#FFFFFF
-main7:
- type=BdmIndex
- width=24
- height=24
- y=375
- x=-150
- wsX=-115
- aligned=2
-main8:
- type=LoadingIcon
- y=-80
- width=32
- height=32
-main9:
- type=ItemText
- x=-124
- y=-155
-appsMain9:
- type=ItemText
- x=-124
- y=-155
-info0:
- type=Background
- pattern=bg
-info1:
- type=StaticImage
- default=bg_info
- aligned=0
- scaled=0
- width=DIM_INF
- height=DIM_INF
- color=#FFFFFF
-info2:
- type=AttributeText
- attribute=Title
- display=0
- aligned=0
- x=33
- y=28
- color=#FFFFFF
-info3:
- type=AttributeText
- attribute=Genre
- display=0
- aligned=0
- x=33
- y=47
- color=#FFFFFF
-info4:
- type=AttributeText
- attribute=Release
- display=0
- aligned=0
- x=33
- y=66
- color=#FFFFFF
-info5:
- type=AttributeText
- attribute=Developer
- display=0
- aligned=0
- x=33
- y=85
- color=#FFFFFF
-info6:
- type=AttributeText
- attribute=#Size
- display=0
- aligned=0
- x=33
- y=104
- color=#FFFFFF
-info7:
- type=AttributeText
- attribute=Description
- display=0
- aligned=0
- x=33
- y=123
- width=574
- height=80
- wrap=1
- color=#FFFFFF
-info8:
- type=AttributeImage
- attribute=#Media
- width=42
- height=22
- x=55
- y=-242
-info9:
- type=AttributeImage
- attribute=#Format
- width=42
- height=22
- x=110
- y=-242
-info10:
- type=AttributeImage
- attribute=Vmode
- width=42
- height=22
- x=165
- y=-242
-info11:
- type=AttributeImage
- attribute=Aspect
- width=42
- height=22
- x=220
- y=-242
-info12:
- type=AttributeImage
- attribute=Scan
- width=42
- height=22
- x=275
- y=-242
-info13:
- type=AttributeImage
- attribute=Rating
- default=rating_0
- x=-74
- y=-242
-info14:
- type=AttributeImage
- attribute=Device
- x=-74
- y=38
-info15:
- type=GameImage
- pattern=SCR
- default=screenshot
- x=184
- y=-133
- width=173
- height=148
-info16:
- type=GameImage
- pattern=SCR2
- default=screenshot
- x=-186
- y=-133
- width=173
- height=148
-info17:
- type=InfoHintText
- aligned=1
- x=POS_MID
- color=#FFFFFF
\ No newline at end of file
+plasma_blend_color = "#FFFFFF";
+
+main0: {
+ type = "Background";
+ default = "settings_bg";
+ aligned = 0;
+ scaled = 0;
+ width = "DIM_INF";
+ height = "DIM_INF";
+};
+main1: {
+ type = "ItemsList";
+ x = 40;
+ y = 38;
+ width = 400;
+ height = 380;
+};
+main2: {
+ type = "StaticImage";
+ default = "lm_case_shadow";
+ x = -106;
+ y = -165;
+ skip = 1;
+};
+main3: {
+ type = "ItemCover";
+ default = "cover";
+ x = -106;
+ y = -296;
+ overlay = "lm_case";
+ overlay_ulx = 0;
+ overlay_uly = 9;
+ overlay_urx = 168;
+ overlay_ury = 19;
+ overlay_llx = 0;
+ overlay_lly = 244;
+ overlay_lrx = 168;
+ overlay_lry = 254;
+};
+appsMain3: {
+ type = "ItemCover";
+ default = "cover_app";
+ x = -106;
+ y = -224;
+ overlay = "lm_apps_case";
+ overlay_ulx = 0;
+ overlay_uly = 9;
+ overlay_urx = 168;
+ overlay_ury = 19;
+ overlay_llx = 0;
+ overlay_lly = 170;
+ overlay_lrx = 168;
+ overlay_lry = 180;
+};
+main4: {
+ type = "ItemIcon";
+ default = "disc";
+ x = -240;
+ y = -210;
+ wsX = -205;
+ width = 128;
+ height = 128;
+ reflection = 1;
+};
+appsMain4: {
+ type = "ItemIcon";
+ width = 0;
+ height = 0;
+};
+main5: {
+ type = "MenuIcon";
+ width = 180;
+ height = 120;
+ x = -1;
+ y = 333;
+ aligned = 2;
+};
+main6: {
+ type = "HintText";
+ aligned = 1;
+ x = "POS_MID";
+ color = "#FFFFFF";
+};
+main7: {
+ type = "BdmIndex";
+ width = 24;
+ height = 24;
+ y = 375;
+ x = -150;
+ wsX = -115;
+ aligned = 2;
+};
+main8: {
+ type = "LoadingIcon";
+ y = -80;
+ width = 32;
+ height = 32;
+};
+info0: {
+ type = "Background";
+ pattern = "BG";
+};
+info1: {
+ type = "StaticImage";
+ default = "info";
+ aligned = 0;
+ scaled = 0;
+ width = "DIM_INF";
+ height = "DIM_INF";
+};
+info2: {
+ type = "AttributeText";
+ attribute = "Title";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 28;
+ color = "#FFFFFF";
+};
+info3: {
+ type = "AttributeText";
+ attribute = "Genre";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 47;
+ color = "#FFFFFF";
+};
+info4: {
+ type = "AttributeText";
+ attribute = "Release";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 66;
+ color = "#FFFFFF";
+};
+info5: {
+ type = "AttributeText";
+ attribute = "Developer";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 85;
+ color = "#FFFFFF";
+};
+info6: {
+ type = "AttributeText";
+ attribute = "#Size";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 104;
+ color = "#FFFFFF";
+};
+info7: {
+ type = "AttributeText";
+ attribute = "Description";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 123;
+ width = 574;
+ height = 80;
+ wrap = 1;
+ color = "#FFFFFF";
+};
+info8: {
+ type = "AttributeImage";
+ attribute = "#Media";
+ width = 42;
+ height = 22;
+ x = 55;
+ y = -242;
+};
+info9: {
+ type = "AttributeImage";
+ attribute = "#Format";
+ width = 42;
+ height = 22;
+ x = 110;
+ y = -242;
+};
+info10: {
+ type = "AttributeImage";
+ attribute = "Aspect";
+ width = 42;
+ height = 22;
+ x = 220;
+ y = -242;
+};
+info11: {
+ type = "AttributeImage";
+ attribute = "Rating";
+ default = "Rating_0";
+ x = -74;
+ y = -242;
+};
+info12: {
+ type = "GameImage";
+ pattern = "SCR";
+ default = "screen";
+ x = 184;
+ y = -133;
+ width = 173;
+ height = 148;
+};
+info13: {
+ type = "GameImage";
+ pattern = "SCR2";
+ default = "screen";
+ x = -186;
+ y = -133;
+ width = 173;
+ height = 148;
+};
+info14: {
+ type = "InfoHintText";
+ aligned = 1;
+ x = "POS_MID";
+ color = "#FFFFFF";
+};
+info15: {
+ type = "AttributeImage";
+ attribute = "$PadEmu";
+ width = 42;
+ height = 22;
+ x = 275;
+ y = -242;
+};
+info16: {
+ type = "AttributeImage";
+ attribute = "$Cheat";
+ width = 42;
+ height = 22;
+ x = 330;
+ y = -242;
+};
+info17: {
+ type = "AttributeImage";
+ attribute = "$GSM";
+ width = 42;
+ height = 22;
+ x = 385;
+ y = -242;
+};
+info18: {
+ type = "AttributeImage";
+ attribute = "Region";
+ width = 42;
+ height = 22;
+ x = 165;
+ y = -242;
+};
+appsInfo3: {
+ type = "AttributeText";
+ attribute = "Version";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 47;
+ color = "#FFFFFF";
+};
+appsInfo7: {
+ type = "AttributeText";
+ attribute = "Description";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 161;
+ width = 574;
+ height = 61;
+ wrap = 1;
+ color = "#FFFFFF";
+};
+appsInfo10: {
+ type = "AttributeText";
+ attribute = "Package";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 123;
+ color = "#FFFFFF";
+};
+appsInfo11: {
+ type = "AttributeText";
+ attribute = "Source";
+ display = 0;
+ aligned = 0;
+ x = 33;
+ y = 142;
+ color = "#FFFFFF";
+};
diff --git a/neutrino_loader/Makefile b/neutrino_loader/Makefile
new file mode 100644
index 00000000..cf414f5e
--- /dev/null
+++ b/neutrino_loader/Makefile
@@ -0,0 +1,19 @@
+EE_LINKFILE := linkfile
+EE_CFLAGS = -D_EE -Os -G0 -Wall
+EE_CFLAGS += -fdata-sections -ffunction-sections
+EE_LDFLAGS = -Wl,-zmax-page-size=128
+EE_LDFLAGS += -s -Wl,--gc-sections
+
+EE_BIN = loader.elf
+
+EE_OBJS = loader.o
+
+EE_LIBS =
+
+all: $(EE_BIN)
+
+clean:
+ rm -f -r $(EE_OBJS) $(EE_BIN)
+
+include $(PS2SDK)/samples/Makefile.pref
+include $(PS2SDK)/samples/Makefile.eeglobal
diff --git a/neutrino_loader/linkfile b/neutrino_loader/linkfile
new file mode 100644
index 00000000..845234c3
--- /dev/null
+++ b/neutrino_loader/linkfile
@@ -0,0 +1,67 @@
+ENTRY(__start);
+
+MEMORY {
+ bios : ORIGIN = 0x00000000, LENGTH = 528K /* 0x00000000 - 0x00084000: BIOS memory */
+ bram : ORIGIN = 0x00084000, LENGTH = 496K /* 0x00084000 - 0x00100000: BIOS unused memory */
+ gram : ORIGIN = 0x00100000, LENGTH = 31M /* 0x00100000 - 0x02000000: GAME memory */
+}
+
+REGION_ALIAS("MAIN_REGION", bram);
+
+PHDRS {
+ text PT_LOAD;
+}
+
+SECTIONS {
+ .text : {
+ *(.text)
+ } >MAIN_REGION :text
+
+ .reginfo : { *(.reginfo) } >MAIN_REGION
+
+ .ctors ALIGN(16): {
+ KEEP(*crtbegin*.o(.ctors))
+ KEEP(*(EXCLUDE_FILE(*crtend*.o) .ctors))
+ KEEP(*(SORT(.ctors.*)))
+ KEEP(*(.ctors))
+ } >MAIN_REGION
+
+ .dtors ALIGN(16): {
+ KEEP(*crtbegin*.o(.dtors))
+ KEEP(*(EXCLUDE_FILE(*crtend*.o) .dtors))
+ KEEP(*(SORT(.dtors.*)))
+ KEEP(*(.dtors))
+ } >MAIN_REGION
+
+ .rodata ALIGN(128): {
+ *(.rodata)
+ } >MAIN_REGION
+
+ .data ALIGN(128): {
+ _fdata = . ;
+ *(.data)
+ SORT(CONSTRUCTORS)
+ } >MAIN_REGION
+
+ _gp = ALIGN(128) + 0x7ff0;
+ .lit4 ALIGN(128): { *(.lit4) } >MAIN_REGION
+ .lit8 ALIGN(128): { *(.lit8) } >MAIN_REGION
+
+ .sdata ALIGN(128): {
+ *(.sdata)
+ } >MAIN_REGION
+
+ .sbss ALIGN(128) : {
+ _fbss = . ;
+ *(.sbss)
+ } >MAIN_REGION
+
+ .bss ALIGN(128) : {
+ *(.bss)
+ } >MAIN_REGION
+
+ PROVIDE(_end = .);
+ PROVIDE(_heap_size = -1);
+ PROVIDE(_stack = .);
+ PROVIDE(_stack_size = ORIGIN(MAIN_REGION) + LENGTH(MAIN_REGION) - _stack);
+}
diff --git a/neutrino_loader/loader.c b/neutrino_loader/loader.c
new file mode 100644
index 00000000..a9159443
--- /dev/null
+++ b/neutrino_loader/loader.c
@@ -0,0 +1,98 @@
+/*
+# _____ ___ ____ ___ ____
+# ____| | ____| | | |____|
+# | ___| |____ ___| ____| | \ PS2DEV Open Source Project.
+#-----------------------------------------------------------------------
+# (c) 2020 Francisco Javier Trujillo Mata
+# Licenced under Academic Free License version 2.0
+# Review ps2sdk README & LICENSE files for further details.
+#
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+// Reduce binary size: these are weak in libc/src/init.c
+void _libcglue_init() {}
+void _libcglue_deinit() {}
+void _libcglue_args_parse(int argc, char **argv) {}
+
+DISABLE_PATCHED_FUNCTIONS();
+DISABLE_EXTRA_TIMERS_FUNCTIONS();
+PS2_DISABLE_AUTOSTART_PTHREAD();
+
+// Clear user memory (PS2Link (C) 2003 Tord Lindstrom / adresd)
+static void wipeUserMem(void)
+{
+ int i;
+ for (i = 0x100000; i < GetMemorySize(); i += 64) {
+ asm volatile("\tsq $0, 0(%0) \n"
+ "\tsq $0, 16(%0) \n"
+ "\tsq $0, 32(%0) \n"
+ "\tsq $0, 48(%0) \n" ::"r"(i));
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ static t_ExecData elfdata;
+ int ret, i;
+
+ elfdata.epc = 0;
+
+ // argv[0] = partition/prefix, or ""
+ // argv[1] = path to ELF
+ if (argc < 2)
+ return -EINVAL;
+
+ char *new_argv[argc - 1];
+ int fullPath_length = 1 + strlen(argv[0]) + strlen(argv[1]);
+ char fullPath[fullPath_length];
+
+ strcpy(fullPath, argv[0]);
+ strcat(fullPath, argv[1]);
+
+ // final new_argv[0] is partition + path to ELF
+ new_argv[0] = fullPath;
+
+ for (i = 2; i < argc; i++)
+ new_argv[i - 1] = argv[i];
+
+ SifInitRpc(0);
+
+ wipeUserMem();
+
+ FlushCache(0);
+
+ SifLoadFileInit();
+ ret = SifLoadElf(argv[1], &elfdata);
+ SifLoadFileExit();
+
+ if (ret == 0 && elfdata.epc != 0) {
+ /*
+ * Removed iopreset
+ *
+ * while(!SifIopReset(NULL, 0)){};
+ * while(!SifIopSync()){};
+ * SifInitRpc(0);
+ * SifLoadFileInit();
+ * SifLoadModule("rom0:SIO2MAN", 0, NULL);
+ * SifLoadModule("rom0:MCMAN", 0, NULL);
+ * SifLoadModule("rom0:MCSERV", 0, NULL);
+ * SifLoadFileExit();
+ */
+
+ SifExitRpc();
+
+ FlushCache(0);
+ FlushCache(2);
+
+ return ExecPS2((void *)elfdata.epc, (void *)elfdata.gp, argc - 1, new_argv);
+ } else {
+ SifExitRpc();
+ return -ENOENT;
+ }
+}
diff --git a/src/appsupport.c b/src/appsupport.c
index 24717703..641d9e19 100644
--- a/src/appsupport.c
+++ b/src/appsupport.c
@@ -7,7 +7,6 @@
#include "include/ioman.h"
#include "include/util.h"
#include "include/module.h"
-
#include "include/bdmsupport.h"
#include "include/ethsupport.h"
#include "include/hddsupport.h"
@@ -37,13 +36,11 @@ typedef struct
char path[APP_PATH_MAX + 1];
char boot[APP_BOOT_MAX + 1];
char argv1[APP_ARGV1_MAX + 1];
- u8 legacy;
} app_info_t;
static int appForceUpdate = 1;
static int appItemCount = 0;
-static config_set_t *configApps;
static app_info_t *appsList;
struct app_info_linked
@@ -60,40 +57,12 @@ int gAPPStartMode;
// App support stuff.
unsigned char shouldAppsUpdate;
-
static void appFreeList(void);
-static int oplScanApps(int (*callback)(const char *path, config_set_t *appConfig, void *arg), void *arg);
+static int oplScanApps(int (*callback)(const char *path, const char *cfgPath, void *arg), void *arg);
static int oplGetAppImage(const char *device, char *folder, int isRelative, char *value, char *suffix, GSTEXTURE *resultTex, short psm);
static int oplShouldAppsUpdate(void);
-static config_set_t *oplGetLegacyAppsConfig(void);
-static config_set_t *oplGetLegacyAppsInfo(char *name);
-static int oplPath2Mode(const char *path);
-
-static struct config_value_t *appGetConfigValue(int id)
-{
- struct config_value_t *cur = configApps->head;
-
- while (id--) {
- cur = cur->next;
- }
-
- return cur;
-}
-
-static char *appGetELFName(char *name)
-{
- // Looking for the ELF name
- char *pos = strrchr(name, '/');
- if (!pos)
- pos = strrchr(name, ':');
- if (pos) {
- return pos + 1;
- }
-
- return name;
-}
static float appGetELFSize(char *path)
{
@@ -142,8 +111,7 @@ static void appInit(item_list_t *itemList)
{
LOG("APPSUPPORT Init\n");
appForceUpdate = 1;
- configGetInt(configGetByType(CONFIG_OPL), "app_frames_delay", &appItemList.delay);
- configApps = oplGetLegacyAppsConfig();
+ appItemList.delay = gAPPFramesDelay;
appsList = NULL;
appItemList.enabled = 1;
}
@@ -157,9 +125,7 @@ item_list_t *appGetObject(int initOnly)
static int appNeedsUpdate(item_list_t *itemList)
{
- int update;
-
- update = 0;
+ int update = 0;
if (appForceUpdate) {
appForceUpdate = 0;
update = 1;
@@ -167,141 +133,197 @@ static int appNeedsUpdate(item_list_t *itemList)
if (oplShouldAppsUpdate())
update = 1;
- if (update)
- configApps = oplGetLegacyAppsConfig();
-
return update;
}
-static int addAppsLegacyList(struct app_info_linked **appsLinkedList)
+static void appCopyStr(char *dst, const char *src, size_t size)
{
- struct config_value_t *cur;
- struct app_info_linked *app;
- int count;
+ if (!dst || !size)
+ return;
- configClear(configApps);
- configRead(configApps);
+ if (!src)
+ src = "";
- count = 0;
- cur = configApps->head;
- while (cur != NULL) {
- if (*appsLinkedList == NULL) {
- *appsLinkedList = malloc(sizeof(struct app_info_linked));
- app = *appsLinkedList;
- app->next = NULL;
- } else {
- app = malloc(sizeof(struct app_info_linked));
- if (app != NULL) {
- app->next = *appsLinkedList;
- *appsLinkedList = app;
+ strncpy(dst, src, size - 1);
+ dst[size - 1] = '\0';
+}
+
+static void appStripValue(char *value)
+{
+ char *comment;
+ int len;
+
+ if (!value)
+ return;
+
+ comment = strchr(value, '#');
+ if (comment)
+ *comment = '\0';
+
+ len = strlen(value);
+ while (len > 0 && (value[len - 1] == '\r' || value[len - 1] == '\n' || value[len - 1] == ' ' || value[len - 1] == '\t'))
+ value[--len] = '\0';
+}
+
+typedef struct
+{
+ const char *key;
+ char *out;
+ size_t out_len;
+ int found;
+} app_key_value_t;
+
+static int appReadKeyValueLine(char *line, app_key_value_t *value)
+{
+ size_t key_len;
+ char *text;
+
+ key_len = strlen(value->key);
+ if (strncmp(line, value->key, key_len) != 0 || line[key_len] != '=')
+ return 0;
+
+ text = line + key_len + 1;
+ appStripValue(text);
+ appCopyStr(value->out, text, value->out_len);
+ value->found = 1;
+
+ return 1;
+}
+
+static int appReadKeyValueFile(const char *path, app_key_value_t *values, int count)
+{
+ int fd, size, i, found;
+ char buffer[4096];
+ char *line, *next;
+
+ if (!path || !values || count <= 0)
+ return 0;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return 0;
+
+ size = read(fd, buffer, sizeof(buffer) - 1);
+ close(fd);
+
+ if (size <= 0)
+ return 0;
+
+ buffer[size] = '\0';
+ found = 0;
+ line = buffer;
+
+ // Skip a leading UTF-8 BOM (EF BB BF) if a text editor left one..
+ if (size >= 3 && (unsigned char)line[0] == 0xEF && (unsigned char)line[1] == 0xBB && (unsigned char)line[2] == 0xBF)
+ line += 3;
+
+ while (line && *line) {
+ next = strchr(line, '\n');
+ if (next)
+ *next++ = '\0';
+
+ for (i = 0; i < count; i++) {
+ if (!values[i].found && appReadKeyValueLine(line, &values[i])) {
+ found++;
+ break;
}
}
- if (app == NULL) {
- LOG("APPSUPPORT unable to allocate memory.\n");
+ if (found == count)
break;
- }
- strncpy(app->app.title, cur->key, APP_TITLE_MAX + 1);
- app->app.title[APP_TITLE_MAX] = '\0';
-
- // Split the boot filename from the path.
- const char *elfname = appGetELFName(cur->val);
- if (elfname != cur->val) {
- strncpy(app->app.boot, elfname, APP_BOOT_MAX + 1);
- app->app.boot[APP_BOOT_MAX] = '\0';
-
- int pathlen = (int)(elfname - cur->val) - 1;
- if (cur->val[pathlen] == ':') // Discard only '/'.
- pathlen++;
- if (pathlen > APP_PATH_MAX)
- pathlen = APP_PATH_MAX;
- strncpy(app->app.path, cur->val, pathlen);
- app->app.path[pathlen] = '\0';
- } else {
- // Cannot split boot filename from the path, somehow.
- strncpy(app->app.boot, cur->val, APP_BOOT_MAX + 1);
- app->app.boot[APP_BOOT_MAX] = '\0';
- strncpy(app->app.path, cur->val, APP_PATH_MAX + 1);
- app->app.path[APP_PATH_MAX] = '\0';
- }
+ line = next;
+ }
+
+ return found;
+}
+
+static int appReadTitleCfg(const char *cfgPath, const char *path, app_info_t *info)
+{
+ app_key_value_t values[3];
+
+ if (!cfgPath || !path || !info)
+ return 0;
- app->app.legacy = 1;
- count++;
- cur = cur->next;
+ memset(info, 0, sizeof(*info));
+
+ values[0].key = APP_CONFIG_TITLE;
+ values[0].out = info->title;
+ values[0].out_len = sizeof(info->title);
+ values[0].found = 0;
+
+ values[1].key = APP_CONFIG_BOOT;
+ values[1].out = info->boot;
+ values[1].out_len = sizeof(info->boot);
+ values[1].found = 0;
+
+ values[2].key = APP_CONFIG_ARGV1;
+ values[2].out = info->argv1;
+ values[2].out_len = sizeof(info->argv1);
+ values[2].found = 0;
+
+ appReadKeyValueFile(cfgPath, values, 3);
+
+ if (!values[0].found || !values[1].found) {
+ LOG("APPSUPPORT item has no boot/title.\n");
+ return 0;
}
- return count;
+ appCopyStr(info->path, path, sizeof(info->path));
+
+ return 1;
}
-static int appScanCallback(const char *path, config_set_t *appConfig, void *arg)
+static int appScanCallback(const char *path, const char *cfgPath, void *arg)
{
struct app_info_linked **appsLinkedList = (struct app_info_linked **)arg;
struct app_info_linked *app;
- const char *title, *boot, *argv1;
+ app_info_t info;
- if (configGetStr(appConfig, APP_CONFIG_TITLE, &title) != 0 && configGetStr(appConfig, APP_CONFIG_BOOT, &boot) != 0) {
- if (*appsLinkedList == NULL) {
- *appsLinkedList = malloc(sizeof(struct app_info_linked));
- app = *appsLinkedList;
- app->next = NULL;
- } else {
- app = malloc(sizeof(struct app_info_linked));
- if (app != NULL) {
- app->next = *appsLinkedList;
- *appsLinkedList = app;
- }
- }
+ if (!appReadTitleCfg(cfgPath, path, &info))
+ return 1;
- if (app == NULL) {
- LOG("APPSUPPORT unable to allocate memory.\n");
- return -1;
+ if (*appsLinkedList == NULL) {
+ *appsLinkedList = malloc(sizeof(struct app_info_linked));
+ app = *appsLinkedList;
+ if (app)
+ app->next = NULL;
+ } else {
+ app = malloc(sizeof(struct app_info_linked));
+ if (app != NULL) {
+ app->next = *appsLinkedList;
+ *appsLinkedList = app;
}
+ }
- strncpy(app->app.title, title, APP_TITLE_MAX + 1);
- app->app.title[APP_TITLE_MAX] = '\0';
- strncpy(app->app.boot, boot, APP_BOOT_MAX + 1);
- app->app.boot[APP_BOOT_MAX] = '\0';
- strncpy(app->app.path, path, APP_PATH_MAX + 1);
- app->app.path[APP_PATH_MAX] = '\0';
- if (configGetStr(appConfig, APP_CONFIG_ARGV1, &argv1) != 0) {
- strncpy(app->app.argv1, argv1, APP_ARGV1_MAX + 1);
- app->app.argv1[APP_ARGV1_MAX] = '\0';
- } else
- app->app.argv1[0] = '\0';
- app->app.legacy = 0;
- return 0;
- } else {
- LOG("APPSUPPORT item has no boot/title.\n");
- return 1;
+ if (app == NULL) {
+ LOG("APPSUPPORT unable to allocate memory.\n");
+ return -1;
}
- return -1;
+ memcpy(&app->app, &info, sizeof(app_info_t));
+
+ return 0;
}
static int appUpdateItemList(item_list_t *itemList)
{
struct app_info_linked *appsLinkedList, *appNext;
- appFreeList();
+ guiSetBootStatusIfActive("Scanning apps...");
+ appFreeList();
appsLinkedList = NULL;
- // Get legacy apps list first, so it is possible to use appGetConfigValue(id).
- appItemCount += addAppsLegacyList(&appsLinkedList);
-
- // Scan devices for apps.
appItemCount += oplScanApps(&appScanCallback, &appsLinkedList);
- // Generate apps list
if (appItemCount > 0) {
appsList = malloc(appItemCount * sizeof(app_info_t));
if (appsList != NULL) {
int i;
- for (i = 0; appsLinkedList != NULL; i++) { // appsLinkedList contains items in reverse order.
+ for (i = 0; appsLinkedList != NULL; i++) {
memcpy(&appsList[appItemCount - i - 1], &appsLinkedList->app, sizeof(app_info_t));
-
appNext = appsLinkedList->next;
free(appsLinkedList);
appsLinkedList = appNext;
@@ -318,7 +340,6 @@ static int appUpdateItemList(item_list_t *itemList)
}
LOG("APPSUPPORT %d apps loaded\n", appItemCount);
-
return appItemCount;
}
@@ -343,98 +364,129 @@ static char *appGetItemName(item_list_t *itemList, int id)
static int appGetItemNameLength(item_list_t *itemList, int id)
{
- return CONFIG_KEY_NAME_LEN;
+ return APP_TITLE_MAX;
}
-/* appGetItemStartup() is called to get the startup path for display & for the art assets.
- The path is used immediately, before a subsequent call to appGetItemStartup(). */
static char *appGetItemStartup(item_list_t *itemList, int id)
{
- if (appsList[id].legacy) {
- struct config_value_t *cur = appGetConfigValue(id);
- return appGetELFName(cur->val);
- } else {
- return appsList[id].boot;
- }
+ return appsList[id].boot;
}
static void appDeleteItem(item_list_t *itemList, int id)
{
- if (appsList[id].legacy) {
- struct config_value_t *cur = appGetConfigValue(id);
- unlink(cur->val);
- cur->key[0] = '\0';
- configApps->modified = 1;
- configWrite(configApps);
- } else {
- sysDeleteFolder(appsList[id].path);
- }
-
+ sysDeleteFolder(appsList[id].path);
appForceUpdate = 1;
}
-static void appRenameItem(item_list_t *itemList, int id, char *newName)
+static int appAppendText(char *out, size_t out_len, size_t *pos, const char *text)
{
- char value[256];
-
- if (appsList[id].legacy) {
- struct config_value_t *cur = appGetConfigValue(id);
+ size_t len;
- strncpy(value, cur->val, sizeof(value));
- configRemoveKey(configApps, cur->key);
- configSetStr(configApps, newName, value);
- configWrite(configApps);
- } else {
- config_set_t *appConfig;
-
- snprintf(value, sizeof(value), "%s/%s", appsList[id].path, APP_TITLE_CONFIG_FILE);
+ if (!out || !pos || !text)
+ return 0;
- appConfig = configAlloc(0, NULL, value);
- if (appConfig != NULL) {
- configRead(appConfig);
- configSetStr(appConfig, APP_CONFIG_TITLE, newName);
- configWrite(appConfig);
+ len = strlen(text);
+ if (*pos + len >= out_len)
+ return 0;
- configFree(appConfig);
- }
- }
+ memcpy(out + *pos, text, len);
+ *pos += len;
+ out[*pos] = '\0';
- appForceUpdate = 1;
+ return 1;
}
-static void appLaunchItem(item_list_t *itemList, int id, config_set_t *configSet)
+static int appUpdateTitleCfgTitle(const char *cfgPath, const char *newName)
{
- int fd;
- char filename[256];
- const char *argv1;
+ int fd, size, found;
+ char inbuf[1024];
+ char outbuf[1024];
+ char tmpPath[300];
+ char *line, *next;
+ size_t pos;
+
+ if (!cfgPath || !newName)
+ return 0;
+
+ fd = open(cfgPath, O_RDONLY);
+ if (fd >= 0) {
+ size = read(fd, inbuf, sizeof(inbuf) - 1);
+ close(fd);
+ if (size < 0)
+ size = 0;
+ } else
+ size = 0;
+
+ inbuf[size] = '\0';
+ outbuf[0] = '\0';
+ pos = 0;
+ found = 0;
+ line = inbuf;
+
+ while (line && *line) {
+ next = strchr(line, '\n');
+ if (next)
+ *next++ = '\0';
+
+ if (strncmp(line, APP_CONFIG_TITLE "=", sizeof(APP_CONFIG_TITLE)) == 0) {
+ if (!appAppendText(outbuf, sizeof(outbuf), &pos, APP_CONFIG_TITLE "=") || !appAppendText(outbuf, sizeof(outbuf), &pos, newName) || !appAppendText(outbuf, sizeof(outbuf), &pos, "\n"))
+ return 0;
+ found = 1;
+ } else {
+ if (!appAppendText(outbuf, sizeof(outbuf), &pos, line) || !appAppendText(outbuf, sizeof(outbuf), &pos, "\n"))
+ return 0;
+ }
- // Retrieve configuration set by appGetConfig()
- configGetStrCopy(configSet, CONFIG_ITEM_STARTUP, filename, sizeof(filename));
+ line = next;
+ }
- // If no device number is specified use mass? to auto find device number
- const char *oldPrefix = "mass:";
- const char *newPrefix = "mass?:";
+ if (!found) {
+ if (!appAppendText(outbuf, sizeof(outbuf), &pos, APP_CONFIG_TITLE "=") || !appAppendText(outbuf, sizeof(outbuf), &pos, newName) || !appAppendText(outbuf, sizeof(outbuf), &pos, "\n"))
+ return 0;
+ }
- if (strncmp(filename, oldPrefix, strlen(oldPrefix)) == 0) {
- size_t oldPrefixLen = strlen(oldPrefix);
- size_t newPrefixLen = strlen(newPrefix);
- memmove(filename + newPrefixLen, filename + oldPrefixLen, strlen(filename) - oldPrefixLen + 1);
+ snprintf(tmpPath, sizeof(tmpPath), "%s.tmp", cfgPath);
+ fd = open(tmpPath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0)
+ return 0;
- memcpy(filename, newPrefix, newPrefixLen);
+ if (write(fd, outbuf, pos) != (int)pos) {
+ close(fd);
+ remove(tmpPath);
+ return 0;
}
- // If legacy apps state mass? find the first connected mass device with the corresponding filename and set the unit number for launch.
- if (!strncmp("mass?", filename, 5)) {
- for (int i = 0; i < BDM_MODE4; i++) {
- filename[4] = i + '0';
- fd = open(filename, O_RDONLY);
- if (fd >= 0) {
- close(fd);
- break;
- }
+ close(fd);
+
+ if (rename(tmpPath, cfgPath) != 0) {
+ remove(cfgPath);
+ if (rename(tmpPath, cfgPath) != 0) {
+ remove(tmpPath);
+ return 0;
}
}
+ return 1;
+}
+
+static void appRenameItem(item_list_t *itemList, int id, char *newName)
+{
+ char cfgPath[256];
+ snprintf(cfgPath, sizeof(cfgPath), "%s/%s", appsList[id].path, APP_TITLE_CONFIG_FILE);
+
+ appUpdateTitleCfgTitle(cfgPath, newName);
+
+ appForceUpdate = 1;
+}
+
+static void appLaunchItem(item_list_t *itemList, int id, per_game_cfg_t *pgcfg)
+{
+ int fd;
+ char filename[256];
+
+ snprintf(filename, sizeof(filename), "%s/%s", appsList[id].path, appsList[id].boot);
+ filename[sizeof(filename) - 1] = '\0';
+
fd = open(filename, O_RDONLY);
if (fd >= 0) {
int mode, argc = 0;
@@ -442,64 +494,93 @@ static void appLaunchItem(item_list_t *itemList, int id, config_set_t *configSet
char *argv[1];
close(fd);
- strcpy(partition, "");
+ partition[0] = '\0';
- // To keep the necessary device accessible, we will assume the mode that owns the device which contains the file to boot.
- mode = oplPath2Mode(filename);
+ mode = sbGetPathMode(filename);
if (mode < 0)
- mode = APP_MODE; // Legacy apps mode on memory card (mc?:/*)
+ mode = APP_MODE;
if (mode == HDD_MODE)
snprintf(partition, sizeof(partition), "%s:", gOPLPart);
- if (configGetStr(configSet, CONFIG_ITEM_ALTSTARTUP, &argv1) != 0) {
- argv[0] = (char *)argv1;
+ if (appsList[id].argv1[0]) {
+ argv[0] = appsList[id].argv1;
argc = 1;
}
- deinit(UNMOUNT_EXCEPTION, mode); // CAREFUL: deinit will call appCleanUp, so configApps/cur will be freed
+ deinit(UNMOUNT_EXCEPTION, mode);
LoadELFFromFileWithPartition(filename, partition, argc, argv);
} else
guiMsgBox(_l(_STR_ERR_FILE_INVALID), 0, NULL);
}
-static config_set_t *appGetConfig(item_list_t *itemList, int id)
+static void appGetInfo(item_list_t *itemList, int id, game_info_t *gi)
{
- config_set_t *config;
- char tmp[8];
+ char cfgPath[256];
+ app_key_value_t values[7];
- if (appsList[id].legacy) {
- struct config_value_t *cur = appGetConfigValue(id);
- config = oplGetLegacyAppsInfo(appGetELFName(cur->val));
- configRead(config);
+ memset(gi, 0, sizeof(*gi));
- configSetStr(config, CONFIG_ITEM_NAME, appGetELFName(cur->val));
- configSetStr(config, CONFIG_ITEM_LONGNAME, cur->key);
- configSetStr(config, CONFIG_ITEM_STARTUP, cur->val);
- configSetStr(config, CONFIG_ITEM_MEDIA, "APP");
- configSetStr(config, CONFIG_ITEM_FORMAT, "ELF");
+ snprintf(cfgPath, sizeof(cfgPath), "%s/%s", appsList[id].path, APP_TITLE_CONFIG_FILE);
- snprintf(tmp, sizeof(tmp), "%.2f", appGetELFSize(cur->val));
- configSetStr(config, CONFIG_ITEM_SIZE, tmp);
- } else {
- char path[256];
- snprintf(path, sizeof(path), "%s/%s", appsList[id].path, APP_TITLE_CONFIG_FILE);
-
- config = configAlloc(0, NULL, path);
- configRead(config); // Does not matter if the config file could be loaded or not.
-
- configSetStr(config, CONFIG_ITEM_NAME, appsList[id].boot);
- configSetStr(config, CONFIG_ITEM_LONGNAME, appsList[id].title);
- configSetStr(config, CONFIG_ITEM_ALTSTARTUP, appsList[id].argv1); // reuse AltStartup for argument 1
- snprintf(path, sizeof(path), "%s/%s", appsList[id].path, appsList[id].boot);
- configSetStr(config, CONFIG_ITEM_STARTUP, path);
- configSetStr(config, CONFIG_ITEM_MEDIA, "APP");
- configSetStr(config, CONFIG_ITEM_FORMAT, "ELF");
-
- snprintf(tmp, sizeof(tmp), "%.2f", appGetELFSize(path));
- configSetStr(config, CONFIG_ITEM_SIZE, tmp);
+ values[0].key = "Title";
+ values[0].out = gi->title;
+ values[0].out_len = sizeof(gi->title);
+ values[0].found = 0;
+
+ values[1].key = "Description";
+ values[1].out = gi->description;
+ values[1].out_len = sizeof(gi->description);
+ values[1].found = 0;
+
+ values[2].key = "Developer";
+ values[2].out = gi->developer;
+ values[2].out_len = sizeof(gi->developer);
+ values[2].found = 0;
+
+ values[3].key = "Release";
+ values[3].out = gi->release;
+ values[3].out_len = sizeof(gi->release);
+ values[3].found = 0;
+
+ values[4].key = "Version";
+ values[4].out = gi->version;
+ values[4].out_len = sizeof(gi->version);
+ values[4].found = 0;
+
+ values[5].key = "Package";
+ values[5].out = gi->package;
+ values[5].out_len = sizeof(gi->package);
+ values[5].found = 0;
+
+ values[6].key = "Source";
+ values[6].out = gi->source;
+ values[6].out_len = sizeof(gi->source);
+ values[6].found = 0;
+
+ appReadKeyValueFile(cfgPath, values, 7);
+
+ // fall back to menu title if no display Title set
+ if (!gi->title[0]) {
+ strncpy(gi->title, appsList[id].title, sizeof(gi->title) - 1);
+ gi->title[sizeof(gi->title) - 1] = '\0';
}
- return config;
+}
+
+static void appGetPgCfg(item_list_t *itemList, int id, per_game_cfg_t *cfg)
+{
+ memset(cfg, 0, sizeof(*cfg));
+ cfg->dma = 7;
+ strcpy(cfg->format, "ELF");
+ strcpy(cfg->media, "APP");
+ char path[256];
+ snprintf(path, sizeof(path), "%s/%s", appsList[id].path, appsList[id].boot);
+ cfg->size_mb = (int)appGetELFSize(path);
+}
+
+static int appSavePgCfg(item_list_t *itemList, int id, const per_game_cfg_t *cfg)
+{
+ return 1;
}
static int appGetImage(item_list_t *itemList, char *folder, int isRelative, char *value, char *suffix, GSTEXTURE *resultTex, short psm)
@@ -533,7 +614,7 @@ static int appGetTextId(item_list_t *itemList)
static int appGetIconId(item_list_t *itemList)
{
- return CATEGORY_APPS_ICON;
+ return APP_ICON;
}
// This may be called, even if appInit() was not.
@@ -559,14 +640,13 @@ static void appShutdown(item_list_t *itemList)
static item_list_t appItemList = {
APP_MODE, -1, 0, MODE_FLAG_NO_COMPAT | MODE_FLAG_NO_UPDATE, MENU_MIN_INACTIVE_FRAMES, APP_MODE_UPDATE_DELAY, NULL, NULL, &appGetTextId, NULL, &appInit, &appNeedsUpdate, &appUpdateItemList,
&appGetItemCount, NULL, &appGetItemName, &appGetItemNameLength, &appGetItemStartup, &appDeleteItem, &appRenameItem, &appLaunchItem,
- &appGetConfig, &appGetImage, &appGetArchivedImage, &appCleanUp, &appShutdown, NULL, &appGetIconId};
+ &appGetInfo, &appGetPgCfg, &appSavePgCfg, &appGetImage, &appGetArchivedImage, &appCleanUp, &appShutdown, NULL, &appGetIconId};
-static int scanApps(int (*callback)(const char *path, config_set_t *appConfig, void *arg), void *arg, char *appsPath, int exception)
+static int scanApps(int (*callback)(const char *path, const char *cfgPath, void *arg), void *arg, char *appsPath, int exception)
{
struct dirent *pdirent;
DIR *pdir;
int count, ret;
- config_set_t *appConfig;
char dir[128];
char path[128];
@@ -584,19 +664,12 @@ static int scanApps(int (*callback)(const char *path, config_set_t *appConfig, v
continue;
snprintf(path, sizeof(path), "%s/%s", dir, APP_TITLE_CONFIG_FILE);
- appConfig = configAlloc(0, NULL, path);
- if (appConfig != NULL) {
- configRead(appConfig);
-
- ret = callback(dir, appConfig, arg);
- configFree(appConfig);
-
- if (ret == 0)
- count++;
- else if (ret < 0) { // Stopped because of unrecoverable error.
- break;
- }
- }
+ ret = callback(dir, path, arg);
+
+ if (ret == 0)
+ count++;
+ else if (ret < 0)
+ break;
}
closedir(pdir);
@@ -606,7 +679,7 @@ static int scanApps(int (*callback)(const char *path, config_set_t *appConfig, v
return count;
}
-static int oplScanApps(int (*callback)(const char *path, config_set_t *appConfig, void *arg), void *arg)
+static int oplScanApps(int (*callback)(const char *path, const char *cfgPath, void *arg), void *arg)
{
int i, count;
item_list_t *listSupport;
@@ -638,7 +711,7 @@ static int oplGetAppImage(const char *device, char *folder, int isRelative, char
elfbootmode = -1;
if (device != NULL) {
- elfbootmode = oplPath2Mode(device);
+ elfbootmode = sbGetPathMode(device);
if (elfbootmode >= 0) {
listSupport = list_support[elfbootmode].support;
@@ -677,96 +750,3 @@ static int oplShouldAppsUpdate(void)
return result;
}
-
-static config_set_t *oplGetLegacyAppsConfig(void)
-{
- int i, fd;
- item_list_t *listSupport;
- config_set_t *appConfig;
- char appsPath[128];
-
- snprintf(appsPath, sizeof(appsPath), "mc?:wOPL/conf_apps.cfg");
- fd = sbOpenFile(appsPath, O_RDONLY);
- if (fd >= 0) {
- appConfig = configAlloc(CONFIG_APPS, NULL, appsPath);
- close(fd);
- return appConfig;
- }
-
- for (i = MODE_COUNT - 1; i >= 0; i--) {
- listSupport = list_support[i].support;
- if ((listSupport != NULL) && (listSupport->enabled) && (listSupport->itemGetPrefix != NULL)) {
- char *prefix = listSupport->itemGetPrefix(listSupport);
- snprintf(appsPath, sizeof(appsPath), "%sconf_apps.cfg", prefix);
-
- fd = sbOpenFile(appsPath, O_RDONLY);
- if (fd >= 0) {
- appConfig = configAlloc(CONFIG_APPS, NULL, appsPath);
- close(fd);
- return appConfig;
- }
- }
- }
-
- /* Apps config not found on any device, go with last tested device.
- Does not matter if the config file could be loaded or not */
- appConfig = configAlloc(CONFIG_APPS, NULL, appsPath);
-
- return appConfig;
-}
-
-static config_set_t *oplGetLegacyAppsInfo(char *name)
-{
- int i, fd;
- item_list_t *listSupport;
- config_set_t *appConfig;
- char appsPath[128];
-
- for (i = MODE_COUNT - 1; i >= 0; i--) {
- listSupport = list_support[i].support;
- if ((listSupport != NULL) && (listSupport->enabled) && (listSupport->itemGetPrefix != NULL)) {
- char *prefix = listSupport->itemGetPrefix(listSupport);
- snprintf(appsPath, sizeof(appsPath), "%sCFG%s%s.cfg", prefix, i == ETH_MODE ? "\\" : "/", name);
-
- fd = sbOpenFile(appsPath, O_RDONLY);
- if (fd >= 0) {
- appConfig = configAlloc(0, NULL, appsPath);
- close(fd);
- return appConfig;
- }
- }
- }
-
- /* Apps config not found on any device, go with last tested device.
- Does not matter if the config file could be loaded or not */
- appConfig = configAlloc(0, NULL, appsPath);
-
- return appConfig;
-}
-
-// For resolving the mode, given an app's path
-static int oplPath2Mode(const char *path)
-{
- char appsPath[64];
- const char *blkdevnameend;
- int i, blkdevnamelen;
- item_list_t *listSupport;
-
- for (i = 0; i < MODE_COUNT; i++) {
- listSupport = list_support[i].support;
- if ((listSupport != NULL) && (listSupport->itemGetPrefix != NULL)) {
- char *prefix = listSupport->itemGetPrefix(listSupport);
- snprintf(appsPath, sizeof(appsPath), "%sAPPS", prefix);
-
- blkdevnameend = strchr(appsPath, ':');
- if (blkdevnameend != NULL) {
- blkdevnamelen = (int)(blkdevnameend - appsPath);
-
- if (strncmp(path, appsPath, blkdevnamelen) == 0)
- return listSupport->mode;
- }
- }
- }
-
- return -1;
-}
\ No newline at end of file
diff --git a/src/art_tar.c b/src/art_tar.c
deleted file mode 100644
index 95f12eff..00000000
--- a/src/art_tar.c
+++ /dev/null
@@ -1,366 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-
-#include "include/art_tar.h"
-
-static ArtTarEntry *s_tarIndex = NULL;
-static u32 s_tarCount = 0;
-static int s_tarFd = -1;
-
-static u64 parseOctal(const char *s, int len)
-{
- u64 val = 0;
- for (int i = 0; i < len; i++) {
- char c = s[i];
- if (c == '\0' || c == ' ')
- break;
- if (c < '0' || c > '7')
- break;
- val = (val << 3) + (u64)(c - '0');
- }
- return val;
-}
-
-static int isZeroBlock(const unsigned char header[TAR_BLOCK_SIZE])
-{
- for (int i = 0; i < TAR_BLOCK_SIZE; i++) {
- if (header[i] != 0)
- return 0;
- }
- return 1;
-}
-
-ArtTarEntry *findTarEntry(const char *filename)
-{
- if (!filename || !s_tarIndex || s_tarCount == 0)
- return NULL;
-
- for (u32 i = 0; i < s_tarCount; i++) {
- if (strcasecmp(s_tarIndex[i].filename, filename) == 0)
- return &s_tarIndex[i];
- }
- return NULL;
-}
-
-int closeTarFile(void)
-{
- if (s_tarFd >= 0) {
- close(s_tarFd);
- s_tarFd = -1;
- }
- free(s_tarIndex);
- s_tarIndex = NULL;
- s_tarCount = 0;
- return 0;
-}
-
-static int readTarCache(const char *cachePath, const char *tarPath)
-{
- int fd = open(cachePath, O_RDONLY);
- if (fd < 0)
- return -1;
-
- struct stat stCache;
- if (fstat(fd, &stCache) < 0) {
- close(fd);
- return -1;
- }
-
- int fileSize = stCache.st_size;
- if (fileSize < (int)sizeof(ArtCacheHeader)) {
- close(fd);
- return -1;
- }
-
- void *buf = memalign(64, fileSize);
- if (!buf) {
- close(fd);
- return -1;
- }
-
- int bytesRead = read(fd, buf, fileSize);
- close(fd);
- if (bytesRead != fileSize) {
- free(buf);
- return -1;
- }
-
- unsigned char *p = (unsigned char *)buf;
- ArtCacheHeader *hdr = (ArtCacheHeader *)p;
-
- if (memcmp(hdr->magic, ARC_MAGIC, 4) != 0 ||
- hdr->version != ARC_VERSION) {
- free(buf);
- return -1;
- }
-
- struct stat stTar;
- if (stat(tarPath, &stTar) < 0 || (u64)stTar.st_size != hdr->tarSize) {
- free(buf);
- return -1;
- }
-
- u32 entryCount = hdr->entryCount;
- int expectedSize = sizeof(ArtCacheHeader) + entryCount * sizeof(ArtTarEntry);
-
- if (fileSize < expectedSize) {
- free(buf);
- return -1;
- }
-
- ArtTarEntry *entries = malloc(sizeof(ArtTarEntry) * entryCount);
- if (!entries) {
- free(buf);
- return -1;
- }
-
- ArtTarEntry *src = (ArtTarEntry *)(p + sizeof(ArtCacheHeader));
- memcpy(entries, src, sizeof(ArtTarEntry) * entryCount);
-
- s_tarIndex = entries;
- s_tarCount = entryCount;
-
- free(buf);
- return 0;
-}
-
-static int writeTarCache(const char *cachePath, const char *tarPath)
-{
- if (!s_tarIndex || s_tarCount == 0)
- return -1;
-
- struct stat stTar;
- if (stat(tarPath, &stTar) < 0)
- return -1;
-
- ArtCacheHeader hdr;
- memcpy(hdr.magic, ARC_MAGIC, 4);
- hdr.version = ARC_VERSION;
- hdr.tarSize = (u64)stTar.st_size;
- hdr.entryCount = s_tarCount;
-
- int fd = open(cachePath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
- if (fd < 0)
- return -1;
-
- if (write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
- close(fd);
- return -1;
- }
-
- int entriesSize = sizeof(ArtTarEntry) * s_tarCount;
- if (write(fd, s_tarIndex, entriesSize) != entriesSize) {
- close(fd);
- return -1;
- }
-
- close(fd);
- return 0;
-}
-
-int loadTarFile(const char *path)
-{
- struct stat st;
- if (stat(path, &st) < 0) {
- return -1;
- }
-
- char dirPath[256];
- strncpy(dirPath, path, sizeof(dirPath));
- dirPath[sizeof(dirPath) - 1] = '\0';
-
- int len = strlen(dirPath);
- for (int i = len - 1; i >= 0; i--) {
- if (dirPath[i] == '/') {
- dirPath[i + 1] = '\0';
- break;
- }
- }
-
- const char *cachePath = "art_cache.bin";
-
- char fullCachePath[256];
- snprintf(fullCachePath, sizeof(fullCachePath), "%s%s", dirPath, cachePath);
-
- if (s_tarFd >= 0 || s_tarIndex)
- closeTarFile();
-
- if (readTarCache(fullCachePath, path) == 0) {
- s_tarFd = open(path, O_RDONLY);
- return (s_tarFd >= 0) ? 0 : -1;
- }
-
- s_tarFd = open(path, O_RDONLY);
- if (s_tarFd < 0)
- return -1;
-
- u32 count = 0;
- lseek64(s_tarFd, 0, SEEK_SET);
-
- while (1) {
- unsigned char header[TAR_BLOCK_SIZE];
- int bytesRead = read(s_tarFd, header, TAR_BLOCK_SIZE);
- if (bytesRead == 0)
- break;
- if (bytesRead != TAR_BLOCK_SIZE)
- goto fail;
-
- if (isZeroBlock(header))
- break;
-
- u64 rawSize64 = parseOctal((const char *)header + 124, 12);
- u64 paddedSize64 = ((rawSize64 + (TAR_BLOCK_SIZE - 1)) / TAR_BLOCK_SIZE) * TAR_BLOCK_SIZE;
-
- if (rawSize64 > MAX_FILE_SIZE || paddedSize64 > MAX_FILE_SIZE)
- goto fail;
-
- count++;
-
- if (lseek64(s_tarFd, paddedSize64, SEEK_CUR) == (u64)-1)
- goto fail;
- }
-
- s_tarIndex = malloc(count * sizeof(ArtTarEntry));
- if (!s_tarIndex)
- goto fail;
-
- s_tarCount = count;
-
- lseek64(s_tarFd, 0, SEEK_SET);
-
- u32 idx = 0;
-
- static const u64 SPACE_MASK =
- ((u64)0x20 << 56) |
- ((u64)0x20 << 48) |
- ((u64)0x20 << 40) |
- ((u64)0x20 << 32) |
- ((u64)0x20 << 24) |
- ((u64)0x20 << 16) |
- ((u64)0x20 << 8) |
- ((u64)0x20 << 0);
-
- while (idx < count) {
- unsigned char header[TAR_BLOCK_SIZE];
- int bytesRead = read(s_tarFd, header, TAR_BLOCK_SIZE);
- if (bytesRead != TAR_BLOCK_SIZE)
- goto fail;
-
- if (isZeroBlock(header))
- break;
-
- u64 rawSize64 = parseOctal((const char *)header + 124, 12);
- u64 paddedSize64 = ((rawSize64 + (TAR_BLOCK_SIZE - 1)) / TAR_BLOCK_SIZE) * TAR_BLOCK_SIZE;
-
- u64 dataOffset = lseek64(s_tarFd, 0, SEEK_CUR);
- if (dataOffset == (u64)-1)
- goto fail;
-
- ArtTarEntry *entry = &s_tarIndex[idx];
-
- {
- const u8 *src = header;
- u8 *dst = entry->filename;
-
- memset(dst, 0, TAR_NAME_FIELD_SIZE);
- memcpy(dst, src, TAR_NAME_FIELD_SIZE);
-
- int n = TAR_NAME_FIELD_SIZE;
-
- while (n >= 8) {
- u64 chunk;
- memcpy(&chunk, dst + n - 8, 8);
-
- u64 tmp = chunk | SPACE_MASK;
- u64 cmp = tmp ^ SPACE_MASK;
-
- if (cmp != 0) {
- int i = 7;
- while (i >= 0) {
- u8 c = (chunk >> (i * 8)) & 0xFF;
- if (c != 0 && c != ' ') {
- n = (n - 8) + i + 1;
- goto done;
- }
- i--;
- }
- }
-
- n -= 8;
- }
-
- while (n > 0) {
- u8 c = dst[n - 1];
- if (c != 0 && c != ' ')
- break;
- n--;
- }
-
- done:
- if (n < TAR_NAME_FIELD_SIZE)
- dst[n] = '\0';
- else
- dst[TAR_NAME_FIELD_SIZE - 1] = '\0';
- }
-
- entry->offset = dataOffset;
- entry->rawSize = (u32)rawSize64;
- entry->paddedSize = (u32)paddedSize64;
-
- idx++;
-
- if (lseek64(s_tarFd, paddedSize64, SEEK_CUR) == (u64)-1)
- goto fail;
- }
-
- writeTarCache(fullCachePath, path);
- return 0;
-
-fail:
- closeTarFile();
- return -1;
-}
-
-int hasTarEntry(const char *filename)
-{
- return findTarEntry(filename) ? 1 : 0;
-}
-
-u32 readFileFromTar(const ArtTarEntry *entry, void *dst, u32 dstSize)
-{
- if (!entry || s_tarFd < 0 || !dst)
- return 0;
-
- if (dstSize < entry->rawSize)
- return 0;
-
- if (lseek64(s_tarFd, entry->offset, SEEK_SET) != entry->offset)
- return 0;
-
- int bytesRead = read(s_tarFd, dst, entry->rawSize);
- if (bytesRead != (int)entry->rawSize)
- return 0;
-
- return (u32)bytesRead;
-}
-
-void *getFileFromTar(const char *filename)
-{
- ArtTarEntry *entry = findTarEntry(filename);
- if (!entry)
- return NULL;
-
- void *buffer = malloc(entry->rawSize);
- if (!buffer)
- return NULL;
-
- u32 bytesRead = readFileFromTar(entry, buffer, entry->rawSize);
- if (bytesRead != entry->rawSize) {
- free(buffer);
- return NULL;
- }
- return buffer;
-}
\ No newline at end of file
diff --git a/src/bdmsupport.c b/src/bdmsupport.c
index 19f488a6..fa93de8c 100644
--- a/src/bdmsupport.c
+++ b/src/bdmsupport.c
@@ -1,4 +1,3 @@
-
#include "include/common.h"
#include "include/lang.h"
#include "include/gui.h"
@@ -11,12 +10,14 @@
#include "include/ioman.h"
#include "include/system.h"
#include "include/extern_irx.h"
+#include "include/tar.h"
#include "include/cheatman.h"
#include "include/sound.h"
-#include "include/art_tar.h"
#include "modules/iopcore/common/cdvd_config.h"
#include "include/module.h"
#include "include/initializer.h"
+#include "include/config_wopl.h"
+#include "include/pathsupport.h"
#include
#include
#include
@@ -35,6 +36,8 @@
#define BDM_MODE_UPDATE_DELAY MENU_UPD_DELAY_GENREFRESH
+#define MAX_BDM_TRUE_DEVICES MAX_BDM_DEVICES
+
#include "include/mcemu.h"
typedef struct
@@ -45,13 +48,14 @@ typedef struct
vmc_spec_t specs; /* Card specifications */
} bdm_vmc_infos_t;
-static int iUSBModLoaded = 0;
+static int bdmModulesLoaded = 0;
+static int usbModLoaded = 0;
static int iLinkModLoaded = 0;
static int mx4sioModLoaded = 0;
static int hddModLoaded = 0;
static s32 bdmLoadModuleLock;
-static item_list_t bdmDeviceList[MAX_BDM_DEVICES];
+static item_list_t bdmDeviceList[MAX_BDM_TRUE_DEVICES];
static int bdmDeviceListInitialized = 0;
int bdmDeviceModeStarted;
@@ -65,43 +69,12 @@ int gEnableBdmHDD;
base_game_info_t *gAutoLaunchBDMGame;
bdm_device_data_t *gAutoLaunchDeviceData;
-
void bdmInitDevicesData();
int bdmUpdateDeviceData(item_list_t *itemList);
-// Identifies the partition that the specified file is stored on and generates a full path to it.
-int bdmFindPartition(char *target, const char *name, int write)
-{
- int i, fd;
- char path[256];
-
- for (i = 0; i < MAX_BDM_DEVICES; i++) {
- if (gBDMPrefix[0] != '\0')
- sprintf(path, "mass%d:%s/%s", i, gBDMPrefix, name);
- else
- sprintf(path, "mass%d:%s", i, name);
- if (write)
- fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0666);
- else
- fd = open(path, O_RDONLY);
-
- if (fd >= 0) {
- if (gBDMPrefix[0] != '\0')
- sprintf(target, "mass%d:%s/", i, gBDMPrefix);
- else
- sprintf(target, "mass%d:", i);
- close(fd);
- return 1;
- }
- }
-
- // default to first partition (for themes, ...)
- if (gBDMPrefix[0] != '\0')
- sprintf(target, "mass0:%s/", gBDMPrefix);
- else
- sprintf(target, "mass0:");
- return 0;
-}
+static const char *bdmGetDevicePrefix(int deviceType);
+static int bdmBuildTruePath(char *path, size_t pathSize, int deviceType, int deviceIndex, int withSlash);
+static int bdmOpenTrueDevice(int deviceType, int deviceIndex);
static unsigned int BdmGeneration = 0;
@@ -110,81 +83,205 @@ static void bdmEventHandler(void *packet, void *opt)
BdmGeneration++;
}
-static void bdmLoadBlockDeviceModules(void)
+static void bdmLoadUSBModules(void)
{
- WaitSema(bdmLoadModuleLock);
+ if (!usbModLoaded) {
+ guiSetBootStatusIfActive("Loading USB modules...");
- if (gEnableUSB && !iUSBModLoaded) {
// Load USB Block Device drivers
+ LOG("[USBD]:\n");
+ sysLoadModuleBuffer(&usbd_irx, size_usbd_irx, 0, NULL);
+
LOG("[USBMASS_BD]:\n");
sysLoadModuleBuffer(&usbmass_bd_irx, size_usbmass_bd_irx, 0, NULL);
- iUSBModLoaded = 1;
+ usbModLoaded = 1;
}
+}
+
+static void bdmLoadiLinkModules(void)
+{
+ if (!iLinkModLoaded) {
+ guiSetBootStatusIfActive("Loading iLink modules...");
- if (gEnableILK && !iLinkModLoaded) {
// Load iLink Block Device drivers
LOG("[ILINKMAN]:\n");
sysLoadModuleBuffer(&iLinkman_irx, size_iLinkman_irx, 0, NULL);
+
LOG("[IEEE1394_BD]:\n");
sysLoadModuleBuffer(&IEEE1394_bd_irx, size_IEEE1394_bd_irx, 0, NULL);
iLinkModLoaded = 1;
}
+}
+
+static void bdmLoadMX4SIOModules(void)
+{
+ if (!mx4sioModLoaded) {
+ guiSetBootStatusIfActive("Loading MX4SIO modules...");
- if (gEnableMX4SIO && !mx4sioModLoaded) {
// Load MX4SIO Block Device drivers
LOG("[MX4SIO_BD]:\n");
sysLoadModuleBuffer(&mx4sio_bd_irx, size_mx4sio_bd_irx, 0, NULL);
mx4sioModLoaded = 1;
}
+}
+
+static void bdmLoadBdmHDDModules(void)
+{
+ if (!hddModLoaded) {
+ guiSetBootStatusIfActive("Loading BDM HDD modules...");
- if (gEnableBdmHDD && !hddModLoaded) {
// Load dev9 and atad device drivers.
LOG("bdmLoadBlockDeviceModules loading hdd drivers...\n");
hddLoadModules();
hddModLoaded = 1;
}
+}
+
+static void bdmLoadBlockDeviceModules(void)
+{
+ WaitSema(bdmLoadModuleLock);
+
+ if (gEnableUSB)
+ bdmLoadUSBModules();
+
+ if (gEnableILK)
+ bdmLoadiLinkModules();
+
+ if (gEnableMX4SIO)
+ bdmLoadMX4SIOModules();
+
+ if (gEnableBdmHDD)
+ bdmLoadBdmHDDModules();
SignalSema(bdmLoadModuleLock);
}
+static int bdmGetDeviceTypeFromPath(const char *path)
+{
+ if (pathHasDevicePrefix(path, "usb"))
+ return BDM_TYPE_USB;
+ if (pathHasDevicePrefix(path, "ilink"))
+ return BDM_TYPE_ILINK;
+ if (pathHasDevicePrefix(path, "mx4sio"))
+ return BDM_TYPE_SDC;
+ if (pathHasDevicePrefix(path, "ata"))
+ return BDM_TYPE_ATA;
+
+ return BDM_TYPE_UNKNOWN;
+}
+
+static void bdmLoadBaseModules(void)
+{
+ if (!bdmModulesLoaded) {
+ guiSetBootStatusIfActive("Loading block device modules...");
+
+ // Load Block Device Manager (BDM)
+ LOG("[BDM]:\n");
+ sysLoadModuleBuffer(&bdm_irx, size_bdm_irx, 0, NULL);
+
+ // Load BDM FATFS driver
+ LOG("[BDMFS_FATFS]:\n");
+ sysLoadModuleBuffer(&bdmfs_fatfs_irx, size_bdmfs_fatfs_irx, 0, NULL);
+
+ LOG("[BDMEVENT]:\n");
+ sysLoadModuleBuffer(&bdmevent_irx, size_bdmevent_irx, 0, NULL);
+
+ SifAddCmdHandler(0, &bdmEventHandler, NULL);
+
+ bdmModulesLoaded = 1;
+ }
+}
+
void bdmLoadModules(void)
{
LOG("BDMSUPPORT LoadModules\n");
- // Load Block Device Manager (BDM)
- LOG("[BDM]:\n");
- sysLoadModuleBuffer(&bdm_irx, size_bdm_irx, 0, NULL);
-
- // Load FATFS (mass:) driver
- LOG("[BDMFS_FATFS]:\n");
- sysLoadModuleBuffer(&bdmfs_fatfs_irx, size_bdmfs_fatfs_irx, 0, NULL);
+ bdmLoadBaseModules();
// Load Optional Block Device drivers
ioPutRequest(IO_CUSTOM_SIMPLEACTION, &bdmLoadBlockDeviceModules);
- LOG("[BDMEVENT]:\n");
- sysLoadModuleBuffer(&bdmevent_irx, size_bdmevent_irx, 0, NULL);
- SifAddCmdHandler(0, &bdmEventHandler, NULL);
-
LOG("BDMSUPPORT Modules loaded\n");
}
+void bdmLoadModulesForPath(const char *path)
+{
+ int deviceType = bdmGetDeviceTypeFromPath(path);
+
+ if (deviceType == BDM_TYPE_UNKNOWN)
+ return;
+
+ LOG("BDMSUPPORT LoadModulesForPath %s\n", path);
+
+ bdmLoadBaseModules();
+
+ WaitSema(bdmLoadModuleLock);
+
+ switch (deviceType) {
+ case BDM_TYPE_USB:
+ bdmLoadUSBModules();
+ break;
+ case BDM_TYPE_ILINK:
+ bdmLoadiLinkModules();
+ break;
+ case BDM_TYPE_SDC:
+ bdmLoadMX4SIOModules();
+ break;
+ case BDM_TYPE_ATA:
+ bdmLoadBdmHDDModules();
+ break;
+ }
+
+ SignalSema(bdmLoadModuleLock);
+}
+
+void bdmLoadModulesForUsbMassCompat(void)
+{
+ LOG("BDMSUPPORT LoadModulesForUsbMassCompat\n");
+
+ bdmLoadBaseModules();
+
+ WaitSema(bdmLoadModuleLock);
+ bdmLoadUSBModules();
+ SignalSema(bdmLoadModuleLock);
+}
+
+void bdmLoadEnabledDeviceModules(void)
+{
+ LOG("BDMSUPPORT LoadEnabledDeviceModules\n");
+
+ if (!gEnableUSB && !gEnableILK && !gEnableMX4SIO && !gEnableBdmHDD) {
+ LOG("BDMSUPPORT no enabled BDM devices\n");
+ return;
+ }
+
+ bdmLoadBaseModules();
+
+ // Settings have now been loaded.. so load enabled BDM drivers synchronously before bdmEnumerateDevices() runs
+ bdmLoadBlockDeviceModules();
+}
+
static void bdmInit(item_list_t *itemList)
{
LOG("BDMSUPPORT Init\n");
bdm_device_data_t *pDeviceData = (bdm_device_data_t *)itemList->priv;
+ if (!pDeviceData) {
+ itemList->enabled = 0;
+ return;
+ }
+
pDeviceData->bdmULSizePrev = -2;
pDeviceData->bdmModifiedCDPrev = 0;
pDeviceData->bdmModifiedDVDPrev = 0;
pDeviceData->bdmGameCount = 0;
pDeviceData->bdmGames = NULL;
bdmLoadModules();
- configGetInt(configGetByType(CONFIG_OPL), "usb_frames_delay", &itemList->delay);
+ itemList->delay = gBDMFramesDelay;
itemList->enabled = 1;
}
@@ -202,6 +299,11 @@ static int bdmNeedsUpdate(item_list_t *itemList)
return 0;
bdm_device_data_t *pDeviceData = (bdm_device_data_t *)itemList->priv;
+ if (!pDeviceData)
+ return 0;
+
+ opl_io_module_t *pOwner = (opl_io_module_t *)itemList->owner;
+ int visible = pOwner != NULL && pOwner->menuItem.visible == 1;
ioPutRequest(IO_CUSTOM_SIMPLEACTION, &bdmLoadBlockDeviceModules);
@@ -213,8 +315,7 @@ static int bdmNeedsUpdate(item_list_t *itemList)
// If the device menu is visible double check the device type and if support for this device type is enabled. If the user switches device support
// to off for a bdm device we want to hide the menu even though the drivers are still loaded and the device is being detected by bdm.
- opl_io_module_t *pOwner = (opl_io_module_t *)itemList->owner;
- if (pOwner != NULL && pOwner->menuItem.visible == 1) {
+ if (visible) {
int deviceEnabled = 0;
switch (pDeviceData->bdmDeviceType) {
case BDM_TYPE_USB:
@@ -241,6 +342,7 @@ static int bdmNeedsUpdate(item_list_t *itemList)
if (pDeviceData->bdmULSizePrev != -2 && pDeviceData->bdmDeviceTick == BdmGeneration)
return 0;
+
pDeviceData->bdmDeviceTick = BdmGeneration;
// Check if the device has been connected or removed.
@@ -254,7 +356,7 @@ static int bdmNeedsUpdate(item_list_t *itemList)
} else if (result == 1)
sfxPlay(SFX_BD_CONNECT);
- sprintf(path, "%sCD", pDeviceData->bdmPrefix);
+ snprintf(path, sizeof(path), "%sCD", pDeviceData->bdmPrefix);
if (stat(path, &st) != 0)
st.st_mtime = 0;
if (pDeviceData->bdmModifiedCDPrev != st.st_mtime) {
@@ -262,7 +364,7 @@ static int bdmNeedsUpdate(item_list_t *itemList)
result = 1;
}
- sprintf(path, "%sDVD", pDeviceData->bdmPrefix);
+ snprintf(path, sizeof(path), "%sDVD", pDeviceData->bdmPrefix);
if (stat(path, &st) != 0)
st.st_mtime = 0;
if (pDeviceData->bdmModifiedDVDPrev != st.st_mtime) {
@@ -275,24 +377,26 @@ static int bdmNeedsUpdate(item_list_t *itemList)
// update Themes
if (!pDeviceData->ThemesLoaded) {
- sprintf(path, "%sTHM", pDeviceData->bdmPrefix);
+ guiSetBootStatusIfActive("Loading block device themes...");
+
+ snprintf(path, sizeof(path), "%sTHM/thm.tar", pDeviceData->bdmPrefix);
+ tarLoadFile(TAR_KIND_THM, path);
+
+ snprintf(path, sizeof(path), "%sTHM", pDeviceData->bdmPrefix);
if (thmAddElements(path, "/", 1) > 0)
pDeviceData->ThemesLoaded = 1;
}
- if (!pDeviceData->ArtArchivedLoaded) {
- sprintf(path, "%sART/art.tar", pDeviceData->bdmPrefix);
- loadTarFile(path);
- pDeviceData->ArtArchivedLoaded = 1;
- }
-
// update Languages
if (!pDeviceData->LanguagesLoaded) {
- sprintf(path, "%sLNG", pDeviceData->bdmPrefix);
+ guiSetBootStatusIfActive("Loading block device languages...");
+
+ snprintf(path, sizeof(path), "%sLNG", pDeviceData->bdmPrefix);
if (lngAddLanguages(path, "/", itemList->mode) > 0)
pDeviceData->LanguagesLoaded = 1;
}
+ guiSetBootStatusIfActive("Checking block device folders...");
sbCreateFolders(pDeviceData->bdmPrefix, 1);
return result;
@@ -302,6 +406,8 @@ static int bdmUpdateGameList(item_list_t *itemList)
{
bdm_device_data_t *pDeviceData = (bdm_device_data_t *)itemList->priv;
+ guiSetBootStatusIfActive("Scanning block device games...");
+
sbReadList(&pDeviceData->bdmGames, pDeviceData->bdmPrefix, &pDeviceData->bdmULSizePrev, &pDeviceData->bdmGameCount);
return pDeviceData->bdmGameCount;
}
@@ -359,7 +465,7 @@ static void bdmRenameGame(item_list_t *itemList, int id, char *newName)
pDeviceData->ForceRefresh = 1;
}
-void bdmLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
+void bdmLaunchGame(item_list_t *itemList, int id, per_game_cfg_t *pgcfg)
{
int i, fd, iop_fd, index, compatmask = 0;
int EnablePS2Logo = 0;
@@ -384,83 +490,111 @@ void bdmLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
game = gAutoLaunchBDMGame;
}
- char vmc_name[32], vmc_path[256], have_error = 0;
- int vmc_id, size_mcemu_irx = 0;
- bdm_vmc_infos_t bdm_vmc_infos;
- vmc_superblock_t vmc_superblock;
-
- for (vmc_id = 0; vmc_id < 2; vmc_id++) {
- memset(&bdm_vmc_infos, 0, sizeof(bdm_vmc_infos_t));
- configGetVMC(configSet, vmc_name, sizeof(vmc_name), vmc_id);
- if (vmc_name[0]) {
- have_error = 1;
- int vmcSizeInMb = sysCheckVMC(pDeviceData->bdmPrefix, "/", vmc_name, 0, &vmc_superblock);
- if (vmcSizeInMb > 0) {
- bdm_vmc_infos.flags = vmc_superblock.mc_flag & 0xFF;
- bdm_vmc_infos.flags |= 0x100;
- bdm_vmc_infos.specs.page_size = vmc_superblock.page_size;
- bdm_vmc_infos.specs.block_size = vmc_superblock.pages_per_block;
- bdm_vmc_infos.specs.card_size = vmc_superblock.pages_per_cluster * vmc_superblock.clusters_per_card;
-
- sprintf(vmc_path, "%sVMC/%s.bin", pDeviceData->bdmPrefix, vmc_name);
-
- fd = open(vmc_path, O_RDONLY);
- if (fd >= 0) {
- iop_fd = ps2sdk_get_iop_fd(fd);
- if (fileXioIoctl2(iop_fd, USBMASS_IOCTL_GET_LBA, NULL, 0, &startingLBA, sizeof(startingLBA)) == 0 && (startCluster = (unsigned int)fileXioIoctl(iop_fd, USBMASS_IOCTL_GET_CLUSTER, vmc_path)) != 0) {
-
- // VMC only supports 32bit LBAs at the moment, so if the starting LBA + size of the VMC crosses the 32bit boundary
- // just report the VMC as being fragmented to prevent file system corruption.
- int vmcSectorCount = vmcSizeInMb * ((1024 * 1024) / 512); // size in MB * sectors per MB
- if (startingLBA + vmcSectorCount > 0x100000000) {
- LOG("BDMSUPPORT VMC bad LBA range\n");
- have_error = 2;
- }
- // Check VMC cluster chain for fragmentation (write operation can cause damage to the filesystem).
- else if (fileXioIoctl(iop_fd, USBMASS_IOCTL_CHECK_CHAIN, "") == 1) {
- LOG("BDMSUPPORT Cluster Chain OK\n");
- have_error = 0;
- bdm_vmc_infos.active = 1;
- bdm_vmc_infos.start_sector = (u32)startingLBA;
- LOG("BDMSUPPORT VMC slot %d start: 0x%X\n", vmc_id, (u32)startingLBA);
- } else {
- LOG("BDMSUPPORT Cluster Chain NG\n");
- have_error = 2;
- }
- }
+ int selectedCore = pgcfg->core_loader == CORE_LOADER_NEUTRINO ? CORE_LOADER_NEUTRINO : CORE_LOADER_WOPL;
- close(fd);
- }
+ neutrino_path_t neutrinoPath;
+ char neutrinoVmc0[256];
+ char neutrinoVmc1[256];
+
+ neutrinoPath.elf[0] = '\0';
+ neutrinoPath.cwd[0] = '\0';
+ neutrinoVmc0[0] = '\0';
+ neutrinoVmc1[0] = '\0';
+
+ if (selectedCore == CORE_LOADER_NEUTRINO) {
+ if (game->format == GAME_FORMAT_USBLD || !strcasecmp(game->extension, ".zso")) {
+ guiWarning("Neutrino does not support this file format, launching with core", 6);
+ selectedCore = CORE_LOADER_WOPL;
+ } else {
+ if (!sbFindNeutrino(&neutrinoPath, pDeviceData->bdmPrefix)) {
+ guiWarning("Neutrino ELF not found, launching with core", 6);
+ selectedCore = CORE_LOADER_WOPL;
}
}
+ }
- if (gAutoLaunchBDMGame == NULL) {
- if (have_error) {
- char error[256];
- if (have_error == 2) // VMC file is fragmented
- snprintf(error, sizeof(error), _l(_STR_ERR_VMC_FRAGMENTED_CONTINUE), vmc_name, (vmc_id + 1));
- else
- snprintf(error, sizeof(error), _l(_STR_ERR_VMC_CONTINUE), vmc_name, (vmc_id + 1));
- if (!guiMsgBox(error, 1, NULL)) {
- return;
+ int size_mcemu_irx = 0;
+
+ if (selectedCore == CORE_LOADER_WOPL) {
+ char vmc_name[32], vmc_path[256], have_error = 0;
+ int vmc_id;
+ bdm_vmc_infos_t bdm_vmc_infos;
+ vmc_superblock_t vmc_superblock;
+
+ for (vmc_id = 0; vmc_id < 2; vmc_id++) {
+ memset(&bdm_vmc_infos, 0, sizeof(bdm_vmc_infos_t));
+ strncpy(vmc_name, vmc_id == 0 ? pgcfg->vmc1 : pgcfg->vmc2, sizeof(vmc_name) - 1);
+ vmc_name[sizeof(vmc_name) - 1] = '\0';
+ if (vmc_name[0]) {
+ have_error = 1;
+ int vmcSizeInMb = sysCheckVMC(pDeviceData->bdmPrefix, "/", vmc_name, 0, &vmc_superblock);
+ if (vmcSizeInMb > 0) {
+ bdm_vmc_infos.flags = vmc_superblock.mc_flag & 0xFF;
+ bdm_vmc_infos.flags |= 0x100;
+ bdm_vmc_infos.specs.page_size = vmc_superblock.page_size;
+ bdm_vmc_infos.specs.block_size = vmc_superblock.pages_per_block;
+ bdm_vmc_infos.specs.card_size = vmc_superblock.pages_per_cluster * vmc_superblock.clusters_per_card;
+
+ snprintf(vmc_path, sizeof(vmc_path), "%sVMC/%s.bin", pDeviceData->bdmPrefix, vmc_name);
+
+ fd = open(vmc_path, O_RDONLY);
+ if (fd >= 0) {
+ iop_fd = ps2sdk_get_iop_fd(fd);
+ if (fileXioIoctl2(iop_fd, USBMASS_IOCTL_GET_LBA, NULL, 0, &startingLBA, sizeof(startingLBA)) == 0 && (startCluster = (unsigned int)fileXioIoctl(iop_fd, USBMASS_IOCTL_GET_CLUSTER, vmc_path)) != 0) {
+
+ // VMC only supports 32bit LBAs at the moment, so if the starting LBA + size of the VMC crosses the 32bit boundary
+ // just report the VMC as being fragmented to prevent file system corruption.
+ int vmcSectorCount = vmcSizeInMb * ((1024 * 1024) / 512); // size in MB * sectors per MB
+ if (startingLBA + vmcSectorCount > 0x100000000) {
+ LOG("BDMSUPPORT VMC bad LBA range\n");
+ have_error = 2;
+ }
+ // Check VMC cluster chain for fragmentation (write operation can cause damage to the filesystem).
+ else if (fileXioIoctl(iop_fd, USBMASS_IOCTL_CHECK_CHAIN, "") == 1) {
+ LOG("BDMSUPPORT Cluster Chain OK\n");
+ have_error = 0;
+ bdm_vmc_infos.active = 1;
+ bdm_vmc_infos.start_sector = (u32)startingLBA;
+ LOG("BDMSUPPORT VMC slot %d start: 0x%X\n", vmc_id, (u32)startingLBA);
+ } else {
+ LOG("BDMSUPPORT Cluster Chain NG\n");
+ have_error = 2;
+ }
+ }
+
+ close(fd);
+ }
}
}
- } else
- LOG("VMC error\n");
- for (i = 0; i < size_bdm_mcemu_irx; i++) {
- if (((u32 *)&bdm_mcemu_irx)[i] == (0xC0DEFAC0 + vmc_id)) {
- if (bdm_vmc_infos.active)
- size_mcemu_irx = size_bdm_mcemu_irx;
- memcpy(&((u32 *)&bdm_mcemu_irx)[i], &bdm_vmc_infos, sizeof(bdm_vmc_infos_t));
- break;
+ if (gAutoLaunchBDMGame == NULL) {
+ if (have_error) {
+ char error[256];
+ if (have_error == 2) // VMC file is fragmented
+ snprintf(error, sizeof(error), _l(_STR_ERR_VMC_FRAGMENTED_CONTINUE), vmc_name, (vmc_id + 1));
+ else
+ snprintf(error, sizeof(error), _l(_STR_ERR_VMC_CONTINUE), vmc_name, (vmc_id + 1));
+ if (!guiMsgBox(error, 1, NULL)) {
+ return;
+ }
+ }
+ } else
+ LOG("VMC error\n");
+
+ for (i = 0; i < size_bdm_mcemu_irx; i++) {
+ if (((u32 *)&bdm_mcemu_irx)[i] == (0xC0DEFAC0 + vmc_id)) {
+ if (bdm_vmc_infos.active)
+ size_mcemu_irx = size_bdm_mcemu_irx;
+ memcpy(&((u32 *)&bdm_mcemu_irx)[i], &bdm_vmc_infos, sizeof(bdm_vmc_infos_t));
+ break;
+ }
}
}
}
void *irx = NULL;
int irx_size = 0;
- if (!strcmp(pDeviceData->bdmDriver, "ata") && strlen(pDeviceData->bdmDriver) == 3) {
+ if (pDeviceData->bdmDeviceType == BDM_TYPE_ATA) {
irx = &bdm_ata_cdvdman_irx;
irx_size = size_bdm_ata_cdvdman_irx;
} else {
@@ -468,7 +602,7 @@ void bdmLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
irx_size = size_bdm_cdvdman_irx;
}
- compatmask = sbPrepare(game, configSet, irx_size, irx, &index);
+ compatmask = sbPrepare(game, pgcfg, irx_size, irx, &index);
settings = (struct cdvdman_settings_bdm *)((u8 *)irx + index);
if (settings == NULL)
return;
@@ -552,25 +686,47 @@ void bdmLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
} else
LOG("Cheats error\n");
}
+
+ if ((result = sbLoadImage(pDeviceData->bdmPrefix, game->startup)) < 0) {
+ if (gAutoLaunchBDMGame == NULL) {
+ guiWarning(_l(_STR_ERR_IMAGE_LOAD_FAILED), 10);
+ } else {
+ LOG("Image error\n");
+ }
+ }
#endif
- if (gRememberLastPlayed) {
- configSetStr(configGetByType(CONFIG_LAST), "last_played", game->startup);
- configSave(CONFIG_LAST, 0);
+ if (gRememberLastPlayed)
+ wOPLLastSave(game->startup);
+
+ if (pgcfg->alt_startup[0]) {
+ strncpy(filename, pgcfg->alt_startup, sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+ } else {
+ strncpy(filename, game->startup, sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
}
- if (configGetStrCopy(configSet, CONFIG_ITEM_ALTSTARTUP, filename, sizeof(filename)) == 0)
- strcpy(filename, game->startup);
+ // deinit will free per device data.. copy resolved device info before free to compare for launch
+ char bdmCurrentDevice[16];
+ int bdmCurrentType = pDeviceData->bdmDeviceType;
- // deinit will free per device data.. copy driver name before free to compare for launch
- char bdmCurrentDriver[32];
- snprintf(bdmCurrentDriver, sizeof(bdmCurrentDriver), "%s", pDeviceData->bdmDriver);
+ snprintf(bdmCurrentDevice, sizeof(bdmCurrentDevice), "%s", pDeviceData->bdmTruePrefix);
settings->bdDeviceId = pDeviceData->massDeviceIndex;
- if (!strcmp(bdmCurrentDriver, "ata") && strlen(bdmCurrentDriver) == 3) {
+ if (bdmCurrentType != BDM_TYPE_USB && bdmCurrentType != BDM_TYPE_ILINK && bdmCurrentType != BDM_TYPE_SDC && bdmCurrentType != BDM_TYPE_ATA) {
+ LOG("BDMSUPPORT: unsupported BDM device type %d (%s)\n", bdmCurrentType, bdmCurrentDevice);
+
+ if (gAutoLaunchBDMGame == NULL)
+ guiMsgBox(_l(_STR_ERR_FILE_INVALID), 0, NULL);
+
+ return;
+ }
+
+ if (bdmCurrentType == BDM_TYPE_ATA) {
// Get DMA settings for ATA mode.
int dmaType = 0, dmaMode = 7;
- configGetInt(configSet, CONFIG_ITEM_DMA, &dmaMode);
+ dmaMode = (pgcfg->dma != 7) ? pgcfg->dma : 7;
// Set DMA mode and spindown time.
if (dmaMode < 3)
@@ -589,26 +745,33 @@ void bdmLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
settings->hddIsLBA48 = pDeviceData->bdmHddIsLBA48;
}
- int coreLoader = 0;
- configGetInt(configSet, CONFIG_ITEM_CORE_LOADER, &coreLoader);
+ if (selectedCore == CORE_LOADER_NEUTRINO) {
+ sbCreateNeutrinoVMCPath(neutrinoVmc0, sizeof(neutrinoVmc0), pDeviceData->bdmPrefix, pgcfg->vmc1);
+ sbCreateNeutrinoVMCPath(neutrinoVmc1, sizeof(neutrinoVmc1), pDeviceData->bdmPrefix, pgcfg->vmc2);
+ }
- const char *neutrinoPath = NULL;
- if (coreLoader) {
- neutrinoPath = sbFileExists(NEUTRINO_PATH) ? NEUTRINO_PATH : (sbFileExists(NEUTRINO_ALT_PATH) ? NEUTRINO_ALT_PATH : NULL);
+ if (!(selectedCore == CORE_LOADER_NEUTRINO && sbPathIsMC(neutrinoPath.elf)))
+ sbMMCESendGameId(game->startup);
- if (game->format == GAME_FORMAT_USBLD || !strcasecmp(game->extension, ".zso")) {
- guiWarning("Neutrino does not support this file format, launching with core", 6);
- coreLoader = 0;
- } else if (neutrinoPath == NULL) {
- guiWarning("Neutrino ELF not found, launching with core", 6);
- coreLoader = 0;
+ int deinitException = NO_EXCEPTION;
+ int deinitMode = gAutoLaunchBDMGame == NULL ? itemList->mode : BDM_MODE;
+
+ if (selectedCore == CORE_LOADER_NEUTRINO) {
+ int elfDevice = -1;
+ int elfMode = sbGetPathModeAndDevice(neutrinoPath.elf, &elfDevice);
+
+ if (elfMode >= 0) {
+ deinitException = UNMOUNT_EXCEPTION;
+ deinitMode = elfMode;
}
+
+ LOG("NEUTRINO ELF MODE=%d DEVICE=%d\n", elfMode, elfDevice);
}
if (gAutoLaunchBDMGame == NULL)
- deinit(NO_EXCEPTION, itemList->mode); // CAREFUL: deinit will call bdmCleanUp, so bdmGames/game will be freed
+ deinit(deinitException, deinitMode); // CAREFUL: deinit will call bdmCleanUp, so bdmGames/game will be freed
else {
- miniDeinit(configSet);
+ miniDeinit();
free(gAutoLaunchBDMGame);
gAutoLaunchBDMGame = NULL;
@@ -619,34 +782,59 @@ void bdmLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
LOG("bdm pre sysLaunchLoaderElf\n");
- if (coreLoader) {
- sysLaunchNeutrino(bdmCurrentDriver, partname, compatmask, EnablePS2Logo, neutrinoPath);
+ if (selectedCore == CORE_LOADER_NEUTRINO) {
+ sysLaunchNeutrino(bdmCurrentDevice, partname, compatmask, EnablePS2Logo, neutrinoPath.elf, neutrinoPath.cwd, neutrinoVmc0, neutrinoVmc1);
return;
}
- if (!strcmp(bdmCurrentDriver, "usb")) {
- settings->common.fakemodule_flags |= FAKE_MODULE_FLAG_USBD;
- if (settings->bdDeviceId == 0)
- sysLaunchLoaderElf(filename, "BDM_USB_MODE0", irx_size, irx, size_mcemu_irx, bdm_mcemu_irx, EnablePS2Logo, compatmask);
- else
- sysLaunchLoaderElf(filename, "BDM_USB_MODE1", irx_size, irx, size_mcemu_irx, bdm_mcemu_irx, EnablePS2Logo, compatmask);
- } else if (!strcmp(bdmCurrentDriver, "sd") && strlen(bdmCurrentDriver) == 2) {
- settings->common.fakemodule_flags |= 0 /* TODO! fake ilinkman ? */;
- sysLaunchLoaderElf(filename, "BDM_ILK_MODE", irx_size, irx, size_mcemu_irx, bdm_mcemu_irx, EnablePS2Logo, compatmask);
- } else if (!strcmp(bdmCurrentDriver, "sdc") && strlen(bdmCurrentDriver) == 3) {
- settings->common.fakemodule_flags |= 0;
- sysLaunchLoaderElf(filename, "BDM_M4S_MODE", irx_size, irx, size_mcemu_irx, bdm_mcemu_irx, EnablePS2Logo, compatmask);
- } else if (!strcmp(bdmCurrentDriver, "ata") && strlen(bdmCurrentDriver) == 3) {
- settings->common.fakemodule_flags |= FAKE_MODULE_FLAG_DEV9;
- settings->common.fakemodule_flags |= FAKE_MODULE_FLAG_ATAD;
- sysLaunchLoaderElf(filename, "BDM_ATA_MODE", irx_size, irx, size_mcemu_irx, bdm_mcemu_irx, EnablePS2Logo, compatmask);
+ switch (bdmCurrentType) {
+ case BDM_TYPE_USB:
+ settings->common.fakemodule_flags |= FAKE_MODULE_FLAG_USBD;
+
+ if (settings->bdDeviceId == 0)
+ sysLaunchLoaderElf(filename, "BDM_USB_MODE0", irx_size, irx, size_mcemu_irx, bdm_mcemu_irx, EnablePS2Logo, compatmask);
+ else
+ sysLaunchLoaderElf(filename, "BDM_USB_MODE1", irx_size, irx, size_mcemu_irx, bdm_mcemu_irx, EnablePS2Logo, compatmask);
+ break;
+
+ case BDM_TYPE_ILINK:
+ settings->common.fakemodule_flags |= 0 /* TODO! fake ilinkman ? */;
+ sysLaunchLoaderElf(filename, "BDM_ILK_MODE", irx_size, irx, size_mcemu_irx, bdm_mcemu_irx, EnablePS2Logo, compatmask);
+ break;
+
+ case BDM_TYPE_SDC:
+ settings->common.fakemodule_flags |= 0;
+ sysLaunchLoaderElf(filename, "BDM_M4S_MODE", irx_size, irx, size_mcemu_irx, bdm_mcemu_irx, EnablePS2Logo, compatmask);
+ break;
+
+ case BDM_TYPE_ATA:
+ settings->common.fakemodule_flags |= FAKE_MODULE_FLAG_DEV9;
+ settings->common.fakemodule_flags |= FAKE_MODULE_FLAG_ATAD;
+ sysLaunchLoaderElf(filename, "BDM_ATA_MODE", irx_size, irx, size_mcemu_irx, bdm_mcemu_irx, EnablePS2Logo, compatmask);
+ break;
+
+ default:
+ LOG("BDMSUPPORT: invalid BDM device type after deinit: %d\n", bdmCurrentType); // dont see how this would ever happen.. but..
+ break;
}
}
-static config_set_t *bdmGetConfig(item_list_t *itemList, int id)
+static void bdmGetInfo(item_list_t *itemList, int id, game_info_t *gi)
+{
+ bdm_device_data_t *pDeviceData = (bdm_device_data_t *)itemList->priv;
+ sbPopulateConfig(&pDeviceData->bdmGames[id], pDeviceData->bdmPrefix, "/", gi, NULL);
+}
+
+static void bdmGetPgCfg(item_list_t *itemList, int id, per_game_cfg_t *cfg)
{
bdm_device_data_t *pDeviceData = (bdm_device_data_t *)itemList->priv;
- return sbPopulateConfig(&pDeviceData->bdmGames[id], pDeviceData->bdmPrefix, "/");
+ sbPopulateConfig(&pDeviceData->bdmGames[id], pDeviceData->bdmPrefix, "/", NULL, cfg);
+}
+
+static int bdmSavePgCfg(item_list_t *itemList, int id, const per_game_cfg_t *cfg)
+{
+ bdm_device_data_t *pDeviceData = (bdm_device_data_t *)itemList->priv;
+ return sbSaveConfig(&pDeviceData->bdmGames[id], pDeviceData->bdmPrefix, "/", cfg);
}
static int bdmGetImage(item_list_t *itemList, char *folder, int isRelative, char *value, char *suffix, GSTEXTURE *resultTex, short psm)
@@ -660,7 +848,7 @@ static int bdmGetImage(item_list_t *itemList, char *folder, int isRelative, char
else
snprintf(path, sizeof(path), "%s%s_%s", folder, value, suffix);
- return texDiscoverLoad(resultTex, path, -1, 0);
+ return texDiscoverLoad(resultTex, path, -1, RES_FILESYSTEM);
}
static int bdmGetArchivedImage(item_list_t *itemList, char *folder, char *value, char *suffix, GSTEXTURE *resultTex, short psm)
@@ -669,7 +857,7 @@ static int bdmGetArchivedImage(item_list_t *itemList, char *folder, char *value,
snprintf(path, sizeof(path), "%s_%s", value, suffix);
- return texDiscoverLoad(resultTex, path, -1, 1);
+ return texDiscoverLoad(resultTex, path, -1, RES_TAR_ART);
}
static int bdmGetTextId(item_list_t *itemList)
@@ -678,32 +866,44 @@ static int bdmGetTextId(item_list_t *itemList)
bdm_device_data_t *pDeviceData = (bdm_device_data_t *)itemList->priv;
- if (!strcmp(pDeviceData->bdmDriver, "usb"))
- mode = _STR_USB_GAMES;
- else if (!strcmp(pDeviceData->bdmDriver, "sd") && strlen(pDeviceData->bdmDriver) == 2)
- mode = _STR_ILINK_GAMES;
- else if (!strcmp(pDeviceData->bdmDriver, "sdc") && strlen(pDeviceData->bdmDriver) == 3)
- mode = _STR_MX4SIO_GAMES;
- else if (!strcmp(pDeviceData->bdmDriver, "ata") && strlen(pDeviceData->bdmDriver) == 3)
- mode = _STR_HDD_GAMES;
+ switch (pDeviceData->bdmDeviceType) {
+ case BDM_TYPE_USB:
+ mode = _STR_USB_GAMES;
+ break;
+ case BDM_TYPE_ILINK:
+ mode = _STR_ILINK_GAMES;
+ break;
+ case BDM_TYPE_SDC:
+ mode = _STR_MX4SIO_GAMES;
+ break;
+ case BDM_TYPE_ATA:
+ mode = _STR_HDD_GAMES;
+ break;
+ }
return mode;
}
static int bdmGetIconId(item_list_t *itemList)
{
- int mode = CATEGORY_EMPTY_BDM_ICON;
+ int mode = BDM_ICON;
bdm_device_data_t *pDeviceData = (bdm_device_data_t *)itemList->priv;
- if (!strcmp(pDeviceData->bdmDriver, "usb"))
- mode = CATEGORY_USB_ICON;
- else if (!strcmp(pDeviceData->bdmDriver, "sd") && strlen(pDeviceData->bdmDriver) == 2)
- mode = CATEGORY_ILINK_ICON;
- else if (!strcmp(pDeviceData->bdmDriver, "sdc") && strlen(pDeviceData->bdmDriver) == 3)
- mode = CATEGORY_MX4SIO_ICON;
- else if (!strcmp(pDeviceData->bdmDriver, "ata") && strlen(pDeviceData->bdmDriver) == 3)
- mode = CATEGORY_HDD_BDM_ICON;
+ switch (pDeviceData->bdmDeviceType) {
+ case BDM_TYPE_USB:
+ mode = USB_ICON;
+ break;
+ case BDM_TYPE_ILINK:
+ mode = ILINK_ICON;
+ break;
+ case BDM_TYPE_SDC:
+ mode = MX4SIO_ICON;
+ break;
+ case BDM_TYPE_ATA:
+ mode = HDD_BD_ICON;
+ break;
+ }
return mode;
}
@@ -715,6 +915,9 @@ static void bdmCleanUp(item_list_t *itemList, int exception)
LOG("BDMSUPPORT CleanUp\n");
bdm_device_data_t *pDeviceData = (bdm_device_data_t *)itemList->priv;
+ if (!pDeviceData)
+ return;
+
free(pDeviceData->bdmGames);
free(pDeviceData);
itemList->priv = NULL;
@@ -731,15 +934,14 @@ static void bdmShutdown(item_list_t *itemList)
LOG("BDMSUPPORT Shutdown\n");
- // Format the device path.
- // Getting the device number is only relevant per module ie usb0 and mx40 will result in both being massDeviceIndex = 0 or mass0, use mode to determine instead.
bdm_device_data_t *pDeviceData = (bdm_device_data_t *)itemList->priv;
- snprintf(path, sizeof(path), "mass%d:", itemList->mode);
- // As required by some (typically 2.5") HDDs, issue the SCSI STOP UNIT command to avoid causing an emergency park.
- fileXioDevctl(path, USBMASS_DEVCTL_STOP_ALL, NULL, 0, NULL, 0);
+ if (pDeviceData && bdmBuildTruePath(path, sizeof(path), pDeviceData->bdmDeviceType, pDeviceData->massDeviceIndex, 0)) {
+ // As required by some (typically 2.5") HDDs, issue the SCSI STOP UNIT command to avoid causing an emergency park.
+ fileXioDevctl(path, USBMASS_DEVCTL_STOP_ALL, NULL, 0, NULL, 0);
+ }
- if (itemList->enabled) {
+ if (itemList->enabled && pDeviceData) {
LOG("BDMSUPPORT Shutdown free data\n");
// Free device data.
@@ -764,7 +966,7 @@ static char *bdmGetPrefix(item_list_t *itemList)
static item_list_t bdmGameList = {
BDM_MODE, 2, 0, 0, MENU_MIN_INACTIVE_FRAMES, BDM_MODE_UPDATE_DELAY, NULL, NULL, &bdmGetTextId, &bdmGetPrefix, &bdmInit, &bdmNeedsUpdate,
&bdmUpdateGameList, &bdmGetGameCount, &bdmGetGame, &bdmGetGameName, &bdmGetGameNameLength, &bdmGetGameStartup, &bdmDeleteGame, &bdmRenameGame,
- &bdmLaunchGame, &bdmGetConfig, &bdmGetImage, &bdmGetArchivedImage, &bdmCleanUp, &bdmShutdown, &bdmCheckVMC, &bdmGetIconId};
+ &bdmLaunchGame, &bdmGetInfo, &bdmGetPgCfg, &bdmSavePgCfg, &bdmGetImage, &bdmGetArchivedImage, &bdmCleanUp, &bdmShutdown, &bdmCheckVMC, &bdmGetIconId};
void bdmInitSemaphore()
{
@@ -782,28 +984,39 @@ void bdmInitDevicesData()
if (bdmDeviceListInitialized == 0) {
bdmDeviceListInitialized = 1;
- for (int i = 0; i < MAX_BDM_DEVICES; i++) {
+ for (int i = 0; i < MAX_BDM_TRUE_DEVICES; i++) {
// Setup the device list item.
item_list_t *pDeviceSupport = &bdmDeviceList[i];
memcpy(pDeviceSupport, &bdmGameList, sizeof(item_list_t));
- pDeviceSupport->mode = i;
+ pDeviceSupport->mode = BDM_MODE + i;
// Setup the per-device data.
bdm_device_data_t *pDeviceData = (bdm_device_data_t *)malloc(sizeof(bdm_device_data_t));
+ if (!pDeviceData) {
+ pDeviceSupport->priv = NULL;
+ continue;
+ }
+
memset(pDeviceData, 0, sizeof(bdm_device_data_t));
+ pDeviceData->bdmDeviceType = BDM_TYPE_UNKNOWN;
pDeviceSupport->priv = pDeviceData;
}
}
// Refresh the visibility of the menu.
- for (int i = 0; i < MAX_BDM_DEVICES; i++) {
+ for (int i = 0; i < MAX_BDM_TRUE_DEVICES; i++) {
// Register the device structure into the UI.
- initSupport(&bdmDeviceList[i], i, 0);
+ initSupport(&bdmDeviceList[i], BDM_MODE + i, 0);
// If bdm support is set to auto then make the page invisible and reset the bdm tick counter, when a bdm device is mounted it will dynamically be made visible.
// If bdm support is set to manual then only make the first page visible.
if (bdmDeviceList[i].owner != NULL) {
opl_io_module_t *pOwner = (opl_io_module_t *)bdmDeviceList[i].owner;
+ bdm_device_data_t *pDeviceData = (bdm_device_data_t *)bdmDeviceList[i].priv;
+ if (!pDeviceData) {
+ pOwner->menuItem.visible = 0;
+ continue;
+ }
if (gBDMStartMode == START_MODE_DISABLED) {
pOwner->menuItem.visible = 0;
@@ -812,12 +1025,12 @@ void bdmInitDevicesData()
// according to device state.
if (bdmDeviceModeStarted == 1) {
pOwner->menuItem.visible = 0;
- ((bdm_device_data_t *)bdmDeviceList[i].priv)->bdmDeviceTick = -1;
+ pDeviceData->bdmDeviceTick = -1;
} else
pOwner->menuItem.visible = (i == 0 ? 1 : 0);
} else if (gBDMStartMode == START_MODE_AUTO) {
pOwner->menuItem.visible = 0;
- ((bdm_device_data_t *)bdmDeviceList[i].priv)->bdmDeviceTick = -1;
+ pDeviceData->bdmDeviceTick = -1;
}
LOG("bdmInitDevicesData: setting device %d %s\n", i, (pOwner->menuItem.visible != 0 ? "visible" : "invisible"));
@@ -845,7 +1058,7 @@ void bdmResolveLBA_UDMA(bdm_device_data_t *pDeviceData)
pDeviceData->bdmHddIsLBA48 = fileXioDevctl("xhdd0:", ATA_DEVCTL_IS_48BIT, NULL, 0, NULL, 0);
if (pDeviceData->bdmHddIsLBA48 < 0) {
// Failed to query the LBA limit of the device, fail safe to LBA28.
- LOG("Mass device %d is backed by ATA but failed to get LBA limit %d\n", pDeviceData->massDeviceIndex, pDeviceData->bdmHddIsLBA48);
+ LOG("BDM device %d is backed by ATA but failed to get LBA limit %d\n", pDeviceData->massDeviceIndex, pDeviceData->bdmHddIsLBA48);
pDeviceData->bdmHddIsLBA48 = 0;
}
@@ -853,7 +1066,7 @@ void bdmResolveLBA_UDMA(bdm_device_data_t *pDeviceData)
pDeviceData->ataHighestUDMAMode = fileXioDevctl("xhdd0:", ATA_DEVCTL_GET_HIGHEST_UDMA_MODE, NULL, 0, NULL, 0);
if (pDeviceData->ataHighestUDMAMode < 0 || pDeviceData->ataHighestUDMAMode > 7) {
// Failed to query highest UDMA mode supported.
- LOG("Mass device %d is backed by ATA but failed to get highest UDMA mode %d\n", pDeviceData->ataHighestUDMAMode);
+ LOG("BDM device %d is backed by ATA but failed to get highest UDMA mode %d\n", pDeviceData->massDeviceIndex, pDeviceData->ataHighestUDMAMode);
pDeviceData->ataHighestUDMAMode = 4;
}
@@ -861,9 +1074,184 @@ void bdmResolveLBA_UDMA(bdm_device_data_t *pDeviceData)
hddSetTransferMode(0x40, pDeviceData->ataHighestUDMAMode);
}
+static const char *bdmGetDevicePrefix(int deviceType)
+{
+ switch (deviceType) {
+ case BDM_TYPE_USB:
+ return "usb";
+ case BDM_TYPE_ILINK:
+ return "ilink";
+ case BDM_TYPE_SDC:
+ return "mx4sio";
+ case BDM_TYPE_ATA:
+ return "ata";
+ default:
+ return NULL;
+ }
+}
+
+static int bdmBuildTruePath(char *path, size_t pathSize, int deviceType, int deviceIndex, int withSlash)
+{
+ const char *prefix = bdmGetDevicePrefix(deviceType);
+ int len;
+
+ if (!path || !pathSize || !prefix || deviceIndex < 0)
+ return 0;
+
+ if (withSlash)
+ len = snprintf(path, pathSize, "%s%d:/", prefix, deviceIndex);
+ else
+ len = snprintf(path, pathSize, "%s%d:", prefix, deviceIndex);
+
+ if (len < 0 || (size_t)len >= pathSize) {
+ path[0] = '\0';
+ return 0;
+ }
+
+ return 1;
+}
+
+static int bdmOpenTrueDevice(int deviceType, int deviceIndex)
+{
+ char path[16];
+
+ if (!bdmBuildTruePath(path, sizeof(path), deviceType, deviceIndex, 1))
+ return -1;
+
+ return fileXioDopen(path);
+}
+
+static int bdmSetDeviceTypeAndTruePrefix(bdm_device_data_t *pDeviceData, item_list_t *itemList, int deviceType, int deviceIndex)
+{
+ if (!pDeviceData)
+ return BDM_TYPE_UNKNOWN;
+
+ pDeviceData->bdmDeviceType = deviceType;
+
+ if (!bdmBuildTruePath(pDeviceData->bdmTruePrefix, sizeof(pDeviceData->bdmTruePrefix), deviceType, deviceIndex, 0)) {
+ pDeviceData->bdmDeviceType = BDM_TYPE_UNKNOWN;
+ pDeviceData->bdmTruePrefix[0] = '\0';
+ return BDM_TYPE_UNKNOWN;
+ }
+
+ if (itemList)
+ itemList->flags = deviceType == BDM_TYPE_ATA ? MODE_FLAG_COMPAT_DMA : 0;
+
+ return pDeviceData->bdmDeviceType;
+}
+
+static int bdmSetupDeviceData(bdm_device_data_t *pDeviceData, item_list_t *itemList, int deviceType, int deviceIndex, int dir)
+{
+ if (!pDeviceData || dir < 0)
+ return 0;
+
+ memset(pDeviceData->bdmDriver, 0, sizeof(pDeviceData->bdmDriver));
+ pDeviceData->bdmTruePrefix[0] = '\0';
+ pDeviceData->bdmPrefix[0] = '\0';
+ pDeviceData->bdmDeviceType = BDM_TYPE_UNKNOWN;
+ pDeviceData->massDeviceIndex = deviceIndex;
+
+ fileXioIoctl2(dir, USBMASS_IOCTL_GET_DRIVERNAME, NULL, 0, &pDeviceData->bdmDriver, sizeof(pDeviceData->bdmDriver) - 1);
+ fileXioIoctl2(dir, USBMASS_IOCTL_GET_DEVICE_NUMBER, NULL, 0, &pDeviceData->massDeviceIndex, sizeof(pDeviceData->massDeviceIndex));
+
+ if (itemList)
+ itemList->flags = 0;
+
+ if (bdmSetDeviceTypeAndTruePrefix(pDeviceData, itemList, deviceType, pDeviceData->massDeviceIndex) == BDM_TYPE_UNKNOWN)
+ return 0;
+
+ if (gBDMPrefix[0] != '\0')
+ snprintf(pDeviceData->bdmPrefix, sizeof(pDeviceData->bdmPrefix), "%s%s/", pDeviceData->bdmTruePrefix, gBDMPrefix);
+ else
+ snprintf(pDeviceData->bdmPrefix, sizeof(pDeviceData->bdmPrefix), "%s", pDeviceData->bdmTruePrefix);
+
+ return 1;
+}
+
+static int bdmBuildUsbMassCompatCandidate(char *out, size_t out_len, const char *truePrefix, const char *tail)
+{
+ int len;
+
+ if (!out || !out_len || !truePrefix || !truePrefix[0])
+ return 0;
+
+ if (tail && tail[0])
+ len = snprintf(out, out_len, "%s%s", truePrefix, tail);
+ else
+ len = snprintf(out, out_len, "%s/", truePrefix);
+
+ if (len < 0 || (size_t)len >= out_len) {
+ out[0] = '\0';
+ return 0;
+ }
+
+ return 1;
+}
+
+static int bdmResolveUsbMassCompatPathPass(char *out, size_t out_len, const char *tail, int massIndex, int strictIndex)
+{
+ int i;
+ struct stat st;
+
+ if (!bdmDeviceListInitialized)
+ return 0;
+
+ for (i = 0; i < MAX_BDM_TRUE_DEVICES; i++) {
+ bdm_device_data_t *pDeviceData = bdmDeviceList[i].priv;
+ char candidate[128];
+
+ if (!pDeviceData || !pDeviceData->bdmTruePrefix[0])
+ continue;
+
+ if (pDeviceData->bdmDeviceType != BDM_TYPE_USB)
+ continue;
+
+ if (strictIndex && massIndex >= 0 && pDeviceData->massDeviceIndex != massIndex)
+ continue;
+
+ if (!bdmBuildUsbMassCompatCandidate(candidate, sizeof(candidate), pDeviceData->bdmTruePrefix, tail))
+ continue;
+
+ if (stat(candidate, &st) != 0)
+ continue;
+
+ snprintf(out, out_len, "%s", candidate);
+ LOG("BDMSUPPORT: resolved USB mass compatibility path '%s'\n", out);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int bdmResolveUsbMassCompatPath(char *out, size_t out_len, const char *path)
+{
+ const char *tail;
+ int massIndex;
+
+ if (!out || !out_len || !path)
+ return 0;
+
+ if (!pathParseDevicePrefix(path, "mass", &massIndex, &tail, 0))
+ return 0;
+
+ // First try a strict match using the reported BDM device number
+ if (bdmResolveUsbMassCompatPathPass(out, out_len, tail, massIndex, 1))
+ return 1;
+
+ // Fallback wLE massN: does not always match the true BDM index
+ if (bdmResolveUsbMassCompatPathPass(out, out_len, tail, massIndex, 0))
+ return 1;
+
+ LOG("BDMSUPPORT: could not resolve USB mass compatibility path '%s'\n", path);
+
+ return 0;
+}
+
int bdmUpdateDeviceData(item_list_t *itemList)
{
- char path[16] = {0};
+ int deviceType;
+ int deviceIndex;
// If bdm mode is disabled bail out as we don't want to update the visibility state of the device pages.
if (gBDMStartMode == START_MODE_DISABLED)
@@ -873,45 +1261,60 @@ int bdmUpdateDeviceData(item_list_t *itemList)
// Get the per-device data and check if the menu item is currently visible.
bdm_device_data_t *pDeviceData = itemList->priv;
+ if (!pDeviceData)
+ return 0;
+
int visible = itemList->owner != NULL ? ((opl_io_module_t *)itemList->owner)->menuItem.visible : 0;
- // Format the device path and try to open the device.
- sprintf(path, "mass%d:/", itemList->mode);
- int dir = fileXioDopen(path);
+ int slotIndex = itemList->mode - BDM_MODE;
+ int found = 0;
+ int dir = -1;
+
+ if (slotIndex < 0 || slotIndex >= MAX_BDM_DEVICES)
+ return 0;
+
+ for (int type = BDM_TYPE_USB; type <= BDM_TYPE_ATA && dir < 0; type++) {
+ if ((type == BDM_TYPE_USB && !gEnableUSB) || (type == BDM_TYPE_ILINK && !gEnableILK) || (type == BDM_TYPE_SDC && !gEnableMX4SIO) || (type == BDM_TYPE_ATA && !gEnableBdmHDD))
+ continue;
+
+ for (int index = 0; index < MAX_BDM_DEVICES; index++) {
+ int testDir = bdmOpenTrueDevice(type, index);
+ if (testDir < 0)
+ continue;
+
+ if (found == slotIndex) {
+ deviceType = type;
+ deviceIndex = index;
+ dir = testDir;
+ break;
+ }
+
+ fileXioDclose(testDir);
+ found++;
+ }
+ }
// LOG("opendir %s -> %d\n", path, dir);
// If we opened the device and the menu isn't visible (OR is visible but hasn't been initialized ex: manual device start) initialize device info.
if (dir >= 0 && (visible == 0 || pDeviceData->bdmPrefix[0] == '\0')) {
- if (gBDMPrefix[0] != '\0')
- snprintf(pDeviceData->bdmPrefix, sizeof(pDeviceData->bdmPrefix), "mass%d:%s/", itemList->mode, gBDMPrefix);
- else
- snprintf(pDeviceData->bdmPrefix, sizeof(pDeviceData->bdmPrefix), "mass%d:", itemList->mode);
+ int hadDevice = pDeviceData->bdmTruePrefix[0] != '\0' || pDeviceData->bdmPrefix[0] != '\0';
+ int oldDeviceType = pDeviceData->bdmDeviceType;
+ int oldMassDeviceIndex = pDeviceData->massDeviceIndex;
+ char oldTruePrefix[sizeof(pDeviceData->bdmTruePrefix)];
- // Get the name of the underlying device driver that backs the fat fs.
- fileXioIoctl2(dir, USBMASS_IOCTL_GET_DRIVERNAME, NULL, 0, &pDeviceData->bdmDriver, sizeof(pDeviceData->bdmDriver) - 1);
- fileXioIoctl2(dir, USBMASS_IOCTL_GET_DEVICE_NUMBER, NULL, 0, &pDeviceData->massDeviceIndex, sizeof(pDeviceData->massDeviceIndex));
+ snprintf(oldTruePrefix, sizeof(oldTruePrefix), "%s", pDeviceData->bdmTruePrefix);
- itemList->flags = 0;
-
- // Determine the bdm device type based on the underlying device driver.
- if (!strcmp(pDeviceData->bdmDriver, "usb"))
- pDeviceData->bdmDeviceType = BDM_TYPE_USB;
- else if (!strcmp(pDeviceData->bdmDriver, "sd") && strlen(pDeviceData->bdmDriver) == 2)
- pDeviceData->bdmDeviceType = BDM_TYPE_ILINK;
- else if (!strcmp(pDeviceData->bdmDriver, "sdc") && strlen(pDeviceData->bdmDriver) == 3)
- pDeviceData->bdmDeviceType = BDM_TYPE_SDC;
- else if (!strcmp(pDeviceData->bdmDriver, "ata") && strlen(pDeviceData->bdmDriver) == 3) {
- pDeviceData->bdmDeviceType = BDM_TYPE_ATA;
- itemList->flags = MODE_FLAG_COMPAT_DMA;
- } else
- pDeviceData->bdmDeviceType = BDM_TYPE_UNKNOWN;
+ if (!bdmSetupDeviceData(pDeviceData, itemList, deviceType, deviceIndex, dir)) {
+ fileXioDclose(dir);
+ return 0;
+ }
// If the device is backed by the ATA driver then get the supported LBA size for the drive.
if (pDeviceData->bdmDeviceType == BDM_TYPE_ATA) {
bdmResolveLBA_UDMA(pDeviceData);
- LOG("Mass device: %d (%d LBA%d UDMA%d) %s -> %s\n", itemList->mode, pDeviceData->massDeviceIndex, (pDeviceData->bdmHddIsLBA48 == 1 ? 48 : 28), pDeviceData->ataHighestUDMAMode, pDeviceData->bdmPrefix, pDeviceData->bdmDriver);
+ LOG("BDM device: %d (%d LBA%d UDMA%d) %s -> %s\n", itemList->mode, pDeviceData->massDeviceIndex, (pDeviceData->bdmHddIsLBA48 == 1 ? 48 : 28), pDeviceData->ataHighestUDMAMode, pDeviceData->bdmPrefix, pDeviceData->bdmDriver);
} else
- LOG("Mass device: %d (%d) %s -> %s\n", itemList->mode, pDeviceData->massDeviceIndex, pDeviceData->bdmPrefix, pDeviceData->bdmDriver);
+ LOG("BDM device: %d (%d) %s -> %s\n", itemList->mode, pDeviceData->massDeviceIndex, pDeviceData->bdmPrefix, pDeviceData->bdmDriver);
// Make the menu item visible.
if (itemList->owner != NULL) {
@@ -921,17 +1324,38 @@ int bdmUpdateDeviceData(item_list_t *itemList)
// Close the device handle.
fileXioDclose(dir);
- return 1;
+
+ if (!hadDevice)
+ return 1;
+
+ if (oldDeviceType != pDeviceData->bdmDeviceType)
+ return 1;
+
+ if (oldMassDeviceIndex != pDeviceData->massDeviceIndex)
+ return 1;
+
+ if (strcmp(oldTruePrefix, pDeviceData->bdmTruePrefix))
+ return 1;
+
+ return 0;
} else if (dir < 0 && visible == 1) {
+ int hadDevice = pDeviceData->bdmTruePrefix[0] != '\0';
+
// Device has been removed, make the menu item invisible. We can't really cleanup resources (like the game list) just yet
// as we don't know if the data is being used asynchronously.
+ pDeviceData->bdmTruePrefix[0] = '\0';
+ pDeviceData->bdmPrefix[0] = '\0';
if (itemList->owner != NULL) {
LOG("bdmUpdateDeviceData: setting device %d invisible\n", itemList->mode);
((opl_io_module_t *)itemList->owner)->menuItem.visible = 0;
}
- LOG("Mass device: %d (%d) disconnected\n", itemList->mode, pDeviceData->massDeviceIndex);
- return -1;
+ if (hadDevice) {
+ LOG("BDM device: %d (%d) disconnected\n", itemList->mode, pDeviceData->massDeviceIndex);
+ return -1;
+ }
+
+ return 0;
}
// No change to the device state detected.
@@ -942,12 +1366,14 @@ int bdmUpdateDeviceData(item_list_t *itemList)
void autoLaunchBDMGame(char *argv[])
{
- char path[256];
- config_set_t *configSet;
-
miniInit(BDM_MODE);
gAutoLaunchBDMGame = malloc(sizeof(base_game_info_t));
+ if (!gAutoLaunchBDMGame) {
+ miniDeinit();
+ return;
+ }
+
memset(gAutoLaunchBDMGame, 0, sizeof(base_game_info_t));
int nameLen;
@@ -975,109 +1401,102 @@ void autoLaunchBDMGame(char *argv[])
gAutoLaunchBDMGame->parts = 1; // ul not supported.
gAutoLaunchDeviceData = malloc(sizeof(bdm_device_data_t));
+ if (!gAutoLaunchDeviceData) {
+ miniDeinit();
+
+ free(gAutoLaunchBDMGame);
+ gAutoLaunchBDMGame = NULL;
+
+ return;
+ }
+
memset(gAutoLaunchDeviceData, 0, sizeof(bdm_device_data_t));
- char apaDevicePrefix[8] = {0};
+ int foundDevice = 0;
+
delay(8);
- snprintf(apaDevicePrefix, sizeof(apaDevicePrefix), "mass0:");
- // Loop through mass0: to mass4:
- for (int i = 0; i <= 4; i++) {
- snprintf(path, sizeof(path), "mass%d:", i);
- int dir = fileXioDopen(path);
-
- if (dir >= 0) {
- fileXioIoctl2(dir, USBMASS_IOCTL_GET_DRIVERNAME, NULL, 0, &gAutoLaunchDeviceData->bdmDriver, sizeof(gAutoLaunchDeviceData->bdmDriver) - 1);
- fileXioIoctl2(dir, USBMASS_IOCTL_GET_DEVICE_NUMBER, NULL, 0, &gAutoLaunchDeviceData->massDeviceIndex, sizeof(gAutoLaunchDeviceData->massDeviceIndex));
-
- if (!strcmp(gAutoLaunchDeviceData->bdmDriver, "ata") && strlen(gAutoLaunchDeviceData->bdmDriver) == 3) {
- bdmResolveLBA_UDMA(gAutoLaunchDeviceData);
- snprintf(apaDevicePrefix, sizeof(apaDevicePrefix), "mass%d:", i);
- fileXioDclose(dir);
- break; // Exit the loop if "ata" device is found
- }
- fileXioDclose(dir);
- } else {
- // Retry for mass0: only
- if (i == 0) {
- delay(6);
- i--;
- } else {
+ // Probe real BDM prefixes only.
+ for (int typeSlot = 0; typeSlot < 4; typeSlot++) {
+ int deviceType;
+
+ switch (typeSlot) {
+ case 0:
+ deviceType = BDM_TYPE_USB;
break;
- }
+ case 1:
+ deviceType = BDM_TYPE_ILINK;
+ break;
+ case 2:
+ deviceType = BDM_TYPE_SDC;
+ break;
+ case 3:
+ deviceType = BDM_TYPE_ATA;
+ break;
+ default:
+ continue;
}
- delay(6);
- }
- if (gBDMPrefix[0] != '\0') {
- snprintf(path, sizeof(path), "%s%s/CFG/%s.cfg", apaDevicePrefix, gBDMPrefix, gAutoLaunchBDMGame->startup);
- snprintf(gAutoLaunchDeviceData->bdmPrefix, sizeof(gAutoLaunchDeviceData->bdmPrefix), "%s%s/", apaDevicePrefix, gBDMPrefix);
- } else {
- snprintf(path, sizeof(path), "%sCFG/%s.cfg", apaDevicePrefix, gAutoLaunchBDMGame->startup);
- snprintf(gAutoLaunchDeviceData->bdmPrefix, sizeof(gAutoLaunchDeviceData->bdmPrefix), "%s", apaDevicePrefix);
- }
-
- configSet = configAlloc(0, NULL, path);
- configRead(configSet);
+ for (int i = 0; i < MAX_BDM_DEVICES; i++) {
+ bdm_device_data_t candidateData;
+ int dir = bdmOpenTrueDevice(deviceType, i);
- bdmLaunchGame(NULL, -1, configSet);
-}
+ if (dir >= 0) {
+ memset(&candidateData, 0, sizeof(candidateData));
+ bdmSetupDeviceData(&candidateData, NULL, deviceType, i, dir);
-static int bdmWaitForDevice(int deviceId, u32 timeoutMs)
-{
- const int RETRY_DELAY = 100;
- char path[16];
+ if (candidateData.bdmDeviceType != BDM_TYPE_UNKNOWN && (!foundDevice || candidateData.bdmDeviceType == BDM_TYPE_ATA)) {
+ memcpy(gAutoLaunchDeviceData, &candidateData, sizeof(bdm_device_data_t));
+ foundDevice = 1;
+ }
- u32 start = GetTimerSystemTime();
- sprintf(path, "mass%d:/", deviceId);
+ if (candidateData.bdmDeviceType == BDM_TYPE_ATA) {
+ bdmResolveLBA_UDMA(gAutoLaunchDeviceData);
+ fileXioDclose(dir);
+ break; // Exit the loop if "ata" device is found
+ }
- while (1) {
- int dir = fileXioDopen(path);
+ fileXioDclose(dir);
+ } else
+ break;
- if (dir >= 0) {
- fileXioDclose(dir);
- return 1; // ready
+ delay(6);
}
+ }
- u32 now = GetTimerSystemTime();
- u32 elapsed_ms = (now - start) / (kBUSCLK / 1000);
-
- if (elapsed_ms > timeoutMs) {
- return 0; // timeout
- }
+ if (!foundDevice) {
+ miniDeinit();
- DelayThread(RETRY_DELAY * 1000);
- }
-}
+ free(gAutoLaunchBDMGame);
+ gAutoLaunchBDMGame = NULL;
-static int bdmDeviceIsPresent(int deviceId)
-{
- char path[16];
- sprintf(path, "mass%d:/", deviceId);
- int dir = fileXioDopen(path);
+ free(gAutoLaunchDeviceData);
+ gAutoLaunchDeviceData = NULL;
- if (dir >= 0) {
- fileXioDclose(dir);
- return 1; // ready
+ return;
}
- return 0;
+ per_game_cfg_t pgcfg;
+ sbPopulateConfig(gAutoLaunchBDMGame, gAutoLaunchDeviceData->bdmPrefix, "/", NULL, &pgcfg);
+
+ bdmLaunchGame(NULL, -1, &pgcfg);
}
static int bdmDeviceIsATA(int deviceId)
{
- char path[16];
bdm_device_data_t data;
- sprintf(path, "mass%d:/", deviceId);
- int dir = fileXioDopen(path);
+ int dir = bdmOpenTrueDevice(BDM_TYPE_ATA, deviceId);
+
if (dir < 0)
return 0;
- fileXioIoctl2(dir, USBMASS_IOCTL_GET_DRIVERNAME, NULL, 0, &data.bdmDriver, sizeof(data.bdmDriver) - 1);
+ memset(&data, 0, sizeof(data));
+ bdmSetupDeviceData(&data, NULL, BDM_TYPE_ATA, deviceId, dir);
+
fileXioDclose(dir);
- return (!strcmp(data.bdmDriver, "ata") && strlen(data.bdmDriver) == 3);
+ return data.bdmDeviceType == BDM_TYPE_ATA;
}
static int bdmGetATADeviceId()
@@ -1092,45 +1511,33 @@ static int bdmGetATADeviceId()
int bdmHDDIsPresent(u32 timeoutMs)
{
- int hdd_id = -1;
- int timedout = 0;
+ const int RETRY_DELAY = 100; // ms
+ u32 start;
if (!hddIsPresent())
return 0;
- // 1. scan via normal methods first...
- hdd_id = bdmGetATADeviceId();
- if (hdd_id >= 0)
+ if (bdmGetATADeviceId() >= 0)
return 1;
- // 2. try to scan as fast as possible if the previous scan fails...
- hdd_id = 0;
+ if (timeoutMs == 0)
+ return 0;
- for (int i = 0; i < MAX_BDM_DEVICES; i++) {
- // find the first inaccessible device - this one should be the HDD once it's mounted (we don't have access to device data at this point yet!)
- if (!bdmDeviceIsPresent(i)) {
- hdd_id = i;
+ start = GetTimerSystemTime();
+
+ while (1) {
+ u32 elapsed_ms = (GetTimerSystemTime() - start) / (kBUSCLK / 1000);
+
+ if (elapsed_ms >= timeoutMs)
break;
- }
- }
- if (bdmWaitForDevice(hdd_id, timeoutMs)) {
- // double-check to see if this indeed is the HDD, and if it is, we can exit early without stalling any further
- if (bdmDeviceIsATA(hdd_id)) {
+ DelayThread(RETRY_DELAY * 1000);
+
+ if (bdmGetATADeviceId() >= 0)
return 1;
- } else
- LOG("bdmHDDIsPresent: device at id %d is not an ATA HDD...\n", hdd_id);
- } else {
- timedout = 1;
- LOG("bdmHDDIsPresent: waiting for hdd at id %d timed out...\n", hdd_id);
}
- // 3. last resort - time out (if needed) and scan again...
- if (!timedout) {
- // if we haven't timed out already, then we need to wait for the devices to wake up... wait for the timeout...
- LOG("bdmHDDIsPresent: waiting for timeout before scanning again...\n", hdd_id);
- DelayThread(timeoutMs * 1000);
- }
+ LOG("bdmHDDIsPresent: waiting for BDM ATA device timed out.\n");
- return bdmGetATADeviceId() >= 0;
-}
\ No newline at end of file
+ return 0;
+}
diff --git a/src/cheatman.c b/src/cheatman.c
index 73cab162..07940679 100644
--- a/src/cheatman.c
+++ b/src/cheatman.c
@@ -34,32 +34,20 @@
#include "include/ioman.h"
#include "include/common.h"
-static int gEnableCheat; // Enables PS2RD Cheat Engine - 0 for Off, 1 for On
-static int gCheatMode; // Cheat Mode - 0 Enable all cheats, 1 Cheats selected by user
+int gEnableCheat; // Enables PS2RD Cheat Engine - 0 for Off, 1 for On
+static int gCheatMode; // Cheat Mode - 0 Enable all cheats, 1 Cheats selected by user
+static int gEnableImage;
int gCheatSource;
static u32 gCheatList[MAX_CHEATLIST]; // Store hooks/codes addr+val pairs
+static u32 gImage[MAX_IMAGEWORDS];
cheat_entry_t gCheats[MAX_CODES];
-void InitCheatsConfig(config_set_t *configSet)
+void InitCheatsConfig(int enable, int mode, int enable_image)
{
- config_set_t *configGame = configGetByType(CONFIG_GAME);
-
- // Default values.
- gCheatSource = 0;
- gEnableCheat = 0;
- gCheatMode = 0;
-
- if (configGetInt(configSet, CONFIG_ITEM_CHEATSSOURCE, &gCheatSource)) {
- // Load the rest of the per-game CHEAT configuration if CHEAT is enabled.
- if (configGetInt(configSet, CONFIG_ITEM_ENABLECHEAT, &gEnableCheat) && gEnableCheat) {
- configGetInt(configSet, CONFIG_ITEM_CHEATMODE, &gCheatMode);
- }
- } else {
- if (configGetInt(configGame, CONFIG_ITEM_ENABLECHEAT, &gEnableCheat) && gEnableCheat) {
- configGetInt(configGame, CONFIG_ITEM_CHEATMODE, &gCheatMode);
- }
- }
+ gEnableCheat = enable;
+ gCheatMode = mode;
+ gEnableImage = enable_image;
}
int GetCheatsEnabled(void)
@@ -72,6 +60,16 @@ u32 *GetCheatsList(void)
return gCheatList;
}
+int GetImageEnabled(void)
+{
+ return gEnableImage;
+}
+
+const u32 *GetImage(void)
+{
+ return gImage;
+}
+
/*
* make_code - Return a code object from string @s.
*/
@@ -363,6 +361,29 @@ static inline char *read_text_file(const char *filename, int maxsize)
return buf;
}
+int LoadImage(const char *filename)
+{
+ int fd = -1;
+ int len;
+ int err = 0;
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ goto fail;
+ len = read(fd, gImage, MAX_IMAGEWORDS * 4);
+ if ((len < 0) || (len > (MAX_IMAGEWORDS * 4)))
+ goto fail;
+
+ goto done;
+
+fail:
+ err = -1;
+
+done:
+ if (fd >= 0)
+ close(fd);
+ return err;
+}
+
/*
* Load cheats from text file.
*/
@@ -389,6 +410,22 @@ int load_cheats(const char *cheatfile)
return (gCheatMode == 0) ? 0 : 1;
}
+int load_cheats_buf(const char *buf, int size)
+{
+ int ret;
+
+ memset(gCheats, 0, sizeof(gCheats));
+
+ if (!buf || size <= 0)
+ return -1;
+
+ ret = parse_buf(buf);
+ if (ret < 0)
+ return ret;
+
+ return (gCheatMode == 0) ? 0 : 1;
+}
+
void set_cheats_list(void)
{
int cheatCount = 0;
diff --git a/src/config.c b/src/config.c
deleted file mode 100644
index 1f0228d5..00000000
--- a/src/config.c
+++ /dev/null
@@ -1,1168 +0,0 @@
-/*
- Copyright 2009, Ifcaro & volca
- Licenced under Academic Free License version 3.0
- Review OpenUsbLd README & LICENSE files for further details.
-*/
-
-#include "include/common.h"
-#include "include/config.h"
-#include "include/util.h"
-#include "include/ioman.h"
-#include "include/sound.h"
-#include "include/gui.h"
-#include "include/menusys.h"
-#include "include/lang.h"
-#include "include/system.h"
-#include "include/pad.h"
-#include "include/renderman.h"
-#include "include/themes.h"
-#include "include/lwnbd.h"
-
-#ifdef __DEBUG
-#include "include/debug.h"
-#endif
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-static u32 currentUID = 0;
-static config_set_t configFiles[CONFIG_INDEX_COUNT];
-static char legacyNetConfigPath[256] = "mc?:SYS-CONF/IPCONFIG.DAT";
-static const char *configFilenames[CONFIG_INDEX_COUNT] = {
- "conf_wopl.cfg",
- "conf_last.cfg",
- "conf_apps.cfg",
- "conf_network.cfg",
- "conf_game.cfg",
-};
-
-char *gBaseMCDir;
-
-static int strToColor(const char *string, unsigned char *color)
-{
- int cnt = 0, n = 0;
- color[0] = 0;
- color[1] = 0;
- color[2] = 0;
-
- if (!string || !*string)
- return 0;
- if (string[0] != '#')
- return 0;
-
- string++;
-
- while (*string) {
- int fh = fromHex(*string);
- if (fh >= 0) {
- color[n] = color[n] * 16 + fh;
- } else {
- break;
- }
-
- // Two characters per color
- if (cnt == 1) {
- cnt = 0;
- n++;
- } else {
- cnt++;
- }
-
- string++;
- }
-
- return 1;
-}
-
-/// true if given a whitespace character
-int isWS(char c)
-{
- return c == ' ' || c == '\t';
-}
-
-static int splitAssignment(char *line, char *key, size_t keymax, char *val, size_t valmax)
-{
- // skip whitespace
- for (; isWS(*line); ++line)
- ;
-
- // find "=".
- // If found, the text before is key, after is val.
- // Otherwise malformed string is encountered
-
- char *eqpos = strchr(line, '=');
-
- if (eqpos) {
- // copy the name and the value
- size_t keylen = MIN(keymax, eqpos - line);
-
- strncpy(key, line, keylen);
-
- eqpos++;
-
- size_t vallen = MIN(valmax, strlen(line) - (eqpos - line));
- strncpy(val, eqpos, vallen);
- }
-
- return (int)eqpos;
-}
-
-static int parsePrefix(char *line, char *prefix)
-{
- // find "=".
- // If found, the text before is key, after is val.
- // Otherwise malformed string is encountered
- char *colpos = strchr(line, ':');
-
- if (colpos && colpos != line) {
- // copy the name and the value
- strncpy(prefix, line, colpos - line);
- prefix[colpos - line] = 0;
-
- return 1;
- } else {
- return 0;
- }
-}
-
-static int configKeyValidate(const char *key)
-{
- if (strlen(key) == 0)
- return 0;
-
- return !strchr(key, '=');
-}
-
-static struct config_value_t *allocConfigItem(const char *key, const char *val)
-{
- struct config_value_t *it = (struct config_value_t *)malloc(sizeof(struct config_value_t));
- strncpy(it->key, key, sizeof(it->key));
- it->key[sizeof(it->key) - 1] = '\0';
- strncpy(it->val, val, sizeof(it->val));
- it->val[sizeof(it->val) - 1] = '\0';
- it->next = NULL;
-
- return it;
-}
-
-/// Low level key addition. Does not check for uniqueness.
-static void addConfigValue(config_set_t *configSet, const char *key, const char *val)
-{
- if (!configSet->tail) {
- configSet->head = allocConfigItem(key, val);
- configSet->tail = configSet->head;
- } else {
- configSet->tail->next = allocConfigItem(key, val);
- configSet->tail = configSet->tail->next;
- }
-}
-
-static struct config_value_t *getConfigItemForName(config_set_t *configSet, const char *name)
-{
- struct config_value_t *val = configSet->head;
-
- while (val) {
- if (strncmp(val->key, name, sizeof(val->key)) == 0)
- break;
-
- val = val->next;
- }
-
- return val;
-}
-
-static char cfgDevice[128];
-
-static int lscstatus = CONFIG_ALL;
-static int lscret = 0;
-
-int configCheckLoadConfigBDM(int types)
-{
- char path[64];
- int value;
- int bdm_result;
- int is_hdd = 0;
-
- // check USB
- bdm_result = bdmFindPartition(path, "conf_wopl.cfg", 0);
- // if not on USB, check BDM HDD
- if (bdm_result == 0) {
- // wait for up to 5 seconds for the HDD to spin up and become accessible...
- if (hddLoadModules() >= 0 && bdmHDDIsPresent(5000)) {
-
- bdm_result = bdmFindPartition(path, "conf_wopl.cfg", 0);
- if (bdm_result)
- is_hdd = 1;
- }
- }
-
- if (bdm_result) {
- configEnd();
- configInit(path);
- value = configReadMulti(types);
- config_set_t *configOPL = configGetByType(CONFIG_OPL);
- configSetInt(configOPL, CONFIG_OPL_BDM_MODE, START_MODE_AUTO);
- if (is_hdd != 0) {
- gEnableBdmHDD = 1;
- configSetInt(configOPL, CONFIG_OPL_ENABLE_BDMHDD, gEnableBdmHDD);
- }
- return value;
- }
-
- return 0;
-}
-
-int configCheckLoadConfigHDD(int types)
-{
- int value;
- char path[64];
-
- hddLoadModules();
- hddLoadSupportModules();
-
- snprintf(path, sizeof(path), "%sconf_wopl.cfg", gHDDPrefix);
- value = open(path, O_RDONLY);
- if (value >= 0) {
- close(value);
- configEnd();
- configInit(gHDDPrefix);
- value = configReadMulti(types);
- config_set_t *configOPL = configGetByType(CONFIG_OPL);
- configSetInt(configOPL, CONFIG_OPL_HDD_MODE, START_MODE_AUTO);
- return value;
- }
-
- return 0;
-}
-
-// When this function is called, the current device for loading/saving config is the memory card.
-static int tryAlternateDevice(int types)
-{
- char pwd[8];
- int value;
- DIR *dir;
-
- getcwd(pwd, sizeof(pwd));
-
- // First, try the device that OPL booted from.
- if (!strncmp(pwd, "mass", 4) && (pwd[4] == ':' || pwd[5] == ':')) {
- if ((value = configCheckLoadConfigBDM(types)) != 0)
- return value;
- } else if (!strncmp(pwd, "hdd", 3) && (pwd[3] == ':' || pwd[4] == ':')) {
- if ((value = configCheckLoadConfigHDD(types)) != 0)
- return value;
- }
-
- // Config was not found on the boot device. Check all supported devices.
- // Check USB device
- if ((value = configCheckLoadConfigBDM(types)) != 0)
- return value;
- // Check HDD
- if ((value = configCheckLoadConfigHDD(types)) != 0)
- return value;
-
- // At this point, the user has no loadable config files on any supported device, so try to find a device to save on.
- // We don't want to get users into alternate mode for their very first launch of OPL (i.e no config file at all, but still want to save on MC)
- // Check for a memory card inserted.
- if (sysCheckMC() >= 0) {
- configPrepareNotifications(gBaseMCDir);
- showCfgPopup = 0;
- return 0;
- }
- // No memory cards? Try a USB device...
- dir = opendir("mass0:");
- if (dir != NULL) {
- closedir(dir);
- configEnd();
- configInit("mass0:");
- } else {
- // No? Check if the save location on the HDD is available.
- dir = opendir(gHDDPrefix);
- if (dir != NULL) {
- closedir(dir);
- configEnd();
- configInit(gHDDPrefix);
- }
- }
- showCfgPopup = 0;
-
- return 0;
-}
-
-void loadConfig()
-{
- int value, themeID = -1, langID = -1;
- const char *temp;
- int result = configReadMulti(lscstatus);
-
- if (lscstatus & CONFIG_OPL) {
- if (!(result & CONFIG_OPL)) {
- result = tryAlternateDevice(lscstatus);
- }
-
- if (result & CONFIG_OPL) {
- config_set_t *configOPL = configGetByType(CONFIG_OPL);
-
- configGetInt(configOPL, CONFIG_OPL_SCROLLING, &gScrollSpeed);
- configGetColor(configOPL, CONFIG_OPL_BGCOLOR, gDefaultBgColor);
- configGetColor(configOPL, CONFIG_OPL_TEXTCOLOR, gDefaultTextColor);
- configGetColor(configOPL, CONFIG_OPL_UI_TEXTCOLOR, gDefaultUITextColor);
- configGetColor(configOPL, CONFIG_OPL_SEL_TEXTCOLOR, gDefaultSelTextColor);
- configGetColor(configOPL, CONFIG_OPL_PLAS_BLEND_COLOR, gDefaultPlasmaBlendColor);
- configGetInt(configOPL, CONFIG_OPL_ENABLE_NOTIFICATIONS, &gEnableNotifications);
- configGetInt(configOPL, CONFIG_OPL_ENABLE_DISCART, &gDiscEnableArt);
- configGetInt(configOPL, CONFIG_OPL_WIDESCREEN, &gWideScreen);
-
- if (!(getKeyPressed(KEY_TRIANGLE) && getKeyPressed(KEY_CROSS))) {
- configGetInt(configOPL, CONFIG_OPL_VMODE, &gVMode);
- } else {
- LOG("--- Select held at boot - setting Video Mode to Auto ---\n");
- gVMode = 0;
- configSetInt(configOPL, CONFIG_OPL_VMODE, gVMode);
- }
-
- configGetInt(configOPL, CONFIG_OPL_XOFF, &gXOff);
- configGetInt(configOPL, CONFIG_OPL_YOFF, &gYOff);
- configGetInt(configOPL, CONFIG_OPL_OVERSCAN, &gOverscan);
-
- configGetInt(configOPL, CONFIG_OPL_BDM_CACHE, &bdmCacheSize);
- configGetInt(configOPL, CONFIG_OPL_HDD_CACHE, &hddCacheSize);
- configGetInt(configOPL, CONFIG_OPL_SMB_CACHE, &smbCacheSize);
-
- if (configGetStr(configOPL, CONFIG_OPL_THEME, &temp))
- themeID = thmFindGuiID(temp);
-
- if (configGetStr(configOPL, CONFIG_OPL_LANGUAGE, &temp))
- langID = lngFindGuiID(temp);
-
- if (configGetInt(configOPL, CONFIG_OPL_SWAP_SEL_BUTTON, &value))
- gSelectButton = value == 0 ? KEY_CIRCLE : KEY_CROSS;
-
- configGetInt(configOPL, CONFIG_OPL_XSENSITIVITY, &gXSensitivity);
- configGetInt(configOPL, CONFIG_OPL_YSENSITIVITY, &gYSensitivity);
- configGetInt(configOPL, CONFIG_OPL_DISABLE_DEBUG, &gEnableDebug);
- configGetInt(configOPL, CONFIG_OPL_BDM_DEBUG, &gBDMDebug);
- configGetInt(configOPL, CONFIG_OPL_PS2LOGO, &gPS2Logo);
- configGetInt(configOPL, CONFIG_OPL_HDD_GAME_LIST_CACHE, &gHDDGameListCache);
- configGetStrCopy(configOPL, CONFIG_OPL_EXIT_PATH, gExitPath, sizeof(gExitPath));
- configGetInt(configOPL, CONFIG_OPL_AUTO_SORT, &gAutosort);
- configGetInt(configOPL, CONFIG_OPL_AUTO_REFRESH, &gAutoRefresh);
- configGetInt(configOPL, CONFIG_OPL_DEFAULT_DEVICE, &gDefaultDevice);
- configGetInt(configOPL, CONFIG_OPL_ENABLE_WRITE, &gEnableWrite);
- configGetInt(configOPL, CONFIG_OPL_HDD_SPINDOWN, &gHDDSpindown);
- configGetStrCopy(configOPL, CONFIG_OPL_MMCE_PREFIX, gMMCEPrefix, sizeof(gMMCEPrefix));
- configGetStrCopy(configOPL, CONFIG_OPL_BDM_PREFIX, gBDMPrefix, sizeof(gBDMPrefix));
- configGetStrCopy(configOPL, CONFIG_OPL_ETH_PREFIX, gETHPrefix, sizeof(gETHPrefix));
- configGetInt(configOPL, CONFIG_OPL_REMEMBER_LAST, &gRememberLastPlayed);
- configGetInt(configOPL, CONFIG_OPL_AUTOSTART_LAST, &gAutoStartLastPlayed);
- configGetInt(configOPL, CONFIG_OPL_BDM_MODE, &gBDMStartMode);
- configGetInt(configOPL, CONFIG_OPL_HDD_MODE, &gHDDStartMode);
- configGetInt(configOPL, CONFIG_OPL_ETH_MODE, &gETHStartMode);
- configGetInt(configOPL, CONFIG_OPL_APP_MODE, &gAPPStartMode);
- configGetInt(configOPL, CONFIG_OPL_FAV_MODE, &gFAVStartMode);
- configGetInt(configOPL, CONFIG_OPL_MMCE_MODE, &gMMCEStartMode);
- configGetInt(configOPL, CONFIG_OPL_MMCE_SLOT, &gMMCESlot);
- configGetInt(configOPL, CONFIG_OPL_MMCEIGR_SLOT, &gMMCEIGRSlot);
-#ifdef __DEBUG
- configGetInt(configOPL, CONFIG_OPL_MMCE_GAMEID, &gMMCEEnableGameID);
-#endif
- configGetInt(configOPL, CONFIG_OPL_MMCE_WAIT_CYCLES, &gMMCEAckWaitCycles);
- configGetInt(configOPL, CONFIG_OPL_MMCE_USE_ALARMS, &gMMCEUseAlarms);
- configGetInt(configOPL, CONFIG_OPL_ENABLE_USB, &gEnableUSB);
- configGetInt(configOPL, CONFIG_OPL_ENABLE_ILINK, &gEnableILK);
- configGetInt(configOPL, CONFIG_OPL_ENABLE_MX4SIO, &gEnableMX4SIO);
- configGetInt(configOPL, CONFIG_OPL_ENABLE_BDMHDD, &gEnableBdmHDD);
- configGetInt(configOPL, CONFIG_OPL_SFX, &gEnableSFX);
- configGetInt(configOPL, CONFIG_OPL_BOOT_SND, &gEnableBootSND);
- configGetInt(configOPL, CONFIG_OPL_BGM, &gEnableBGM);
- configGetInt(configOPL, CONFIG_OPL_SFX_VOLUME, &gSFXVolume);
- configGetInt(configOPL, CONFIG_OPL_BOOT_SND_VOLUME, &gBootSndVolume);
- configGetInt(configOPL, CONFIG_OPL_BGM_VOLUME, &gBGMVolume);
- configGetStrCopy(configOPL, CONFIG_OPL_DEFAULT_BGM_PATH, gDefaultBGMPath, sizeof(gDefaultBGMPath));
- }
- }
-
- if (lscstatus & CONFIG_NETWORK) {
- if (!(result & CONFIG_NETWORK)) {
- result = tryAlternateDevice(lscstatus);
- }
-
- if (result & CONFIG_NETWORK) {
- config_set_t *configNet = configGetByType(CONFIG_NETWORK);
-
- configGetInt(configNet, CONFIG_NET_ETH_LINKM, &gETHOpMode);
-
- configGetInt(configNet, CONFIG_NET_PS2_DHCP, &ps2_ip_use_dhcp);
- configGetInt(configNet, CONFIG_NET_SMB_NBNS, &gPCShareAddressIsNetBIOS);
- configGetStrCopy(configNet, CONFIG_NET_SMB_NB_ADDR, gPCShareNBAddress, sizeof(gPCShareNBAddress));
-
- if (configGetStr(configNet, CONFIG_NET_SMB_IP_ADDR, &temp))
- sscanf(temp, "%d.%d.%d.%d", &pc_ip[0], &pc_ip[1], &pc_ip[2], &pc_ip[3]);
-
- configGetInt(configNet, CONFIG_NET_SMB_PORT, &gPCPort);
-
- configGetStrCopy(configNet, CONFIG_NET_SMB_SHARE, gPCShareName, sizeof(gPCShareName));
- configGetStrCopy(configNet, CONFIG_NET_SMB_USER, gPCUserName, sizeof(gPCUserName));
- configGetStrCopy(configNet, CONFIG_NET_SMB_PASSW, gPCPassword, sizeof(gPCPassword));
-
- if (configGetStr(configNet, CONFIG_NET_PS2_IP, &temp))
- sscanf(temp, "%d.%d.%d.%d", &ps2_ip[0], &ps2_ip[1], &ps2_ip[2], &ps2_ip[3]);
- if (configGetStr(configNet, CONFIG_NET_PS2_NETM, &temp))
- sscanf(temp, "%d.%d.%d.%d", &ps2_netmask[0], &ps2_netmask[1], &ps2_netmask[2], &ps2_netmask[3]);
- if (configGetStr(configNet, CONFIG_NET_PS2_GATEW, &temp))
- sscanf(temp, "%d.%d.%d.%d", &ps2_gateway[0], &ps2_gateway[1], &ps2_gateway[2], &ps2_gateway[3]);
- if (configGetStr(configNet, CONFIG_NET_PS2_DNS, &temp))
- sscanf(temp, "%d.%d.%d.%d", &ps2_dns[0], &ps2_dns[1], &ps2_dns[2], &ps2_dns[3]);
-
- configGetStrCopy(configNet, CONFIG_NET_NBD_DEFAULT_EXPORT, gExportName, sizeof(gExportName));
- }
- }
-
- configApply(themeID, langID, 0);
-
- lscret = result;
- lscstatus = 0;
- showCfgPopup = 1;
-}
-
-static int trySaveConfigBDM(int types)
-{
- char path[64];
- int bdm_result;
-
- // check USB
- bdm_result = bdmFindPartition(path, "conf_wopl.cfg", 1);
- // if not on USB, check BDM HDD
- if (bdm_result == 0) {
- // wait for up to 5 seconds for the HDD to spin up and become accessible...
- if (hddLoadModules() >= 0 && bdmHDDIsPresent(5000)) {
- bdm_result = bdmFindPartition(path, "conf_wopl.cfg", 1);
- }
- }
-
- if (bdm_result) {
- configSetMove(path);
- return configWriteMulti(types);
- }
-
- return -ENOENT;
-}
-
-static int trySaveConfigHDD(int types)
-{
- hddLoadModules();
- // Check that the formatted & usable HDD is connected.
- if (hddCheck() == 0) {
- configSetMove(gHDDPrefix);
- return configWriteMulti(types);
- }
-
- return -ENOENT;
-}
-
-static int trySaveConfigMC(int types)
-{
- configSetMove(NULL);
- return configWriteMulti(types);
-}
-
-static int trySaveAlternateDevice(int types)
-{
- char pwd[8];
- int value;
-
- getcwd(pwd, sizeof(pwd));
-
- // First, try the device that OPL booted from.
- if (!strncmp(pwd, "mass", 4) && (pwd[4] == ':' || pwd[5] == ':')) {
- if ((value = trySaveConfigBDM(types)) > 0)
- return value;
- } else if (!strncmp(pwd, "hdd", 3) && (pwd[3] == ':' || pwd[4] == ':')) {
- if ((value = trySaveConfigHDD(types)) > 0)
- return value;
- }
-
- // Config was not saved to the boot device. Try all supported devices.
- // Try memory cards
- if (sysCheckMC() >= 0) {
- if ((value = trySaveConfigMC(types)) > 0)
- return value;
- }
- // Try a USB device
- if ((value = trySaveConfigBDM(types)) > 0)
- return value;
- // Try the HDD
- if ((value = trySaveConfigHDD(types)) > 0)
- return value;
-
- // We tried everything, but...
- return 0;
-}
-
-static void saveConfig()
-{
- char temp[256];
-
- if (lscstatus & CONFIG_OPL) {
- config_set_t *configOPL = configGetByType(CONFIG_OPL);
- configSetInt(configOPL, CONFIG_OPL_SCROLLING, gScrollSpeed);
- configSetStr(configOPL, CONFIG_OPL_THEME, thmGetValue());
- configSetStr(configOPL, CONFIG_OPL_LANGUAGE, lngGetValue());
- configSetColor(configOPL, CONFIG_OPL_BGCOLOR, gDefaultBgColor);
- configSetColor(configOPL, CONFIG_OPL_TEXTCOLOR, gDefaultTextColor);
- configSetColor(configOPL, CONFIG_OPL_UI_TEXTCOLOR, gDefaultUITextColor);
- configSetColor(configOPL, CONFIG_OPL_SEL_TEXTCOLOR, gDefaultSelTextColor);
- configSetColor(configOPL, CONFIG_OPL_PLAS_BLEND_COLOR, gDefaultPlasmaBlendColor);
- configSetInt(configOPL, CONFIG_OPL_ENABLE_NOTIFICATIONS, gEnableNotifications);
- configSetInt(configOPL, CONFIG_OPL_ENABLE_DISCART, gDiscEnableArt);
- configSetInt(configOPL, CONFIG_OPL_WIDESCREEN, gWideScreen);
- configSetInt(configOPL, CONFIG_OPL_VMODE, gVMode);
- configSetInt(configOPL, CONFIG_OPL_XOFF, gXOff);
- configSetInt(configOPL, CONFIG_OPL_YOFF, gYOff);
- configSetInt(configOPL, CONFIG_OPL_OVERSCAN, gOverscan);
- configSetInt(configOPL, CONFIG_OPL_DISABLE_DEBUG, gEnableDebug);
- configSetInt(configOPL, CONFIG_OPL_BDM_DEBUG, gBDMDebug);
- configSetInt(configOPL, CONFIG_OPL_PS2LOGO, gPS2Logo);
- configSetInt(configOPL, CONFIG_OPL_HDD_GAME_LIST_CACHE, gHDDGameListCache);
- configSetStr(configOPL, CONFIG_OPL_EXIT_PATH, gExitPath);
- configSetInt(configOPL, CONFIG_OPL_AUTO_SORT, gAutosort);
- configSetInt(configOPL, CONFIG_OPL_AUTO_REFRESH, gAutoRefresh);
- configSetInt(configOPL, CONFIG_OPL_DEFAULT_DEVICE, gDefaultDevice);
- configSetInt(configOPL, CONFIG_OPL_ENABLE_WRITE, gEnableWrite);
- configSetInt(configOPL, CONFIG_OPL_HDD_SPINDOWN, gHDDSpindown);
- configSetStr(configOPL, CONFIG_OPL_MMCE_PREFIX, gMMCEPrefix);
- configSetStr(configOPL, CONFIG_OPL_BDM_PREFIX, gBDMPrefix);
- configSetStr(configOPL, CONFIG_OPL_ETH_PREFIX, gETHPrefix);
- configSetInt(configOPL, CONFIG_OPL_REMEMBER_LAST, gRememberLastPlayed);
- configSetInt(configOPL, CONFIG_OPL_AUTOSTART_LAST, gAutoStartLastPlayed);
- configSetInt(configOPL, CONFIG_OPL_BDM_MODE, gBDMStartMode);
- configSetInt(configOPL, CONFIG_OPL_HDD_MODE, gHDDStartMode);
- configSetInt(configOPL, CONFIG_OPL_ETH_MODE, gETHStartMode);
- configSetInt(configOPL, CONFIG_OPL_APP_MODE, gAPPStartMode);
- configSetInt(configOPL, CONFIG_OPL_FAV_MODE, gFAVStartMode);
- configSetInt(configOPL, CONFIG_OPL_MMCE_MODE, gMMCEStartMode);
- configSetInt(configOPL, CONFIG_OPL_MMCE_SLOT, gMMCESlot);
- configSetInt(configOPL, CONFIG_OPL_MMCEIGR_SLOT, gMMCEIGRSlot);
-#ifdef __DEBUG
- configSetInt(configOPL, CONFIG_OPL_MMCE_GAMEID, gMMCEEnableGameID);
-#endif
- configSetInt(configOPL, CONFIG_OPL_MMCE_WAIT_CYCLES, gMMCEAckWaitCycles);
- configSetInt(configOPL, CONFIG_OPL_MMCE_USE_ALARMS, gMMCEUseAlarms);
- configSetInt(configOPL, CONFIG_OPL_BDM_CACHE, bdmCacheSize);
- configSetInt(configOPL, CONFIG_OPL_HDD_CACHE, hddCacheSize);
- configSetInt(configOPL, CONFIG_OPL_SMB_CACHE, smbCacheSize);
- configSetInt(configOPL, CONFIG_OPL_ENABLE_USB, gEnableUSB);
- configSetInt(configOPL, CONFIG_OPL_ENABLE_ILINK, gEnableILK);
- configSetInt(configOPL, CONFIG_OPL_ENABLE_MX4SIO, gEnableMX4SIO);
- configSetInt(configOPL, CONFIG_OPL_ENABLE_BDMHDD, gEnableBdmHDD);
- configSetInt(configOPL, CONFIG_OPL_SFX, gEnableSFX);
- configSetInt(configOPL, CONFIG_OPL_BOOT_SND, gEnableBootSND);
- configSetInt(configOPL, CONFIG_OPL_BGM, gEnableBGM);
- configSetInt(configOPL, CONFIG_OPL_SFX_VOLUME, gSFXVolume);
- configSetInt(configOPL, CONFIG_OPL_BOOT_SND_VOLUME, gBootSndVolume);
- configSetInt(configOPL, CONFIG_OPL_BGM_VOLUME, gBGMVolume);
- configSetStr(configOPL, CONFIG_OPL_DEFAULT_BGM_PATH, gDefaultBGMPath);
- configSetInt(configOPL, CONFIG_OPL_XSENSITIVITY, gXSensitivity);
- configSetInt(configOPL, CONFIG_OPL_YSENSITIVITY, gYSensitivity);
-
- configSetInt(configOPL, CONFIG_OPL_SWAP_SEL_BUTTON, gSelectButton == KEY_CIRCLE ? 0 : 1);
- }
-
- if (lscstatus & CONFIG_NETWORK) {
- config_set_t *configNet = configGetByType(CONFIG_NETWORK);
-
- snprintf(temp, sizeof(temp), "%d.%d.%d.%d", ps2_ip[0], ps2_ip[1], ps2_ip[2], ps2_ip[3]);
- configSetStr(configNet, CONFIG_NET_PS2_IP, temp);
- snprintf(temp, sizeof(temp), "%d.%d.%d.%d", ps2_netmask[0], ps2_netmask[1], ps2_netmask[2], ps2_netmask[3]);
- configSetStr(configNet, CONFIG_NET_PS2_NETM, temp);
- snprintf(temp, sizeof(temp), "%d.%d.%d.%d", ps2_gateway[0], ps2_gateway[1], ps2_gateway[2], ps2_gateway[3]);
- configSetStr(configNet, CONFIG_NET_PS2_GATEW, temp);
- snprintf(temp, sizeof(temp), "%d.%d.%d.%d", ps2_dns[0], ps2_dns[1], ps2_dns[2], ps2_dns[3]);
- configSetStr(configNet, CONFIG_NET_PS2_DNS, temp);
-
- configSetInt(configNet, CONFIG_NET_ETH_LINKM, gETHOpMode);
- configSetInt(configNet, CONFIG_NET_PS2_DHCP, ps2_ip_use_dhcp);
- configSetInt(configNet, CONFIG_NET_SMB_NBNS, gPCShareAddressIsNetBIOS);
- configSetStr(configNet, CONFIG_NET_SMB_NB_ADDR, gPCShareNBAddress);
- snprintf(temp, sizeof(temp), "%d.%d.%d.%d", pc_ip[0], pc_ip[1], pc_ip[2], pc_ip[3]);
- configSetStr(configNet, CONFIG_NET_SMB_IP_ADDR, temp);
- configSetInt(configNet, CONFIG_NET_SMB_PORT, gPCPort);
- configSetStr(configNet, CONFIG_NET_SMB_SHARE, gPCShareName);
- configSetStr(configNet, CONFIG_NET_SMB_USER, gPCUserName);
- configSetStr(configNet, CONFIG_NET_SMB_PASSW, gPCPassword);
- }
-
- char *path = configGetDir();
- if (!strncmp(path, "mc", 2)) {
- sbCheckMCFolder();
- configPrepareNotifications(gBaseMCDir);
- }
-
- lscret = configWriteMulti(lscstatus);
- if (lscret == 0)
- lscret = trySaveAlternateDevice(lscstatus);
- lscstatus = 0;
-}
-
-
-char *configGetDir(void)
-{
- static char path[256];
-
- if (!strncmp(cfgDevice, "mc", 2))
- snprintf(path, sizeof(path), "mc%d:wOPL/", sbGetmcID() & 1);
- else
- snprintf(path, sizeof(path), "%s", cfgDevice);
-
- return path;
-}
-
-void configPrepareNotifications(char *prefix)
-{
- snprintf(cfgDevice, sizeof(cfgDevice), prefix);
-}
-
-void configInit(char *prefix)
-{
- char path[256];
- int i;
-
- if (prefix)
- snprintf(legacyNetConfigPath, sizeof(legacyNetConfigPath), "%s/IPCONFIG.DAT", prefix);
- else
- prefix = gBaseMCDir;
-
- for (i = 0; i < CONFIG_INDEX_COUNT; i++) {
- snprintf(path, sizeof(path), "%s/%s", prefix, configFilenames[i]);
- configAlloc(1 << i, &configFiles[i], path);
- }
-
- configPrepareNotifications(prefix);
-}
-
-void configSetMove(char *prefix)
-{
- char path[256];
- int i;
-
- if (prefix)
- snprintf(legacyNetConfigPath, sizeof(legacyNetConfigPath), "%s/IPCONFIG.DAT", prefix);
- else
- prefix = gBaseMCDir;
-
- for (i = 0; i < CONFIG_INDEX_COUNT; i++) {
- snprintf(path, sizeof(path), "%s/%s", prefix, configFilenames[i]);
- configMove(&configFiles[i], path);
- }
-
- configPrepareNotifications(prefix);
-}
-
-void configEnd()
-{
- int index = 0;
- while (index < CONFIG_INDEX_COUNT) {
- config_set_t *configSet = &configFiles[index];
-
- configClear(configSet);
- free(configSet->filename);
- configSet->filename = NULL;
- index++;
- }
-}
-
-config_set_t *configAlloc(int type, config_set_t *configSet, char *fileName)
-{
- if (!configSet)
- configSet = (config_set_t *)malloc(sizeof(config_set_t));
-
- configSet->uid = ++currentUID;
- configSet->type = type;
- configSet->head = NULL;
- configSet->tail = NULL;
- if (fileName) {
- int length = strlen(fileName) + 1;
- configSet->filename = (char *)malloc(length * sizeof(char));
- memcpy(configSet->filename, fileName, length);
- } else
- configSet->filename = NULL;
- configSet->modified = 0;
- return configSet;
-}
-
-void configMove(config_set_t *configSet, const char *fileName)
-{
- int length = strlen(fileName) + 1;
- configSet->filename = realloc(configSet->filename, length);
- memcpy(configSet->filename, fileName, length);
-}
-
-void configFree(config_set_t *configSet)
-{
- configClear(configSet);
- free(configSet->filename);
- free(configSet);
-}
-
-config_set_t *configGetByType(int type)
-{
- int index = 0;
- while (index < CONFIG_INDEX_COUNT) {
- config_set_t *configSet = &configFiles[index];
-
- if (configSet->type == type)
- return configSet;
- index++;
- }
- return NULL;
-}
-
-int configSetStr(config_set_t *configSet, const char *key, const char *value)
-{
- if (!configKeyValidate(key))
- return 0;
-
- struct config_value_t *it = getConfigItemForName(configSet, key);
-
- if (it) {
- if (strncmp(it->val, value, sizeof(it->val)) != 0) {
- strncpy(it->val, value, sizeof(it->val));
- it->val[sizeof(it->val) - 1] = '\0';
- if (it->key[0] != '#')
- configSet->modified = 1;
- }
- } else {
- addConfigValue(configSet, key, value);
- if (key[0] != '#')
- configSet->modified = 1;
- }
-
- return 1;
-}
-
-// sets the value to point to the value str in the config. Do not overwrite - it will overwrite the string in config
-int configGetStr(config_set_t *configSet, const char *key, const char **value)
-{
- if (!configKeyValidate(key))
- return 0;
-
- struct config_value_t *it = getConfigItemForName(configSet, key);
-
- if (it) {
- *value = it->val;
- return 1;
- } else
- return 0;
-}
-
-int configGetStrCopy(config_set_t *configSet, const char *key, char *value, int length)
-{
- const char *valref = NULL;
- if (configGetStr(configSet, key, &valref)) {
- strncpy(value, valref, length);
- value[length - 1] = '\0';
- return 1;
- } else {
- value[0] = '\0';
- return 0;
- }
-}
-
-int configSetInt(config_set_t *configSet, const char *key, const int value)
-{
- char tmp[12];
- snprintf(tmp, sizeof(tmp), "%d", value);
- return configSetStr(configSet, key, tmp);
-}
-
-int configGetInt(config_set_t *configSet, const char *key, int *value)
-{
- const char *valref = NULL;
- if (configGetStr(configSet, key, &valref)) {
- *value = atoi(valref);
- return 1;
- } else {
- return 0;
- }
-}
-
-int configSetColor(config_set_t *configSet, const char *key, unsigned char *color)
-{
- char tmp[8];
- snprintf(tmp, sizeof(tmp), "#%02X%02X%02X", color[0], color[1], color[2]);
- return configSetStr(configSet, key, tmp);
-}
-
-int configGetColor(config_set_t *configSet, const char *key, unsigned char *color)
-{
- const char *valref = NULL;
- if (configGetStr(configSet, key, &valref)) {
- strToColor(valref, color);
- return 1;
- } else {
- return 0;
- }
-}
-
-int configRemoveKey(config_set_t *configSet, const char *key)
-{
- if (!configKeyValidate(key))
- return 0;
-
- struct config_value_t *val = configSet->head;
- struct config_value_t *prev = NULL;
-
- while (val) {
- if (strncmp(val->key, key, sizeof(val->key)) == 0) {
- if (key[0] != '#')
- configSet->modified = 1;
-
- if (val == configSet->tail)
- configSet->tail = prev;
-
- val = val->next;
- if (prev) {
- free(prev->next);
- prev->next = val;
- } else {
- free(configSet->head);
- configSet->head = val;
- }
- } else {
- prev = val;
- val = val->next;
- }
- }
-
- return 1;
-}
-
-static int configReadLegacyIP(void)
-{
- config_set_t *configSet;
- char temp[16];
-
- int fd = sbOpenFile(legacyNetConfigPath, O_RDONLY);
- if (fd >= 0) {
- char ipconfig[256];
- int size = sbGetFileSize(fd);
- read(fd, &ipconfig, size);
- close(fd);
-
- sscanf(ipconfig, "%d.%d.%d.%d %d.%d.%d.%d %d.%d.%d.%d", &ps2_ip[0], &ps2_ip[1], &ps2_ip[2], &ps2_ip[3],
- &ps2_netmask[0], &ps2_netmask[1], &ps2_netmask[2], &ps2_netmask[3],
- &ps2_gateway[0], &ps2_gateway[1], &ps2_gateway[2], &ps2_gateway[3]);
-
- configSet = &configFiles[CONFIG_INDEX_NETWORK];
-
- snprintf(temp, sizeof(temp), "%d.%d.%d.%d", ps2_ip[0], ps2_ip[1], ps2_ip[2], ps2_ip[3]);
- configSetStr(configSet, CONFIG_NET_PS2_IP, temp);
- snprintf(temp, sizeof(temp), "%d.%d.%d.%d", ps2_netmask[0], ps2_netmask[1], ps2_netmask[2], ps2_netmask[3]);
- configSetStr(configSet, CONFIG_NET_PS2_NETM, temp);
- snprintf(temp, sizeof(temp), "%d.%d.%d.%d", ps2_gateway[0], ps2_gateway[1], ps2_gateway[2], ps2_gateway[3]);
- configSetStr(configSet, CONFIG_NET_PS2_GATEW, temp);
- // The legacy format has no setting for the DNS server, so duplicate the gateway address.
- configSetStr(configSet, CONFIG_NET_PS2_DNS, temp);
-
- return 1;
- }
-
- return 0;
-}
-
-// dst has to have 5 bytes space
-void configGetDiscIDBinary(config_set_t *configSet, void *dst)
-{
- memset(dst, 0, 5);
-
- const char *gid = NULL;
- if (configGetStr(configSet, CONFIG_ITEM_DNAS, &gid)) {
- // convert from hex to binary
- char *cdst = dst;
- int p = 0;
- while (*gid && p < 10) {
- int dv = -1;
-
- while (dv < 0 && *gid) // skip spaces, etc
- dv = fromHex(*(gid++));
-
- if (dv < 0)
- break;
-
- *cdst = *cdst * 16 + dv;
- if ((++p & 1) == 0)
- cdst++;
- }
- }
-}
-
-static int configReadFileBuffer(file_buffer_t *fileBuffer, config_set_t *configSet)
-{
- char *line;
- unsigned int lineno = 0;
-
- char prefix[CONFIG_KEY_NAME_LEN];
- memset(prefix, 0, sizeof(prefix));
-
- while (sbReadFileBuffer(fileBuffer, &line)) {
- lineno++;
-
- char key[CONFIG_KEY_NAME_LEN], val[CONFIG_KEY_VALUE_LEN];
- memset(key, 0, sizeof(key));
- memset(val, 0, sizeof(val));
-
- if (splitAssignment(line, key, sizeof(key), val, sizeof(val))) {
- /* if the line does not start with whitespace,
- * the prefix ends and we have to reset it
- */
- if (!isWS(line[0]))
- memset(prefix, 0, sizeof(prefix));
-
- // insert config value
- if (prefix[0]) {
- // we have a prefix
- char composedKey[CONFIG_KEY_NAME_LEN];
-
- snprintf(composedKey, sizeof(composedKey), "%s_%s", prefix, key);
- configSetStr(configSet, composedKey, val);
- } else {
- configSetStr(configSet, key, val);
- }
- } else if (parsePrefix(line, prefix)) {
- // prefix is set, that's about it
- } else {
- LOG("CONFIG Malformed file '%s' line %d: '%s'\n", configSet->filename, lineno, line);
- }
- }
- configSet->modified = 0;
- return 1;
-}
-
-int configReadBuffer(config_set_t *configSet, const void *buffer, int size)
-{
- int ret;
- file_buffer_t *fileBuffer = sbOpenFileBufferBuffer(0, buffer, size);
- if (!fileBuffer) {
- configSet->modified = 0;
- return 0;
- }
-
- ret = configReadFileBuffer(fileBuffer, configSet);
-
- sbCloseFileBuffer(fileBuffer);
- return ret;
-}
-
-int configRead(config_set_t *configSet)
-{
- int ret;
- file_buffer_t *fileBuffer = sbOpenFileBuffer(configSet->filename, O_RDONLY, 0, 4096);
- if (!fileBuffer) {
- LOG("CONFIG No file %s.\n", configSet->filename);
- configSet->modified = 0;
- return 0;
- }
-
- ret = configReadFileBuffer(fileBuffer, configSet);
-
- sbCloseFileBuffer(fileBuffer);
- return ret;
-}
-
-int configWrite(config_set_t *configSet)
-{
- if (configSet->modified) {
- file_buffer_t *fileBuffer = sbOpenFileBuffer(configSet->filename, O_WRONLY | O_CREAT | O_TRUNC, 0, 4096);
- if (fileBuffer) {
- char line[512];
-
- bgmMute();
- struct config_value_t *cur = configSet->head;
- while (cur) {
- if ((cur->key[0] != '\0') && (cur->key[0] != '#')) {
- snprintf(line, sizeof(line), "%s=%s\r\n", cur->key, cur->val); // add windows CR+LF (0x0D 0x0A)
- sbWriteFileBuffer(fileBuffer, line, strlen(line));
- }
-
- // and advance
- cur = cur->next;
- }
-
- sbCloseFileBuffer(fileBuffer);
- configSet->modified = 0;
- bgmUnMute();
- return 1;
- }
- return 0;
- }
- return 1;
-}
-
-void configClear(config_set_t *configSet)
-{
- while (configSet->head) {
- struct config_value_t *cur = configSet->head;
- configSet->head = cur->next;
-
- free(cur);
- }
-
- configSet->head = NULL;
- configSet->tail = NULL;
- configSet->modified = 1;
-}
-
-int configReadMulti(int types)
-{
- int result = 0, index;
-
- for (index = 0; index < CONFIG_INDEX_COUNT; index++) {
- config_set_t *configSet = &configFiles[index];
-
- if (configSet->type & types) {
- configClear(configSet);
- if (configRead(configSet))
- result |= configSet->type;
- }
- }
-
- // If the network configuration is to be loaded and one cannot be loaded, attempt to load from the legacy network config file.
- if ((types & CONFIG_NETWORK) && !(result & CONFIG_NETWORK))
- if (configReadLegacyIP())
- result |= CONFIG_NETWORK;
-
- return result;
-}
-
-int configWriteMulti(int types)
-{
- int result = 0, index;
-
- for (index = 0; index < CONFIG_INDEX_COUNT; index++) {
- config_set_t *configSet = &configFiles[index];
-
- if (configSet->type & types)
- result += configWrite(configSet);
- }
-
- return result;
-}
-
-void configGetVMC(config_set_t *configSet, char *vmc, int length, int slot)
-{
- char gkey[CONFIG_KEY_NAME_LEN];
- snprintf(gkey, sizeof(gkey), "%s_%d", CONFIG_ITEM_VMC, slot);
- configGetStrCopy(configSet, gkey, vmc, length);
-}
-
-void configSetVMC(config_set_t *configSet, const char *vmc, int slot)
-{
- char gkey[CONFIG_KEY_NAME_LEN];
- if (vmc[0] == '\0') {
- configRemoveVMC(configSet, slot);
- return;
- }
- snprintf(gkey, sizeof(gkey), "%s_%d", CONFIG_ITEM_VMC, slot);
- configSetStr(configSet, gkey, vmc);
-}
-
-void configRemoveVMC(config_set_t *configSet, int slot)
-{
- char gkey[CONFIG_KEY_NAME_LEN];
- snprintf(gkey, sizeof(gkey), "%s_%d", CONFIG_ITEM_VMC, slot);
- configRemoveKey(configSet, gkey);
-}
-
-
-void configApply(int themeID, int langID, int skipDeviceRefresh)
-{
- if (gDefaultDevice < 0 || gDefaultDevice > MMCE_MODE)
- gDefaultDevice = APP_MODE;
-
- guiUpdateScrollSpeed();
-
- guiSetFrameHook(&menuUpdateHook);
-
- guiLock();
- int changed = rmSetMode(0);
- guiUnlock();
- if (changed) {
- bgmMute();
- // reinit the graphics...
- thmReloadScreenExtents();
- guiReloadScreenExtents();
- }
-
- // theme must be set after color, and lng after theme
- changed = thmSetGuiValue(themeID, changed);
- int langChanged = lngSetGuiValue(langID);
-
- guiUpdateScreenScale();
-
- // Check if we should refresh device support as well.
- if (skipDeviceRefresh == 0) {
- initAllSupport(0);
-
- for (int i = 0; i < MODE_COUNT; i++) {
- if (list_support[i].support == NULL)
- continue;
-
- moduleUpdateMenuInternal(&list_support[i], changed, langChanged);
- }
- } else {
- if (changed) {
- for (int i = 0; i < MODE_COUNT; i++) {
- if (list_support[i].support && list_support[i].subMenu)
- submenuRebuildCache(list_support[i].subMenu);
- }
- }
- }
-
- bgmUnMute();
-
-#ifdef __DEBUG
- debugApplyConfig();
-#endif
-}
-
-int configLoad(int types)
-{
- lscstatus = types;
- lscret = 0;
-
- guiHandleDeferedIO(&lscstatus, _l(_STR_LOADING_SETTINGS), IO_CUSTOM_SIMPLEACTION, &loadConfig);
-
- return lscret;
-}
-
-int configSave(int types, int showUI)
-{
- char notification[128];
- lscstatus = types;
- lscret = 0;
-
- guiHandleDeferedIO(&lscstatus, _l(_STR_SAVING_SETTINGS), IO_CUSTOM_SIMPLEACTION, &saveConfig);
-
- if (showUI) {
- if (lscret) {
- char *path = configGetDir();
- if (path != NULL) {
- char *colpos = strchr(path, ':');
- if (colpos != NULL)
- *(colpos + 1) = '\0';
- }
-
- snprintf(notification, sizeof(notification), _l(_STR_SETTINGS_SAVED), path);
-
- guiMsgBox(notification, 0, NULL);
- } else
- guiMsgBox(_l(_STR_ERROR_SAVING_SETTINGS), 0, NULL);
- }
-
- return lscret;
-}
\ No newline at end of file
diff --git a/src/config_migration.c b/src/config_migration.c
new file mode 100644
index 00000000..1aaba965
--- /dev/null
+++ b/src/config_migration.c
@@ -0,0 +1,1413 @@
+// ---------------------------------------------------------------------------
+// Legacy migration
+//
+// Called when libconfig fails to parse the file.. the file is in the old
+// key=value format.. uses the existing config.c to read all
+// values into globals, then wOPLSave() immediately rewrites them in
+// the new libconfig format.. phase out eventually
+// ---------------------------------------------------------------------------
+// DELETE_WITH_MIGRATION whole file
+
+#include "include/common.h"
+#include "include/config_wopl.h"
+#include "include/ioman.h"
+#include "include/util.h"
+#include "include/gui.h"
+#include "include/renderman.h"
+#include "include/system.h"
+#include "include/themes.h"
+#include "include/lang.h"
+#include "include/pad.h"
+#include "include/sound.h"
+#include "include/lwnbd.h"
+#include "include/supportbase.h"
+#include "include/config_migration.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#ifdef __DEBUG
+#include "include/debug.h"
+#endif
+
+#ifdef GSM
+#include "include/pggsm.h"
+#endif
+
+#ifdef CHEAT
+#include "include/cheatman.h"
+#endif
+
+static int configReadLegacyIP(void);
+
+#define CFG_MIG_HAS_PG 1
+#define CFG_MIG_HAS_INFO 2
+
+static void cfgClearStackConfig(config_set_t *cfg)
+{
+ if (!cfg)
+ return;
+
+ configClear(cfg);
+ free(cfg->filename);
+ cfg->filename = NULL;
+}
+
+static int cfgFileExists(const char *path)
+{
+ FILE *f = fopen(path, "r");
+ if (!f)
+ return 0;
+
+ fclose(f);
+ return 1;
+}
+
+static int cfgWriteLibconfig(config_t *cfg, const char *path, int keepBackup)
+{
+ char tmp[256];
+ char bak[256];
+ int hadOriginal;
+ int backedOriginal = 0;
+
+ snprintf(tmp, sizeof(tmp), "%s.tmp", path);
+ snprintf(bak, sizeof(bak), "%s.bak", path);
+
+ hadOriginal = cfgFileExists(path);
+
+ unlink(tmp);
+
+ if (!config_write_file(cfg, tmp)) {
+ LOG("CONFIG_MIGRATION: failed to write temp file '%s'\n", tmp);
+ unlink(tmp);
+ return 0;
+ }
+
+ if (hadOriginal) {
+ unlink(bak);
+
+ if (rename(path, bak) != 0) {
+ LOG("CONFIG_MIGRATION: failed to backup '%s' to '%s'\n", path, bak);
+ unlink(tmp);
+ return 0;
+ }
+
+ backedOriginal = 1;
+ }
+
+ if (rename(tmp, path) != 0) {
+ LOG("CONFIG_MIGRATION: failed to rename '%s' to '%s'\n", tmp, path);
+
+ if (backedOriginal)
+ rename(bak, path);
+
+ unlink(tmp);
+ return 0;
+ }
+
+ if (backedOriginal && !keepBackup)
+ unlink(bak);
+
+ return 1;
+}
+
+int cfgMigrateLegacyOPL(const char *path, int *out_theme_id, int *out_lang_id)
+{
+ config_set_t legacy;
+ config_set_t *cfg = configAlloc(CONFIG_OPL, &legacy, (char *)path);
+ if (!cfg)
+ return 0;
+
+ if (!configRead(cfg)) {
+ cfgClearStackConfig(cfg);
+ return 0;
+ }
+
+ const char *temp;
+ int value;
+
+ configGetInt(cfg, CONFIG_OPL_SCROLLING, &gScrollSpeed);
+ configGetColor(cfg, CONFIG_OPL_BGCOLOR, gDefaultBgColor);
+ configGetColor(cfg, CONFIG_OPL_TEXTCOLOR, gDefaultTextColor);
+ configGetColor(cfg, CONFIG_OPL_UI_TEXTCOLOR, gDefaultUITextColor);
+ configGetColor(cfg, CONFIG_OPL_SEL_TEXTCOLOR, gDefaultSelTextColor);
+ configGetColor(cfg, CONFIG_OPL_PLAS_BLEND_COLOR, gDefaultPlasmaBlendColor);
+ configGetInt(cfg, CONFIG_OPL_ENABLE_NOTIFICATIONS, &gEnableNotifications);
+ configGetInt(cfg, CONFIG_OPL_ENABLE_DISCART, &gDiscEnableArt);
+ configGetInt(cfg, CONFIG_OPL_WIDESCREEN, &gWideScreen);
+ configGetInt(cfg, CONFIG_OPL_VMODE, &gVMode);
+ configGetInt(cfg, CONFIG_OPL_XOFF, &gXOff);
+ configGetInt(cfg, CONFIG_OPL_YOFF, &gYOff);
+ configGetInt(cfg, CONFIG_OPL_OVERSCAN, &gOverscan);
+ configGetInt(cfg, CONFIG_OPL_BDM_CACHE, &bdmCacheSize);
+ configGetInt(cfg, CONFIG_OPL_HDD_CACHE, &hddCacheSize);
+ configGetInt(cfg, CONFIG_OPL_SMB_CACHE, &smbCacheSize);
+
+ if (configGetStr(cfg, CONFIG_OPL_THEME, &temp))
+ *out_theme_id = thmFindGuiID(temp);
+ if (configGetStr(cfg, CONFIG_OPL_LANGUAGE, &temp))
+ *out_lang_id = lngFindGuiID(temp);
+
+ if (configGetInt(cfg, CONFIG_OPL_SWAP_SEL_BUTTON, &value))
+ gSelectButton = value == 0 ? KEY_CIRCLE : KEY_CROSS;
+
+ configGetInt(cfg, CONFIG_OPL_XSENSITIVITY, &gXSensitivity);
+ configGetInt(cfg, CONFIG_OPL_YSENSITIVITY, &gYSensitivity);
+ configGetInt(cfg, CONFIG_OPL_DISABLE_DEBUG, &gEnableDebug);
+ configGetInt(cfg, CONFIG_OPL_BDM_DEBUG, &gBDMDebug);
+ configGetInt(cfg, CONFIG_OPL_PS2LOGO, &gPS2Logo);
+ configGetInt(cfg, CONFIG_OPL_HDD_GAME_LIST_CACHE, &gHDDGameListCache);
+ configGetStrCopy(cfg, CONFIG_OPL_EXIT_PATH, gExitPath, sizeof(gExitPath));
+ configGetInt(cfg, CONFIG_OPL_AUTO_SORT, &gAutosort);
+ configGetInt(cfg, CONFIG_OPL_AUTO_REFRESH, &gAutoRefresh);
+ configGetInt(cfg, CONFIG_OPL_DEFAULT_DEVICE, &gDefaultDevice);
+ configGetInt(cfg, CONFIG_OPL_ENABLE_WRITE, &gEnableWrite);
+ configGetInt(cfg, CONFIG_OPL_HDD_SPINDOWN, &gHDDSpindown);
+ configGetStrCopy(cfg, CONFIG_OPL_MMCE_PREFIX, gMMCEPrefix, sizeof(gMMCEPrefix));
+ configGetStrCopy(cfg, CONFIG_OPL_BDM_PREFIX, gBDMPrefix, sizeof(gBDMPrefix));
+ configGetStrCopy(cfg, CONFIG_OPL_ETH_PREFIX, gETHPrefix, sizeof(gETHPrefix));
+ configGetInt(cfg, CONFIG_OPL_REMEMBER_LAST, &gRememberLastPlayed);
+ configGetInt(cfg, CONFIG_OPL_AUTOSTART_LAST, &gAutoStartLastPlayed);
+ configGetInt(cfg, CONFIG_OPL_BDM_MODE, &gBDMStartMode);
+ configGetInt(cfg, CONFIG_OPL_HDD_MODE, &gHDDStartMode);
+ configGetInt(cfg, CONFIG_OPL_ETH_MODE, &gETHStartMode);
+ configGetInt(cfg, CONFIG_OPL_APP_MODE, &gAPPStartMode);
+ configGetInt(cfg, CONFIG_OPL_FAV_MODE, &gFAVStartMode);
+ configGetInt(cfg, CONFIG_OPL_MMCE_MODE, &gMMCEStartMode);
+ configGetInt(cfg, CONFIG_OPL_MMCE_SLOT, &gMMCESlot);
+ configGetInt(cfg, CONFIG_OPL_MMCEIGR_SLOT, &gMMCEIGRSlot);
+ configGetInt(cfg, CONFIG_OPL_MMCE_WAIT_CYCLES, &gMMCEAckWaitCycles);
+ configGetInt(cfg, CONFIG_OPL_MMCE_USE_ALARMS, &gMMCEUseAlarms);
+ configGetInt(cfg, CONFIG_OPL_ENABLE_USB, &gEnableUSB);
+ configGetInt(cfg, CONFIG_OPL_ENABLE_ILINK, &gEnableILK);
+ configGetInt(cfg, CONFIG_OPL_ENABLE_MX4SIO, &gEnableMX4SIO);
+ configGetInt(cfg, CONFIG_OPL_ENABLE_BDMHDD, &gEnableBdmHDD);
+ configGetInt(cfg, CONFIG_OPL_SFX, &gEnableSFX);
+ configGetInt(cfg, CONFIG_OPL_BOOT_SND, &gEnableBootSND);
+ configGetInt(cfg, CONFIG_OPL_BGM, &gEnableBGM);
+ configGetInt(cfg, CONFIG_OPL_SFX_VOLUME, &gSFXVolume);
+ configGetInt(cfg, CONFIG_OPL_BOOT_SND_VOLUME, &gBootSndVolume);
+ configGetInt(cfg, CONFIG_OPL_BGM_VOLUME, &gBGMVolume);
+ configGetStrCopy(cfg, CONFIG_OPL_DEFAULT_BGM_PATH, gDefaultBGMPath, sizeof(gDefaultBGMPath));
+#ifdef __DEBUG
+ configGetInt(cfg, CONFIG_OPL_MMCE_GAMEID, &gMMCEEnableGameID);
+#endif
+ configGetInt(cfg, CONFIG_OPL_COVERFLOW_COUNT, &gCoverflowCount);
+ if (gCoverflowCount != 3 && gCoverflowCount != 5)
+ gCoverflowCount = 3;
+
+ configGetInt(cfg, CONFIG_OPL_COVERFLOW_SCALE, &gCoverflowCenterScale);
+ configGetInt(cfg, CONFIG_OPL_COVERFLOW_ANIM, &gCoverflowAnimSpeed);
+ configGetInt(cfg, CONFIG_OPL_COVERFLOW_DIM, &gCoverflowDimCovers);
+
+ configGetStrCopy(cfg, CONFIG_OPL_PARENTAL_LOCK_PWD, gParentalLockPassword, sizeof(gParentalLockPassword));
+
+ int delay;
+ if (configGetInt(cfg, "fav_frames_delay", &delay))
+ gFAVFramesDelay = delay;
+ if (configGetInt(cfg, "usb_frames_delay", &delay)) {
+ gBDMFramesDelay = delay;
+ gMMCEFramesDelay = delay;
+ }
+ if (configGetInt(cfg, "app_frames_delay", &delay))
+ gAPPFramesDelay = delay;
+ if (configGetInt(cfg, "eth_frames_delay", &delay))
+ gETHFramesDelay = delay;
+ if (configGetInt(cfg, "hdd_frames_delay", &delay))
+ gHDDFramesDelay = delay;
+
+ cfgClearStackConfig(cfg);
+ return 1;
+}
+
+// legacy migration.. phase out eventually
+int cfgMigrateLegacyNet(const char *path)
+{
+ config_set_t legacy;
+ config_set_t *cfg = configAlloc(CONFIG_NETWORK, &legacy, (char *)path);
+ if (!cfg)
+ return 0;
+
+ if (!configRead(cfg)) {
+ cfgClearStackConfig(cfg);
+ return 0;
+ }
+
+ const char *temp;
+
+ configGetInt(cfg, CONFIG_NET_ETH_LINKM, &gETHOpMode);
+ configGetInt(cfg, CONFIG_NET_PS2_DHCP, &ps2_ip_use_dhcp);
+ configGetInt(cfg, CONFIG_NET_SMB_NBNS, &gPCShareAddressIsNetBIOS);
+ configGetStrCopy(cfg, CONFIG_NET_SMB_NB_ADDR, gPCShareNBAddress, sizeof(gPCShareNBAddress));
+ configGetInt(cfg, CONFIG_NET_SMB_PORT, &gPCPort);
+ configGetStrCopy(cfg, CONFIG_NET_SMB_SHARE, gPCShareName, sizeof(gPCShareName));
+ configGetStrCopy(cfg, CONFIG_NET_SMB_USER, gPCUserName, sizeof(gPCUserName));
+ configGetStrCopy(cfg, CONFIG_NET_SMB_PASSW, gPCPassword, sizeof(gPCPassword));
+ configGetStrCopy(cfg, CONFIG_NET_NBD_DEFAULT_EXPORT, gExportName, sizeof(gExportName));
+
+ if (configGetStr(cfg, CONFIG_NET_SMB_IP_ADDR, &temp))
+ sscanf(temp, "%d.%d.%d.%d", &pc_ip[0], &pc_ip[1], &pc_ip[2], &pc_ip[3]);
+ if (configGetStr(cfg, CONFIG_NET_PS2_IP, &temp))
+ sscanf(temp, "%d.%d.%d.%d", &ps2_ip[0], &ps2_ip[1], &ps2_ip[2], &ps2_ip[3]);
+ if (configGetStr(cfg, CONFIG_NET_PS2_NETM, &temp))
+ sscanf(temp, "%d.%d.%d.%d", &ps2_netmask[0], &ps2_netmask[1], &ps2_netmask[2], &ps2_netmask[3]);
+ if (configGetStr(cfg, CONFIG_NET_PS2_GATEW, &temp))
+ sscanf(temp, "%d.%d.%d.%d", &ps2_gateway[0], &ps2_gateway[1], &ps2_gateway[2], &ps2_gateway[3]);
+ if (configGetStr(cfg, CONFIG_NET_PS2_DNS, &temp))
+ sscanf(temp, "%d.%d.%d.%d", &ps2_dns[0], &ps2_dns[1], &ps2_dns[2], &ps2_dns[3]);
+
+ cfgClearStackConfig(cfg);
+ return 1;
+}
+
+int cfgMigrateLegacyGlobalGame(const char *path)
+{
+ config_set_t tmp;
+ config_set_t *old = configAlloc(CONFIG_GAME, &tmp, (char *)path);
+ if (!old || !configRead(old)) {
+ if (old)
+ cfgClearStackConfig(old);
+ return 0;
+ }
+#ifdef GSM
+ configGetInt(old, CONFIG_ITEM_ENABLEGSM, &gGlobalGameCfg.gsm_enable);
+ configGetInt(old, CONFIG_ITEM_GSMVMODE, &gGlobalGameCfg.gsm_vmode);
+ configGetInt(old, CONFIG_ITEM_GSMXOFFSET, &gGlobalGameCfg.gsm_xoffset);
+ configGetInt(old, CONFIG_ITEM_GSMYOFFSET, &gGlobalGameCfg.gsm_yoffset);
+ configGetInt(old, CONFIG_ITEM_GSMFIELDFIX, &gGlobalGameCfg.gsm_fieldfix);
+#endif
+#ifdef CHEAT
+ configGetInt(old, CONFIG_ITEM_ENABLECHEAT, &gGlobalGameCfg.cheat_enable);
+ configGetInt(old, CONFIG_ITEM_CHEATMODE, &gGlobalGameCfg.cheat_mode);
+ configGetInt(old, CONFIG_ITEM_ENABLEIMAGE, &gGlobalGameCfg.cheat_enable_image);
+#endif
+#ifdef PADEMU
+ configGetInt(old, CONFIG_ITEM_ENABLEPADEMU, &gGlobalGameCfg.pademu_enable);
+ configGetInt(old, CONFIG_ITEM_PADEMUSETTINGS, &gGlobalGameCfg.pademu_settings);
+ configGetInt(old, CONFIG_ITEM_PADMACROSETTINGS, &gGlobalGameCfg.padmacro_settings);
+#endif
+ configGetInt(old, CONFIG_ITEM_OSD_SETTINGS_ENABLE, &gGlobalGameCfg.osd_enable);
+ configGetInt(old, CONFIG_ITEM_OSD_SETTINGS_LANGID, &gGlobalGameCfg.osd_langid);
+ configGetInt(old, CONFIG_ITEM_OSD_SETTINGS_TV_ASP, &gGlobalGameCfg.osd_tv_aspect);
+ configGetInt(old, CONFIG_ITEM_OSD_SETTINGS_VMODE, &gGlobalGameCfg.osd_vmode);
+ cfgClearStackConfig(old);
+ return 1;
+}
+
+int cfgMigrateLegacyAppTitleCfg(const char *path)
+{
+ config_set_t tmp;
+ config_set_t *old = configAlloc(0, &tmp, (char *)path);
+ if (!old)
+ return 0;
+
+ if (!configRead(old)) {
+ cfgClearStackConfig(old);
+ return 0;
+ }
+
+ config_t lcfg;
+ config_init(&lcfg);
+ config_setting_t *root = config_root_setting(&lcfg);
+
+ const char *fields[] = {
+ "title", "boot", "argv1",
+ "Title", "Description", "Developer",
+ "Version", "Release", "Package", "Source",
+ NULL};
+ const char *value;
+ int found = 0;
+ int f;
+
+ for (f = 0; fields[f]; f++) {
+ if (configGetStr(old, fields[f], &value)) {
+ config_setting_t *s = config_setting_add(root, fields[f], CONFIG_TYPE_STRING);
+ if (s) {
+ config_setting_set_string(s, value);
+ found = 1;
+ }
+ }
+ }
+
+ cfgClearStackConfig(old);
+
+ if (!found) {
+ config_destroy(&lcfg);
+ return 0;
+ }
+
+ int ok = cfgWriteLibconfig(&lcfg, path, 1);
+ config_destroy(&lcfg);
+
+ return ok;
+}
+
+int cfgMigrateLegacyTheme(const char *oldPath, const char *newPath)
+{
+ config_t cfg;
+ int ok;
+
+ // old filename exists but already contains valid libconfig..
+ config_init(&cfg);
+ if (config_read_file(&cfg, oldPath)) {
+ ok = cfgWriteLibconfig(&cfg, newPath, 1);
+ config_destroy(&cfg);
+ return ok;
+ }
+ config_destroy(&cfg);
+
+ // parse old legacy theme syntax and write it as libconfig..
+ FILE *f = fopen(oldPath, "r");
+ if (!f)
+ return 0;
+
+ fseek(f, 0, SEEK_END);
+ long sz = ftell(f);
+ rewind(f);
+ if (sz <= 0) {
+ fclose(f);
+ return 0;
+ }
+
+ char *buf = malloc(sz + 1);
+ if (!buf) {
+ fclose(f);
+ return 0;
+ }
+
+ if (fread(buf, 1, sz, f) != (size_t)sz) {
+ free(buf);
+ fclose(f);
+ return 0;
+ }
+ fclose(f);
+ buf[sz] = '\0';
+
+ config_init(&cfg);
+ config_setting_t *root = config_root_setting(&cfg);
+ config_setting_t *section = NULL;
+ char *p = buf;
+ char *end = buf + sz;
+ int imported = 0;
+
+ while (p < end) {
+ char *line = p;
+ while (p < end && *p != '\n')
+ p++;
+ if (p < end)
+ *p++ = '\0';
+
+ char *lineEnd = line + strlen(line);
+ while (lineEnd > line && (lineEnd[-1] == '\r' || lineEnd[-1] == ' ' || lineEnd[-1] == '\t'))
+ *--lineEnd = '\0';
+
+ char *text = line;
+ while (*text == ' ' || *text == '\t')
+ text++;
+
+ int indented = text != line;
+ if (!text[0] || text[0] == '#')
+ continue;
+
+ char *textEnd = text + strlen(text);
+ if (!indented && textEnd > text && textEnd[-1] == ':') {
+ *--textEnd = '\0';
+ while (textEnd > text && (textEnd[-1] == ' ' || textEnd[-1] == '\t'))
+ *--textEnd = '\0';
+
+ section = text[0] ? config_setting_add(root, text, CONFIG_TYPE_GROUP) : NULL;
+ continue;
+ }
+
+ char *eq = strchr(text, '=');
+ if (!eq)
+ continue;
+
+ *eq = '\0';
+ char *key = text;
+ char *keyEnd = eq;
+ while (keyEnd > key && (keyEnd[-1] == ' ' || keyEnd[-1] == '\t'))
+ *--keyEnd = '\0';
+
+ char *val = eq + 1;
+ while (*val == ' ' || *val == '\t')
+ val++;
+
+ char *valEnd = val + strlen(val);
+ while (valEnd > val && (valEnd[-1] == '\r' || valEnd[-1] == ' ' || valEnd[-1] == '\t'))
+ *--valEnd = '\0';
+
+ config_setting_t *parent = indented ? section : root;
+ if (!parent || !key[0])
+ continue;
+
+ char *endp;
+ long ival = strtol(val, &endp, 10);
+ config_setting_t *setting;
+
+ if (endp != val && *endp == '\0') {
+ setting = config_setting_add(parent, key, CONFIG_TYPE_INT);
+ if (setting)
+ config_setting_set_int(setting, (int)ival);
+ } else {
+ setting = config_setting_add(parent, key, CONFIG_TYPE_STRING);
+ if (setting)
+ config_setting_set_string(setting, val);
+ }
+
+ if (setting)
+ imported++;
+ }
+
+ free(buf);
+
+ if (imported == 0) {
+ config_destroy(&cfg);
+ return 0;
+ }
+
+ ok = cfgWriteLibconfig(&cfg, newPath, 1);
+ config_destroy(&cfg);
+
+ return ok;
+}
+
+static int cfgMigrateLegacyPerGame(const char *path, per_game_cfg_t *pgcfg, game_info_t *gi)
+{
+ int flags = 0;
+ const char *str;
+
+ config_set_t tmp;
+ config_set_t *old = configAlloc(0, &tmp, (char *)path);
+ if (!old || !configRead(old)) {
+ if (old)
+ cfgClearStackConfig(old);
+ return 0;
+ }
+
+ if (pgcfg) {
+ if (configGetInt(old, CONFIG_ITEM_COMPAT, &pgcfg->compat))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_DMA, &pgcfg->dma))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_CORE_LOADER, &pgcfg->core_loader))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetStrCopy(old, CONFIG_ITEM_DNAS, pgcfg->dnas, sizeof(pgcfg->dnas)))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetStrCopy(old, CONFIG_ITEM_ALTSTARTUP, pgcfg->alt_startup, sizeof(pgcfg->alt_startup)))
+ flags |= CFG_MIG_HAS_PG;
+
+ configGetVMC(old, pgcfg->vmc1, sizeof(pgcfg->vmc1), 0);
+ if (pgcfg->vmc1[0] != '\0')
+ flags |= CFG_MIG_HAS_PG;
+
+ configGetVMC(old, pgcfg->vmc2, sizeof(pgcfg->vmc2), 1);
+ if (pgcfg->vmc2[0] != '\0')
+ flags |= CFG_MIG_HAS_PG;
+
+#ifdef GSM
+ if (configGetInt(old, CONFIG_ITEM_GSMSOURCE, &pgcfg->gsm_source))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_ENABLEGSM, &pgcfg->gsm_enable))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_GSMVMODE, &pgcfg->gsm_vmode))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_GSMXOFFSET, &pgcfg->gsm_xoffset))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_GSMYOFFSET, &pgcfg->gsm_yoffset))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_GSMFIELDFIX, &pgcfg->gsm_fieldfix))
+ flags |= CFG_MIG_HAS_PG;
+#endif
+
+#ifdef CHEAT
+ if (configGetInt(old, CONFIG_ITEM_CHEATSSOURCE, &pgcfg->cheat_source))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_ENABLECHEAT, &pgcfg->cheat_enable))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_CHEATMODE, &pgcfg->cheat_mode))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_ENABLEIMAGE, &pgcfg->cheat_enable_image))
+ flags |= CFG_MIG_HAS_PG;
+#endif
+
+#ifdef PADEMU
+ if (configGetInt(old, CONFIG_ITEM_PADEMUSOURCE, &pgcfg->pademu_source))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_ENABLEPADEMU, &pgcfg->pademu_enable))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_PADEMUSETTINGS, &pgcfg->pademu_settings))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_PADMACROSOURCE, &pgcfg->padmacro_source))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_PADMACROSETTINGS, &pgcfg->padmacro_settings))
+ flags |= CFG_MIG_HAS_PG;
+#endif
+
+ if (configGetInt(old, CONFIG_ITEM_OSD_SETTINGS_SOURCE, &pgcfg->osd_source))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_OSD_SETTINGS_ENABLE, &pgcfg->osd_enable))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_OSD_SETTINGS_LANGID, &pgcfg->osd_langid))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_OSD_SETTINGS_TV_ASP, &pgcfg->osd_tv_aspect))
+ flags |= CFG_MIG_HAS_PG;
+ if (configGetInt(old, CONFIG_ITEM_OSD_SETTINGS_VMODE, &pgcfg->osd_vmode))
+ flags |= CFG_MIG_HAS_PG;
+ }
+
+ if (gi) {
+ int val;
+
+ if (configGetStr(old, CONFIG_ITEM_NAME, &str) || configGetStr(old, "Title", &str)) {
+ strncpy(gi->title, str, sizeof(gi->title) - 1);
+ gi->title[sizeof(gi->title) - 1] = '\0';
+ flags |= CFG_MIG_HAS_INFO;
+ }
+
+ if (configGetStr(old, "Description", &str)) {
+ strncpy(gi->description, str, sizeof(gi->description) - 1);
+ gi->description[sizeof(gi->description) - 1] = '\0';
+ flags |= CFG_MIG_HAS_INFO;
+ }
+
+ if (configGetStr(old, "Developer", &str)) {
+ strncpy(gi->developer, str, sizeof(gi->developer) - 1);
+ gi->developer[sizeof(gi->developer) - 1] = '\0';
+ flags |= CFG_MIG_HAS_INFO;
+ }
+
+ if (configGetStr(old, "Genre", &str)) {
+ strncpy(gi->genre, str, sizeof(gi->genre) - 1);
+ gi->genre[sizeof(gi->genre) - 1] = '\0';
+ flags |= CFG_MIG_HAS_INFO;
+ }
+
+ if (configGetStr(old, "Release", &str)) {
+ strncpy(gi->release, str, sizeof(gi->release) - 1);
+ gi->release[sizeof(gi->release) - 1] = '\0';
+ flags |= CFG_MIG_HAS_INFO;
+ }
+
+ if (configGetStr(old, "Aspect", &str)) {
+ strncpy(gi->aspect, str, sizeof(gi->aspect) - 1);
+ gi->aspect[sizeof(gi->aspect) - 1] = '\0';
+ flags |= CFG_MIG_HAS_INFO;
+ }
+
+ if (configGetStr(old, "Parental", &str)) {
+ strncpy(gi->parental, str, sizeof(gi->parental) - 1);
+ gi->parental[sizeof(gi->parental) - 1] = '\0';
+ flags |= CFG_MIG_HAS_INFO;
+ }
+
+ if (configGetInt(old, "Players", &val)) {
+ gi->players = val;
+ flags |= CFG_MIG_HAS_INFO;
+ } else if (configGetStr(old, "Players", &str)) {
+ const char *slash = strrchr(str, '/');
+
+ if (slash && slash[1])
+ str = slash + 1;
+
+ gi->players = atoi(str);
+ flags |= CFG_MIG_HAS_INFO;
+ }
+
+ if (configGetStr(old, "Vmode", &str) || configGetStr(old, "Region", &str)) {
+ const char *value = str;
+ const char *slash = strrchr(str, '/');
+
+ if (slash && slash[1])
+ value = slash + 1;
+
+ snprintf(gi->region, sizeof(gi->region), "Region/%s", value);
+ flags |= CFG_MIG_HAS_INFO;
+ }
+
+ if (configGetInt(old, "Rating", &val)) {
+ gi->user_rating = val;
+ flags |= CFG_MIG_HAS_INFO;
+ } else if (configGetStr(old, "Rating", &str)) {
+ const char *slash = strrchr(str, '/');
+
+ if (slash && slash[1])
+ str = slash + 1;
+
+ gi->user_rating = atoi(str);
+ flags |= CFG_MIG_HAS_INFO;
+ }
+ }
+
+ cfgClearStackConfig(old);
+
+ return flags;
+}
+
+int cfgBatchMigratePerGame(const char *inputPrefix, const char *outputPrefix, int keepOriginals, void (*progressCb)(int done, int total))
+{
+ char cfgDir[256];
+ snprintf(cfgDir, sizeof(cfgDir), "%sCFG", inputPrefix);
+
+ // count cfg files so we can show x/total in gui..
+ int total = 0;
+ DIR *dir = opendir(cfgDir);
+ if (!dir)
+ return 0;
+
+ struct dirent *entry;
+ while ((entry = readdir(dir)) != NULL) {
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
+ continue;
+ int l = strlen(entry->d_name);
+ if (l >= 5 && strcasecmp(entry->d_name + l - 4, ".cfg") == 0)
+ total++;
+ }
+ closedir(dir);
+
+ if (progressCb)
+ progressCb(0, total);
+
+ if (total == 0)
+ return 0;
+
+ // lets go..
+ dir = opendir(cfgDir);
+ if (!dir)
+ return 0;
+
+ if (strcmp(inputPrefix, outputPrefix) != 0) {
+ char outDir[256];
+ snprintf(outDir, sizeof(outDir), "%sCFG", outputPrefix);
+ mkdir(outDir, 0777);
+ }
+
+ int count = 0;
+ int processed = 0;
+
+ while ((entry = readdir(dir)) != NULL) {
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
+ continue;
+
+ int len = strlen(entry->d_name);
+ if (len < 5 || strcasecmp(entry->d_name + len - 4, ".cfg") != 0)
+ continue;
+
+ if (progressCb)
+ progressCb(++processed, total);
+
+ char inputPath[256];
+ char outputCfgPath[256];
+ char outputInfoPath[256];
+ char tmpCfgPath[256];
+ char tmpInfoPath[256];
+ char bakPath[256];
+
+ snprintf(inputPath, sizeof(inputPath), "%sCFG/%s", inputPrefix, entry->d_name);
+ snprintf(outputCfgPath, sizeof(outputCfgPath), "%sCFG/%s", outputPrefix, entry->d_name);
+ snprintf(tmpCfgPath, sizeof(tmpCfgPath), "%s.tmp", outputCfgPath);
+ snprintf(bakPath, sizeof(bakPath), "%s.bak", inputPath);
+
+ char basename[128];
+ int baseLen = len - 4;
+ if (baseLen >= (int)sizeof(basename))
+ baseLen = sizeof(basename) - 1;
+
+ strncpy(basename, entry->d_name, baseLen);
+ basename[baseLen] = '\0';
+
+ snprintf(outputInfoPath, sizeof(outputInfoPath), "%sCFG/%s.info", outputPrefix, basename);
+ snprintf(tmpInfoPath, sizeof(tmpInfoPath), "%s.tmp", outputInfoPath);
+
+ per_game_cfg_t pgcfg;
+ memset(&pgcfg, 0, sizeof(pgcfg));
+ pgcfg.dma = 7;
+
+ game_info_t gi;
+ memset(&gi, 0, sizeof(gi));
+
+ int flags = cfgMigrateLegacyPerGame(inputPath, &pgcfg, &gi);
+ if (!flags)
+ continue;
+
+ int hasPg = (flags & CFG_MIG_HAS_PG) != 0;
+ int hasInfo = (flags & CFG_MIG_HAS_INFO) != 0;
+ int samePath = (strcmp(inputPath, outputCfgPath) == 0);
+ int wroteInfoTmp = 0;
+ int backedOriginal = 0;
+
+ unlink(tmpCfgPath);
+ unlink(tmpInfoPath);
+
+ if (hasPg) {
+ if (!wOPLPerGameSave(tmpCfgPath, &pgcfg)) {
+ unlink(tmpCfgPath);
+ unlink(tmpInfoPath);
+ continue;
+ }
+ }
+
+ if (hasInfo) {
+ if (!wOPLGameInfoSave(tmpInfoPath, &gi)) {
+ unlink(tmpCfgPath);
+ unlink(tmpInfoPath);
+ continue;
+ }
+
+ wroteInfoTmp = 1;
+ }
+
+ if (wroteInfoTmp) {
+ unlink(outputInfoPath);
+
+ if (rename(tmpInfoPath, outputInfoPath) != 0) {
+ LOG("CONFIG_MIGRATION: failed to rename '%s' to '%s'\n", tmpInfoPath, outputInfoPath);
+ unlink(tmpCfgPath);
+ unlink(tmpInfoPath);
+ continue;
+ }
+ }
+
+ // if converting in same place.. move the old cfg out of the way before
+ // replacing it.. If keepOriginals is off.. delete the backup after
+ if (samePath) {
+ unlink(bakPath);
+
+ if (rename(inputPath, bakPath) != 0) {
+ LOG("CONFIG_MIGRATION: failed to backup '%s' to '%s'\n", inputPath, bakPath);
+ unlink(tmpCfgPath);
+ continue;
+ }
+
+ backedOriginal = 1;
+ } else if (hasPg) {
+ unlink(outputCfgPath);
+ }
+
+ if (hasPg) {
+ if (rename(tmpCfgPath, outputCfgPath) != 0) {
+ LOG("CONFIG_MIGRATION: failed to rename '%s' to '%s'\n", tmpCfgPath, outputCfgPath);
+
+ if (backedOriginal)
+ rename(bakPath, inputPath);
+
+ unlink(tmpCfgPath);
+ continue;
+ }
+ }
+
+ if (!keepOriginals) {
+ if (samePath) {
+ if (backedOriginal)
+ unlink(bakPath);
+ } else {
+ unlink(inputPath);
+ }
+ }
+
+ count++;
+ }
+
+ closedir(dir);
+
+ return count;
+}
+
+// ---------------------------------------------------------------------------
+// DELETE_WITH_MIGRATION.. legacy key=value config infrastructure
+// Everything in this file is only needed during the migration period
+// ---------------------------------------------------------------------------
+
+static u32 currentUID = 0;
+static config_set_t configFiles[CONFIG_INDEX_COUNT];
+static char legacyNetConfigPath[256] = "mc?:SYS-CONF/IPCONFIG.DAT";
+static const char *configFilenames[CONFIG_INDEX_COUNT] = {
+ "conf_wopl.cfg",
+ "conf_last.cfg",
+ "conf_apps.cfg",
+ "conf_network.cfg",
+ "conf_game.cfg",
+};
+
+static char cfgDevice[128];
+
+static int strToColor(const char *string, unsigned char *color)
+{
+ int cnt = 0, n = 0;
+ color[0] = 0;
+ color[1] = 0;
+ color[2] = 0;
+
+ if (!string || !*string)
+ return 0;
+ if (string[0] != '#')
+ return 0;
+
+ string++;
+
+ while (*string) {
+ int fh = fromHex(*string);
+ if (fh >= 0) {
+ color[n] = color[n] * 16 + fh;
+ } else {
+ break;
+ }
+
+ // Two characters per color
+ if (cnt == 1) {
+ cnt = 0;
+ n++;
+ } else {
+ cnt++;
+ }
+
+ string++;
+ }
+
+ return 1;
+}
+
+/// true if given a whitespace character
+int isWS(char c)
+{
+ return c == ' ' || c == '\t';
+}
+
+static int splitAssignment(char *line, char *key, size_t keymax, char *val, size_t valmax)
+{
+ // skip whitespace
+ for (; isWS(*line); ++line)
+ ;
+
+ // find "=".
+ // If found, the text before is key, after is val.
+ // Otherwise malformed string is encountered
+
+ char *eqpos = strchr(line, '=');
+
+ if (eqpos) {
+ // copy the name and the value
+ size_t keylen = MIN(keymax, eqpos - line);
+
+ strncpy(key, line, keylen);
+
+ eqpos++;
+
+ size_t vallen = MIN(valmax, strlen(line) - (eqpos - line));
+ strncpy(val, eqpos, vallen);
+ }
+
+ return (int)eqpos;
+}
+
+static int parsePrefix(char *line, char *prefix)
+{
+ // find "=".
+ // If found, the text before is key, after is val.
+ // Otherwise malformed string is encountered
+ char *colpos = strchr(line, ':');
+
+ if (colpos && colpos != line) {
+ // copy the name and the value
+ strncpy(prefix, line, colpos - line);
+ prefix[colpos - line] = 0;
+
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int configKeyValidate(const char *key)
+{
+ if (strlen(key) == 0)
+ return 0;
+
+ return !strchr(key, '=');
+}
+
+static struct config_kv_t *allocConfigItem(const char *key, const char *val)
+{
+ struct config_kv_t *it = (struct config_kv_t *)malloc(sizeof(struct config_kv_t));
+ strncpy(it->key, key, sizeof(it->key));
+ it->key[sizeof(it->key) - 1] = '\0';
+ strncpy(it->val, val, sizeof(it->val));
+ it->val[sizeof(it->val) - 1] = '\0';
+ it->next = NULL;
+
+ return it;
+}
+
+/// Low level key addition. Does not check for uniqueness.
+static void addConfigValue(config_set_t *configSet, const char *key, const char *val)
+{
+ if (!configSet->tail) {
+ configSet->head = allocConfigItem(key, val);
+ configSet->tail = configSet->head;
+ } else {
+ configSet->tail->next = allocConfigItem(key, val);
+ configSet->tail = configSet->tail->next;
+ }
+}
+
+static struct config_kv_t *getConfigItemForName(config_set_t *configSet, const char *name)
+{
+ struct config_kv_t *val = configSet->head;
+
+ while (val) {
+ if (strncmp(val->key, name, sizeof(val->key)) == 0)
+ break;
+
+ val = val->next;
+ }
+
+ return val;
+}
+
+static int configReadFileBuffer(file_buffer_t *fileBuffer, config_set_t *configSet)
+{
+ char *line;
+ unsigned int lineno = 0;
+
+ char prefix[CONFIG_KEY_NAME_LEN];
+ memset(prefix, 0, sizeof(prefix));
+
+ while (sbReadFileBuffer(fileBuffer, &line)) {
+ lineno++;
+
+ char key[CONFIG_KEY_NAME_LEN], val[CONFIG_KEY_VALUE_LEN];
+ memset(key, 0, sizeof(key));
+ memset(val, 0, sizeof(val));
+
+ if (splitAssignment(line, key, sizeof(key), val, sizeof(val))) {
+ /* if the line does not start with whitespace,
+ * the prefix ends and we have to reset it
+ */
+ if (!isWS(line[0]))
+ memset(prefix, 0, sizeof(prefix));
+
+ // insert config value
+ if (prefix[0]) {
+ // we have a prefix
+ char composedKey[CONFIG_KEY_NAME_LEN];
+
+ snprintf(composedKey, sizeof(composedKey), "%s_%s", prefix, key);
+ configSetStr(configSet, composedKey, val);
+ } else {
+ configSetStr(configSet, key, val);
+ }
+ } else if (parsePrefix(line, prefix)) {
+ // prefix is set, that's about it
+ } else {
+ LOG("CONFIG Malformed file '%s' line %d: '%s'\n", configSet->filename, lineno, line);
+ }
+ }
+ configSet->modified = 0;
+ return 1;
+}
+
+config_set_t *configAlloc(int type, config_set_t *configSet, char *fileName)
+{
+ if (!configSet)
+ configSet = (config_set_t *)malloc(sizeof(config_set_t));
+
+ configSet->uid = ++currentUID;
+ configSet->type = type;
+ configSet->head = NULL;
+ configSet->tail = NULL;
+ if (fileName) {
+ int length = strlen(fileName) + 1;
+ configSet->filename = (char *)malloc(length * sizeof(char));
+ memcpy(configSet->filename, fileName, length);
+ } else
+ configSet->filename = NULL;
+ configSet->modified = 0;
+ return configSet;
+}
+
+void configMove(config_set_t *configSet, const char *fileName)
+{
+ int length = strlen(fileName) + 1;
+ configSet->filename = realloc(configSet->filename, length);
+ memcpy(configSet->filename, fileName, length);
+}
+
+void configFree(config_set_t *configSet)
+{
+ configClear(configSet);
+ free(configSet->filename);
+ free(configSet);
+}
+
+void configClear(config_set_t *configSet)
+{
+ while (configSet->head) {
+ struct config_kv_t *cur = configSet->head;
+ configSet->head = cur->next;
+
+ free(cur);
+ }
+
+ configSet->head = NULL;
+ configSet->tail = NULL;
+ configSet->modified = 1;
+}
+
+config_set_t *configGetByType(int type)
+{
+ int index = 0;
+ while (index < CONFIG_INDEX_COUNT) {
+ config_set_t *configSet = &configFiles[index];
+
+ if (configSet->type == type)
+ return configSet;
+ index++;
+ }
+ return NULL;
+}
+
+int configSetStr(config_set_t *configSet, const char *key, const char *value)
+{
+ if (!configKeyValidate(key))
+ return 0;
+
+ struct config_kv_t *it = getConfigItemForName(configSet, key);
+
+ if (it) {
+ if (strncmp(it->val, value, sizeof(it->val)) != 0) {
+ strncpy(it->val, value, sizeof(it->val));
+ it->val[sizeof(it->val) - 1] = '\0';
+ if (it->key[0] != '#')
+ configSet->modified = 1;
+ }
+ } else {
+ addConfigValue(configSet, key, value);
+ if (key[0] != '#')
+ configSet->modified = 1;
+ }
+
+ return 1;
+}
+
+// sets the value to point to the value str in the config. Do not overwrite - it will overwrite the string in config
+int configGetStr(config_set_t *configSet, const char *key, const char **value)
+{
+ if (!configKeyValidate(key))
+ return 0;
+
+ struct config_kv_t *it = getConfigItemForName(configSet, key);
+
+ if (it) {
+ *value = it->val;
+ return 1;
+ } else
+ return 0;
+}
+
+int configGetStrCopy(config_set_t *configSet, const char *key, char *value, int length)
+{
+ const char *valref = NULL;
+ if (configGetStr(configSet, key, &valref)) {
+ strncpy(value, valref, length);
+ value[length - 1] = '\0';
+ return 1;
+ } else {
+ value[0] = '\0';
+ return 0;
+ }
+}
+
+int configSetInt(config_set_t *configSet, const char *key, const int value)
+{
+ char tmp[12];
+ snprintf(tmp, sizeof(tmp), "%d", value);
+ return configSetStr(configSet, key, tmp);
+}
+
+int configGetInt(config_set_t *configSet, const char *key, int *value)
+{
+ const char *valref = NULL;
+ if (configGetStr(configSet, key, &valref)) {
+ *value = atoi(valref);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int configSetColor(config_set_t *configSet, const char *key, unsigned char *color)
+{
+ char tmp[8];
+ snprintf(tmp, sizeof(tmp), "#%02X%02X%02X", color[0], color[1], color[2]);
+ return configSetStr(configSet, key, tmp);
+}
+
+int configGetColor(config_set_t *configSet, const char *key, unsigned char *color)
+{
+ const char *valref = NULL;
+ if (configGetStr(configSet, key, &valref)) {
+ strToColor(valref, color);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int configRemoveKey(config_set_t *configSet, const char *key)
+{
+ if (!configKeyValidate(key))
+ return 0;
+
+ struct config_kv_t *val = configSet->head;
+ struct config_kv_t *prev = NULL;
+
+ while (val) {
+ if (strncmp(val->key, key, sizeof(val->key)) == 0) {
+ if (key[0] != '#')
+ configSet->modified = 1;
+
+ if (val == configSet->tail)
+ configSet->tail = prev;
+
+ val = val->next;
+ if (prev) {
+ free(prev->next);
+ prev->next = val;
+ } else {
+ free(configSet->head);
+ configSet->head = val;
+ }
+ } else {
+ prev = val;
+ val = val->next;
+ }
+ }
+
+ return 1;
+}
+
+void configGetVMC(config_set_t *configSet, char *vmc, int length, int slot)
+{
+ char gkey[CONFIG_KEY_NAME_LEN];
+ snprintf(gkey, sizeof(gkey), "%s_%d", CONFIG_ITEM_VMC, slot);
+ configGetStrCopy(configSet, gkey, vmc, length);
+}
+
+void configSetVMC(config_set_t *configSet, const char *vmc, int slot)
+{
+ char gkey[CONFIG_KEY_NAME_LEN];
+ if (vmc[0] == '\0') {
+ configRemoveVMC(configSet, slot);
+ return;
+ }
+ snprintf(gkey, sizeof(gkey), "%s_%d", CONFIG_ITEM_VMC, slot);
+ configSetStr(configSet, gkey, vmc);
+}
+
+void configRemoveVMC(config_set_t *configSet, int slot)
+{
+ char gkey[CONFIG_KEY_NAME_LEN];
+ snprintf(gkey, sizeof(gkey), "%s_%d", CONFIG_ITEM_VMC, slot);
+ configRemoveKey(configSet, gkey);
+}
+
+int configReadBuffer(config_set_t *configSet, const void *buffer, int size)
+{
+ int ret;
+ file_buffer_t *fileBuffer = sbOpenFileBufferBuffer(0, buffer, size);
+ if (!fileBuffer) {
+ configSet->modified = 0;
+ return 0;
+ }
+
+ ret = configReadFileBuffer(fileBuffer, configSet);
+
+ sbCloseFileBuffer(fileBuffer);
+ return ret;
+}
+
+int configRead(config_set_t *configSet)
+{
+ int ret;
+ file_buffer_t *fileBuffer = sbOpenFileBuffer(configSet->filename, O_RDONLY, 0, 4096);
+ if (!fileBuffer) {
+ LOG("CONFIG No file %s.\n", configSet->filename);
+ configSet->modified = 0;
+ return 0;
+ }
+
+ ret = configReadFileBuffer(fileBuffer, configSet);
+
+ sbCloseFileBuffer(fileBuffer);
+ return ret;
+}
+
+int configReadMulti(int types)
+{
+ int result = 0, index;
+
+ for (index = 0; index < CONFIG_INDEX_COUNT; index++) {
+ config_set_t *configSet = &configFiles[index];
+
+ if (configSet->type & types) {
+ configClear(configSet);
+ if (configRead(configSet))
+ result |= configSet->type;
+ }
+ }
+
+ // If the network configuration is to be loaded and one cannot be loaded, attempt to load from the legacy network config file.
+ if ((types & CONFIG_NETWORK) && !(result & CONFIG_NETWORK))
+ if (configReadLegacyIP())
+ result |= CONFIG_NETWORK;
+
+ return result;
+}
+
+int configWrite(config_set_t *configSet)
+{
+ if (configSet->modified) {
+ file_buffer_t *fileBuffer = sbOpenFileBuffer(configSet->filename, O_WRONLY | O_CREAT | O_TRUNC, 0, 4096);
+ if (fileBuffer) {
+ char line[512];
+
+ struct config_kv_t *cur = configSet->head;
+ while (cur) {
+ if ((cur->key[0] != '\0') && (cur->key[0] != '#')) {
+ snprintf(line, sizeof(line), "%s=%s\r\n", cur->key, cur->val); // add windows CR+LF (0x0D 0x0A)
+ sbWriteFileBuffer(fileBuffer, line, strlen(line));
+ }
+
+ // and advance
+ cur = cur->next;
+ }
+
+ sbCloseFileBuffer(fileBuffer);
+ configSet->modified = 0;
+ return 1;
+ }
+ return 0;
+ }
+ return 1;
+}
+
+int configWriteMulti(int types)
+{
+ int result = 0, index;
+
+ for (index = 0; index < CONFIG_INDEX_COUNT; index++) {
+ config_set_t *configSet = &configFiles[index];
+
+ if (configSet->type & types)
+ result += configWrite(configSet);
+ }
+
+ return result;
+}
+
+void configInit(char *prefix)
+{
+ char path[256];
+ int i;
+
+ if (prefix)
+ snprintf(legacyNetConfigPath, sizeof(legacyNetConfigPath), "%s/IPCONFIG.DAT", prefix);
+ else
+ prefix = gBaseMCDir;
+
+ for (i = 0; i < CONFIG_INDEX_COUNT; i++) {
+ snprintf(path, sizeof(path), "%s/%s", prefix, configFilenames[i]);
+ configAlloc(1 << i, &configFiles[i], path);
+ }
+
+ configPrepareNotifications(prefix);
+}
+
+void configSetMove(char *prefix)
+{
+ char path[256];
+ int i;
+
+ if (prefix)
+ snprintf(legacyNetConfigPath, sizeof(legacyNetConfigPath), "%s/IPCONFIG.DAT", prefix);
+ else
+ prefix = gBaseMCDir;
+
+ for (i = 0; i < CONFIG_INDEX_COUNT; i++) {
+ snprintf(path, sizeof(path), "%s/%s", prefix, configFilenames[i]);
+ configMove(&configFiles[i], path);
+ }
+
+ configPrepareNotifications(prefix);
+}
+
+void configEnd()
+{
+ int index = 0;
+ while (index < CONFIG_INDEX_COUNT) {
+ config_set_t *configSet = &configFiles[index];
+
+ configClear(configSet);
+ free(configSet->filename);
+ configSet->filename = NULL;
+ index++;
+ }
+}
+
+char *configGetDir(void)
+{
+ static char path[256];
+
+ if (!strncmp(cfgDevice, "mc", 2))
+ snprintf(path, sizeof(path), "mc%d:%s/", sbGetmcID() & 1, WOPL_CONFIG_NAME);
+ else
+ snprintf(path, sizeof(path), "%s", cfgDevice);
+
+ return path;
+}
+
+void configPrepareNotifications(char *prefix)
+{
+ snprintf(cfgDevice, sizeof(cfgDevice), prefix);
+}
+
+static int configReadLegacyIP(void)
+{
+ config_set_t *configSet;
+ char temp[16];
+
+ int fd = sbOpenFile(legacyNetConfigPath, O_RDONLY);
+ if (fd >= 0) {
+ char ipconfig[256];
+ int size = sbGetFileSize(fd);
+ read(fd, &ipconfig, size);
+ close(fd);
+
+ sscanf(ipconfig, "%d.%d.%d.%d %d.%d.%d.%d %d.%d.%d.%d", &ps2_ip[0], &ps2_ip[1], &ps2_ip[2], &ps2_ip[3],
+ &ps2_netmask[0], &ps2_netmask[1], &ps2_netmask[2], &ps2_netmask[3],
+ &ps2_gateway[0], &ps2_gateway[1], &ps2_gateway[2], &ps2_gateway[3]);
+
+ configSet = &configFiles[CONFIG_INDEX_NETWORK];
+
+ snprintf(temp, sizeof(temp), "%d.%d.%d.%d", ps2_ip[0], ps2_ip[1], ps2_ip[2], ps2_ip[3]);
+ configSetStr(configSet, CONFIG_NET_PS2_IP, temp);
+ snprintf(temp, sizeof(temp), "%d.%d.%d.%d", ps2_netmask[0], ps2_netmask[1], ps2_netmask[2], ps2_netmask[3]);
+ configSetStr(configSet, CONFIG_NET_PS2_NETM, temp);
+ snprintf(temp, sizeof(temp), "%d.%d.%d.%d", ps2_gateway[0], ps2_gateway[1], ps2_gateway[2], ps2_gateway[3]);
+ configSetStr(configSet, CONFIG_NET_PS2_GATEW, temp);
+ // The legacy format has no setting for the DNS server, so duplicate the gateway address.
+ configSetStr(configSet, CONFIG_NET_PS2_DNS, temp);
+
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/config_wopl.c b/src/config_wopl.c
new file mode 100644
index 00000000..f4072eee
--- /dev/null
+++ b/src/config_wopl.c
@@ -0,0 +1,1855 @@
+/*
+ Copyright 2026, KrahJohlito
+ Licenced under Academic Free License version 3.0
+ Review wOPL README & LICENSE files for further details.
+*/
+
+#include "include/common.h"
+#include "include/config_wopl.h"
+#include "include/ioman.h"
+#include "include/util.h"
+#include "include/gui.h"
+#include "include/renderman.h"
+#include "include/system.h"
+#include "include/themes.h"
+#include "include/lang.h"
+#include "include/pad.h"
+#include "include/sound.h"
+#include "include/lwnbd.h"
+#include "include/supportbase.h"
+#include "include/bdmsupport.h"
+#include "include/hddsupport.h"
+#include "include/mmcesupport.h"
+#include "include/config_migration.h" // DELETE_WITH_MIGRATION
+#include "include/module.h"
+#include "include/pathsupport.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef __DEBUG
+#include "include/debug.h"
+#endif
+
+#ifdef GSM
+#include "include/pggsm.h"
+#endif
+
+#ifdef CHEAT
+#include "include/cheatman.h"
+#endif
+
+#define WOPL_FILENAME "wopl_settings.cfg"
+#define WOPL_FILENAME_OLD "conf_wopl.cfg" // DELETE_WITH_MIGRATION
+
+#define NET_FILENAME "wopl_network.cfg"
+#define NET_FILENAME_OLD "conf_network.cfg" // DELETE_WITH_MIGRATION
+
+#define GAME_FILENAME "wopl_global_game.cfg"
+#define GAME_FILENAME_OLD "conf_game.cfg" // DELETE_WITH_MIGRATION
+
+#define LAST_FILENAME "wopl_last_played.cfg"
+#define BOOT_FILENAME "wopl_boot.cfg"
+
+static char config_dir[128] = {0};
+static char boot_dir[128] = {0};
+static char last_played[256] = {0};
+
+static char s_theme_name[128] = {0};
+static char s_lang_name[128] = {0};
+
+char gParentalLockPassword[256] = {0};
+
+global_game_cfg_t gGlobalGameCfg = {0};
+
+int gBDMFramesDelay = MENU_MIN_INACTIVE_FRAMES;
+int gETHFramesDelay = MENU_MIN_INACTIVE_FRAMES;
+int gHDDFramesDelay = MENU_MIN_INACTIVE_FRAMES;
+int gMMCEFramesDelay = MENU_MIN_INACTIVE_FRAMES;
+int gAPPFramesDelay = MENU_MIN_INACTIVE_FRAMES;
+int gFAVFramesDelay = MENU_MIN_INACTIVE_FRAMES;
+
+// ---------------------------------------------------------------------------
+// Config error logging
+// ---------------------------------------------------------------------------
+
+static char log_label[256]; // file source label for entry logging
+
+static void write_config_log(const char *path, const char *msg)
+{
+ char log_dir[256];
+ if (config_dir[0]) {
+ strncpy(log_dir, config_dir, sizeof(log_dir) - 1);
+ log_dir[sizeof(log_dir) - 1] = '\0';
+ } else if (path) {
+ strncpy(log_dir, path, sizeof(log_dir) - 1);
+ log_dir[sizeof(log_dir) - 1] = '\0';
+ char *sep = strrchr(log_dir, '/');
+ if (!sep)
+ sep = strrchr(log_dir, '\\');
+ if (sep)
+ *(sep + 1) = '\0';
+ else
+ log_dir[0] = '\0';
+ } else
+ log_dir[0] = '\0';
+
+ if (log_dir[0] == '\0')
+ return;
+
+ char log_path[256];
+ snprintf(log_path, sizeof(log_path), "%sconfig_errors.log", log_dir);
+
+ FILE *f = fopen(log_path, "a");
+ if (!f)
+ return;
+ fputs(msg, f);
+ fclose(f);
+}
+
+// Whole file syntax error.. FILE_IO (missing file) is normal and ignored
+void log_config_error(const char *path, const config_t *cfg)
+{
+ if (config_error_type(cfg) != CONFIG_ERR_PARSE)
+ return;
+
+ const char *label = path ? path : "?";
+
+ char msg[512];
+ snprintf(msg, sizeof(msg), "[%s] line %d: %s\n", label, config_error_line(cfg), config_error_text(cfg));
+ LOG("CONFIG: parse error %s", msg);
+ write_config_log(path, msg);
+}
+
+// Per entry error.. key exists but is the wrong type for how we read it
+static void log_config_entry(const config_setting_t *setting, const char *expected)
+{
+ if (!setting)
+ return;
+
+ const char *name = config_setting_name(setting);
+ const char *label = log_label[0] ? log_label : "?";
+
+ char msg[256];
+ snprintf(msg, sizeof(msg), "[%s] line %d: '%s' wrong type, expected %s\n", label, config_setting_source_line(setting), name ? name : "?", expected);
+
+ LOG("CONFIG: %s", msg);
+ write_config_log(log_label[0] ? log_label : NULL, msg);
+}
+
+void cfgValidateBegin(const char *label)
+{
+ if (label) {
+ strncpy(log_label, label, sizeof(log_label) - 1);
+ log_label[sizeof(log_label) - 1] = '\0';
+ } else
+ log_label[0] = '\0';
+}
+
+void cfgValidateEnd(void)
+{
+ log_label[0] = '\0';
+}
+
+// Checked getters..
+// present + right type -> writes *out, returns 1
+// absent -> leaves *out, returns 0 (silent.. same as before)
+// present + wrong type -> logs the line, returns 0
+int cfgGetInt(const config_t *cfg, const char *path, int *out)
+{
+ const config_setting_t *s = config_lookup(cfg, path);
+ if (!s)
+ return 0;
+ int t = config_setting_type(s);
+ if (t == CONFIG_TYPE_INT) {
+ *out = config_setting_get_int(s);
+ return 1;
+ }
+ if (t == CONFIG_TYPE_BOOL) {
+ *out = config_setting_get_bool(s) ? 1 : 0;
+ return 1;
+ }
+ log_config_entry(s, "integer");
+ return 0;
+}
+
+int cfgGetStr(const config_t *cfg, const char *path, const char **out)
+{
+ const config_setting_t *s = config_lookup(cfg, path);
+ if (!s)
+ return 0;
+ if (config_setting_type(s) == CONFIG_TYPE_STRING) {
+ *out = config_setting_get_string(s);
+ return 1;
+ }
+ log_config_entry(s, "string");
+ return 0;
+}
+
+// For dynamic keys (value may legitimately be string or int)
+// Call after both reads failed.. log only if the key actually exists
+void cfgCheckExists(const config_t *cfg, const char *path, const char *expected)
+{
+ const config_setting_t *s = config_lookup(cfg, path);
+ if (s)
+ log_config_entry(s, expected);
+}
+
+// ---------------------------------------------------------------------------
+// Shared helpers
+// ---------------------------------------------------------------------------
+
+void dnas_to_binary(const char *dnas, char *out, int out_size)
+{
+ memset(out, 0, out_size);
+ if (!dnas || !dnas[0])
+ return;
+
+ const char *s = dnas;
+ for (int i = 0; i < out_size && s[0] && s[1]; i++, s += 2) {
+ int hi = fromHex(s[0]);
+ int lo = fromHex(s[1]);
+ if (hi < 0 || lo < 0)
+ return;
+ out[i] = (hi << 4) | lo;
+ }
+}
+
+static void color_to_str(const unsigned char *color, char *out, size_t len)
+{
+ snprintf(out, len, "#%02X%02X%02X", color[0], color[1], color[2]);
+}
+
+static void str_to_color(const char *str, unsigned char *color)
+{
+ color[0] = color[1] = color[2] = 0;
+ if (!str || str[0] != '#')
+ return;
+
+ str++;
+ for (int i = 0; i < 3; i++) {
+ int hi = fromHex(*str++);
+ int lo = fromHex(*str++);
+ if (hi < 0 || lo < 0)
+ return;
+
+ color[i] = (unsigned char)((hi << 4) | lo);
+ }
+}
+
+static void ip_to_str(const int *ip, char *out, size_t len)
+{
+ snprintf(out, len, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+}
+
+static void str_to_ip(const char *str, int *ip)
+{
+ ip[0] = ip[1] = ip[2] = ip[3] = 0;
+ if (str)
+ sscanf(str, "%d.%d.%d.%d", &ip[0], &ip[1], &ip[2], &ip[3]);
+}
+
+static int lookup_int(config_t *cfg, const char *path, int def)
+{
+ int value;
+ return cfgGetInt(cfg, path, &value) ? value : def;
+}
+
+static int lookup_bool(config_t *cfg, const char *path, int def)
+{
+ return lookup_int(cfg, path, def) != 0;
+}
+
+static const char *lookup_str(config_t *cfg, const char *path, const char *def)
+{
+ const char *value;
+ return cfgGetStr(cfg, path, &value) ? value : def;
+}
+
+static config_setting_t *add_group(config_setting_t *parent, const char *name)
+{
+ return config_setting_add(parent, name, CONFIG_TYPE_GROUP);
+}
+
+static void set_int(config_setting_t *group, const char *name, int val)
+{
+ config_setting_t *setting = config_setting_add(group, name, CONFIG_TYPE_INT);
+ if (setting)
+ config_setting_set_int(setting, val);
+}
+
+static void set_bool(config_setting_t *group, const char *name, int val)
+{
+ config_setting_t *setting = config_setting_add(group, name, CONFIG_TYPE_BOOL);
+ if (setting)
+ config_setting_set_bool(setting, val ? CONFIG_TRUE : CONFIG_FALSE);
+}
+
+static void set_str(config_setting_t *group, const char *name, const char *val)
+{
+ config_setting_t *setting = config_setting_add(group, name, CONFIG_TYPE_STRING);
+ if (setting)
+ config_setting_set_string(setting, val ? val : "");
+}
+
+static void set_color(config_setting_t *group, const char *name, const unsigned char *color)
+{
+ char buf[8];
+ color_to_str(color, buf, sizeof(buf));
+ set_str(group, name, buf);
+}
+
+static void set_ip(config_setting_t *group, const char *name, const int *ip)
+{
+ char buf[16];
+ ip_to_str(ip, buf, sizeof(buf));
+ set_str(group, name, buf);
+}
+
+static int file_exists(const char *path)
+{
+ FILE *fd = fopen(path, "r");
+ if (!fd)
+ return 0;
+
+ fclose(fd);
+ return 1;
+}
+
+static int path_exists(const char *path)
+{
+ struct stat st;
+ return path && stat(path, &st) == 0;
+}
+
+static void copy_str(char *dst, const char *src, size_t size)
+{
+ if (!size)
+ return;
+
+ strncpy(dst, src, size - 1);
+ dst[size - 1] = '\0';
+}
+
+static void sanitize_frame_delays(void)
+{
+ if (gBDMFramesDelay < 0)
+ gBDMFramesDelay = MENU_MIN_INACTIVE_FRAMES;
+ if (gETHFramesDelay < 0)
+ gETHFramesDelay = MENU_MIN_INACTIVE_FRAMES;
+ if (gHDDFramesDelay < 0)
+ gHDDFramesDelay = MENU_MIN_INACTIVE_FRAMES;
+ if (gMMCEFramesDelay < 0)
+ gMMCEFramesDelay = MENU_MIN_INACTIVE_FRAMES;
+ if (gAPPFramesDelay < 0)
+ gAPPFramesDelay = MENU_MIN_INACTIVE_FRAMES;
+ if (gFAVFramesDelay < 0)
+ gFAVFramesDelay = MENU_MIN_INACTIVE_FRAMES;
+}
+
+static void sanitize_pad_sensitivity(void)
+{
+ if (gXSensitivity < 0 || gXSensitivity > 2)
+ gXSensitivity = 0;
+ if (gYSensitivity < 0 || gYSensitivity > 2)
+ gYSensitivity = 0;
+}
+
+static void prepare_config_root_modules(const char *path)
+{
+ if (!path || !path[0])
+ return;
+
+ // BDM/FAT roots: usbN:, mx4sioN:, ilinkN:, ataN:
+ bdmLoadModulesForPath(path);
+
+ // APA/PFS internal HDD root: hddN:
+ if (pathHasDevicePrefix(path, "hdd")) {
+ guiSetBootStatusIfActive("Loading HDD config root...");
+ LOG("CONFIG: loading HDD modules for config root '%s'\n", path);
+ hddLoadModules();
+ hddLoadSupportModules();
+ return;
+ }
+
+ // MMCE root: mmceN:
+ if (pathHasDevicePrefix(path, "mmce")) {
+ guiSetBootStatusIfActive("Loading MMCE config root...");
+ LOG("CONFIG: loading MMCE modules for config root '%s'\n", path);
+ mmceLoadModules();
+ return;
+ }
+}
+
+#define CONFIG_ROOT_READY_RETRIES 10
+#define CONFIG_ROOT_READY_DELAY 3
+
+static int wait_for_config_root_ready(const char *path)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_ROOT_READY_RETRIES; i++) {
+ if (path_exists(path)) {
+ if (i > 0)
+ LOG("CONFIG: config root ready after %d retries '%s'\n", i, path);
+
+ return 1;
+ }
+
+ delay(CONFIG_ROOT_READY_DELAY);
+ }
+
+ return 0;
+}
+
+static int normalise_config_root_dir(char *out, size_t out_len, const char *dir, int allowUsbMassCompat)
+{
+ int usbMassCompatPath;
+
+ if (!out || !out_len || !dir || !dir[0]) {
+ LOG("CONFIG: normalise failed invalid dir='%s'\n", dir ? dir : "(null)");
+ return 0;
+ }
+
+ usbMassCompatPath = pathIsUsbMassCompatPath(dir);
+
+ if (usbMassCompatPath) {
+ if (!allowUsbMassCompat) {
+ LOG("CONFIG: rejecting USB mass compatibility path '%s'\n", dir);
+ return 0;
+ }
+
+ guiSetBootStatusIfActive("Loading USB mass compatibility support...");
+ bdmLoadModulesForUsbMassCompat();
+
+ delay(8);
+
+ copy_str(out, dir, out_len);
+ pathNormaliseDir(out, out_len);
+
+ LOG("CONFIG: using legacy launch config dir '%s'\n", out);
+
+ return 1;
+ } else
+ copy_str(out, dir, out_len);
+
+ pathNormaliseDir(out, out_len);
+
+ if (!pathIsDevicePath(out)) {
+ LOG("CONFIG: rejected non-device config dir '%s'\n", out);
+ return 0;
+ }
+
+ // Load only the modules required by this selected boot/config root before probing it.
+ prepare_config_root_modules(out);
+
+ // BDM modules may be loaded but the filesystem/device might not be ready..
+ if (!wait_for_config_root_ready(out)) {
+ LOG("CONFIG: config root not ready '%s'\n", out);
+ return 0;
+ }
+
+ LOG("CONFIG: config root ready '%s'\n", out);
+
+ return 1;
+}
+
+static int load_boot_config_from_dir(const char *launch_dir)
+{
+ char boot_root[128];
+ char boot_path[256];
+ char resolved_dir[128];
+ const char *value;
+ config_t cfg;
+ int have_config_dir = 0;
+
+ if (!launch_dir || !launch_dir[0])
+ return 0;
+
+ // Prepare the boot/cwd root before trying to read wopl_boot.cfg from it
+ // Legacy massN: is allowed here only because this path came from argv0/cwd
+ if (!normalise_config_root_dir(boot_root, sizeof(boot_root), launch_dir, 1))
+ return 0;
+
+ copy_str(boot_dir, boot_root, sizeof(boot_dir));
+
+ if (!pathJoin(boot_path, sizeof(boot_path), boot_root, BOOT_FILENAME)) {
+ LOG("CONFIG: failed to build boot cfg path from '%s'\n", boot_root);
+ return 0;
+ }
+
+ LOG("CONFIG: boot root='%s' boot cfg='%s'\n", boot_root, boot_path);
+
+ if (!file_exists(boot_path)) {
+ LOG("CONFIG: no boot cfg at '%s', using boot root\n", boot_path);
+ copy_str(config_dir, boot_root, sizeof(config_dir));
+ return 1;
+ }
+
+ config_init(&cfg);
+
+ if (!config_read_file(&cfg, boot_path)) {
+ log_config_error(boot_path, &cfg);
+ config_destroy(&cfg);
+ return 0;
+ }
+
+ cfgValidateBegin(boot_path);
+
+ if (cfgGetStr(&cfg, "boot.config_dir", &value) && value[0]) {
+ // boot.config_dir is user selected config root.. do not accept USB mass compatibility paths here
+ if (pathIsUsbMassCompatPath(value)) {
+ LOG("CONFIG: ignoring legacy boot.config_dir '%s'\n", value);
+ } else if (normalise_config_root_dir(resolved_dir, sizeof(resolved_dir), value, 0)) {
+ copy_str(config_dir, resolved_dir, sizeof(config_dir));
+ have_config_dir = 1;
+ } else
+ LOG("CONFIG: ignoring invalid boot.config_dir '%s'\n", value);
+ }
+
+ if (!have_config_dir) {
+ copy_str(config_dir, boot_root, sizeof(config_dir));
+ have_config_dir = 1;
+ }
+
+ cfgValidateEnd();
+ config_destroy(&cfg);
+
+ LOG("CONFIG: boot config '%s' config_dir='%s'\n", boot_path, config_dir);
+
+ return have_config_dir;
+}
+
+static int pick_default_config_dir(void)
+{
+ char dir[128];
+ char true_dir[128];
+ int mc;
+
+ if (pathGetBootDir(dir, sizeof(dir))) {
+ if (load_boot_config_from_dir(dir))
+ return 1;
+
+ if (normalise_config_root_dir(true_dir, sizeof(true_dir), dir, 1)) {
+ copy_str(boot_dir, true_dir, sizeof(boot_dir));
+ copy_str(config_dir, true_dir, sizeof(config_dir));
+ LOG("CONFIG: using boot/cwd config_dir='%s'\n", config_dir);
+ return 1;
+ }
+ }
+
+ // Fallback only if no usable boot/cwd root exists
+ // Prefer the MC slot that already has the wOPL config folder
+ mc = sbCheckMC();
+
+ if (mc >= 0) {
+ snprintf(config_dir, sizeof(config_dir), "mc%c:%s/", mc, WOPL_CONFIG_NAME);
+ copy_str(boot_dir, config_dir, sizeof(boot_dir));
+ LOG("CONFIG: falling back to MC config_dir='%s'\n", config_dir);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int ensure_config_dir(void)
+{
+ if (config_dir[0])
+ return 1;
+
+ return pick_default_config_dir();
+}
+
+static int do_save_at_dir(const char *dir, const char *filename, void (*build)(config_setting_t *))
+{
+ char path[256];
+
+ if (!dir || !dir[0])
+ return 0;
+
+ if (!pathJoin(path, sizeof(path), dir, filename))
+ return 0;
+
+ config_t cfg;
+ config_init(&cfg);
+ config_setting_t *root = config_root_setting(&cfg);
+
+ build(root);
+
+ int ok = config_write_file(&cfg, path);
+ config_destroy(&cfg);
+
+ if (!ok) {
+ LOG("CONFIG: failed to write '%s'\n", path);
+ return 0;
+ }
+
+ copy_str(config_dir, dir, sizeof(config_dir));
+ LOG("CONFIG: saved to '%s'\n", path);
+
+ return 1;
+}
+
+static int do_save(const char *filename, void (*build)(config_setting_t *))
+{
+ if (!ensure_config_dir())
+ return 0;
+
+ return do_save_at_dir(config_dir, filename, build);
+}
+
+// ---------------------------------------------------------------------------
+// Bootstrap config (wopl_boot.cfg)
+// ---------------------------------------------------------------------------
+
+static void build_boot(config_setting_t *root)
+{
+ config_setting_t *group;
+
+ group = add_group(root, "boot");
+ set_str(group, "config_dir", config_dir);
+}
+
+static int save_boot_config(void)
+{
+ char path[256];
+ config_t cfg;
+ config_setting_t *root;
+ int ok;
+
+ if (!boot_dir[0] || !config_dir[0])
+ return 1;
+
+ if (pathIsUsbMassCompatPath(config_dir)) {
+ LOG("CONFIG: not saving unresolved legacy boot config_dir '%s'\n", config_dir);
+ return 1;
+ }
+
+ if (pathHasDevicePrefix(boot_dir, "mc") && !sbEnsureMCConfigFolder(boot_dir)) {
+ LOG("CONFIG: failed to prepare MC boot folder '%s'\n", boot_dir);
+ return 0;
+ }
+
+ if (!pathJoin(path, sizeof(path), boot_dir, BOOT_FILENAME))
+ return 0;
+
+ config_init(&cfg);
+ root = config_root_setting(&cfg);
+ build_boot(root);
+
+ ok = config_write_file(&cfg, path);
+ config_destroy(&cfg);
+
+ if (!ok) {
+ LOG("CONFIG: failed to write boot config '%s'\n", path);
+ return 0;
+ }
+
+ LOG("CONFIG: saved boot config '%s'\n", path);
+
+ return 1;
+}
+
+// ---------------------------------------------------------------------------
+// wOPL config (wopl_settings.cfg)
+// ---------------------------------------------------------------------------
+
+const char *wOPLGetDir(void)
+{
+ return config_dir[0] ? config_dir : NULL;
+}
+
+const char *wOPLGetThemeName(void)
+{
+ return s_theme_name[0] ? s_theme_name : NULL;
+}
+
+const char *wOPLGetLanguageName(void)
+{
+ return s_lang_name[0] ? s_lang_name : NULL;
+}
+
+static void parse_display(config_t *cfg)
+{
+ gWideScreen = lookup_bool(cfg, "display.widescreen", gWideScreen);
+ gVMode = lookup_int(cfg, "display.vmode", gVMode);
+ gXOff = lookup_int(cfg, "display.x_offset", gXOff);
+ gYOff = lookup_int(cfg, "display.y_offset", gYOff);
+ gOverscan = lookup_int(cfg, "display.overscan", gOverscan);
+ gScrollSpeed = lookup_int(cfg, "display.scroll_speed", gScrollSpeed);
+
+ const char *color;
+ if ((color = lookup_str(cfg, "display.bg_color", NULL)))
+ str_to_color(color, gDefaultBgColor);
+ if ((color = lookup_str(cfg, "display.text_color", NULL)))
+ str_to_color(color, gDefaultTextColor);
+ if ((color = lookup_str(cfg, "display.ui_text_color", NULL)))
+ str_to_color(color, gDefaultUITextColor);
+ if ((color = lookup_str(cfg, "display.sel_text_color", NULL)))
+ str_to_color(color, gDefaultSelTextColor);
+ if ((color = lookup_str(cfg, "display.plasma_blend_color", NULL)))
+ str_to_color(color, gDefaultPlasmaBlendColor);
+}
+
+static void parse_ui(config_t *cfg, int *out_theme_id, int *out_lang_id)
+{
+ const char *theme_name = lookup_str(cfg, "ui.theme", NULL);
+ if (theme_name) {
+ copy_str(s_theme_name, theme_name, sizeof(s_theme_name));
+ if (out_theme_id)
+ *out_theme_id = thmFindGuiID(theme_name);
+ }
+
+ const char *lang_name = lookup_str(cfg, "ui.language", NULL);
+ if (lang_name) {
+ copy_str(s_lang_name, lang_name, sizeof(s_lang_name));
+ if (out_lang_id)
+ *out_lang_id = lngFindGuiID(lang_name);
+ }
+
+ const char *pwd = lookup_str(cfg, "ui.parental_lock_password", NULL);
+ if (pwd)
+ copy_str(gParentalLockPassword, pwd, sizeof(gParentalLockPassword));
+
+ gSelectButton = lookup_bool(cfg, "ui.swap_button", 0) ? KEY_CROSS : KEY_CIRCLE;
+ gXSensitivity = lookup_int(cfg, "ui.x_sensitivity", gXSensitivity);
+ gYSensitivity = lookup_int(cfg, "ui.y_sensitivity", gYSensitivity);
+ gEnableNotifications = lookup_bool(cfg, "ui.notifications", gEnableNotifications);
+ gDiscEnableArt = lookup_bool(cfg, "ui.disc_art", gDiscEnableArt);
+}
+
+static void parse_audio(config_t *cfg)
+{
+ gEnableSFX = lookup_bool(cfg, "audio.sfx", gEnableSFX);
+ gSFXVolume = lookup_int(cfg, "audio.sfx_volume", gSFXVolume);
+ gEnableBootSND = lookup_bool(cfg, "audio.boot_sound", gEnableBootSND);
+ gBootSndVolume = lookup_int(cfg, "audio.boot_volume", gBootSndVolume);
+ gEnableBGM = lookup_bool(cfg, "audio.bgm", gEnableBGM);
+ gBGMVolume = lookup_int(cfg, "audio.bgm_volume", gBGMVolume);
+
+ const char *path = lookup_str(cfg, "audio.bgm_path", NULL);
+ if (path)
+ copy_str(gDefaultBGMPath, path, sizeof(gDefaultBGMPath));
+}
+
+static void parse_startup(config_t *cfg)
+{
+ gDefaultDevice = lookup_int(cfg, "startup.default_device", gDefaultDevice);
+ gAutosort = lookup_bool(cfg, "startup.auto_sort", gAutosort);
+ gAutoRefresh = lookup_bool(cfg, "startup.auto_refresh", gAutoRefresh);
+ gRememberLastPlayed = lookup_bool(cfg, "startup.remember_last", gRememberLastPlayed);
+ gAutoStartLastPlayed = lookup_bool(cfg, "startup.autostart_last", gAutoStartLastPlayed);
+ gBDMStartMode = lookup_int(cfg, "startup.bdm_start_mode", gBDMStartMode);
+ gHDDStartMode = lookup_int(cfg, "startup.hdd_start_mode", gHDDStartMode);
+ gETHStartMode = lookup_int(cfg, "startup.eth_start_mode", gETHStartMode);
+ gAPPStartMode = lookup_int(cfg, "startup.app_start_mode", gAPPStartMode);
+ gFAVStartMode = lookup_int(cfg, "startup.fav_start_mode", gFAVStartMode);
+ gMMCEStartMode = lookup_int(cfg, "startup.mmce_start_mode", gMMCEStartMode);
+
+ const char *path = lookup_str(cfg, "startup.exit_path", NULL);
+ if (path)
+ copy_str(gExitPath, path, sizeof(gExitPath));
+}
+
+static void parse_devices(config_t *cfg)
+{
+ gEnableUSB = lookup_bool(cfg, "devices.usb_enabled", gEnableUSB);
+ gEnableILK = lookup_bool(cfg, "devices.ilink_enabled", gEnableILK);
+ gEnableMX4SIO = lookup_bool(cfg, "devices.mx4sio_enabled", gEnableMX4SIO);
+ gEnableBdmHDD = lookup_bool(cfg, "devices.bdm_hdd_enabled", gEnableBdmHDD);
+ bdmCacheSize = lookup_int(cfg, "devices.bdm_cache", bdmCacheSize);
+ hddCacheSize = lookup_int(cfg, "devices.hdd_cache", hddCacheSize);
+ smbCacheSize = lookup_int(cfg, "devices.smb_cache", smbCacheSize);
+ gHDDSpindown = lookup_int(cfg, "devices.hdd_spindown", gHDDSpindown);
+ gHDDGameListCache = lookup_bool(cfg, "devices.hdd_game_list_cache", gHDDGameListCache);
+ gEnableWrite = lookup_bool(cfg, "devices.enable_write", gEnableWrite);
+}
+
+static void parse_paths(config_t *cfg)
+{
+ const char *path;
+
+ if ((path = lookup_str(cfg, "paths.bdm_prefix", NULL)))
+ copy_str(gBDMPrefix, path, sizeof(gBDMPrefix));
+
+ if ((path = lookup_str(cfg, "paths.eth_prefix", NULL)))
+ copy_str(gETHPrefix, path, sizeof(gETHPrefix));
+
+ if ((path = lookup_str(cfg, "paths.mmce_prefix", NULL)))
+ copy_str(gMMCEPrefix, path, sizeof(gMMCEPrefix));
+}
+
+static void parse_mmce(config_t *cfg)
+{
+ gMMCESlot = lookup_int(cfg, "mmce.slot", gMMCESlot);
+ gMMCEIGRSlot = lookup_int(cfg, "mmce.igr_slot", gMMCEIGRSlot);
+ gMMCEAckWaitCycles = lookup_int(cfg, "mmce.mmce_wait_cycles", gMMCEAckWaitCycles);
+ gMMCEUseAlarms = lookup_bool(cfg, "mmce.use_alarms", gMMCEUseAlarms);
+}
+
+static void parse_debug(config_t *cfg)
+{
+ gEnableDebug = lookup_bool(cfg, "debug.enable_debug", gEnableDebug);
+ gBDMDebug = lookup_bool(cfg, "debug.bdm_debug", gBDMDebug);
+ gPS2Logo = lookup_bool(cfg, "debug.ps2_logo", gPS2Logo);
+#ifdef __DEBUG
+ gMMCEEnableGameID = lookup_bool(cfg, "debug.mmce_gameid", gMMCEEnableGameID);
+#endif
+}
+
+static void parse_coverflow(config_t *cfg)
+{
+ gCoverflowCount = lookup_int(cfg, "coverflow.count", gCoverflowCount);
+ gCoverflowCenterScale = lookup_int(cfg, "coverflow.center_scale", gCoverflowCenterScale);
+ gCoverflowAnimSpeed = lookup_int(cfg, "coverflow.anim_speed", gCoverflowAnimSpeed);
+ gCoverflowDimCovers = lookup_int(cfg, "coverflow.dim_covers", gCoverflowDimCovers);
+}
+
+static void parse_frames_delay(config_t *cfg)
+{
+ gAPPFramesDelay = lookup_int(cfg, "frames_delay.app_frames_delay", gAPPFramesDelay);
+ gFAVFramesDelay = lookup_int(cfg, "frames_delay.fav_frames_delay", gFAVFramesDelay);
+ gBDMFramesDelay = lookup_int(cfg, "frames_delay.bdm_frames_delay", gBDMFramesDelay);
+ gETHFramesDelay = lookup_int(cfg, "frames_delay.eth_frames_delay", gETHFramesDelay);
+ gHDDFramesDelay = lookup_int(cfg, "frames_delay.hdd_frames_delay", gHDDFramesDelay);
+ gMMCEFramesDelay = lookup_int(cfg, "frames_delay.mmce_frames_delay", gMMCEFramesDelay);
+}
+
+static void build_opl(config_setting_t *root)
+{
+ config_setting_t *group;
+
+ group = add_group(root, "display");
+ set_bool(group, "widescreen", gWideScreen);
+ set_int(group, "vmode", gVMode);
+ set_int(group, "x_offset", gXOff);
+ set_int(group, "y_offset", gYOff);
+ set_int(group, "overscan", gOverscan);
+ set_int(group, "scroll_speed", gScrollSpeed);
+ set_color(group, "bg_color", gDefaultBgColor);
+ set_color(group, "text_color", gDefaultTextColor);
+ set_color(group, "ui_text_color", gDefaultUITextColor);
+ set_color(group, "sel_text_color", gDefaultSelTextColor);
+ set_color(group, "plasma_blend_color", gDefaultPlasmaBlendColor);
+
+ group = add_group(root, "ui");
+ set_str(group, "theme", thmGetValue());
+ set_str(group, "language", lngGetValue());
+ set_str(group, "parental_lock_password", gParentalLockPassword);
+ set_bool(group, "swap_button", gSelectButton == KEY_CROSS);
+ set_int(group, "x_sensitivity", gXSensitivity);
+ set_int(group, "y_sensitivity", gYSensitivity);
+ set_bool(group, "notifications", gEnableNotifications);
+ set_bool(group, "disc_art", gDiscEnableArt);
+
+ group = add_group(root, "audio");
+ set_bool(group, "sfx", gEnableSFX);
+ set_int(group, "sfx_volume", gSFXVolume);
+ set_bool(group, "boot_sound", gEnableBootSND);
+ set_int(group, "boot_volume", gBootSndVolume);
+ set_bool(group, "bgm", gEnableBGM);
+ set_int(group, "bgm_volume", gBGMVolume);
+ set_str(group, "bgm_path", gDefaultBGMPath);
+
+ group = add_group(root, "startup");
+ set_int(group, "default_device", gDefaultDevice);
+ set_bool(group, "auto_sort", gAutosort);
+ set_bool(group, "auto_refresh", gAutoRefresh);
+ set_bool(group, "remember_last", gRememberLastPlayed);
+ set_bool(group, "autostart_last", gAutoStartLastPlayed);
+ set_str(group, "exit_path", gExitPath);
+ set_int(group, "bdm_start_mode", gBDMStartMode);
+ set_int(group, "hdd_start_mode", gHDDStartMode);
+ set_int(group, "eth_start_mode", gETHStartMode);
+ set_int(group, "app_start_mode", gAPPStartMode);
+ set_int(group, "fav_start_mode", gFAVStartMode);
+ set_int(group, "mmce_start_mode", gMMCEStartMode);
+
+ group = add_group(root, "devices");
+ set_bool(group, "usb_enabled", gEnableUSB);
+ set_bool(group, "ilink_enabled", gEnableILK);
+ set_bool(group, "mx4sio_enabled", gEnableMX4SIO);
+ set_bool(group, "bdm_hdd_enabled", gEnableBdmHDD);
+ set_int(group, "bdm_cache", bdmCacheSize);
+ set_int(group, "hdd_cache", hddCacheSize);
+ set_int(group, "smb_cache", smbCacheSize);
+ set_int(group, "hdd_spindown", gHDDSpindown);
+ set_bool(group, "hdd_game_list_cache", gHDDGameListCache);
+ set_bool(group, "enable_write", gEnableWrite);
+
+ group = add_group(root, "paths");
+ set_str(group, "bdm_prefix", gBDMPrefix);
+ set_str(group, "eth_prefix", gETHPrefix);
+ set_str(group, "mmce_prefix", gMMCEPrefix);
+
+ group = add_group(root, "mmce");
+ set_int(group, "slot", gMMCESlot);
+ set_int(group, "igr_slot", gMMCEIGRSlot);
+ set_int(group, "mmce_wait_cycles", gMMCEAckWaitCycles);
+ set_bool(group, "use_alarms", gMMCEUseAlarms);
+
+ group = add_group(root, "debug");
+ set_bool(group, "enable_debug", gEnableDebug);
+ set_bool(group, "bdm_debug", gBDMDebug);
+ set_bool(group, "ps2_logo", gPS2Logo);
+#ifdef __DEBUG
+ set_bool(group, "mmce_gameid", gMMCEEnableGameID);
+#endif
+
+ group = add_group(root, "coverflow");
+ set_int(group, "count", gCoverflowCount);
+ set_int(group, "center_scale", gCoverflowCenterScale);
+ set_int(group, "anim_speed", gCoverflowAnimSpeed);
+ set_int(group, "dim_covers", gCoverflowDimCovers);
+
+ group = add_group(root, "frames_delay");
+ set_int(group, "app_frames_delay", gAPPFramesDelay);
+ set_int(group, "fav_frames_delay", gFAVFramesDelay);
+ set_int(group, "bdm_frames_delay", gBDMFramesDelay);
+ set_int(group, "eth_frames_delay", gETHFramesDelay);
+ set_int(group, "hdd_frames_delay", gHDDFramesDelay);
+ set_int(group, "mmce_frames_delay", gMMCEFramesDelay);
+}
+
+static void parse_opl_cfg(config_t *cfg, int *out_theme_id, int *out_lang_id)
+{
+ parse_display(cfg);
+ parse_ui(cfg, out_theme_id, out_lang_id);
+ parse_audio(cfg);
+ parse_startup(cfg);
+ parse_devices(cfg);
+ parse_paths(cfg);
+ parse_mmce(cfg);
+ parse_debug(cfg);
+ parse_coverflow(cfg);
+ parse_frames_delay(cfg);
+
+ sanitize_frame_delays();
+ sanitize_pad_sensitivity();
+}
+
+int wOPLLoad(int *out_theme_id, int *out_lang_id)
+{
+ char path[256];
+ char old_path[256];
+ config_t cfg;
+
+ if (out_theme_id)
+ *out_theme_id = 0;
+ if (out_lang_id)
+ *out_lang_id = 0;
+
+ LOG("CONFIG_WOPL: enter config_dir='%s'\n", config_dir);
+
+ if (!ensure_config_dir())
+ return 0;
+
+ // 1. Try new filename
+ if (!pathJoin(path, sizeof(path), config_dir, WOPL_FILENAME))
+ return 0;
+
+ config_init(&cfg);
+ if (config_read_file(&cfg, path)) {
+ cfgValidateBegin(path);
+ parse_opl_cfg(&cfg, out_theme_id, out_lang_id);
+ cfgValidateEnd();
+ config_destroy(&cfg);
+
+ LOG("CONFIG_WOPL: loaded from '%s'\n", path);
+ return 1;
+ }
+
+ LOG("CONFIG_WOPL: failed new config '%s'\n", path);
+ log_config_error(path, &cfg);
+ config_destroy(&cfg);
+
+ // DELETE_WITH_MIGRATION
+ // 2. Try old filename from the selected config root only.
+ if (!pathJoin(old_path, sizeof(old_path), config_dir, WOPL_FILENAME_OLD))
+ return 0;
+
+ config_init(&cfg);
+ int ok = 0;
+
+ if (config_read_file(&cfg, old_path) && config_lookup(&cfg, "display") != NULL) {
+ parse_opl_cfg(&cfg, out_theme_id, out_lang_id);
+ ok = 1;
+ }
+ config_destroy(&cfg);
+
+ if (!ok) {
+ LOG("CONFIG_WOPL: old format detected at selected config root, attempting legacy migration\n");
+ ok = cfgMigrateLegacyOPL(old_path, out_theme_id, out_lang_id);
+ }
+
+ if (!ok)
+ return 0;
+
+ if (wOPLSave()) {
+ char bak[256];
+
+ snprintf(bak, sizeof(bak), "%s.bak", old_path);
+ rename(old_path, bak);
+ LOG("CONFIG_WOPL: migrated to '%s'\n", WOPL_FILENAME);
+ }
+
+ return 1;
+ // DELETE_WITH_MIGRATION
+}
+
+int wOPLSave(void)
+{
+ return do_save(WOPL_FILENAME, build_opl);
+}
+
+// ---------------------------------------------------------------------------
+// Network config (wopl_network.cfg)
+// ---------------------------------------------------------------------------
+
+static void parse_net(config_t *cfg)
+{
+ const char *string;
+
+ gETHOpMode = lookup_int(cfg, "eth.link_mode", gETHOpMode);
+
+ ps2_ip_use_dhcp = lookup_bool(cfg, "ps2.dhcp", ps2_ip_use_dhcp);
+ if ((string = lookup_str(cfg, "ps2.ip", NULL)))
+ str_to_ip(string, ps2_ip);
+ if ((string = lookup_str(cfg, "ps2.netmask", NULL)))
+ str_to_ip(string, ps2_netmask);
+ if ((string = lookup_str(cfg, "ps2.gateway", NULL)))
+ str_to_ip(string, ps2_gateway);
+ if ((string = lookup_str(cfg, "ps2.dns", NULL)))
+ str_to_ip(string, ps2_dns);
+
+ gPCShareAddressIsNetBIOS = lookup_bool(cfg, "smb.use_netbios", gPCShareAddressIsNetBIOS);
+ if ((string = lookup_str(cfg, "smb.nb_address", NULL)))
+ copy_str(gPCShareNBAddress, string, sizeof(gPCShareNBAddress));
+ if ((string = lookup_str(cfg, "smb.ip", NULL)))
+ str_to_ip(string, pc_ip);
+
+ gPCPort = lookup_int(cfg, "smb.port", gPCPort);
+ if ((string = lookup_str(cfg, "smb.share", NULL)))
+ copy_str(gPCShareName, string, sizeof(gPCShareName));
+ if ((string = lookup_str(cfg, "smb.username", NULL)))
+ copy_str(gPCUserName, string, sizeof(gPCUserName));
+ if ((string = lookup_str(cfg, "smb.password", NULL)))
+ copy_str(gPCPassword, string, sizeof(gPCPassword));
+ if ((string = lookup_str(cfg, "nbd.export", NULL)))
+ copy_str(gExportName, string, sizeof(gExportName));
+}
+
+static void build_net(config_setting_t *root)
+{
+ config_setting_t *group;
+
+ group = add_group(root, "eth");
+ set_int(group, "link_mode", gETHOpMode);
+
+ group = add_group(root, "ps2");
+ set_bool(group, "dhcp", ps2_ip_use_dhcp);
+ set_ip(group, "ip", ps2_ip);
+ set_ip(group, "netmask", ps2_netmask);
+ set_ip(group, "gateway", ps2_gateway);
+ set_ip(group, "dns", ps2_dns);
+
+ group = add_group(root, "smb");
+ set_bool(group, "use_netbios", gPCShareAddressIsNetBIOS);
+ set_str(group, "nb_address", gPCShareNBAddress);
+ set_ip(group, "ip", pc_ip);
+ set_int(group, "port", gPCPort);
+ set_str(group, "share", gPCShareName);
+ set_str(group, "username", gPCUserName);
+ set_str(group, "password", gPCPassword);
+
+ group = add_group(root, "nbd");
+ set_str(group, "export", gExportName);
+}
+
+int wOPLNetLoad(void)
+{
+ LOG("CONFIG_NET: enter config_dir='%s'\n", config_dir);
+
+ if (!ensure_config_dir())
+ return 0;
+
+ char path[256];
+ char old_path[256];
+ config_t cfg;
+
+ if (!pathJoin(path, sizeof(path), config_dir, NET_FILENAME))
+ return 0;
+
+ // 1. Try new filename
+ config_init(&cfg);
+ if (config_read_file(&cfg, path)) {
+ cfgValidateBegin(path);
+ parse_net(&cfg);
+ cfgValidateEnd();
+ config_destroy(&cfg);
+ LOG("CONFIG_NET: loaded from '%s'\n", path);
+ return 1;
+ }
+ log_config_error(path, &cfg);
+ config_destroy(&cfg);
+
+ // DELETE_WITH_MIGRATION
+ // 2. Try old filename.. migrate to new filename and delete old
+ if (!pathJoin(old_path, sizeof(old_path), config_dir, NET_FILENAME_OLD))
+ return 0;
+
+ config_init(&cfg);
+ int ok = 0;
+ if (config_read_file(&cfg, old_path) && config_lookup(&cfg, "ps2") != NULL) {
+ parse_net(&cfg);
+ ok = 1;
+ }
+ config_destroy(&cfg);
+
+ if (!ok) {
+ LOG("CONFIG_NET: old format detected, attempting legacy migration\n");
+ ok = cfgMigrateLegacyNet(old_path);
+ }
+
+ if (!ok)
+ return 0;
+
+ if (wOPLNetSave()) {
+ char bak[256];
+ snprintf(bak, sizeof(bak), "%s.bak", old_path);
+ rename(old_path, bak);
+ LOG("CONFIG_NET: migrated to '%s'\n", NET_FILENAME);
+ }
+ // DELETE_WITH_MIGRATION
+
+ return 1;
+}
+
+int wOPLNetSave(void)
+{
+ return do_save(NET_FILENAME, build_net);
+}
+
+// ---------------------------------------------------------------------------
+// Last config (wopl_last_played.cfg)
+// ---------------------------------------------------------------------------
+
+// No legacy migration.. pointless
+int wOPLLastLoad(void)
+{
+ if (!ensure_config_dir())
+ return 0;
+
+ char path[256];
+
+ if (!pathJoin(path, sizeof(path), config_dir, LAST_FILENAME))
+ return 0;
+
+ config_t cfg;
+ config_init(&cfg);
+
+ if (!config_read_file(&cfg, path)) {
+ log_config_error(path, &cfg);
+ config_destroy(&cfg);
+ return 0;
+ }
+
+ cfgValidateBegin(path);
+ const char *string = lookup_str(&cfg, "last_played", NULL);
+ if (string)
+ copy_str(last_played, string, sizeof(last_played));
+ cfgValidateEnd();
+
+ config_destroy(&cfg);
+
+ return 1;
+}
+
+int wOPLLastSave(const char *startup)
+{
+ if (!ensure_config_dir() || !startup)
+ return 0;
+
+ char path[256];
+
+ if (!pathJoin(path, sizeof(path), config_dir, LAST_FILENAME))
+ return 0;
+
+ config_t cfg;
+ config_init(&cfg);
+ config_setting_t *root = config_root_setting(&cfg);
+
+ config_setting_t *setting = config_setting_add(root, "last_played", CONFIG_TYPE_STRING);
+ if (setting)
+ config_setting_set_string(setting, startup);
+
+ int ok = config_write_file(&cfg, path);
+ config_destroy(&cfg);
+
+ if (ok)
+ copy_str(last_played, startup, sizeof(last_played));
+
+ return ok;
+}
+
+const char *wOPLLastGet(void)
+{
+ return last_played[0] ? last_played : NULL;
+}
+
+// ---------------------------------------------------------------------------
+// Global Game config (wopl_global_game.cfg)
+// ---------------------------------------------------------------------------
+
+static void parse_global_game(config_t *cfg)
+{
+ int val;
+#ifdef GSM
+ if (cfgGetInt(cfg, "gsm.enable", &val))
+ gGlobalGameCfg.gsm_enable = val;
+ if (cfgGetInt(cfg, "gsm.vmode", &val))
+ gGlobalGameCfg.gsm_vmode = val;
+ if (cfgGetInt(cfg, "gsm.x_offset", &val))
+ gGlobalGameCfg.gsm_xoffset = val;
+ if (cfgGetInt(cfg, "gsm.y_offset", &val))
+ gGlobalGameCfg.gsm_yoffset = val;
+ if (cfgGetInt(cfg, "gsm.field_fix", &val))
+ gGlobalGameCfg.gsm_fieldfix = val;
+#endif
+#ifdef CHEAT
+ if (cfgGetInt(cfg, "cheat.enable", &val))
+ gGlobalGameCfg.cheat_enable = val;
+ if (cfgGetInt(cfg, "cheat.mode", &val))
+ gGlobalGameCfg.cheat_mode = val;
+ if (cfgGetInt(cfg, "cheat.enable_image", &val))
+ gGlobalGameCfg.cheat_enable_image = val;
+#endif
+#ifdef PADEMU
+ if (cfgGetInt(cfg, "pademu.enable", &val))
+ gGlobalGameCfg.pademu_enable = val;
+ if (cfgGetInt(cfg, "pademu.settings", &val))
+ gGlobalGameCfg.pademu_settings = val;
+ if (cfgGetInt(cfg, "padmacro.settings", &val))
+ gGlobalGameCfg.padmacro_settings = val;
+#endif
+ if (cfgGetInt(cfg, "osd.enable", &val))
+ gGlobalGameCfg.osd_enable = val;
+ if (cfgGetInt(cfg, "osd.lang_id", &val))
+ gGlobalGameCfg.osd_langid = val;
+ if (cfgGetInt(cfg, "osd.tv_aspect", &val))
+ gGlobalGameCfg.osd_tv_aspect = val;
+ if (cfgGetInt(cfg, "osd.vmode", &val))
+ gGlobalGameCfg.osd_vmode = val;
+}
+
+static void build_global_game(config_setting_t *root)
+{
+ config_setting_t *group;
+#ifdef GSM
+ group = add_group(root, "gsm");
+ set_int(group, "enable", gGlobalGameCfg.gsm_enable);
+ set_int(group, "vmode", gGlobalGameCfg.gsm_vmode);
+ set_int(group, "x_offset", gGlobalGameCfg.gsm_xoffset);
+ set_int(group, "y_offset", gGlobalGameCfg.gsm_yoffset);
+ set_int(group, "field_fix", gGlobalGameCfg.gsm_fieldfix);
+#endif
+#ifdef CHEAT
+ group = add_group(root, "cheat");
+ set_int(group, "enable", gGlobalGameCfg.cheat_enable);
+ set_int(group, "mode", gGlobalGameCfg.cheat_mode);
+ set_int(group, "enable_image", gGlobalGameCfg.cheat_enable_image);
+#endif
+#ifdef PADEMU
+ group = add_group(root, "pademu");
+ set_int(group, "enable", gGlobalGameCfg.pademu_enable);
+ set_int(group, "settings", gGlobalGameCfg.pademu_settings);
+ group = add_group(root, "padmacro");
+ set_int(group, "settings", gGlobalGameCfg.padmacro_settings);
+#endif
+ group = add_group(root, "osd");
+ set_int(group, "enable", gGlobalGameCfg.osd_enable);
+ set_int(group, "lang_id", gGlobalGameCfg.osd_langid);
+ set_int(group, "tv_aspect", gGlobalGameCfg.osd_tv_aspect);
+ set_int(group, "vmode", gGlobalGameCfg.osd_vmode);
+}
+
+int wOPLGlobalGameLoad(void)
+{
+ LOG("CONFIG_GAME: enter config_dir='%s'\n", config_dir);
+
+ if (!ensure_config_dir())
+ return 0;
+
+ char path[256];
+ config_t cfg;
+
+ if (!pathJoin(path, sizeof(path), config_dir, GAME_FILENAME))
+ return 0;
+
+ config_init(&cfg);
+ if (config_read_file(&cfg, path)) {
+ cfgValidateBegin(path);
+ parse_global_game(&cfg);
+ cfgValidateEnd();
+ config_destroy(&cfg);
+ LOG("CONFIG_GAME: loaded from '%s'\n", path);
+ return 1;
+ }
+ log_config_error(path, &cfg);
+ config_destroy(&cfg);
+
+ // DELETE_WITH_MIGRATION v
+ if (!pathJoin(path, sizeof(path), config_dir, GAME_FILENAME_OLD))
+ return 0;
+
+ if (cfgMigrateLegacyGlobalGame(path)) {
+ if (wOPLGlobalGameSave()) {
+ char bak[256];
+ snprintf(bak, sizeof(bak), "%s.bak", path);
+ rename(path, bak);
+ LOG("CONFIG_GAME: migrated to '%s'\n", GAME_FILENAME);
+ }
+ return 1;
+ }
+ // DELETE_WITH_MIGRATION ^
+
+ return 0;
+}
+
+int wOPLGlobalGameSave(void)
+{
+ return do_save(GAME_FILENAME, build_global_game);
+}
+
+// ---------------------------------------------------------------------------
+// Per Game config (SLES1234.cfg)
+// ---------------------------------------------------------------------------
+
+static void init_per_game_cfg(per_game_cfg_t *cfg)
+{
+ memset(cfg, 0, sizeof(*cfg));
+ cfg->dma = 7; // 7 = not set, use device default
+}
+
+static void parse_per_game(config_t *cfg, per_game_cfg_t *pg)
+{
+ int val;
+ const char *str;
+
+ if (cfgGetInt(cfg, "compat", &val))
+ pg->compat = val;
+ if (cfgGetInt(cfg, "dma", &val))
+ pg->dma = val;
+ if (cfgGetInt(cfg, "core_loader", &val))
+ pg->core_loader = val;
+ if (cfgGetStr(cfg, "dnas", &str))
+ copy_str(pg->dnas, str, sizeof(pg->dnas));
+ if (cfgGetStr(cfg, "alt_startup", &str))
+ copy_str(pg->alt_startup, str, sizeof(pg->alt_startup));
+ if (cfgGetStr(cfg, "vmc1", &str))
+ copy_str(pg->vmc1, str, sizeof(pg->vmc1));
+ if (cfgGetStr(cfg, "vmc2", &str))
+ copy_str(pg->vmc2, str, sizeof(pg->vmc2));
+
+#ifdef GSM
+ if (cfgGetInt(cfg, "gsm.source", &val))
+ pg->gsm_source = val;
+ if (cfgGetInt(cfg, "gsm.enable", &val))
+ pg->gsm_enable = val;
+ if (cfgGetInt(cfg, "gsm.vmode", &val))
+ pg->gsm_vmode = val;
+ if (cfgGetInt(cfg, "gsm.x_offset", &val))
+ pg->gsm_xoffset = val;
+ if (cfgGetInt(cfg, "gsm.y_offset", &val))
+ pg->gsm_yoffset = val;
+ if (cfgGetInt(cfg, "gsm.field_fix", &val))
+ pg->gsm_fieldfix = val;
+#endif
+#ifdef CHEAT
+ if (cfgGetInt(cfg, "cheat.source", &val))
+ pg->cheat_source = val;
+ if (cfgGetInt(cfg, "cheat.enable", &val))
+ pg->cheat_enable = val;
+ if (cfgGetInt(cfg, "cheat.mode", &val))
+ pg->cheat_mode = val;
+ if (cfgGetInt(cfg, "cheat.enable_image", &val))
+ pg->cheat_enable_image = val;
+#endif
+#ifdef PADEMU
+ if (cfgGetInt(cfg, "pademu.source", &val))
+ pg->pademu_source = val;
+ if (cfgGetInt(cfg, "pademu.enable", &val))
+ pg->pademu_enable = val;
+ if (cfgGetInt(cfg, "pademu.settings", &val))
+ pg->pademu_settings = val;
+ if (cfgGetInt(cfg, "padmacro.source", &val))
+ pg->padmacro_source = val;
+ if (cfgGetInt(cfg, "padmacro.settings", &val))
+ pg->padmacro_settings = val;
+#endif
+ if (cfgGetInt(cfg, "osd.source", &val))
+ pg->osd_source = val;
+ if (cfgGetInt(cfg, "osd.enable", &val))
+ pg->osd_enable = val;
+ if (cfgGetInt(cfg, "osd.lang_id", &val))
+ pg->osd_langid = val;
+ if (cfgGetInt(cfg, "osd.tv_aspect", &val))
+ pg->osd_tv_aspect = val;
+ if (cfgGetInt(cfg, "osd.vmode", &val))
+ pg->osd_vmode = val;
+}
+
+static void build_per_game(config_setting_t *root, const per_game_cfg_t *pg)
+{
+ set_int(root, "compat", pg->compat);
+ set_int(root, "dma", pg->dma);
+ set_int(root, "core_loader", pg->core_loader);
+ set_str(root, "dnas", pg->dnas);
+ set_str(root, "alt_startup", pg->alt_startup);
+ set_str(root, "vmc1", pg->vmc1);
+ set_str(root, "vmc2", pg->vmc2);
+
+ config_setting_t *group;
+#ifdef GSM
+ group = add_group(root, "gsm");
+ set_int(group, "source", pg->gsm_source);
+ set_int(group, "enable", pg->gsm_enable);
+ set_int(group, "vmode", pg->gsm_vmode);
+ set_int(group, "x_offset", pg->gsm_xoffset);
+ set_int(group, "y_offset", pg->gsm_yoffset);
+ set_int(group, "field_fix", pg->gsm_fieldfix);
+#endif
+#ifdef CHEAT
+ group = add_group(root, "cheat");
+ set_int(group, "source", pg->cheat_source);
+ set_int(group, "enable", pg->cheat_enable);
+ set_int(group, "mode", pg->cheat_mode);
+ set_int(group, "enable_image", pg->cheat_enable_image);
+#endif
+#ifdef PADEMU
+ group = add_group(root, "pademu");
+ set_int(group, "source", pg->pademu_source);
+ set_int(group, "enable", pg->pademu_enable);
+ set_int(group, "settings", pg->pademu_settings);
+ group = add_group(root, "padmacro");
+ set_int(group, "source", pg->padmacro_source);
+ set_int(group, "settings", pg->padmacro_settings);
+#endif
+ group = add_group(root, "osd");
+ set_int(group, "source", pg->osd_source);
+ set_int(group, "enable", pg->osd_enable);
+ set_int(group, "lang_id", pg->osd_langid);
+ set_int(group, "tv_aspect", pg->osd_tv_aspect);
+ set_int(group, "vmode", pg->osd_vmode);
+}
+
+int wOPLPerGameLoad(const char *path, per_game_cfg_t *cfg)
+{
+ init_per_game_cfg(cfg);
+
+ config_t lcfg;
+ config_init(&lcfg);
+ if (config_read_file(&lcfg, path)) {
+ cfgValidateBegin(path);
+ parse_per_game(&lcfg, cfg);
+ cfgValidateEnd();
+ config_destroy(&lcfg);
+ LOG("CONFIG_PERGAME: loaded from '%s'\n", path);
+
+ return 1;
+ }
+
+ log_config_error(path, &lcfg);
+ config_destroy(&lcfg);
+
+ return 0;
+}
+
+int wOPLPerGameSave(const char *path, const per_game_cfg_t *cfg)
+{
+ config_t lcfg;
+ config_init(&lcfg);
+ config_setting_t *root = config_root_setting(&lcfg);
+ build_per_game(root, cfg);
+
+ int ok = config_write_file(&lcfg, path);
+ config_destroy(&lcfg);
+
+ if (!ok)
+ LOG("CONFIG_PERGAME: failed to write '%s'\n", path);
+ else
+ LOG("CONFIG_PERGAME: saved to '%s'\n", path);
+
+ return ok;
+}
+
+// ---------------------------------------------------------------------------
+// Per Game info (SLES1234.info)
+// ---------------------------------------------------------------------------
+
+static int lookup_info_int(config_t *cfg, const char *key, int *out)
+{
+ const config_setting_t *setting = config_lookup(cfg, key);
+
+ if (!setting)
+ return 0;
+
+ switch (config_setting_type(setting)) {
+ case CONFIG_TYPE_INT:
+ *out = config_setting_get_int(setting);
+ return 1;
+ case CONFIG_TYPE_BOOL:
+ *out = config_setting_get_bool(setting) ? 1 : 0;
+ return 1;
+ case CONFIG_TYPE_STRING: {
+ const char *str = config_setting_get_string(setting);
+ const char *slash = strrchr(str, '/');
+
+ if (slash && slash[1])
+ str = slash + 1;
+
+ *out = atoi(str);
+ return 1;
+ }
+ default:
+ cfgCheckExists(cfg, key, "integer or string");
+ return 0;
+ }
+}
+
+static void parse_game_info(config_t *cfg, game_info_t *gi)
+{
+ const char *str;
+
+ if (cfgGetStr(cfg, "Title", &str))
+ copy_str(gi->title, str, sizeof(gi->title));
+ if (cfgGetStr(cfg, "Serial", &str))
+ copy_str(gi->serial, str, sizeof(gi->serial));
+ if (cfgGetStr(cfg, "Description", &str))
+ copy_str(gi->description, str, sizeof(gi->description));
+ if (cfgGetStr(cfg, "Developer", &str))
+ copy_str(gi->developer, str, sizeof(gi->developer));
+ if (cfgGetStr(cfg, "Genre", &str))
+ copy_str(gi->genre, str, sizeof(gi->genre));
+ if (cfgGetStr(cfg, "Publisher", &str))
+ copy_str(gi->publisher, str, sizeof(gi->publisher));
+ if (cfgGetStr(cfg, "Release", &str))
+ copy_str(gi->release, str, sizeof(gi->release));
+ if (cfgGetStr(cfg, "Aspect", &str))
+ copy_str(gi->aspect, str, sizeof(gi->aspect));
+ if (cfgGetStr(cfg, "Parental", &str))
+ copy_str(gi->parental, str, sizeof(gi->parental));
+ if (cfgGetStr(cfg, "Region", &str))
+ copy_str(gi->region, str, sizeof(gi->region));
+
+ lookup_info_int(cfg, "Players", &gi->players);
+ lookup_info_int(cfg, "UserRating", &gi->user_rating);
+
+ if (cfgGetStr(cfg, "Version", &str))
+ copy_str(gi->version, str, sizeof(gi->version));
+ if (cfgGetStr(cfg, "Package", &str))
+ copy_str(gi->package, str, sizeof(gi->package));
+ if (cfgGetStr(cfg, "Source", &str))
+ copy_str(gi->source, str, sizeof(gi->source));
+}
+
+static void build_game_info(config_setting_t *root, const game_info_t *gi)
+{
+ if (gi->title[0])
+ set_str(root, "Title", gi->title);
+ if (gi->serial[0])
+ set_str(root, "Serial", gi->serial);
+ if (gi->description[0])
+ set_str(root, "Description", gi->description);
+ if (gi->developer[0])
+ set_str(root, "Developer", gi->developer);
+ if (gi->genre[0])
+ set_str(root, "Genre", gi->genre);
+ if (gi->publisher[0])
+ set_str(root, "Publisher", gi->publisher);
+ if (gi->release[0])
+ set_str(root, "Release", gi->release);
+ if (gi->aspect[0])
+ set_str(root, "Aspect", gi->aspect);
+ if (gi->parental[0])
+ set_str(root, "Parental", gi->parental);
+ if (gi->players)
+ set_int(root, "Players", gi->players);
+ if (gi->region[0])
+ set_str(root, "Region", gi->region);
+ if (gi->user_rating)
+ set_int(root, "UserRating", gi->user_rating);
+
+ if (gi->version[0])
+ set_str(root, "Version", gi->version);
+ if (gi->package[0])
+ set_str(root, "Package", gi->package);
+ if (gi->source[0])
+ set_str(root, "Source", gi->source);
+}
+
+int wOPLGameInfoLoad(const char *path, game_info_t *gi)
+{
+ memset(gi, 0, sizeof(*gi));
+ config_t cfg;
+ config_init(&cfg);
+ if (config_read_file(&cfg, path)) {
+ cfgValidateBegin(path);
+ parse_game_info(&cfg, gi);
+ cfgValidateEnd();
+ config_destroy(&cfg);
+
+ return 1;
+ }
+ log_config_error(path, &cfg);
+ config_destroy(&cfg);
+
+ return 0;
+}
+
+int wOPLGameInfoSave(const char *path, const game_info_t *gi)
+{
+ config_t cfg;
+ config_init(&cfg);
+ config_setting_t *root = config_root_setting(&cfg);
+ build_game_info(root, gi);
+ int ok = config_write_file(&cfg, path);
+ config_destroy(&cfg);
+
+ return ok;
+}
+
+// ---------------------------------------------------------------------------
+// Application level config handling
+// ---------------------------------------------------------------------------
+
+char *gBaseMCDir; // used for thm/lang even after migration
+
+static int lscstatus = CONFIG_ALL;
+static int lscret = 0;
+
+void configApply(int themeID, int langID, int skipDeviceRefresh)
+{
+ if (gDefaultDevice < 0 || gDefaultDevice > MMCE_MODE)
+ gDefaultDevice = APP_MODE;
+
+ guiUpdateScrollSpeed();
+
+ guiSetFrameHook(&menuUpdateHook);
+
+ guiLock();
+ int changed = rmSetMode(0);
+ guiUnlock();
+ if (changed) {
+ // reinit the graphics...
+ thmReloadScreenExtents();
+ guiReloadScreenExtents();
+ }
+
+ // theme must be set after color, and lng after theme
+ changed = thmSetGuiValue(themeID, changed);
+ int langChanged = lngSetGuiValue(langID);
+
+ guiUpdateScreenScale();
+
+ // Check if we should refresh device support as well.
+ if (skipDeviceRefresh == 0) {
+ bdmLoadEnabledDeviceModules();
+
+ initAllSupport(0);
+ for (int i = 0; i < MODE_COUNT; i++) {
+ if (list_support[i].support == NULL)
+ continue;
+ moduleUpdateMenuInternal(&list_support[i], changed, langChanged);
+ }
+ } else {
+ if (changed) {
+ for (int i = 0; i < MODE_COUNT; i++) {
+ if (list_support[i].support && list_support[i].subMenu)
+ submenuRebuildCache(list_support[i].subMenu);
+ }
+ }
+ }
+
+#ifdef __DEBUG
+ debugApplyConfig();
+#endif
+}
+
+static int resolve_usb_mass_config_path(char *dir, size_t dir_len, const char *label)
+{
+ char resolved[128];
+
+ if (!pathIsUsbMassCompatPath(dir))
+ return 1;
+
+ LOG("CONFIG: resolving USB mass compatibility %s '%s'\n", label, dir);
+
+ if (!bdmResolveUsbMassCompatPath(resolved, sizeof(resolved), dir)) {
+ LOG("CONFIG: could not resolve USB mass compatibility %s '%s'\n", label, dir);
+ return 0;
+ }
+
+ pathNormaliseDir(resolved, sizeof(resolved));
+
+ if (!pathIsDevicePath(resolved) || pathIsUsbMassCompatPath(resolved)) {
+ LOG("CONFIG: rejected resolved %s '%s'\n", label, resolved);
+ return 0;
+ }
+
+ LOG("CONFIG: resolved %s '%s' -> '%s'\n", label, dir, resolved);
+ copy_str(dir, resolved, dir_len);
+
+ return 1;
+}
+
+static int resolve_usb_mass_config_paths(void)
+{
+ if (!resolve_usb_mass_config_path(config_dir, sizeof(config_dir), "config_dir"))
+ return 0;
+
+ if (!resolve_usb_mass_config_path(boot_dir, sizeof(boot_dir), "boot_dir"))
+ return 0;
+
+ return 1;
+}
+
+void _loadConfig() // called directly by initializer at boot before GUI is ready
+{
+ int themeID = -1, langID = -1;
+ int result = 0;
+ int have_config_dir;
+
+ have_config_dir = ensure_config_dir();
+
+ LOG("CONFIG: initial config root result=%d config_dir='%s' boot_dir='%s'\n", have_config_dir, config_dir, boot_dir);
+
+ if (lscstatus & CONFIG_OPL) {
+ if (wOPLLoad(&themeID, &langID))
+ result |= CONFIG_OPL;
+ if (getKeyPressed(KEY_TRIANGLE) && getKeyPressed(KEY_CROSS)) {
+ LOG("--- Triangle+Cross held at boot - setting Video Mode to Auto ---\n");
+ gVMode = 0;
+ }
+ }
+ if (lscstatus & CONFIG_NETWORK) {
+ if (wOPLNetLoad())
+ result |= CONFIG_NETWORK;
+ }
+ if (lscstatus & CONFIG_GAME)
+ if (wOPLGlobalGameLoad())
+ result |= CONFIG_GAME;
+
+ LOG("CONFIG: load requested=0x%X result=0x%X config_dir='%s'\n", lscstatus, result, config_dir);
+
+ configApply(themeID, langID, 0);
+ resolve_usb_mass_config_paths();
+
+ lscret = result;
+ lscstatus = 0;
+ if (result)
+ showCfgPopup = 1;
+
+#ifdef PADEMU
+ gEnablePadEmu = gGlobalGameCfg.pademu_enable;
+ sysInitPadEmu();
+#endif
+}
+
+int configLoad(int types)
+{
+ lscstatus = types;
+ lscret = 0;
+
+ guiHandleDeferedIO(&lscstatus, _l(_STR_LOADING_SETTINGS), IO_CUSTOM_SIMPLEACTION, &_loadConfig);
+
+ return lscret;
+}
+
+static int config_type_count(int types)
+{
+ int count = 0;
+
+ if (types & CONFIG_OPL)
+ count++;
+ if (types & CONFIG_NETWORK)
+ count++;
+ if (types & CONFIG_GAME)
+ count++;
+
+ return count;
+}
+
+static int save_all_to_current_dir(int types) // like the old configWriteMulti()
+{
+ int result = 0;
+ int expected = config_type_count(types);
+
+ if (!ensure_config_dir()) {
+ LOG("CONFIG: no config_dir selected for save\n");
+ return 0;
+ }
+
+ if (!resolve_usb_mass_config_paths())
+ return 0;
+
+ LOG("CONFIG: saving to config_dir '%s'\n", config_dir);
+
+ if (pathHasDevicePrefix(config_dir, "mc") && !sbEnsureMCConfigFolder(config_dir)) {
+ LOG("CONFIG: failed to prepare MC config folder '%s'\n", config_dir);
+ return 0;
+ }
+
+ if (types & CONFIG_OPL)
+ result += do_save_at_dir(config_dir, WOPL_FILENAME, build_opl);
+ if (types & CONFIG_NETWORK)
+ result += do_save_at_dir(config_dir, NET_FILENAME, build_net);
+ if (types & CONFIG_GAME)
+ result += do_save_at_dir(config_dir, GAME_FILENAME, build_global_game);
+
+ if (result != expected)
+ return result;
+
+ if (!save_boot_config())
+ return 0;
+
+ return result;
+}
+
+static void _saveConfig()
+{
+ lscret = save_all_to_current_dir(lscstatus);
+ lscstatus = 0;
+}
+
+int configSave(int types, int showUI)
+{
+ lscstatus = types;
+ lscret = 0;
+
+ guiHandleDeferedIO(&lscstatus, _l(_STR_SAVING_SETTINGS), IO_CUSTOM_SIMPLEACTION, &_saveConfig);
+
+ if (showUI) {
+ if (lscret) {
+ char notification[128];
+ char path[128] = {0};
+ const char *rawPath = wOPLGetDir();
+ if (rawPath) {
+ strncpy(path, rawPath, sizeof(path) - 1);
+ char *colpos = strchr(path, ':');
+ if (colpos != NULL)
+ *(colpos + 1) = '\0';
+ }
+ snprintf(notification, sizeof(notification), _l(_STR_SETTINGS_SAVED), path[0] ? path : "?");
+ guiMsgBox(notification, 0, NULL);
+ } else
+ guiMsgBox(_l(_STR_ERROR_SAVING_SETTINGS), 0, NULL);
+ }
+
+ return lscret;
+}
diff --git a/src/dia.c b/src/dia.c
index 39b27889..ae117db4 100644
--- a/src/dia.c
+++ b/src/dia.c
@@ -70,10 +70,11 @@ int diaShowKeyb(char *text, int maxLen, int hide_text, const char *title)
char *commands[KEYB_HEIGHT] = {_l(_STR_BACKSPACE), _l(_STR_SPACE), _l(_STR_ENTER), _l(_STR_MODE)};
GSTEXTURE *cmdicons[KEYB_HEIGHT];
- cmdicons[0] = thmGetTexture(BUTTON_SYMBOL_SQUARE_ICON);
- cmdicons[1] = thmGetTexture(BUTTON_SYMBOL_TRIANGLE_ICON);
- cmdicons[2] = thmGetTexture(BUTTON_START_ICON);
- cmdicons[3] = thmGetTexture(BUTTON_SELECT_ICON);
+ cmdicons[0] = thmGetTexture(SQUARE_ICON);
+ cmdicons[1] = thmGetTexture(TRIANGLE_ICON);
+ cmdicons[2] = thmGetTexture(START_ICON);
+ cmdicons[3] = thmGetTexture(SELECT_ICON);
+
rmGetScreenExtents(&screenWidth, &screenHeight);
@@ -90,7 +91,7 @@ int diaShowKeyb(char *text, int maxLen, int hide_text, const char *title)
readPads();
guiStartFrame();
- if (guiDrawBGMain() == 0)
+ if (guiDrawBGSettings() == 0)
guiDrawBGPlasma();
rmDrawRect(0, 0, screenWidth, screenHeight, gColDarker);
@@ -123,7 +124,7 @@ int diaShowKeyb(char *text, int maxLen, int hide_text, const char *title)
if (cmdicons[i]) {
int w = (cmdicons[i]->Width * 20) / cmdicons[i]->Height;
int h = 20;
- rmDrawPixmap(cmdicons[i], 436, 170 + 3 * UI_SPACING_H * i, ALIGN_NONE, w, h, SCALING_RATIO, gDefaultCol);
+ rmDrawPixmap(cmdicons[i], 436, 170 + 3 * UI_SPACING_H * i, ALIGN_NONE, w, h, SCALING_RATIO, gDefaultCol, 0);
}
x = 477;
@@ -132,8 +133,7 @@ int diaShowKeyb(char *text, int maxLen, int hide_text, const char *title)
diaDrawBoundingBox(x, 170 + 3 * UI_SPACING_H * i, w, UI_SPACING_H, 0);
}
- guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? BUTTON_SYMBOL_CROSS_ICON : BUTTON_SYMBOL_CIRCLE_ICON, _STR_CANCEL, gTheme->fonts[0], 500, 417, gTheme->selTextColor);
-
+ guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? CROSS_ICON : CIRCLE_ICON, _STR_CANCEL, gTheme->fonts[0], 500, 417, gTheme->textColor);
guiEndFrame();
if (getKey(KEY_LEFT)) {
@@ -285,8 +285,9 @@ static int diaShowColSel(unsigned char *r, unsigned char *g, unsigned char *b)
readPads();
guiStartFrame();
- if (guiDrawBGMain() == 0)
+ if (guiDrawBGSettings() == 0)
guiDrawBGPlasma();
+
rmDrawRect(0, 0, screenWidth, screenHeight, gColDarker);
// "Color selection"
@@ -322,8 +323,8 @@ static int diaShowColSel(unsigned char *r, unsigned char *g, unsigned char *b)
rmDrawRect(x, y, 70, 70, GS_SETREG_RGBA(0x60, 0x60, 0x60, 0x80));
rmDrawRect(x + 5, y + 5, 60, 60, dcol);
- guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? BUTTON_SYMBOL_CIRCLE_ICON : BUTTON_SYMBOL_CROSS_ICON, _STR_OK, gTheme->fonts[0], 420, 417, gTheme->selTextColor);
- guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? BUTTON_SYMBOL_CROSS_ICON : BUTTON_SYMBOL_CIRCLE_ICON, _STR_CANCEL, gTheme->fonts[0], 500, 417, gTheme->selTextColor);
+ guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? CIRCLE_ICON : CROSS_ICON, _STR_OK, gTheme->fonts[0], 420, 417, gTheme->textColor);
+ guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? CROSS_ICON : CIRCLE_ICON, _STR_CANCEL, gTheme->fonts[0], 500, 417, gTheme->textColor);
guiEndFrame();
@@ -575,7 +576,7 @@ static int scrollOffset = 0;
/// renders whole ui screen (for given dialog setup)
void diaRenderUI(struct UIItem *ui, short inMenu, struct UIItem *cur, int haveFocus)
{
- if (guiDrawBGMain() == 0)
+ if (guiDrawBGSettings() == 0)
guiDrawBGPlasma();
int x0 = 20;
@@ -622,13 +623,13 @@ void diaRenderUI(struct UIItem *ui, short inMenu, struct UIItem *cur, int haveFo
}
int uiHints[2] = {_STR_SELECT, _STR_BACK};
- int uiIcons[2] = {BUTTON_SYMBOL_CIRCLE_ICON, BUTTON_SYMBOL_CROSS_ICON};
+ int uiIcons[2] = {CIRCLE_ICON, CROSS_ICON};
int uiY = gTheme->usedHeight - 32;
int uiX = guiAlignSubMenuHints(2, uiHints, uiIcons, gTheme->fonts[0], 12, 2);
- uiX = guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? uiIcons[0] : uiIcons[1], uiHints[0], gTheme->fonts[0], uiX, uiY, gTheme->selTextColor);
+ uiX = guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? uiIcons[0] : uiIcons[1], uiHints[0], gTheme->fonts[0], uiX, uiY, gTheme->textColor);
uiX += 12;
- uiX = guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? uiIcons[1] : uiIcons[0], uiHints[1], gTheme->fonts[0], uiX, uiY, gTheme->selTextColor);
+ uiX = guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? uiIcons[1] : uiIcons[0], uiHints[1], gTheme->fonts[0], uiX, uiY, gTheme->textColor);
}
/// sets the ui item value to the default again
diff --git a/src/dialogs.c b/src/dialogs.c
index d52920aa..b83ccbf6 100644
--- a/src/dialogs.c
+++ b/src/dialogs.c
@@ -342,6 +342,8 @@ struct UIItem diaUIConfig[] = {
{UI_LABEL, 0, 1, 1, -1, -40, 0, {.label = {NULL, _STR_THEME}}},
{UI_SPACER},
{UI_ENUM, UICFG_THEME, 1, 1, -1, 0, 0, {.intvalue = {0, 0}}},
+ {UI_SPACER},
+ {UI_BUTTON, UICFG_COVERFLOW_BUTTON, 1, 1, -1, 0, 0, {.label = {"Coverflow Settings", -1}}},
{UI_BREAK},
{UI_LABEL, 0, 1, 1, -1, -40, 0, {.label = {NULL, _STR_LANGUAGE}}},
@@ -361,7 +363,7 @@ struct UIItem diaUIConfig[] = {
{UI_LABEL, 0, 1, 1, -1, -40, 0, {.label = {NULL, _STR_DISCART}}},
{UI_SPACER},
- {UI_BOOL, UICFG_DISCART, 1, 1, _STR_HINT_DISCART, 0, 0, {.intvalue = {0, 0}}},
+ {UI_BOOL, UICFG_DISCART, 1, 1, -1, 0, 0, {.intvalue = {0, 0}}},
{UI_BREAK},
{UI_LABEL, 0, 1, 1, -1, -40, 0, {.label = {NULL, _STR_ENABLE_NOTIFICATIONS}}},
@@ -422,6 +424,38 @@ struct UIItem diaUIConfig[] = {
// end of dialog
{UI_TERMINATOR}};
+// Coverflow Settings Menu
+struct UIItem diaCoverflowConfig[] = {
+ {UI_HEADER, 0, 1, 1, -1, 0, 0, {.label = {"Coverflow Settings", -1}}},
+ {UI_SPLITTER},
+
+ {UI_LABEL, 0, 1, 1, -1, -40, 0, {.label = {"Cover Count", -1}}},
+ {UI_SPACER},
+ {UI_ENUM, CFG_COVERFLOW_COUNT, 1, 1, -1, 0, 0, {.intvalue = {0, 0}}},
+ {UI_BREAK},
+
+ {UI_LABEL, 0, 1, 1, -1, -40, 0, {.label = {"Center Scale", -1}}},
+ {UI_SPACER},
+ {UI_ENUM, CFG_COVERFLOW_SCALE, 1, 1, -1, 0, 0, {.intvalue = {0, 0}}},
+ {UI_BREAK},
+
+ {UI_LABEL, 0, 1, 1, -1, -40, 0, {.label = {"Animation Speed", -1}}},
+ {UI_SPACER},
+ {UI_ENUM, CFG_COVERFLOW_ANIM, 1, 1, -1, 0, 0, {.intvalue = {0, 0}}},
+ {UI_BREAK},
+
+ {UI_LABEL, 0, 1, 1, -1, -40, 0, {.label = {"Dim Side Covers", -1}}},
+ {UI_SPACER},
+ {UI_BOOL, CFG_COVERFLOW_DIM, 1, 1, -1, 0, 0, {.intvalue = {0, 0}}},
+ {UI_BREAK},
+
+ // buttons
+ {UI_OK, 0, 1, 1, -1, 0, 0, {.label = {NULL, _STR_OK}}},
+ {UI_BREAK},
+
+ // end of dialog
+ {UI_TERMINATOR}};
+
// Per-Game Modes Menu
struct UIItem diaCompatConfig[] = {
{UI_HEADER, 0, 1, 1, -1, -30, 0, {.label = {NULL, _STR_COMPAT_SETTINGS}}},
@@ -619,6 +653,12 @@ struct UIItem diaCheatConfig[] = {
{UI_ENUM, CHTCFG_CHEATMODE, 1, 1, _STR_HINT_CHEATMODE, 0, 0, {.intvalue = {0, 0}}},
{UI_BREAK},
+ {UI_BREAK},
+ {UI_LABEL, 0, 1, 1, -1, -40, 0, {.label = {NULL, _STR_ENABLEIMAGE}}},
+ {UI_SPACER},
+ {UI_BOOL, CHTCFG_ENABLEIMAGE, 1, 1, _STR_HINT_ENABLEIMAGE, 0, 0, {.intvalue = {1, 1}}},
+ {UI_BREAK},
+
// buttons
{UI_OK, 0, 1, 1, -1, 0, 0, {.label = {NULL, _STR_OK}}},
{UI_BREAK},
@@ -935,19 +975,19 @@ struct UIItem diaAbout[] = {
{UI_BREAK},
{UI_SPACER},
- {UI_LABEL, 0, 1, 1, -1, 0, 15, {.label = {"BatRastard - belek666 - crazyc - dlanor - doctorxyz", -1}}},
+ {UI_LABEL, 0, 1, 1, -1, 0, 15, {.label = {"BatRastard - belek666 - Chase Bo Camp - crazyc - dlanor", -1}}},
{UI_BREAK},
{UI_SPACER},
- {UI_LABEL, 0, 1, 1, -1, 0, 15, {.label = {"Ripto - Wolf3s - Chase Bo Camp", -1}}},
+ {UI_LABEL, 0, 1, 1, -1, 0, 15, {.label = {"doctorxyz - hominem.te.esse - ifcaro - izdubar - jimmikaelkael", -1}}},
{UI_BREAK},
{UI_SPACER},
- {UI_LABEL, 0, 1, 1, -1, 0, 15, {.label = {"hominem.te.esse - ifcaro - izdubar - jimmikaelkael - KrahJohlito", -1}}},
+ {UI_LABEL, 0, 1, 1, -1, 0, 15, {.label = {"KrahJohlito - kr_ps2 - Maximus32 - misfire - Polo35", -1}}},
{UI_BREAK},
{UI_SPACER},
- {UI_LABEL, 0, 1, 1, -1, 0, 15, {.label = {"kr_ps2 - Maximus32 - misfire - Polo35 - reprep - SP193 - volca", -1}}},
+ {UI_LABEL, 0, 1, 1, -1, 0, 15, {.label = {"reprep - Ripto - SP193 - volca - Wolf3s", -1}}},
{UI_BREAK},
{UI_SPACER},
@@ -1120,3 +1160,33 @@ struct UIItem diaOSDConfig[] = {
{UI_BREAK},
// end of dialog
{UI_TERMINATOR}};
+
+// DELETE_WITH_MIGRATION v
+struct UIItem diaCfgMigration[] = {
+ {UI_HEADER, 0, 1, 1, -1, 0, 0, {.label = {"Config Migration", -1}}},
+ {UI_SPLITTER},
+
+ {UI_LABEL, 0, 1, 1, -1, -40, 0, {.label = {"Input Folder", -1}}},
+ {UI_SPACER},
+ {UI_ENUM, CFG_MIG_INPUT, 1, 1, -1, 0, 0, {.intvalue = {0, 0}}},
+ {UI_BREAK},
+
+ {UI_LABEL, 0, 1, 1, -1, -40, 0, {.label = {"Output Folder", -1}}},
+ {UI_SPACER},
+ {UI_ENUM, CFG_MIG_OUTPUT, 1, 1, -1, 0, 0, {.intvalue = {0, 0}}},
+ {UI_BREAK},
+
+ {UI_LABEL, 0, 1, 1, -1, -40, 0, {.label = {"Keep Originals", -1}}},
+ {UI_SPACER},
+ {UI_BOOL, CFG_MIG_KEEP_ORIGINALS, 1, 1, -1, 0, 0, {.intvalue = {1, 1}}},
+ {UI_BREAK},
+
+ {UI_BREAK},
+ {UI_BUTTON, CFG_MIG_CONVERT, 1, 1, -1, 0, 0, {.label = {"Convert", -1}}},
+ {UI_BREAK},
+
+ {UI_OK, 0, 1, 1, -1, 0, 0, {.label = {NULL, _STR_OK}}},
+ {UI_BREAK},
+
+ {UI_TERMINATOR}};
+// DELETE_WITH_MIGRATION ^
diff --git a/src/ethsupport.c b/src/ethsupport.c
index 5fe925a6..b8dbb390 100644
--- a/src/ethsupport.c
+++ b/src/ethsupport.c
@@ -1,4 +1,3 @@
-
#include "include/common.h"
#include "include/lang.h"
#include "include/gui.h"
@@ -11,11 +10,12 @@
#include "include/ioman.h"
#include "include/system.h"
#include "include/extern_irx.h"
+#include "include/tar.h"
#ifdef CHEAT
#include "include/cheatman.h"
#endif
-#include "include/art_tar.h"
#include "modules/iopcore/common/cdvd_config.h"
+#include "include/config_wopl.h"
#include
#include
#include
@@ -48,7 +48,6 @@ static int ethULSizePrev = -2;
static time_t ethModifiedCDPrev;
static time_t ethModifiedDVDPrev;
static int ethGameCount = 0;
-static int ethEnableArchivedArt = 0;
static unsigned char ethModulesLoaded = 0;
static base_game_info_t *ethGames = NULL;
@@ -104,6 +103,10 @@ static void ethSMBConnect(void)
smbOpenShare_in_t openshare;
int result;
+ memset(&logon, 0, sizeof(logon));
+ memset(&echo, 0, sizeof(echo));
+ memset(&openshare, 0, sizeof(openshare));
+
if (gETHPrefix[0] != '\0')
sprintf(ethPrefix, "%s%s\\", ethBase, gETHPrefix);
else
@@ -111,6 +114,8 @@ static void ethSMBConnect(void)
// open tcp connection with the server / logon to SMB server
if (gPCShareAddressIsNetBIOS) {
+ guiSetBootStatusIfActive("Resolving SMB server...");
+
if (nbnsFindName(gPCShareNBAddress, share_ip_address) != 0) {
gNetworkStartup = ERROR_ETH_SMB_CONN;
return;
@@ -127,9 +132,11 @@ static void ethSMBConnect(void)
smbGetPasswordHashes_in_t passwd;
smbGetPasswordHashes_out_t passwdhashes;
+ memset(&passwd, 0, sizeof(passwd));
+
// we'll try to generate hashed password first
- strncpy(logon.User, gPCUserName, sizeof(logon.User));
- strncpy(passwd.password, gPCPassword, sizeof(passwd.password));
+ strncpy(logon.User, gPCUserName, sizeof(logon.User) - 1);
+ strncpy(passwd.password, gPCPassword, sizeof(passwd.password) - 1);
if (fileXioDevctl(ethBase, SMB_DEVCTL_GETPASSWORDHASHES, (void *)&passwd, sizeof(passwd), (void *)&passwdhashes, sizeof(passwdhashes)) == 0) {
// hash generated okay, can use
@@ -139,17 +146,19 @@ static void ethSMBConnect(void)
openshare.PasswordType = HASHED_PASSWORD;
} else {
// failed hashing, failback to plaintext
- strncpy(logon.Password, gPCPassword, sizeof(logon.Password));
+ strncpy(logon.Password, gPCPassword, sizeof(logon.Password) - 1);
logon.PasswordType = PLAINTEXT_PASSWORD;
- strncpy(openshare.Password, gPCPassword, sizeof(openshare.Password));
+ strncpy(openshare.Password, gPCPassword, sizeof(openshare.Password) - 1);
openshare.PasswordType = PLAINTEXT_PASSWORD;
}
} else {
- strncpy(logon.User, gPCUserName, sizeof(logon.User));
+ strncpy(logon.User, gPCUserName, sizeof(logon.User) - 1);
logon.PasswordType = NO_PASSWORD;
openshare.PasswordType = NO_PASSWORD;
}
+ guiSetBootStatusIfActive("Connecting to SMB server...");
+
if ((result = fileXioDevctl(ethBase, SMB_DEVCTL_LOGON, (void *)&logon, sizeof(logon), NULL, 0)) >= 0) {
// SMB server alive test
strcpy(echo.echo, "ALIVE ECHO TEST");
@@ -163,12 +172,16 @@ static void ethSMBConnect(void)
pc_ip[3] = share_ip_address[3];
}
+ guiSetBootStatusIfActive("Checking SMB connection...");
+
if (fileXioDevctl(ethBase, SMB_DEVCTL_ECHO, (void *)&echo, sizeof(echo), NULL, 0) >= 0) {
gNetworkStartup = ERROR_ETH_SMB_OPENSHARE;
if (gPCShareName[0]) {
+ guiSetBootStatusIfActive("Opening SMB share...");
+
// connect to the share
- strcpy(openshare.ShareName, gPCShareName);
+ strncpy(openshare.ShareName, gPCShareName, sizeof(openshare.ShareName) - 1);
if (fileXioDevctl(ethBase, SMB_DEVCTL_OPENSHARE, (void *)&openshare, sizeof(openshare), NULL, 0) >= 0) {
// everything is ok
@@ -246,25 +259,36 @@ static int ethInitApplyConfig(void)
{
LOG("ETHSUPPORT ApplyConfig\n");
+ guiSetBootStatusIfActive("Waiting for network link...");
+
do {
if (ethWaitValidNetIFLinkState() != 0) {
gNetworkStartup = ERROR_ETH_LINK_FAIL;
return ERROR_ETH_LINK_FAIL;
}
+
+ guiSetBootStatusIfActive("Applying network settings...");
} while (ethApplyNetIFConfig() != 0);
+ guiSetBootStatusIfActive("Checking network link...");
+
// Before the network configuration is applied, wait for a valid link status.
if (ethWaitValidNetIFLinkState() != 0) {
gNetworkStartup = ERROR_ETH_LINK_FAIL;
return ERROR_ETH_LINK_FAIL;
}
+ guiSetBootStatusIfActive("Configuring IP address...");
ethApplyIPConfig();
// Wait for DHCP to initialize, if DHCP is enabled.
- if (ps2_ip_use_dhcp && (ethWaitValidDHCPState() != 0)) {
- gNetworkStartup = ERROR_ETH_DHCP_FAIL;
- return ERROR_ETH_DHCP_FAIL;
+ if (ps2_ip_use_dhcp) {
+ guiSetBootStatusIfActive("Waiting for DHCP...");
+
+ if (ethWaitValidDHCPState() != 0) {
+ gNetworkStartup = ERROR_ETH_DHCP_FAIL;
+ return ERROR_ETH_DHCP_FAIL;
+ }
}
return 0;
@@ -300,16 +324,23 @@ static void ethInitSMB(void)
if (gNetworkStartup == 0) {
// update Themes
char path[256];
+
+ guiSetBootStatusIfActive("Loading network themes...");
+
+ sprintf(path, "%sTHM/thm.tar", ethPrefix);
+ tarLoadFile(TAR_KIND_THM, path);
+
sprintf(path, "%sTHM", ethPrefix);
thmAddElements(path, "\\", 1);
+ guiSetBootStatusIfActive("Loading network languages...");
sprintf(path, "%sLNG", ethPrefix);
lngAddLanguages(path, "\\", ethGameList.mode);
+ guiSetBootStatusIfActive("Checking network folders...");
sbCreateFolders(ethPrefix, 1);
- } else if (gPCShareName[0] || !(gNetworkStartup >= ERROR_ETH_SMB_OPENSHARE)) {
+ } else if (gPCShareName[0] || !(gNetworkStartup >= ERROR_ETH_SMB_OPENSHARE))
ethDisplayErrorStatus();
- }
}
static int ethLoadModules(void)
@@ -317,6 +348,7 @@ static int ethLoadModules(void)
LOG("ETHSUPPORT LoadModules\n");
if (!ethModulesLoaded) {
+ guiSetBootStatusIfActive("Loading network modules...");
ethModulesLoaded = 1;
sysInitDev9();
@@ -429,6 +461,7 @@ static void smbLoadModules(void)
int ret;
LOG("SMBSUPPORT LoadModules\n");
+ guiSetBootStatusIfActive("Loading SMB modules...");
WaitSema(ethInitSemaID);
ret = ethLoadModules();
@@ -470,7 +503,7 @@ static void ethInit(item_list_t *itemList)
ethModifiedDVDPrev = 0;
ethGameCount = 0;
ethGames = NULL;
- configGetInt(configGetByType(CONFIG_OPL), "eth_frames_delay", ðGameList.delay);
+ itemList->delay = gETHFramesDelay;
gNetworkStartup = ERROR_ETH_NOT_STARTED;
ioPutRequest(IO_CUSTOM_SIMPLEACTION, &smbLoadModules);
ethGameList.enabled = 1;
@@ -514,12 +547,6 @@ static int ethNeedsUpdate(item_list_t *itemList)
result = 1;
}
- if (!ethEnableArchivedArt) {
- sprintf(path, "%sART/art.tar", ethPrefix);
- loadTarFile(path);
- result = 1;
- }
-
if (!sbIsSameSize(ethPrefix, ethULSizePrev))
result = 1;
}
@@ -533,6 +560,8 @@ static int ethUpdateGameList(item_list_t *itemList)
if (gNetworkStartup != 0)
return 0;
+ guiSetBootStatusIfActive("Scanning network games...");
+
if ((sbReadList(ðGames, ethPrefix, ðULSizePrev, ðGameCount)) < 0) {
gNetworkStartup = ERROR_ETH_SMB_LISTGAMES;
ethDisplayErrorStatus();
@@ -548,6 +577,8 @@ static int ethUpdateGameList(item_list_t *itemList)
getsharelist.EE_addr = (void *)&sharelist[0];
getsharelist.maxent = 128;
+ guiSetBootStatusIfActive("Listing SMB shares...");
+
count = fileXioDevctl(ethBase, SMB_DEVCTL_GETSHARELIST, (void *)&getsharelist, sizeof(getsharelist), NULL, 0);
if (count > 0) {
free(ethGames);
@@ -613,7 +644,7 @@ static void ethRenameGame(item_list_t *itemList, int id, char *newName)
ethULSizePrev = -2;
}
-static void ethLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
+static void ethLaunchGame(item_list_t *itemList, int id, per_game_cfg_t *pgcfg)
{
int i, compatmask;
int EnablePS2Logo = 0;
@@ -627,7 +658,8 @@ static void ethLaunchGame(item_list_t *itemList, int id, config_set_t *configSet
unsigned short int layer1_part;
if (!gPCShareName[0]) {
- memcpy(gPCShareName, game->name, sizeof(gPCShareName));
+ strncpy(gPCShareName, game->name, sizeof(gPCShareName) - 1);
+ gPCShareName[sizeof(gPCShareName) - 1] = '\0';
ethULSizePrev = -2;
ethGameCount = 0;
ioPutRequest(IO_MENU_UPDATE_DEFFERED, ðGameList.mode); // clear the share list
@@ -643,7 +675,8 @@ static void ethLaunchGame(item_list_t *itemList, int id, config_set_t *configSet
for (vmc_id = 0; vmc_id < 2; vmc_id++) {
memset(&smb_vmc_infos, 0, sizeof(smb_vmc_infos_t));
- configGetVMC(configSet, vmc_name, sizeof(vmc_name), vmc_id);
+ strncpy(vmc_name, vmc_id == 0 ? pgcfg->vmc1 : pgcfg->vmc2, sizeof(vmc_name) - 1);
+ vmc_name[sizeof(vmc_name) - 1] = '\0';
if (vmc_name[0]) {
if (sysCheckVMC(ethPrefix, "\\", vmc_name, 0, &vmc_superblock) > 0) {
smb_vmc_infos.flags = vmc_superblock.mc_flag & 0xFF;
@@ -675,12 +708,10 @@ static void ethLaunchGame(item_list_t *itemList, int id, config_set_t *configSet
}
}
- if (gRememberLastPlayed) {
- configSetStr(configGetByType(CONFIG_LAST), "last_played", game->startup);
- configSave(CONFIG_LAST, 0);
- }
+ if (gRememberLastPlayed)
+ wOPLLastSave(game->startup);
- compatmask = sbPrepare(game, configSet, size_smb_cdvdman_irx, smb_cdvdman_irx, &i);
+ compatmask = sbPrepare(game, pgcfg, size_smb_cdvdman_irx, smb_cdvdman_irx, &i);
#ifdef CHEAT
if ((result = sbLoadCheats(ethPrefix, game->startup)) < 0) {
switch (result) {
@@ -691,6 +722,10 @@ static void ethLaunchGame(item_list_t *itemList, int id, config_set_t *configSet
guiWarning(_l(_STR_ERR_CHEATS_LOAD_FAILED), 10);
}
}
+
+ if ((result = sbLoadImage(ethPrefix, game->startup)) < 0) {
+ guiWarning(_l(_STR_ERR_IMAGE_LOAD_FAILED), 10);
+ }
#endif
settings = (struct cdvdman_settings_smb *)((u8 *)(&smb_cdvdman_irx) + i);
@@ -706,12 +741,12 @@ static void ethLaunchGame(item_list_t *itemList, int id, config_set_t *configSet
settings->common.flags |= IOPCORE_SMB_FORMAT_USBLD;
}
- sprintf(settings->smb_ip, "%u.%u.%u.%u", pc_ip[0], pc_ip[1], pc_ip[2], pc_ip[3]);
+ snprintf(settings->smb_ip, sizeof(settings->smb_ip), "%u.%u.%u.%u", pc_ip[0], pc_ip[1], pc_ip[2], pc_ip[3]);
settings->smb_port = gPCPort;
- strcpy(settings->smb_share, gPCShareName);
- strcpy(settings->smb_prefix, gETHPrefix);
- strcpy(settings->smb_user, gPCUserName);
- strcpy(settings->smb_password, gPCPassword);
+ snprintf(settings->smb_share, sizeof(settings->smb_share), "%s", gPCShareName);
+ snprintf(settings->smb_prefix, sizeof(settings->smb_prefix), "%s", gETHPrefix);
+ snprintf(settings->smb_user, sizeof(settings->smb_user), "%s", gPCUserName);
+ snprintf(settings->smb_password, sizeof(settings->smb_password), "%s", gPCPassword);
// Initialize layer 1 information.
sbCreatePath(game, partname, ethPrefix, "\\", 0);
@@ -746,8 +781,16 @@ static void ethLaunchGame(item_list_t *itemList, int id, config_set_t *configSet
}
settings->common.layer1_start = layer1_start;
- if (configGetStrCopy(configSet, CONFIG_ITEM_ALTSTARTUP, filename, sizeof(filename)) == 0)
- strcpy(filename, game->startup);
+ if (pgcfg->alt_startup[0]) {
+ strncpy(filename, pgcfg->alt_startup, sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+ } else {
+ strncpy(filename, game->startup, sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+ }
+
+ sbMMCESendGameId(game->startup);
+
deinit(NO_EXCEPTION, ETH_MODE); // CAREFUL: deinit will call ethCleanUp, so ethGames/game will be freed
settings->common.fakemodule_flags |= FAKE_MODULE_FLAG_DEV9;
@@ -759,9 +802,19 @@ static void ethLaunchGame(item_list_t *itemList, int id, config_set_t *configSet
sysLaunchLoaderElf(filename, "ETH_MODE", size_smb_cdvdman_irx, smb_cdvdman_irx, size_mcemu_irx, smb_mcemu_irx, EnablePS2Logo, compatmask);
}
-static config_set_t *ethGetConfig(item_list_t *itemList, int id)
+static void ethGetInfo(item_list_t *itemList, int id, game_info_t *gi)
+{
+ sbPopulateConfig(ðGames[id], ethPrefix, "\\", gi, NULL);
+}
+
+static void ethGetPgCfg(item_list_t *itemList, int id, per_game_cfg_t *cfg)
+{
+ sbPopulateConfig(ðGames[id], ethPrefix, "\\", NULL, cfg);
+}
+
+static int ethSavePgCfg(item_list_t *itemList, int id, const per_game_cfg_t *cfg)
{
- return sbPopulateConfig(ðGames[id], ethPrefix, "\\");
+ return sbSaveConfig(ðGames[id], ethPrefix, "\\", cfg);
}
static int ethGetImage(item_list_t *itemList, char *folder, int isRelative, char *value, char *suffix, GSTEXTURE *resultTex, short psm)
@@ -771,7 +824,7 @@ static int ethGetImage(item_list_t *itemList, char *folder, int isRelative, char
snprintf(path, sizeof(path), "%s%s\\%s_%s", ethPrefix, folder, value, suffix);
else
snprintf(path, sizeof(path), "%s%s_%s", folder, value, suffix);
- return texDiscoverLoad(resultTex, path, -1, 0);
+ return texDiscoverLoad(resultTex, path, -1, RES_FILESYSTEM);
}
static int ethGetArchivedImage(item_list_t *itemList, char *folder, char *value, char *suffix, GSTEXTURE *resultTex, short psm)
@@ -780,7 +833,7 @@ static int ethGetArchivedImage(item_list_t *itemList, char *folder, char *value,
snprintf(path, sizeof(path), "%s_%s", value, suffix);
- return texDiscoverLoad(resultTex, path, -1, 1);
+ return texDiscoverLoad(resultTex, path, -1, RES_TAR_ART);
}
static int ethGetTextId(item_list_t *itemList)
@@ -790,7 +843,7 @@ static int ethGetTextId(item_list_t *itemList)
static int ethGetIconId(item_list_t *itemList)
{
- return CATEGORY_NET_SMB_ICON;
+ return ETH_ICON;
}
// This may be called, even if ethInit() was not.
@@ -843,7 +896,7 @@ static char *ethGetPrefix(item_list_t *itemList)
static item_list_t ethGameList = {
ETH_MODE, 1, 0, 0, MENU_MIN_INACTIVE_FRAMES, ETH_MODE_UPDATE_DELAY, NULL, NULL, ðGetTextId, ðGetPrefix, ðInit, ðNeedsUpdate,
ðUpdateGameList, ðGetGameCount, ðGetGame, ðGetGameName, ðGetGameNameLength, ðGetGameStartup, ðDeleteGame, ðRenameGame,
- ðLaunchGame, ðGetConfig, ðGetImage, ðGetArchivedImage, ðCleanUp, ðShutdown, ðCheckVMC, ðGetIconId};
+ ðLaunchGame, ðGetInfo, ðGetPgCfg, ðSavePgCfg, ðGetImage, ðGetArchivedImage, ðCleanUp, ðShutdown, ðCheckVMC, ðGetIconId};
static int ethReadNetConfig(void)
{
diff --git a/src/favsupport.c b/src/favsupport.c
index bd866e4d..f14963f8 100644
--- a/src/favsupport.c
+++ b/src/favsupport.c
@@ -10,6 +10,7 @@
#include "include/themes.h"
#include "include/favsupport.h"
#include "include/common.h"
+#include "include/config_wopl.h"
#include
#include
@@ -24,7 +25,7 @@ void menuClearGameList(opl_io_module_t *mdl);
void favInit(item_list_t *itemList)
{
LOG("FAVSUPPORT Init\n");
- configGetInt(configGetByType(CONFIG_OPL), "fav_frames_delay", &favItemList.delay);
+ favItemList.delay = gFAVFramesDelay;
favItemList.enabled = 1;
itemList->enabled = 1;
}
@@ -42,7 +43,11 @@ static int favNeedsUpdate(item_list_t *itemList)
FILE *file;
int fileSize = 0;
- snprintf(filename, sizeof(filename), "%sfavourites.bin", configGetDir());
+ const char *dir = wOPLGetDir();
+ if (!dir)
+ return 1;
+
+ snprintf(filename, sizeof(filename), "%sfavourites.bin", dir);
file = fopen(filename, "rb");
if (file == NULL)
@@ -63,6 +68,8 @@ static int favUpdateItemList(item_list_t *itemList)
// we need to keep the id of the owner to use its corresponding functions
// return a count of 0 so updateMenuFromGameList() doesn't attempt to update the list, we've done it already with loadFavourites()
+ guiSetBootStatusIfActive("Loading favourites...");
+
loadFavourites();
return 0;
}
@@ -121,23 +128,39 @@ static int favGetTextId(item_list_t *itemList)
static int favGetIconId(item_list_t *itemList)
{
- return CATEGORY_FAV_ICON;
+ return FAV_ICON;
}
-static void favLaunchItem(item_list_t *itemList, int id, config_set_t *configSet)
+static void favLaunchItem(item_list_t *itemList, int id, per_game_cfg_t *pgcfg)
{
opl_io_module_t *pOwner = (opl_io_module_t *)itemList->owner;
item_list_t *favOwner = (item_list_t *)pOwner->menuItem.current->item.owner;
+ favOwner->itemLaunch(favOwner, id, pgcfg);
+}
- return favOwner->itemLaunch(favOwner, id, configSet);
+static void favGetInfo(item_list_t *itemList, int id, game_info_t *gi)
+{
+ opl_io_module_t *pOwner = (opl_io_module_t *)itemList->owner;
+ item_list_t *favOwner = (item_list_t *)pOwner->menuItem.current->item.owner;
+ if (favOwner->itemGetInfo)
+ favOwner->itemGetInfo(favOwner, id, gi);
}
-static config_set_t *favGetConfig(item_list_t *itemList, int id)
+static void favGetPgCfg(item_list_t *itemList, int id, per_game_cfg_t *cfg)
{
opl_io_module_t *pOwner = (opl_io_module_t *)itemList->owner;
item_list_t *favOwner = (item_list_t *)pOwner->menuItem.current->item.owner;
+ if (favOwner->itemGetPgCfg)
+ favOwner->itemGetPgCfg(favOwner, id, cfg);
+}
- return favOwner->itemGetConfig(favOwner, id);
+static int favSavePgCfg(item_list_t *itemList, int id, const per_game_cfg_t *cfg)
+{
+ opl_io_module_t *pOwner = (opl_io_module_t *)itemList->owner;
+ item_list_t *favOwner = (item_list_t *)pOwner->menuItem.current->item.owner;
+ if (favOwner->itemSavePgCfg)
+ return favOwner->itemSavePgCfg(favOwner, id, cfg);
+ return 0;
}
static int favGetImage(item_list_t *itemList, char *folder, int isRelative, char *value, char *suffix, GSTEXTURE *resultTex, short psm)
@@ -186,7 +209,7 @@ static void favShutdown(item_list_t *itemList)
static item_list_t favItemList = {
FAV_MODE, -1, 0, 0, MENU_MIN_INACTIVE_FRAMES, FAV_MODE_UPDATE_DELAY, NULL, NULL, &favGetTextId, NULL, &favInit, &favNeedsUpdate, &favUpdateItemList,
&favGetItemCount, NULL, &favGetItemName, &favGetItemNameLength, &favGetItemStartup, &favDeleteItem, &favRenameItem, &favLaunchItem,
- &favGetConfig, &favGetImage, &favGetArchivedImage, &favCleanUp, &favShutdown, NULL, &favGetIconId};
+ &favGetInfo, &favGetPgCfg, &favSavePgCfg, &favGetImage, &favGetArchivedImage, &favCleanUp, &favShutdown, NULL, &favGetIconId};
unsigned char favGetFlags(item_list_t *itemList)
{
@@ -210,7 +233,11 @@ void writeFavouritesFile(submenu_item_t *items, int size)
int count = size / sizeof(submenu_item_t);
int i;
- snprintf(filename, sizeof(filename), "%sfavourites.bin", configGetDir());
+ const char *dir = wOPLGetDir();
+ if (!dir)
+ return;
+
+ snprintf(filename, sizeof(filename), "%sfavourites.bin", dir);
file = fopen(filename, "wb");
if (file != NULL) {
for (i = 0; i < count; ++i) {
@@ -239,7 +266,11 @@ submenu_item_t *readFavouritesFile(int *out_size)
submenu_item_t *items = NULL;
int size, count = 0, i;
- snprintf(filename, sizeof(filename), "%sfavourites.bin", configGetDir());
+ const char *dir = wOPLGetDir();
+ if (!dir)
+ return NULL;
+
+ snprintf(filename, sizeof(filename), "%sfavourites.bin", dir);
file = fopen(filename, "rb");
if (file != NULL) {
fseek(file, 0, SEEK_END);
@@ -374,24 +405,33 @@ void loadFavourites(void)
void addFavouriteItem(const submenu_item_t *item)
{
- int size;
+ int size, i;
submenu_item_t *items = readFavouritesFile(&size);
if (items != NULL) {
// add the new item
+ int count = size / sizeof(submenu_item_t);
int new_size = size + sizeof(submenu_item_t);
submenu_item_t *new_items = memalign(64, new_size);
if (new_items != NULL) {
memcpy(new_items, items, size);
memcpy((char *)new_items + size, item, sizeof(submenu_item_t));
- new_items[size / sizeof(submenu_item_t)].text = strdup(item->text);
- writeFavouritesFile(new_items, new_size);
+ new_items[count].text = strdup(item->text);
+
+ if (new_items[count].text != NULL) {
+ writeFavouritesFile(new_items, new_size);
+ free(new_items[count].text);
+ } else
+ LOG("Failed to allocate memory for new favourite text.\n");
+
free(new_items);
- } else {
- free(items); // free old memory if allocation fails
+ } else
LOG("Failed to allocate memory for new favourite.\n");
- }
+
+ for (i = 0; i < count; ++i)
+ free(items[i].text);
+ free(items);
} else {
// if no existing items, create new list
int new_size = sizeof(submenu_item_t);
@@ -400,7 +440,13 @@ void addFavouriteItem(const submenu_item_t *item)
if (new_items != NULL) {
memcpy(new_items, item, sizeof(submenu_item_t));
new_items[0].text = strdup(item->text);
- writeFavouritesFile(new_items, new_size);
+
+ if (new_items[0].text != NULL) {
+ writeFavouritesFile(new_items, new_size);
+ free(new_items[0].text);
+ } else
+ LOG("Failed to allocate memory for new favourite text.\n");
+
free(new_items);
} else
LOG("Failed to allocate memory for new favourite.\n");
diff --git a/src/fntsys.c b/src/fntsys.c
index 3644f58c..56a3256d 100644
--- a/src/fntsys.c
+++ b/src/fntsys.c
@@ -306,6 +306,36 @@ int fntLoadFile(char *path, int fontSize)
return FNT_ERROR;
}
+int fntLoadFromBuffer(void *buf, int bufSize, int fontSize)
+{
+ font_t *font;
+ int i = 1;
+
+ for (; i < FNT_MAX_COUNT; i++) {
+ font = &fonts[i];
+ if (!font->isValid) {
+ fntInitSlot(font);
+
+ font->dataPtr = buf;
+
+ int error = FT_New_Memory_Face(font_library, (FT_Byte *)buf, bufSize, 0, &font->face);
+ if (error) {
+ LOG("FNTSYS Freetype font loading failed with %x!\n", error);
+ fntDeleteSlot(font);
+ return FNT_ERROR;
+ }
+
+ font->isValid = 1;
+ font->fontSize = fontSize;
+ fntUpdateAspectRatio();
+
+ return i;
+ }
+ }
+
+ return FNT_ERROR;
+}
+
int fntLoadDefault(char *path)
{
font_t newFont, oldFont;
diff --git a/src/gsm.c b/src/gsm.c
index a25a33e3..25036f2e 100644
--- a/src/gsm.c
+++ b/src/gsm.c
@@ -12,7 +12,8 @@
*/
#include "include/common.h"
-#include "include/config.h"
+#include "include/config_wopl.h"
+#include "include/guigame.h"
#include "include/util.h"
#include "include/system.h"
#include "include/ioman.h"
@@ -25,20 +26,16 @@
#include
#include
-static int gEnableGSM; // Enables GSM - 0 for Off, 1 for On
-static int gGSMVMode; // See the related predef_vmode
-static int gGSMXOffset; // 0 - Off, Any other positive or negative value - Relative position for X Offset
-static int gGSMYOffset; // 0 - Off, Any other positive or negative value - Relative position for Y Offset
-static int gGSMFIELDFix; // Enables/disables the FIELD flipping emulation option. 0 for Off, 1 for On.
-
+int gEnableGSM; // Enables GSM - 0 for Off, 1 for On
+int gGSMVMode; // See the related predef_vmode
+int gGSMXOffset; // 0 - Off, Any other positive or negative value - Relative position for X Offset
+int gGSMYOffset; // 0 - Off, Any other positive or negative value - Relative position for Y Offset
+int gGSMFIELDFix; // Enables/disables the FIELD flipping emulation option. 0 for Off, 1 for On.
int gGSMSource;
-void InitGSMConfig(config_set_t *configSet)
+void InitGSMConfig(const per_game_cfg_t *pgcfg)
{
- config_set_t *configGame = configGetByType(CONFIG_GAME);
-
- // Default values.
gGSMSource = 0;
gEnableGSM = 0;
gGSMVMode = 0;
@@ -46,21 +43,19 @@ void InitGSMConfig(config_set_t *configSet)
gGSMYOffset = 0;
gGSMFIELDFix = 0;
- if (configGetInt(configSet, CONFIG_ITEM_GSMSOURCE, &gGSMSource)) {
- // Load the rest of the per-game GSM configuration, only if GSM is enabled.
- if (configGetInt(configSet, CONFIG_ITEM_ENABLEGSM, &gEnableGSM) && gEnableGSM) {
- configGetInt(configSet, CONFIG_ITEM_GSMVMODE, &gGSMVMode);
- configGetInt(configSet, CONFIG_ITEM_GSMXOFFSET, &gGSMXOffset);
- configGetInt(configSet, CONFIG_ITEM_GSMYOFFSET, &gGSMYOffset);
- configGetInt(configSet, CONFIG_ITEM_GSMFIELDFIX, &gGSMFIELDFix);
- }
+ if (pgcfg && pgcfg->gsm_source == SETTINGS_PERGAME) {
+ gGSMSource = SETTINGS_PERGAME;
+ gEnableGSM = pgcfg->gsm_enable;
+ gGSMVMode = pgcfg->gsm_vmode;
+ gGSMXOffset = pgcfg->gsm_xoffset;
+ gGSMYOffset = pgcfg->gsm_yoffset;
+ gGSMFIELDFix = pgcfg->gsm_fieldfix;
} else {
- if (configGetInt(configGame, CONFIG_ITEM_ENABLEGSM, &gEnableGSM) && gEnableGSM) {
- configGetInt(configGame, CONFIG_ITEM_GSMVMODE, &gGSMVMode);
- configGetInt(configGame, CONFIG_ITEM_GSMXOFFSET, &gGSMXOffset);
- configGetInt(configGame, CONFIG_ITEM_GSMYOFFSET, &gGSMYOffset);
- configGetInt(configGame, CONFIG_ITEM_GSMFIELDFIX, &gGSMFIELDFix);
- }
+ gEnableGSM = gGlobalGameCfg.gsm_enable;
+ gGSMVMode = gGlobalGameCfg.gsm_vmode;
+ gGSMXOffset = gGlobalGameCfg.gsm_xoffset;
+ gGSMYOffset = gGlobalGameCfg.gsm_yoffset;
+ gGSMFIELDFix = gGlobalGameCfg.gsm_fieldfix;
}
}
@@ -69,7 +64,7 @@ int GetGSMEnabled(void)
return gEnableGSM;
}
-void PrepareGSM(char *cmdline, struct GsmConfig_t *config)
+void PrepareGSM(char *cmdline, struct GsmConfig_t *config, int vmode, int xoff, int yoff, int fieldfix)
{
/* Preparing GSM */
LOG("Preparing GSM...\n");
@@ -114,8 +109,8 @@ void PrepareGSM(char *cmdline, struct GsmConfig_t *config)
char romver[16], romverNum[5], *pROMDate;
#ifdef _DTL_T10000
- if (predef_vmode[gGSMVMode].mode == GS_MODE_DTV_576P) // There is no 576P code implemented for development TOOLs.
- gGSMVMode = 2; // Change to PAL instead.
+ if (predef_vmode[vmode].mode == GS_MODE_DTV_576P) // There is no 576P code implemented for development TOOLs.
+ vmode = 2; // Change to PAL instead.
#endif
k576p_fix = 0;
@@ -144,31 +139,31 @@ void PrepareGSM(char *cmdline, struct GsmConfig_t *config)
kGsDxDyOffsetSupported = (strtoul(pROMDate, NULL, 10) > 20010608);
}
- FIELD_fix = gGSMFIELDFix != 0 ? 1 : 0;
+ FIELD_fix = fieldfix != 0 ? 1 : 0;
if (cmdline) {
- sprintf(cmdline, "%hhu %hhu %hhu %llu %llu %hu %u %u %d %d %d", predef_vmode[gGSMVMode].interlace,
- predef_vmode[gGSMVMode].mode,
- predef_vmode[gGSMVMode].ffmd,
- predef_vmode[gGSMVMode].display,
- predef_vmode[gGSMVMode].syncv,
- ((predef_vmode[gGSMVMode].ffmd) << 1) | (predef_vmode[gGSMVMode].interlace),
- (u32)gGSMXOffset,
- (u32)gGSMYOffset,
+ sprintf(cmdline, "%hhu %hhu %hhu %llu %llu %hu %u %u %d %d %d", predef_vmode[vmode].interlace,
+ predef_vmode[vmode].mode,
+ predef_vmode[vmode].ffmd,
+ predef_vmode[vmode].display,
+ predef_vmode[vmode].syncv,
+ ((predef_vmode[vmode].ffmd) << 1) | (predef_vmode[vmode].interlace),
+ (u32)xoff,
+ (u32)yoff,
k576p_fix,
kGsDxDyOffsetSupported,
FIELD_fix);
}
if (config) {
- config->interlace = predef_vmode[gGSMVMode].interlace;
- config->mode = predef_vmode[gGSMVMode].mode;
- config->ffmd = predef_vmode[gGSMVMode].ffmd;
- config->display = predef_vmode[gGSMVMode].display;
- config->syncv = predef_vmode[gGSMVMode].syncv;
- config->smode2 = ((predef_vmode[gGSMVMode].ffmd) << 1) | (predef_vmode[gGSMVMode].interlace);
- config->dx_offset = (u32)gGSMXOffset;
- config->dy_offset = (u32)gGSMYOffset;
+ config->interlace = predef_vmode[vmode].interlace;
+ config->mode = predef_vmode[vmode].mode;
+ config->ffmd = predef_vmode[vmode].ffmd;
+ config->display = predef_vmode[vmode].display;
+ config->syncv = predef_vmode[vmode].syncv;
+ config->smode2 = ((predef_vmode[vmode].ffmd) << 1) | (predef_vmode[vmode].interlace);
+ config->dx_offset = (u32)xoff;
+ config->dy_offset = (u32)yoff;
config->k576P_fix = k576p_fix;
config->kGsDxDyOffsetSupported = kGsDxDyOffsetSupported;
config->FIELD_fix = FIELD_fix;
diff --git a/src/gui.c b/src/gui.c
index 10b81893..a4346eff 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -13,7 +13,8 @@
#include "include/themes.h"
#include "include/pad.h"
#include "include/util.h"
-#include "include/config.h"
+#include "include/config_wopl.h"
+#include "include/config_migration.h" // DELETE_WITH_MIGRATION
#include "include/system.h"
#include "include/ethsupport.h"
#ifdef GSM
@@ -38,7 +39,6 @@
// Last Played Auto Start
#include
-
static int gScheduledOps;
static int gCompletedOps;
static int gTerminate;
@@ -80,9 +80,25 @@ static float fps = 0.0f;
extern GSGLOBAL *gsGlobal;
#endif
-
#define VMODE_CHANGE_CONFIRMATION_TIMEOUT_MS 10000
+#define BOOT_TEXT_FONT_SIZE 14
+#define BOOT_ANIM_START_LOGO LOGO_01
+#define BOOT_ANIM_END_LOGO LOGO_21
+#define BOOT_FADE_LOGO LOGO_17
+#define BOOT_LOGO_FRAME_DELAY 6
+#define BOOT_LOGO_FRAME_COUNT (BOOT_ANIM_END_LOGO - BOOT_ANIM_START_LOGO + 1)
+
+static int gBootLogoFrame = BOOT_ANIM_START_LOGO;
+static int gBootLogoFrameDelay = 0;
+static int gBootLogoFadeCountdown = -1;
+static int gBootLogoReadyToFade = 0;
+
+static int gBootTextFont = FNT_ERROR;
+static int gBootTextFontLoaded = 0;
+static int gBootStatusActive = 0;
+static char gBootStatus[256];
+
// Global data
int guiInactiveFrames;
int guiFrameId;
@@ -149,6 +165,12 @@ void guiInit(void)
gInitComplete = 0;
gScheduledOps = 0;
gCompletedOps = 0;
+ gBootStatusActive = 1;
+ gBootStatus[0] = '\0';
+ gBootLogoFrame = BOOT_ANIM_START_LOGO;
+ gBootLogoFrameDelay = 0;
+ gBootLogoFadeCountdown = -1;
+ gBootLogoReadyToFade = 0;
gUpdateList = NULL;
gUpdateEnd = NULL;
@@ -189,6 +211,15 @@ void guiInit(void)
void guiEnd()
{
+ gBootStatusActive = 0;
+ gBootStatus[0] = '\0';
+
+ if (gBootTextFontLoaded && gBootTextFont != FNT_ERROR)
+ fntRelease(gBootTextFont);
+
+ gBootTextFont = FNT_ERROR;
+ gBootTextFontLoaded = 0;
+
if (gBackgroundTex.Mem)
free(gBackgroundTex.Mem);
@@ -318,7 +349,8 @@ static void guiShowNotifications(void)
}
if (showCfgPopup) {
- snprintf(notification, sizeof(notification), _l(_STR_CFG_NOTIFICATION), configGetDir());
+ const char *cfgDir = wOPLGetDir();
+ snprintf(notification, sizeof(notification), _l(_STR_CFG_NOTIFICATION), cfgDir ? cfgDir : "?");
if ((col_pos = strchr(notification, ':')) != NULL)
*(col_pos + 1) = '\0';
@@ -669,6 +701,7 @@ void guiShowUIConfig(void)
diaSetInt(diaUIConfig, UICFG_XOFF, gXOff);
diaSetInt(diaUIConfig, UICFG_YOFF, gYOff);
diaSetInt(diaUIConfig, UICFG_OVERSCAN, gOverscan);
+ diaSetVisible(diaUIConfig, UICFG_COVERFLOW_BUTTON, gTheme->coverflow != NULL);
guiUIUpdater(1);
int ret = diaExecuteDialog(diaUIConfig, -1, 1, guiUIUpdater);
@@ -691,6 +724,9 @@ void guiShowUIConfig(void)
diaGetInt(diaUIConfig, UICFG_YOFF, &gYOff);
diaGetInt(diaUIConfig, UICFG_OVERSCAN, &gOverscan);
+ if (ret == UICFG_COVERFLOW_BUTTON)
+ guiShowCoverflowConfig();
+
if (ret == UICFG_RESETCOL)
setDefaultColors();
@@ -831,28 +867,26 @@ void guiShowNetConfig(void)
void guiShowParentalLockConfig(void)
{
int result;
- char password[CONFIG_KEY_VALUE_LEN];
- config_set_t *configOPL = configGetByType(CONFIG_OPL);
+ char password[sizeof(gParentalLockPassword)];
- // Set current values
- configGetStrCopy(configOPL, CONFIG_OPL_PARENTAL_LOCK_PWD, password, CONFIG_KEY_VALUE_LEN); // This will return the current password, or a blank string if it is not set.
+ strncpy(password, gParentalLockPassword, sizeof(password));
diaSetString(diaParentalLockConfig, CFG_PARENLOCK_PASSWORD, password);
result = diaExecuteDialog(diaParentalLockConfig, -1, 1, NULL);
if (result) {
- diaGetString(diaParentalLockConfig, CFG_PARENLOCK_PASSWORD, password, CONFIG_KEY_VALUE_LEN);
+ diaGetString(diaParentalLockConfig, CFG_PARENLOCK_PASSWORD, password, sizeof(password));
if (strlen(password) > 0) {
- if (strncmp(PARENTAL_LOCK_MASTER_PASS, password, CONFIG_KEY_VALUE_LEN) != 0) {
+ if (strncmp(PARENTAL_LOCK_MASTER_PASS, password, sizeof(password)) != 0) {
// Store password
- configSetStr(configOPL, CONFIG_OPL_PARENTAL_LOCK_PWD, password);
+ strncpy(gParentalLockPassword, password, sizeof(gParentalLockPassword) - 1);
+ gParentalLockPassword[sizeof(gParentalLockPassword) - 1] = '\0';
} else {
// Password not acceptable (i.e. master password entered).
guiMsgBox(_l(_STR_PARENLOCK_INVALID_PASSWORD), 0, NULL);
}
} else {
- configRemoveKey(configOPL, CONFIG_OPL_PARENTAL_LOCK_PWD);
-
+ gParentalLockPassword[0] = '\0';
guiMsgBox(_l(_STR_PARENLOCK_DISABLE_WARNING), 0, diaParentalLockConfig);
}
@@ -937,6 +971,129 @@ void guiShowControllerConfig(void)
}
}
+void guiShowCoverflowConfig(void)
+{
+ int ret;
+
+ const char *coverCounts[] = {"3", "5", NULL};
+ const char *scaleNames[] = {"None", "Small", "Medium", "Large", NULL};
+ const char *animNames[] = {"Off", "Fast", "Normal", "Slow", NULL};
+
+ int scaleValues[] = {0, 15, 30, 45};
+ int animValues[] = {0, 100, 200, 400};
+
+ diaSetEnum(diaCoverflowConfig, CFG_COVERFLOW_COUNT, coverCounts);
+ diaSetEnum(diaCoverflowConfig, CFG_COVERFLOW_SCALE, scaleNames);
+ diaSetEnum(diaCoverflowConfig, CFG_COVERFLOW_ANIM, animNames);
+
+ diaSetInt(diaCoverflowConfig, CFG_COVERFLOW_COUNT, (gCoverflowCount == 5) ? 1 : 0);
+
+ int i, scaleId = 2; // default medium
+ for (i = 0; i < 4; i++)
+ if (scaleValues[i] == gCoverflowCenterScale)
+ scaleId = i;
+ diaSetInt(diaCoverflowConfig, CFG_COVERFLOW_SCALE, scaleId);
+
+ int animId = 2; // default normal
+ for (i = 0; i < 4; i++)
+ if (animValues[i] == gCoverflowAnimSpeed)
+ animId = i;
+ diaSetInt(diaCoverflowConfig, CFG_COVERFLOW_ANIM, animId);
+
+ diaSetInt(diaCoverflowConfig, CFG_COVERFLOW_DIM, gCoverflowDimCovers);
+
+ ret = diaExecuteDialog(diaCoverflowConfig, -1, 1, NULL);
+ if (ret) {
+ int id;
+ diaGetInt(diaCoverflowConfig, CFG_COVERFLOW_COUNT, &id);
+ gCoverflowCount = (id == 1) ? 5 : 3;
+
+ diaGetInt(diaCoverflowConfig, CFG_COVERFLOW_SCALE, &id);
+ gCoverflowCenterScale = scaleValues[id];
+
+ diaGetInt(diaCoverflowConfig, CFG_COVERFLOW_ANIM, &id);
+ gCoverflowAnimSpeed = animValues[id];
+
+ diaGetInt(diaCoverflowConfig, CFG_COVERFLOW_DIM, &gCoverflowDimCovers);
+ }
+}
+
+// DELETE_WITH_MIGRATION v
+static int migStatus = 0;
+static int migDone = 0;
+static int migTotal = 0;
+static int migResult = 0;
+static const char *migInput = NULL;
+static const char *migOutput = NULL;
+static int migKeep = 0;
+
+static void migProgressCb(int done, int total)
+{
+ migDone = done;
+ migTotal = total;
+}
+
+static void _runBatchMigration(void)
+{
+ migResult = cfgBatchMigratePerGame(migInput, migOutput, migKeep, migProgressCb);
+ migStatus = 0;
+}
+
+void guiShowCfgMigration(void)
+{
+#define CFG_MIG_MAX_DEVICES 8
+ static char pathStorage[CFG_MIG_MAX_DEVICES][64];
+ static char labelStorage[CFG_MIG_MAX_DEVICES][80];
+ static const char *labelEnum[CFG_MIG_MAX_DEVICES + 1];
+
+ int count = menuGetDevicePaths(pathStorage, labelStorage, CFG_MIG_MAX_DEVICES);
+ if (count == 0) {
+ guiMsgBox("No accessible devices found.", 0, NULL);
+ return;
+ }
+
+ for (int i = 0; i < count; i++)
+ labelEnum[i] = labelStorage[i];
+ labelEnum[count] = NULL;
+
+ diaSetEnum(diaCfgMigration, CFG_MIG_INPUT, labelEnum);
+ diaSetEnum(diaCfgMigration, CFG_MIG_OUTPUT, labelEnum);
+ diaSetInt(diaCfgMigration, CFG_MIG_INPUT, 0);
+ diaSetInt(diaCfgMigration, CFG_MIG_OUTPUT, 0);
+ diaSetInt(diaCfgMigration, CFG_MIG_KEEP_ORIGINALS, 1);
+
+ int ret;
+ while ((ret = diaExecuteDialog(diaCfgMigration, -1, 1, NULL)) == CFG_MIG_CONVERT) {
+ int inputIdx = 0, outputIdx = 0, keepOriginals = 1;
+ diaGetInt(diaCfgMigration, CFG_MIG_INPUT, &inputIdx);
+ diaGetInt(diaCfgMigration, CFG_MIG_OUTPUT, &outputIdx);
+ diaGetInt(diaCfgMigration, CFG_MIG_KEEP_ORIGINALS, &keepOriginals);
+
+ migInput = pathStorage[inputIdx];
+ migOutput = pathStorage[outputIdx];
+ migKeep = keepOriginals;
+ migDone = 0;
+ migTotal = 0;
+ migStatus = 1;
+
+ ioPutRequest(IO_CUSTOM_SIMPLEACTION, &_runBatchMigration);
+
+ char progMsg[64];
+ while (migStatus) {
+ if (migTotal > 0)
+ snprintf(progMsg, sizeof(progMsg), "Converting %d / %d..", migDone, migTotal);
+ else
+ snprintf(progMsg, sizeof(progMsg), "Scanning..");
+ guiRenderTextScreen(progMsg);
+ }
+
+ char msg[64];
+ snprintf(msg, sizeof(msg), "Converted %d / %d cfg file(s).", migResult, migTotal);
+ guiMsgBox(msg, 0, NULL);
+ }
+}
+// DELETE_WITH_MIGRATION ^
+
int guiShowKeyboard(char *value, int maxLength)
{
char tmp[maxLength];
@@ -1008,6 +1165,7 @@ static void guiHandleOp(struct gui_update_t *item)
DisableCron = 0; // Release Auto Start Last Played counter
}
+ item->menu.menu->last = result;
break;
case GUI_OP_SELECT_MENU:
@@ -1020,6 +1178,7 @@ static void guiHandleOp(struct gui_update_t *item)
item->menu.menu->submenu = NULL;
item->menu.menu->current = NULL;
item->menu.menu->pagestart = NULL;
+ item->menu.menu->last = NULL;
break;
case GUI_OP_SORT:
@@ -1030,6 +1189,11 @@ static void guiHandleOp(struct gui_update_t *item)
item->menu.menu->current = item->menu.menu->submenu;
item->menu.menu->pagestart = item->menu.menu->current;
+
+ submenu_list_t *tail = item->menu.menu->submenu;
+ while (tail && tail->next)
+ tail = tail->next;
+ item->menu.menu->last = tail;
break;
case GUI_OP_ADD_HINT:
@@ -1071,24 +1235,150 @@ void guiExecDeferredOps(void)
static void guiDrawBusy(int alpha)
{
if (gTheme->loadingIcon) {
- GSTEXTURE *texture = thmGetTexture(LOADING_1_ICON + (guiFrameId >> 1) % gTheme->loadingIconCount);
+ GSTEXTURE *texture = thmGetTexture(LOAD1_ICON + (guiFrameId >> 1) % gTheme->loadingIconCount);
if (texture && texture->Mem) {
u64 mycolor = GS_SETREG_RGBA(0x80, 0x80, 0x80, alpha);
- rmDrawPixmap(texture, gTheme->loadingIcon->posX, gTheme->loadingIcon->posY, gTheme->loadingIcon->aligned, gTheme->loadingIcon->width, gTheme->loadingIcon->height, gTheme->loadingIcon->scaled, mycolor);
+ rmDrawPixmap(texture, gTheme->loadingIcon->posX, gTheme->loadingIcon->posY, gTheme->loadingIcon->aligned, gTheme->loadingIcon->width, gTheme->loadingIcon->height, gTheme->loadingIcon->scaled, mycolor, 0);
}
}
}
-static void guiRenderGreeting(int alpha)
+static int guiGetBootTextFont(void)
+{
+ if (gBootTextFont == FNT_ERROR) {
+ gBootTextFont = fntLoadFile(NULL, BOOT_TEXT_FONT_SIZE);
+
+ if (gBootTextFont != FNT_ERROR) {
+ gBootTextFontLoaded = 1;
+ } else {
+ gBootTextFont = gTheme->fonts[0];
+ gBootTextFontLoaded = 0;
+ }
+ }
+
+ return gBootTextFont;
+}
+
+void guiSetBootStatusIfActive(const char *status)
+{
+ if (gBootStatusActive) {
+ if (status) {
+ strncpy(gBootStatus, status, sizeof(gBootStatus) - 1);
+ gBootStatus[sizeof(gBootStatus) - 1] = '\0';
+ } else
+ gBootStatus[0] = '\0';
+ }
+}
+
+static void guiDrawBootStatus(int alpha)
+{
+ int font;
+ int y;
+
+ if (!gBootStatus[0])
+ return;
+
+ font = guiGetBootTextFont();
+ y = (gTheme->usedHeight >> 1) + 90;
+
+ fntRenderString(font, screenWidth >> 1, y, ALIGN_CENTER, 0, 0, gBootStatus, GS_SETREG_RGBA(0x70, 0x70, 0x70, alpha));
+}
+
+static void guiDrawBootVersion(int alpha)
+{
+ char version[96];
+ int font;
+ int width;
+ int x;
+ int y;
+
+ snprintf(version, sizeof(version), "wOPL %s", WOPL_VERSION);
+
+ font = guiGetBootTextFont();
+
+ width = rmUnScaleX(fntCalcDimensions(font, version));
+ x = screenWidth - width - 16;
+ y = gTheme->usedHeight - 20;
+
+ fntRenderString(font, x, y, ALIGN_NONE, 0, 0, version, GS_SETREG_RGBA(0x50, 0x50, 0x50, alpha));
+}
+
+static int guiGetBootLogoFrameDistance(int from, int to)
+{
+ int distance = to - from;
+
+ if (distance < 0)
+ distance += BOOT_LOGO_FRAME_COUNT;
+
+ return distance;
+}
+
+static void guiAdvanceBootLogoFrame(void)
+{
+ if (gBootLogoFrame < BOOT_ANIM_END_LOGO)
+ gBootLogoFrame++;
+ else
+ gBootLogoFrame = BOOT_ANIM_START_LOGO;
+}
+
+static GSTEXTURE *guiGetGreetingLogo(void)
+{
+ GSTEXTURE *logo = thmGetTexture(gBootLogoFrame);
+
+ if (!logo)
+ logo = thmGetTexture(LOGO_01);
+
+ if (!gBootLogoReadyToFade) {
+ gBootLogoFrameDelay++;
+
+ if (gBootLogoFrameDelay >= BOOT_LOGO_FRAME_DELAY) {
+ gBootLogoFrameDelay = 0;
+
+ guiAdvanceBootLogoFrame();
+
+ if (gBootLogoFadeCountdown > 0) {
+ gBootLogoFadeCountdown--;
+
+ if (gBootLogoFadeCountdown == 0) {
+ gBootLogoFrame = BOOT_FADE_LOGO;
+ gBootLogoReadyToFade = 1;
+
+ logo = thmGetTexture(gBootLogoFrame);
+ if (!logo)
+ logo = thmGetTexture(LOGO_01);
+ }
+ }
+ }
+ }
+
+ return logo;
+}
+
+static void guiRenderGreetingFrame(int alpha, GSTEXTURE *logo)
{
u64 mycolor = GS_SETREG_RGBA(0x00, 0x00, 0x00, alpha);
rmDrawRect(0, 0, screenWidth, screenHeight, mycolor);
- GSTEXTURE *logo = thmGetTexture(LOGO_1_ICON + (guiFrameId / 6) % gTheme->logoIconCount);
if (logo) {
mycolor = GS_SETREG_RGBA(0xFF, 0xFF, 0xFF, alpha);
- rmDrawPixmap(logo, screenWidth >> 1, gTheme->usedHeight >> 1, ALIGN_CENTER, logo->Width, logo->Height, SCALING_RATIO, mycolor);
+ rmDrawPixmap(logo, screenWidth >> 1, gTheme->usedHeight >> 1, ALIGN_CENTER, logo->Width, logo->Height, SCALING_RATIO, mycolor, 0);
}
+
+ guiDrawBootStatus(alpha);
+ guiDrawBootVersion(alpha);
+}
+
+// For early boot only.. before guiIntroLoop() is running
+void guiShowBootStatus(const char *status)
+{
+ if (!gBootStatusActive)
+ return;
+
+ guiSetBootStatusIfActive(status);
+
+ guiStartFrame();
+ guiRenderGreetingFrame(0x80, NULL);
+ guiEndFrame();
}
static float mix(float a, float b, float t)
@@ -1292,9 +1582,9 @@ void guiDrawBGPlasma()
rmSetBackground(&gBackgroundTex);
}
-int guiDrawBGMain(void)
+int guiDrawBGSettings(void)
{
- GSTEXTURE *bg = thmGetTexture(BG_MAIN);
+ GSTEXTURE *bg = thmGetTexture(SETTINGS_BG);
if (bg) {
rmSetBackground(bg);
return 1;
@@ -1315,7 +1605,7 @@ int guiDrawIconAndText(int iconId, int textId, int font, int x, int y, u64 color
if (iconTex && iconTex->Mem) {
y += h >> 1;
- rmDrawPixmap(iconTex, x, y, ALIGN_VCENTER, w, h, SCALING_RATIO, gDefaultCol);
+ rmDrawPixmap(iconTex, x, y, ALIGN_VCENTER, w, h, SCALING_RATIO, gDefaultCol, 0);
x += rmWideScale(w) + 2;
} else {
// HACK: font is aligned to VCENTER, the default height icon height is 20
@@ -1377,14 +1667,14 @@ int guiAlignSubMenuHints(int hintCount, int *textID, int *iconID, int font, int
void guiDrawSubMenuHints(void)
{
int subMenuHints[2] = {_STR_SELECT, _STR_GAMES_LIST};
- int subMenuIcons[2] = {BUTTON_SYMBOL_CIRCLE_ICON, BUTTON_SYMBOL_CROSS_ICON};
+ int subMenuIcons[2] = {CIRCLE_ICON, CROSS_ICON};
int x = guiAlignSubMenuHints(2, subMenuHints, subMenuIcons, gTheme->fonts[0], 12, 2);
int y = gTheme->usedHeight - 32;
- x = guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? subMenuIcons[0] : subMenuIcons[1], subMenuHints[0], gTheme->fonts[0], x, y, gTheme->selTextColor);
+ x = guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? subMenuIcons[0] : subMenuIcons[1], subMenuHints[0], gTheme->fonts[0], x, y, gTheme->textColor);
x += 12;
- guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? subMenuIcons[1] : subMenuIcons[0], subMenuHints[1], gTheme->fonts[0], x, y, gTheme->selTextColor);
+ guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? subMenuIcons[1] : subMenuIcons[0], subMenuHints[1], gTheme->fonts[0], x, y, gTheme->textColor);
}
static int endIntro = 0; // Break intro loop and start 'Last Played Auto Start' countdown
@@ -1515,26 +1805,58 @@ void guiIntroLoop(void)
int greetingAlpha = 0x80;
const int fadeFrameCount = 0x80 / 2;
const int fadeDuration = (fadeFrameCount * 1000) / 55; // Average between 50 and 60 fps
+ const int frameDuration = (BOOT_LOGO_FRAME_DELAY * 1000) / 55;
+ int bootFinalizeStarted = 0;
+ int bootSoundStarted = 0;
+ int bootSoundLeadFrames = 0;
+ int bootSoundDelayDuration = 0;
clock_t tFadeDelayEnd = 0;
while (!endIntro) {
guiStartFrame();
- if (greetingAlpha < 0x80)
- guiShow();
+ if (gInitComplete && !bootFinalizeStarted) {
+ bootFinalizeStarted = 1;
- if (greetingAlpha > 0)
- guiRenderGreeting(greetingAlpha);
+ guiSetBootStatusIfActive(NULL);
+
+ gBootLogoFadeCountdown = guiGetBootLogoFrameDistance(gBootLogoFrame, BOOT_FADE_LOGO);
+
+ if (gBootLogoFadeCountdown <= 0) {
+ gBootLogoFrame = BOOT_FADE_LOGO;
+ gBootLogoReadyToFade = 1;
+ }
+
+ if (gEnableBootSND) {
+ bootSoundDelayDuration = sfxGetSoundDuration(SFX_BOOT) - fadeDuration;
+
+ if (bootSoundDelayDuration < 0)
+ bootSoundDelayDuration = 0;
+
+ bootSoundLeadFrames = bootSoundDelayDuration / frameDuration;
+
+ if (bootSoundDelayDuration > 0 && bootSoundLeadFrames <= 0)
+ bootSoundLeadFrames = 1;
+ } else {
+ bootSoundStarted = 1;
+ tFadeDelayEnd = clock();
+ }
+ }
+
+ if (bootFinalizeStarted && gEnableBootSND && !bootSoundStarted && gBootLogoFadeCountdown <= bootSoundLeadFrames) {
+ bootSoundStarted = 1;
- // Initialize boot sound
- if (gInitComplete && !tFadeDelayEnd && gEnableBootSND) {
- // Start playing sound
sfxPlay(SFX_BOOT);
- // Calculate transition delay
- tFadeDelayEnd = clock() + (sfxGetSoundDuration(SFX_BOOT) - fadeDuration) * (CLOCKS_PER_SEC / 1000);
+ tFadeDelayEnd = clock() + bootSoundDelayDuration * (CLOCKS_PER_SEC / 1000);
}
- if (gInitComplete && clock() >= tFadeDelayEnd)
+ if (greetingAlpha < 0x80)
+ guiShow();
+
+ if (greetingAlpha > 0)
+ guiRenderGreetingFrame(greetingAlpha, guiGetGreetingLogo());
+
+ if (gBootLogoReadyToFade && bootSoundStarted && clock() >= tFadeDelayEnd)
greetingAlpha -= 2;
if (greetingAlpha <= 0)
@@ -1553,6 +1875,9 @@ void guiIntroLoop(void)
void guiMainLoop(void)
{
+ gBootStatusActive = 0;
+ gBootStatus[0] = '\0';
+
guiResetNotifications();
guiCheckNotifications(1, 1);
@@ -1663,9 +1988,9 @@ int guiMsgBox(const char *text, int addAccept, struct UIItem *ui)
rmDrawLine(50, 410, screenWidth - 50, 410, gTheme->textColor);
fntRenderString(gTheme->fonts[0], screenWidth >> 1, gTheme->usedHeight >> 1, ALIGN_CENTER, 0, 0, text, gTheme->textColor);
- guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? BUTTON_SYMBOL_CROSS_ICON : BUTTON_SYMBOL_CIRCLE_ICON, _STR_BACK, gTheme->fonts[0], 500, 417, gTheme->selTextColor);
+ guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? CROSS_ICON : CIRCLE_ICON, _STR_BACK, gTheme->fonts[0], 500, 417, gTheme->selTextColor);
if (addAccept)
- guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? BUTTON_SYMBOL_CIRCLE_ICON : BUTTON_SYMBOL_CROSS_ICON, _STR_ACCEPT, gTheme->fonts[0], 70, 417, gTheme->selTextColor);
+ guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? CIRCLE_ICON : CROSS_ICON, _STR_ACCEPT, gTheme->fonts[0], 70, 417, gTheme->selTextColor);
guiEndFrame();
}
@@ -1765,8 +2090,8 @@ int guiConfirmVideoMode(void)
rmDrawLine(50, 410, screenWidth - 50, 410, gTheme->textColor);
fntRenderString(gTheme->fonts[0], screenWidth >> 1, gTheme->usedHeight >> 1, ALIGN_CENTER, 0, 0, _l(_STR_CFM_VMODE_CHG), gTheme->textColor);
- guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? BUTTON_SYMBOL_CROSS_ICON : BUTTON_SYMBOL_CIRCLE_ICON, _STR_BACK, gTheme->fonts[0], 500, 417, gTheme->selTextColor);
- guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? BUTTON_SYMBOL_CIRCLE_ICON : BUTTON_SYMBOL_CROSS_ICON, _STR_ACCEPT, gTheme->fonts[0], 70, 417, gTheme->selTextColor);
+ guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? CROSS_ICON : CIRCLE_ICON, _STR_BACK, gTheme->fonts[0], 500, 417, gTheme->selTextColor);
+ guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? CIRCLE_ICON : CROSS_ICON, _STR_ACCEPT, gTheme->fonts[0], 70, 417, gTheme->selTextColor);
guiEndFrame();
}
@@ -1781,7 +2106,7 @@ int guiConfirmVideoMode(void)
return terminate - 1;
}
-int guiGameShowRemoveSettings(config_set_t *configSet, config_set_t *configGame)
+int guiGameShowRemoveSettings(per_game_cfg_t *pgcfg)
{
int terminate = 0;
char message[256];
@@ -1811,10 +2136,10 @@ int guiGameShowRemoveSettings(config_set_t *configSet, config_set_t *configGame)
fntRenderString(gTheme->fonts[0], screenWidth >> 1, gTheme->usedHeight >> 1, ALIGN_CENTER, 0, 0, _l(_STR_GAME_SETTINGS_PROMPT), gTheme->textColor);
- guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? BUTTON_SYMBOL_CROSS_ICON : BUTTON_SYMBOL_CIRCLE_ICON, _STR_BACK, gTheme->fonts[0], 500, 417, gTheme->selTextColor);
- guiDrawIconAndText(BUTTON_SYMBOL_SQUARE_ICON, _STR_GLOBAL_SETTINGS, gTheme->fonts[0], 213, 417, gTheme->selTextColor);
- guiDrawIconAndText(BUTTON_SYMBOL_TRIANGLE_ICON, _STR_ALL_SETTINGS, gTheme->fonts[0], 356, 417, gTheme->selTextColor);
- guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? BUTTON_SYMBOL_CIRCLE_ICON : BUTTON_SYMBOL_CROSS_ICON, _STR_PERGAME_SETTINGS, gTheme->fonts[0], 70, 417, gTheme->selTextColor);
+ guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? CROSS_ICON : CIRCLE_ICON, _STR_BACK, gTheme->fonts[0], 500, 417, gTheme->selTextColor);
+ guiDrawIconAndText(SQUARE_ICON, _STR_GLOBAL_SETTINGS, gTheme->fonts[0], 213, 417, gTheme->selTextColor);
+ guiDrawIconAndText(TRIANGLE_ICON, _STR_ALL_SETTINGS, gTheme->fonts[0], 356, 417, gTheme->selTextColor);
+ guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? CIRCLE_ICON : CROSS_ICON, _STR_PERGAME_SETTINGS, gTheme->fonts[0], 70, 417, gTheme->selTextColor);
guiEndFrame();
}
@@ -1823,14 +2148,14 @@ int guiGameShowRemoveSettings(config_set_t *configSet, config_set_t *configGame)
sfxPlay(SFX_CANCEL);
return 0;
} else if (terminate == 2) {
- guiGameRemoveSettings(configSet);
+ guiGameRemoveSettings(pgcfg);
snprintf(message, sizeof(message), _l(_STR_GAME_SETTINGS_REMOVED), _l(_STR_PERGAME_SETTINGS));
} else if (terminate == 3) {
- guiGameRemoveGlobalSettings(configGame);
+ guiGameRemoveGlobalSettings();
snprintf(message, sizeof(message), _l(_STR_GAME_SETTINGS_REMOVED), _l(_STR_GLOBAL_SETTINGS));
} else if (terminate == 4) {
- guiGameRemoveSettings(configSet);
- guiGameRemoveGlobalSettings(configGame);
+ guiGameRemoveSettings(pgcfg);
+ guiGameRemoveGlobalSettings();
snprintf(message, sizeof(message), _l(_STR_GAME_SETTINGS_REMOVED), _l(_STR_ALL_SETTINGS));
}
sfxPlay(SFX_CONFIRM);
@@ -1918,9 +2243,9 @@ void guiManageCheats(void)
renderedCheats++;
}
- guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? BUTTON_SYMBOL_CIRCLE_ICON : BUTTON_SYMBOL_CROSS_ICON, _STR_SELECT, gTheme->fonts[0], 70, 417, gTheme->selTextColor);
- guiDrawIconAndText(BUTTON_START_ICON, _STR_RUN, gTheme->fonts[0], 500, 417, gTheme->selTextColor);
- guiDrawIconAndText(BUTTON_SYMBOL_SQUARE_ICON, _STR_DISABLE_ALL, gTheme->fonts[0], 290, 417, gTheme->selTextColor);
+ guiDrawIconAndText(gSelectButton == KEY_CIRCLE ? CIRCLE_ICON : CROSS_ICON, _STR_SELECT, gTheme->fonts[0], 70, 417, gTheme->selTextColor);
+ guiDrawIconAndText(START_ICON, _STR_RUN, gTheme->fonts[0], 500, 417, gTheme->selTextColor);
+ guiDrawIconAndText(SQUARE_ICON, _STR_DISABLE_ALL, gTheme->fonts[0], 290, 417, gTheme->selTextColor);
guiEndFrame();
}
diff --git a/src/guigame.c b/src/guigame.c
index 2782d27d..e7402fb8 100644
--- a/src/guigame.c
+++ b/src/guigame.c
@@ -8,13 +8,14 @@
#include "include/ioman.h"
#include "include/lang.h"
#include "include/pad.h"
-#include "include/config.h"
+#include "include/config_wopl.h"
#include "include/ethsupport.h"
#include "include/cheatman.h"
#include "include/system.h"
#include "include/guigame.h"
#include "include/vmc_groups.h"
#include "include/supportbase.h"
+
#include
#ifdef GSM
#include "include/pggsm.h"
@@ -28,7 +29,9 @@
#define NEWLIB_PORT_AWARE
#include // fileXioDevctl("genvmc:", ***)
-static int configSourceID;
+// module level pointer to currently edited per game config
+static per_game_cfg_t *s_pgcfg = NULL;
+
static int dmaMode;
static int compatMode;
static int coreLoader;
@@ -42,14 +45,13 @@ static int GSMFIELDFix;
#endif
#ifdef CHEAT
-static int EnableCheat;
static int CheatMode;
+static int EnableImage;
#endif
static int forceGlobalOSDLanguage;
#ifdef PADEMU
-static int EnablePadEmu;
static int PadEmuSettings;
static union
{
@@ -76,21 +78,20 @@ static char altStartup[32];
static char vmc1[32];
static char vmc2[32];
static char hexDiscID[15];
-static char configSource[128];
// forward declarations.
#ifdef GSM
-static void guiGameLoadGSMConfig(config_set_t *configSet, config_set_t *configGame);
+static void guiGameLoadGSMConfig(const per_game_cfg_t *pg);
#endif
#ifdef CHEAT
-static void guiGameLoadCheatsConfig(config_set_t *configSet, config_set_t *configGame);
+static void guiGameLoadCheatsConfig(const per_game_cfg_t *pg);
#endif
#ifdef PADEMU
-static void guiGameLoadPadEmuConfig(config_set_t *configSet, config_set_t *configGame);
-static void guiGameLoadPadMacroConfig(config_set_t *configSet, config_set_t *configGame);
+static void guiGameLoadPadEmuConfig(const per_game_cfg_t *pg);
+static void guiGameLoadPadMacroConfig(const per_game_cfg_t *pg);
#endif
-static int guiGameSaveOSDLanguageGameConfig(config_set_t *configSet, int result);
-static void guiGameLoadOSDLanguageConfig(config_set_t *configSet, config_set_t *configGame);
+static void guiGameSaveOSDLanguageGameConfig(per_game_cfg_t *pg);
+static void guiGameLoadOSDLanguageConfig(const per_game_cfg_t *pg);
int guiGameAltStartupNameHandler(char *text, int maxLen)
{
@@ -107,12 +108,6 @@ int guiGameAltStartupNameHandler(char *text, int maxLen)
return result;
}
-char *gameConfigSource(void)
-{
- char *source = configSource;
- return source;
-}
-
// VMC
typedef struct
{ // size = 76
@@ -219,9 +214,10 @@ static int guiGameShowVMCConfig(int id, item_list_t *support, char *VMCName, int
int result = validate ? VMC_BUTTON_CREATE : 0;
char vmc[32];
- if (strlen(VMCName))
- strncpy(vmc, VMCName, sizeof(vmc));
- else {
+ if (strlen(VMCName)) {
+ strncpy(vmc, VMCName, sizeof(vmc) - 1);
+ vmc[sizeof(vmc) - 1] = '\0';
+ } else {
if (validate)
return 1; // nothing to validate if no user input
@@ -350,32 +346,41 @@ void guiGameShowVMCMenu(int id, item_list_t *support)
static void guiGameSetGSMSettingsState(void)
{
int previousSource = gGSMSource;
-
diaGetInt(diaGSConfig, GSMCFG_GSMSOURCE, &gGSMSource);
- // update GUI to display per-game or global settings if changed
- if (previousSource != gGSMSource && gGSMSource == SETTINGS_GLOBAL) {
- config_set_t *configSet = gameMenuLoadConfig(diaGSConfig);
- configRemoveKey(configSet, CONFIG_ITEM_GSMSOURCE);
- guiGameLoadGSMConfig(configSet, configGetByType(CONFIG_GAME));
- } else if (previousSource != gGSMSource && gGSMSource == SETTINGS_PERGAME) {
- config_set_t *configSet = gameMenuLoadConfig(diaGSConfig);
- configSetInt(configSet, CONFIG_ITEM_GSMSOURCE, gGSMSource);
- guiGameLoadGSMConfig(configSet, configGetByType(CONFIG_GAME));
+ if (previousSource != gGSMSource) {
+ if (gGSMSource == SETTINGS_GLOBAL) {
+ gEnableGSM = gGlobalGameCfg.gsm_enable;
+ gGSMVMode = gGlobalGameCfg.gsm_vmode;
+ gGSMXOffset = gGlobalGameCfg.gsm_xoffset;
+ gGSMYOffset = gGlobalGameCfg.gsm_yoffset;
+ gGSMFIELDFix = gGlobalGameCfg.gsm_fieldfix;
+ } else if (s_pgcfg) {
+ gEnableGSM = s_pgcfg->gsm_enable;
+ gGSMVMode = s_pgcfg->gsm_vmode;
+ gGSMXOffset = s_pgcfg->gsm_xoffset;
+ gGSMYOffset = s_pgcfg->gsm_yoffset;
+ gGSMFIELDFix = s_pgcfg->gsm_fieldfix;
+ }
+
+ diaSetInt(diaGSConfig, GSMCFG_ENABLEGSM, gEnableGSM);
+ diaSetInt(diaGSConfig, GSMCFG_GSMVMODE, gGSMVMode);
+ diaSetInt(diaGSConfig, GSMCFG_GSMXOFFSET, gGSMXOffset);
+ diaSetInt(diaGSConfig, GSMCFG_GSMYOFFSET, gGSMYOffset);
+ diaSetInt(diaGSConfig, GSMCFG_GSMFIELDFIX, gGSMFIELDFix);
}
- diaGetInt(diaGSConfig, GSMCFG_ENABLEGSM, &EnableGSM);
- diaSetEnabled(diaGSConfig, GSMCFG_GSMVMODE, EnableGSM);
- diaSetEnabled(diaGSConfig, GSMCFG_GSMXOFFSET, EnableGSM);
- diaSetEnabled(diaGSConfig, GSMCFG_GSMYOFFSET, EnableGSM);
- diaSetEnabled(diaGSConfig, GSMCFG_GSMFIELDFIX, EnableGSM);
+ diaGetInt(diaGSConfig, GSMCFG_ENABLEGSM, &gEnableGSM);
+ diaSetEnabled(diaGSConfig, GSMCFG_GSMVMODE, gEnableGSM);
+ diaSetEnabled(diaGSConfig, GSMCFG_GSMXOFFSET, gEnableGSM);
+ diaSetEnabled(diaGSConfig, GSMCFG_GSMYOFFSET, gEnableGSM);
+ diaSetEnabled(diaGSConfig, GSMCFG_GSMFIELDFIX, gEnableGSM);
}
static int guiGameGSMUpdater(int modified)
{
- if (modified) {
+ if (modified)
guiGameSetGSMSettingsState();
- }
return 0;
}
@@ -435,30 +440,34 @@ void guiGameShowGSConfig(void)
static void guiGameSetCheatSettingsState(void)
{
int previousSource = gCheatSource;
-
diaGetInt(diaCheatConfig, CHTCFG_CHEATSOURCE, &gCheatSource);
- // update GUI to display per-game or global settings if changed
- if (previousSource != gCheatSource && gCheatSource == SETTINGS_GLOBAL) {
- config_set_t *configSet = gameMenuLoadConfig(diaCheatConfig);
- configRemoveKey(configSet, CONFIG_ITEM_CHEATSSOURCE);
- guiGameLoadCheatsConfig(configSet, configGetByType(CONFIG_GAME));
- } else if (previousSource != gCheatSource && gCheatSource == SETTINGS_PERGAME) {
- config_set_t *configSet = gameMenuLoadConfig(diaCheatConfig);
- configSetInt(configSet, CONFIG_ITEM_CHEATSSOURCE, gCheatSource);
- guiGameLoadCheatsConfig(configSet, configGetByType(CONFIG_GAME));
+ if (previousSource != gCheatSource) {
+ if (gCheatSource == SETTINGS_GLOBAL) {
+ gEnableCheat = gGlobalGameCfg.cheat_enable;
+ CheatMode = gGlobalGameCfg.cheat_mode;
+ EnableImage = gGlobalGameCfg.cheat_enable_image;
+ } else if (s_pgcfg) {
+ gEnableCheat = s_pgcfg->cheat_enable;
+ CheatMode = s_pgcfg->cheat_mode;
+ EnableImage = s_pgcfg->cheat_enable_image;
+ }
+
+ diaSetInt(diaCheatConfig, CHTCFG_ENABLECHEAT, gEnableCheat);
+ diaSetInt(diaCheatConfig, CHTCFG_CHEATMODE, CheatMode);
+ diaSetInt(diaCheatConfig, CHTCFG_ENABLEIMAGE, EnableImage);
}
- diaGetInt(diaCheatConfig, CHTCFG_ENABLECHEAT, &EnableCheat);
+ diaGetInt(diaCheatConfig, CHTCFG_ENABLECHEAT, &gEnableCheat);
diaGetInt(diaCheatConfig, CHTCFG_CHEATMODE, &CheatMode);
- diaSetEnabled(diaCheatConfig, CHTCFG_CHEATMODE, EnableCheat);
+ diaGetInt(diaCheatConfig, CHTCFG_ENABLEIMAGE, &EnableImage);
+ diaSetEnabled(diaCheatConfig, CHTCFG_CHEATMODE, gEnableCheat);
}
static int guiGameCheatUpdater(int modified)
{
- if (modified) {
+ if (modified)
guiGameSetCheatSettingsState();
- }
return 0;
}
@@ -471,7 +480,7 @@ void guiGameShowCheatConfig(void)
diaSetEnum(diaCheatConfig, CHTCFG_CHEATSOURCE, settingsSource);
diaSetEnum(diaCheatConfig, CHTCFG_CHEATMODE, cheatmodeNames);
- diaSetEnabled(diaCheatConfig, CHTCFG_CHEATMODE, EnableCheat);
+ diaSetEnabled(diaCheatConfig, CHTCFG_CHEATMODE, gEnableCheat);
diaExecuteDialog(diaCheatConfig, -1, 1, &guiGameCheatUpdater);
}
@@ -621,17 +630,18 @@ static int guiGamePadEmuUpdater(int modified)
diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_SOURCE, &gPadEmuSource);
// update GUI to display per-game or global settings if changed
- if (previousSource != gPadEmuSource && gPadEmuSource == SETTINGS_GLOBAL) {
- config_set_t *configSet = gameMenuLoadConfig(diaPadEmuConfig);
- configRemoveKey(configSet, CONFIG_ITEM_PADEMUSOURCE);
- guiGameLoadPadEmuConfig(configSet, configGetByType(CONFIG_GAME));
- } else if (previousSource != gPadEmuSource && gPadEmuSource == SETTINGS_PERGAME) {
- config_set_t *configSet = gameMenuLoadConfig(diaPadEmuConfig);
- configSetInt(configSet, CONFIG_ITEM_PADEMUSOURCE, gPadEmuSource);
- guiGameLoadPadEmuConfig(configSet, configGetByType(CONFIG_GAME));
+ if (previousSource != gPadEmuSource) {
+ if (gPadEmuSource == SETTINGS_GLOBAL) {
+ guiGameLoadPadEmuConfig(NULL);
+ } else if (s_pgcfg) {
+ int saved = s_pgcfg->pademu_source;
+ s_pgcfg->pademu_source = SETTINGS_PERGAME;
+ guiGameLoadPadEmuConfig(s_pgcfg);
+ s_pgcfg->pademu_source = saved;
+ }
}
- diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_ENABLE, &EnablePadEmu);
+ diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_ENABLE, &gEnablePadEmu);
diaGetInt(diaPadEmuConfig, PADCFG_PADPORT, &PadPort);
diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_PORT, &PadEmuPort);
diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_VIB, &PadEmuVib);
@@ -640,32 +650,32 @@ static int guiGamePadEmuUpdater(int modified)
diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_MTAP_PORT, &PadEmuMtapPort);
diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND, &PadEmuWorkaround);
- diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_MTAP, EnablePadEmu);
+ diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_MTAP, gEnablePadEmu);
diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_MTAP_PORT, PadEmuMtap);
- diaSetEnabled(diaPadEmuConfig, PADCFG_PADPORT, EnablePadEmu);
- diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_VIB, PadEmuPort & EnablePadEmu);
+ diaSetEnabled(diaPadEmuConfig, PADCFG_PADPORT, gEnablePadEmu);
+ diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_VIB, PadEmuPort & gEnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_USBDG_MAC, EnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_PAD_MAC, EnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_PAIR, EnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_USBDG_MAC, gEnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_PAD_MAC, gEnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_PAIR, gEnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_USBDG_MAC_STR, EnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_PAD_MAC_STR, EnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_PAIR_STR, EnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_USBDG_MAC_STR, gEnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_PAD_MAC_STR, gEnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_PAIR_STR, gEnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_BTINFO, EnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND, EnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND_STR, EnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_BTINFO, gEnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND, gEnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND_STR, gEnablePadEmu);
if (modified) {
if (PadEmuMtap) {
diaSetEnum(diaPadEmuConfig, PADCFG_PADPORT, PadEmuPorts_enums[PadEmuMtapPort]);
- diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_PORT, (PadPort == 0) & EnablePadEmu);
+ diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_PORT, (PadPort == 0) & gEnablePadEmu);
PadEmuSettings |= 0x00000E00;
} else {
diaSetEnum(diaPadEmuConfig, PADCFG_PADPORT, PadEmuPorts_enums[0]);
- diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_PORT, EnablePadEmu);
+ diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_PORT, gEnablePadEmu);
PadEmuSettings &= 0xFFFF03FF;
if (PadPort > 1) {
PadPort = 0;
@@ -784,26 +794,26 @@ void guiGameShowPadEmuConfig(int forceGlobal)
diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_SOURCE, !forceGlobalPadEmu);
if (forceGlobalPadEmu)
- guiGameLoadPadEmuConfig(NULL, configGetByType(CONFIG_GAME));
+ guiGameLoadPadEmuConfig(NULL);
diaSetEnum(diaPadEmuConfig, PADCFG_PADEMU_SOURCE, settingsSource);
PadEmuMtap = (PadEmuSettings >> 24) & 1;
PadEmuMtapPort = ((PadEmuSettings >> 25) & 1) + 1;
- diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_PORT, EnablePadEmu);
+ diaSetEnabled(diaPadEmuConfig, PADCFG_PADEMU_PORT, gEnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_USBDG_MAC, PadEmuSettings & EnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_PAD_MAC, PadEmuSettings & EnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_PAIR, PadEmuSettings & EnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_USBDG_MAC, PadEmuSettings & gEnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_PAD_MAC, PadEmuSettings & gEnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_PAIR, PadEmuSettings & gEnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_USBDG_MAC_STR, PadEmuSettings & EnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_PAD_MAC_STR, PadEmuSettings & EnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_PAIR_STR, PadEmuSettings & EnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_USBDG_MAC_STR, PadEmuSettings & gEnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_PAD_MAC_STR, PadEmuSettings & gEnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_PAIR_STR, PadEmuSettings & gEnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_BTINFO, PadEmuSettings & EnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND, PadEmuSettings & EnablePadEmu);
- diaSetVisible(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND_STR, PadEmuSettings & EnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_BTINFO, PadEmuSettings & gEnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND, PadEmuSettings & gEnablePadEmu);
+ diaSetVisible(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND_STR, PadEmuSettings & gEnablePadEmu);
if (PadEmuMtap) {
diaSetEnum(diaPadEmuConfig, PADCFG_PADPORT, PadEmuPorts_enums[PadEmuMtapPort]);
@@ -854,14 +864,15 @@ static int guiGamePadMacroUpdater(int modified)
diaGetInt(diaPadMacroConfig, PADMACRO_CFG_SOURCE, &gPadMacroSource);
// update GUI to display per-game or global settings if changed
- if (previousSource != gPadMacroSource && gPadMacroSource == SETTINGS_GLOBAL) {
- config_set_t *configSet = gameMenuLoadConfig(diaPadMacroConfig);
- configRemoveKey(configSet, CONFIG_ITEM_PADMACROSOURCE);
- guiGameLoadPadMacroConfig(configSet, configGetByType(CONFIG_GAME));
- } else if (previousSource != gPadMacroSource && gPadMacroSource == SETTINGS_PERGAME) {
- config_set_t *configSet = gameMenuLoadConfig(diaPadMacroConfig);
- configSetInt(configSet, CONFIG_ITEM_PADMACROSOURCE, gPadMacroSource);
- guiGameLoadPadMacroConfig(configSet, configGetByType(CONFIG_GAME));
+ if (previousSource != gPadMacroSource) {
+ if (gPadMacroSource == SETTINGS_GLOBAL) {
+ guiGameLoadPadMacroConfig(NULL);
+ } else if (s_pgcfg) {
+ int saved = s_pgcfg->padmacro_source;
+ s_pgcfg->padmacro_source = SETTINGS_PERGAME;
+ guiGameLoadPadMacroConfig(s_pgcfg);
+ s_pgcfg->padmacro_source = saved;
+ }
}
int slowdown_l, slowdown_r;
@@ -902,9 +913,8 @@ void guiGameShowPadMacroConfig(int forceGlobal)
forceGlobalPadMacro = forceGlobal;
diaSetEnabled(diaPadMacroConfig, PADMACRO_CFG_SOURCE, !forceGlobalPadMacro);
- if (forceGlobalPadMacro) {
- guiGameLoadPadMacroConfig(NULL, configGetByType(CONFIG_GAME));
- }
+ if (forceGlobalPadMacro)
+ guiGameLoadPadMacroConfig(NULL);
diaSetEnum(diaPadMacroConfig, PADMACRO_CFG_SOURCE, settingsSource);
diaSetEnum(diaPadMacroConfig, PADMACRO_SLOWDOWN_L, button_names_enum);
@@ -922,60 +932,55 @@ void guiGameShowPadMacroConfig(int forceGlobal)
}
}
-static int guiGameSavePadEmuGameConfig(config_set_t *configSet, int result)
+static void guiGameSavePadEmuGameConfig(per_game_cfg_t *pg)
{
if (gPadEmuSource == SETTINGS_PERGAME) {
- diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_ENABLE, &EnablePadEmu);
-
- result = configSetInt(configSet, CONFIG_ITEM_PADEMUSOURCE, gPadEmuSource);
- if (EnablePadEmu != 0)
- result = configSetInt(configSet, CONFIG_ITEM_ENABLEPADEMU, EnablePadEmu);
- else
- configRemoveKey(configSet, CONFIG_ITEM_ENABLEPADEMU);
-
- if (PadEmuSettings != 0)
- result = configSetInt(configSet, CONFIG_ITEM_PADEMUSETTINGS, PadEmuSettings);
- else
- configRemoveKey(configSet, CONFIG_ITEM_PADEMUSETTINGS);
- }
-
- return result;
+ diaGetInt(diaPadEmuConfig, PADCFG_PADEMU_ENABLE, &gEnablePadEmu);
+ pg->pademu_source = SETTINGS_PERGAME;
+ pg->pademu_enable = gEnablePadEmu;
+ pg->pademu_settings = PadEmuSettings;
+ } else
+ pg->pademu_source = pg->pademu_enable = pg->pademu_settings = 0;
}
-static int guiGameSavePadMacroGameConfig(config_set_t *configSet, int result)
+static void guiGameSavePadMacroGameConfig(per_game_cfg_t *pg)
{
if (gPadMacroSource == SETTINGS_PERGAME) {
- result = configSetInt(configSet, CONFIG_ITEM_PADMACROSOURCE, gPadMacroSource);
- if (PadMacroSettings.raw != 0)
- result = configSetInt(configSet, CONFIG_ITEM_PADMACROSETTINGS, PadMacroSettings.raw);
- else
- configRemoveKey(configSet, CONFIG_ITEM_PADMACROSETTINGS);
- }
-
- return result;
+ pg->padmacro_source = SETTINGS_PERGAME;
+ pg->padmacro_settings = PadMacroSettings.raw;
+ } else
+ pg->padmacro_source = pg->padmacro_settings = 0;
}
-void guiGameSavePadEmuGlobalConfig(config_set_t *configGame)
+void guiGameSavePadEmuGlobalConfig(void)
{
if (gPadEmuSource == SETTINGS_GLOBAL) {
- configSetInt(configGame, CONFIG_ITEM_ENABLEPADEMU, EnablePadEmu);
- configSetInt(configGame, CONFIG_ITEM_PADEMUSETTINGS, PadEmuSettings);
+ gGlobalGameCfg.pademu_enable = gEnablePadEmu;
+ gGlobalGameCfg.pademu_settings = PadEmuSettings;
}
}
-void guiGameSavePadMacroGlobalConfig(config_set_t *configGame)
+void guiGameSavePadMacroGlobalConfig(void)
{
- if (gPadMacroSource == SETTINGS_GLOBAL) {
- configSetInt(configGame, CONFIG_ITEM_PADMACROSETTINGS, PadMacroSettings.raw);
- }
+ if (gPadMacroSource == SETTINGS_GLOBAL)
+ gGlobalGameCfg.padmacro_settings = PadMacroSettings.raw;
}
#endif
-void guiGameShowCompatConfig(int id, item_list_t *support, config_set_t *configSet)
+static int guiGameNormaliseCoreLoader(int value)
+{
+ return value == CORE_LOADER_NEUTRINO ? CORE_LOADER_NEUTRINO : CORE_LOADER_WOPL;
+}
+
+void guiGameShowCompatConfig(int id, item_list_t *support)
{
int i;
- const char *loaders[] = {"", "Neutrino", NULL};
+ const char *loaders[] = {
+ [CORE_LOADER_WOPL] = "",
+ [CORE_LOADER_NEUTRINO] = "Neutrino",
+ NULL};
+
diaSetEnum(diaCompatConfig, COMPAT_LOADER, loaders);
if (support->flags & MODE_FLAG_COMPAT_DMA) {
@@ -1003,357 +1008,219 @@ void guiGameShowCompatConfig(int id, item_list_t *support, config_set_t *configS
}
diaGetInt(diaCompatConfig, COMPAT_LOADER, &coreLoader);
+ coreLoader = guiGameNormaliseCoreLoader(coreLoader);
+
diaGetInt(diaCompatConfig, COMPAT_DMA, &dmaMode);
diaGetString(diaCompatConfig, COMPAT_GAMEID, hexid, sizeof(hexid));
diaGetString(diaCompatConfig, COMPAT_ALTSTARTUP, altStartup, sizeof(altStartup));
}
}
-// sets variables without writing to users cfg file.. follow up with menuSaveConfig() to write
-int guiGameSaveConfig(config_set_t *configSet, item_list_t *support)
+int guiGameSaveConfig(per_game_cfg_t *pg, item_list_t *support)
{
int i;
- int result = 0;
- config_set_t *configGame = configGetByType(CONFIG_GAME);
-
compatMode = 0;
for (i = 0; i < COMPAT_MODE_COUNT; ++i) {
int mdpart;
diaGetInt(diaCompatConfig, COMPAT_MODE_BASE + i, &mdpart);
compatMode |= (mdpart ? 1 : 0) << i;
}
+ pg->compat = compatMode;
if (support->flags & MODE_FLAG_COMPAT_DMA) {
diaGetInt(diaCompatConfig, COMPAT_DMA, &dmaMode);
- if (dmaMode != 7)
- result = configSetInt(configSet, CONFIG_ITEM_DMA, dmaMode);
- else
- configRemoveKey(configSet, CONFIG_ITEM_DMA);
+ pg->dma = dmaMode;
}
- if (compatMode != 0)
- result = configSetInt(configSet, CONFIG_ITEM_COMPAT, compatMode);
- else
- configRemoveKey(configSet, CONFIG_ITEM_COMPAT);
-
diaGetInt(diaCompatConfig, COMPAT_LOADER, &coreLoader);
- if (coreLoader != 0)
- result = configSetInt(configSet, CONFIG_ITEM_CORE_LOADER, coreLoader);
- else
- configRemoveKey(configSet, CONFIG_ITEM_CORE_LOADER);
+ coreLoader = guiGameNormaliseCoreLoader(coreLoader);
+ pg->core_loader = coreLoader;
- /// GSM ///
#ifdef GSM
- diaGetInt(diaGSConfig, GSMCFG_ENABLEGSM, &EnableGSM);
- diaGetInt(diaGSConfig, GSMCFG_GSMVMODE, &GSMVMode);
- diaGetInt(diaGSConfig, GSMCFG_GSMXOFFSET, &GSMXOffset);
- diaGetInt(diaGSConfig, GSMCFG_GSMYOFFSET, &GSMYOffset);
- diaGetInt(diaGSConfig, GSMCFG_GSMFIELDFIX, &GSMFIELDFix);
+ diaGetInt(diaGSConfig, GSMCFG_ENABLEGSM, &gEnableGSM);
+ diaGetInt(diaGSConfig, GSMCFG_GSMVMODE, &gGSMVMode);
+ diaGetInt(diaGSConfig, GSMCFG_GSMXOFFSET, &gGSMXOffset);
+ diaGetInt(diaGSConfig, GSMCFG_GSMYOFFSET, &gGSMYOffset);
+ diaGetInt(diaGSConfig, GSMCFG_GSMFIELDFIX, &gGSMFIELDFix);
if (gGSMSource == SETTINGS_PERGAME) {
- result = configSetInt(configSet, CONFIG_ITEM_GSMSOURCE, gGSMSource);
- if (EnableGSM != 0)
- result = configSetInt(configSet, CONFIG_ITEM_ENABLEGSM, EnableGSM);
- else
- configRemoveKey(configSet, CONFIG_ITEM_ENABLEGSM);
-
- if (GSMVMode != 0)
- result = configSetInt(configSet, CONFIG_ITEM_GSMVMODE, GSMVMode);
- else
- configRemoveKey(configSet, CONFIG_ITEM_GSMVMODE);
-
- if (GSMXOffset != 0)
- result = configSetInt(configSet, CONFIG_ITEM_GSMXOFFSET, GSMXOffset);
- else
- configRemoveKey(configSet, CONFIG_ITEM_GSMXOFFSET);
-
- if (GSMYOffset != 0)
- result = configSetInt(configSet, CONFIG_ITEM_GSMYOFFSET, GSMYOffset);
- else
- configRemoveKey(configSet, CONFIG_ITEM_GSMYOFFSET);
+ pg->gsm_source = SETTINGS_PERGAME;
+ pg->gsm_enable = gEnableGSM;
+ pg->gsm_vmode = gGSMVMode;
+ pg->gsm_xoffset = gGSMXOffset;
+ pg->gsm_yoffset = gGSMYOffset;
+ pg->gsm_fieldfix = gGSMFIELDFix;
+ } else {
+ pg->gsm_source = pg->gsm_enable = pg->gsm_vmode = pg->gsm_xoffset = pg->gsm_yoffset = pg->gsm_fieldfix = 0;
+ }
- if (GSMFIELDFix != 0)
- result = configSetInt(configSet, CONFIG_ITEM_GSMFIELDFIX, GSMFIELDFix);
- else
- configRemoveKey(configSet, CONFIG_ITEM_GSMFIELDFIX);
- } else if (gGSMSource == SETTINGS_GLOBAL) {
- configSetInt(configGame, CONFIG_ITEM_ENABLEGSM, EnableGSM);
- configSetInt(configGame, CONFIG_ITEM_GSMVMODE, GSMVMode);
- configSetInt(configGame, CONFIG_ITEM_GSMXOFFSET, GSMXOffset);
- configSetInt(configGame, CONFIG_ITEM_GSMYOFFSET, GSMYOffset);
- configSetInt(configGame, CONFIG_ITEM_GSMFIELDFIX, GSMFIELDFix);
+ if (gGSMSource == SETTINGS_GLOBAL) {
+ gGlobalGameCfg.gsm_enable = gEnableGSM;
+ gGlobalGameCfg.gsm_vmode = gGSMVMode;
+ gGlobalGameCfg.gsm_xoffset = gGSMXOffset;
+ gGlobalGameCfg.gsm_yoffset = gGSMYOffset;
+ gGlobalGameCfg.gsm_fieldfix = gGSMFIELDFix;
}
#endif
- /// Cheats ///
#ifdef CHEAT
diaGetInt(diaCheatConfig, CHTCFG_CHEATSOURCE, &gCheatSource);
- diaGetInt(diaCheatConfig, CHTCFG_ENABLECHEAT, &EnableCheat);
+ diaGetInt(diaCheatConfig, CHTCFG_ENABLECHEAT, &gEnableCheat);
diaGetInt(diaCheatConfig, CHTCFG_CHEATMODE, &CheatMode);
+ diaGetInt(diaCheatConfig, CHTCFG_ENABLEIMAGE, &EnableImage);
if (gCheatSource == SETTINGS_PERGAME) {
- result = configSetInt(configSet, CONFIG_ITEM_CHEATSSOURCE, gCheatSource);
- if (EnableCheat != 0)
- result = configSetInt(configSet, CONFIG_ITEM_ENABLECHEAT, EnableCheat);
- else
- configRemoveKey(configSet, CONFIG_ITEM_ENABLECHEAT);
+ pg->cheat_source = SETTINGS_PERGAME;
+ pg->cheat_enable = gEnableCheat;
+ pg->cheat_mode = CheatMode;
+ pg->cheat_enable_image = EnableImage;
+ } else {
+ pg->cheat_source = pg->cheat_enable = pg->cheat_mode = pg->cheat_enable_image = 0;
+ }
- if (CheatMode != 0)
- result = configSetInt(configSet, CONFIG_ITEM_CHEATMODE, CheatMode);
- else
- configRemoveKey(configSet, CONFIG_ITEM_CHEATMODE);
- } else if (gCheatSource == SETTINGS_GLOBAL) {
- configSetInt(configGame, CONFIG_ITEM_ENABLECHEAT, EnableCheat);
- configSetInt(configGame, CONFIG_ITEM_CHEATMODE, CheatMode);
+ if (gCheatSource == SETTINGS_GLOBAL) {
+ gGlobalGameCfg.cheat_enable = gEnableCheat;
+ gGlobalGameCfg.cheat_mode = CheatMode;
+ gGlobalGameCfg.cheat_enable_image = EnableImage;
}
#endif
#ifdef PADEMU
- /// PADEMU ///
- result = guiGameSavePadEmuGameConfig(configSet, result);
- guiGameSavePadEmuGlobalConfig(configGame);
- result = guiGameSavePadMacroGameConfig(configSet, result);
- guiGameSavePadMacroGlobalConfig(configGame);
+ guiGameSavePadEmuGameConfig(pg);
+ guiGameSavePadEmuGlobalConfig();
+ guiGameSavePadMacroGameConfig(pg);
+ guiGameSavePadMacroGlobalConfig();
#endif
diaGetString(diaCompatConfig, COMPAT_GAMEID, hexid, sizeof(hexid));
- if (hexid[0] != '\0')
- result = configSetStr(configSet, CONFIG_ITEM_DNAS, hexid);
-
diaGetString(diaCompatConfig, COMPAT_ALTSTARTUP, altStartup, sizeof(altStartup));
- if (altStartup[0] != '\0')
- result = configSetStr(configSet, CONFIG_ITEM_ALTSTARTUP, altStartup);
- else
- configRemoveKey(configSet, CONFIG_ITEM_ALTSTARTUP);
-
- /// VMC ///
- configSetVMC(configSet, vmc1, 0);
- configSetVMC(configSet, vmc2, 1);
-
- result = guiGameSaveOSDLanguageGameConfig(configSet, result);
- guiGameSaveOSDLanguageGlobalConfig(configGame);
-
- return result;
+ strncpy(pg->dnas, hexid, sizeof(pg->dnas) - 1);
+ pg->dnas[sizeof(pg->dnas) - 1] = '\0';
+ strncpy(pg->alt_startup, altStartup, sizeof(pg->alt_startup) - 1);
+ pg->alt_startup[sizeof(pg->alt_startup) - 1] = '\0';
+ strncpy(pg->vmc1, vmc1, sizeof(pg->vmc1) - 1);
+ pg->vmc1[sizeof(pg->vmc1) - 1] = '\0';
+ strncpy(pg->vmc2, vmc2, sizeof(pg->vmc2) - 1);
+ pg->vmc2[sizeof(pg->vmc2) - 1] = '\0';
+
+ guiGameSaveOSDLanguageGameConfig(pg);
+ guiGameSaveOSDLanguageGlobalConfig();
+
+ return 1;
}
-void guiGameRemoveGlobalSettings(config_set_t *configGame)
+void guiGameRemoveGlobalSettings(void)
{
if (menuCheckParentalLock() == 0) {
- // Cheats
-#ifdef CHEAT
- configRemoveKey(configGame, CONFIG_ITEM_ENABLECHEAT);
- configRemoveKey(configGame, CONFIG_ITEM_CHEATMODE);
-#endif
- // GSM
-#ifdef GSM
- configRemoveKey(configGame, CONFIG_ITEM_ENABLEGSM);
- configRemoveKey(configGame, CONFIG_ITEM_GSMVMODE);
- configRemoveKey(configGame, CONFIG_ITEM_GSMXOFFSET);
- configRemoveKey(configGame, CONFIG_ITEM_GSMYOFFSET);
- configRemoveKey(configGame, CONFIG_ITEM_GSMFIELDFIX);
-#endif
-
- // OSD Language
- configRemoveKey(configGame, CONFIG_ITEM_OSD_SETTINGS_LANGID);
- configRemoveKey(configGame, CONFIG_ITEM_OSD_SETTINGS_TV_ASP);
- configRemoveKey(configGame, CONFIG_ITEM_OSD_SETTINGS_VMODE);
- configRemoveKey(configGame, CONFIG_ITEM_OSD_SETTINGS_ENABLE);
-
-#ifdef PADEMU
- // PADEMU
- configRemoveKey(configGame, CONFIG_ITEM_ENABLEPADEMU);
- configRemoveKey(configGame, CONFIG_ITEM_PADEMUSETTINGS);
- configRemoveKey(configGame, CONFIG_ITEM_PADMACROSETTINGS);
-#endif
+ memset(&gGlobalGameCfg, 0, sizeof(gGlobalGameCfg));
configSave(CONFIG_GAME, 0);
}
}
-void guiGameRemoveSettings(config_set_t *configSet)
+void guiGameRemoveSettings(per_game_cfg_t *pg)
{
if (menuCheckParentalLock() == 0) {
- configRemoveKey(configSet, CONFIG_ITEM_CONFIGSOURCE);
- configRemoveKey(configSet, CONFIG_ITEM_DMA);
- configRemoveKey(configSet, CONFIG_ITEM_CORE_LOADER);
- configRemoveKey(configSet, CONFIG_ITEM_COMPAT);
- configRemoveKey(configSet, CONFIG_ITEM_DNAS);
- configRemoveKey(configSet, CONFIG_ITEM_ALTSTARTUP);
-
-#ifdef GSM
- // GSM
- configRemoveKey(configSet, CONFIG_ITEM_GSMSOURCE);
- configRemoveKey(configSet, CONFIG_ITEM_ENABLEGSM);
- configRemoveKey(configSet, CONFIG_ITEM_GSMVMODE);
- configRemoveKey(configSet, CONFIG_ITEM_GSMXOFFSET);
- configRemoveKey(configSet, CONFIG_ITEM_GSMYOFFSET);
- configRemoveKey(configSet, CONFIG_ITEM_GSMFIELDFIX);
-#endif
-
-#ifdef CHEAT
- // Cheats
- configRemoveKey(configSet, CONFIG_ITEM_CHEATSSOURCE);
- configRemoveKey(configSet, CONFIG_ITEM_ENABLECHEAT);
- configRemoveKey(configSet, CONFIG_ITEM_CHEATMODE);
-#endif
-
- // OSD Language
- configRemoveKey(configSet, CONFIG_ITEM_OSD_SETTINGS_LANGID);
- configRemoveKey(configSet, CONFIG_ITEM_OSD_SETTINGS_TV_ASP);
- configRemoveKey(configSet, CONFIG_ITEM_OSD_SETTINGS_VMODE);
- configRemoveKey(configSet, CONFIG_ITEM_OSD_SETTINGS_SOURCE);
- configRemoveKey(configSet, CONFIG_ITEM_OSD_SETTINGS_ENABLE);
-
-#ifdef PADEMU
- // PADEMU
- configRemoveKey(configSet, CONFIG_ITEM_PADEMUSOURCE);
- configRemoveKey(configSet, CONFIG_ITEM_ENABLEPADEMU);
- configRemoveKey(configSet, CONFIG_ITEM_PADEMUSETTINGS);
- configRemoveKey(configSet, CONFIG_ITEM_PADMACROSETTINGS);
- configRemoveKey(configSet, CONFIG_ITEM_PADMACROSOURCE);
-#endif
- // VMC
- configRemoveVMC(configSet, 0);
- configRemoveVMC(configSet, 1);
-
+ memset(pg, 0, sizeof(*pg));
+ pg->dma = 7;
menuSaveConfig();
}
}
-void guiGameTestSettings(int id, item_list_t *support, config_set_t *configSet)
+void guiGameTestSettings(int id, item_list_t *support, per_game_cfg_t *pg)
{
- guiGameSaveConfig(configSet, support);
- support->itemLaunch(support, id, configSet);
+ guiGameSaveConfig(pg, support);
+ support->itemLaunch(support, id, pg);
}
#ifdef GSM
-static void guiGameLoadGSMConfig(config_set_t *configSet, config_set_t *configGame)
+static void guiGameLoadGSMConfig(const per_game_cfg_t *pg)
{
- EnableGSM = 0;
- GSMVMode = 0;
- GSMXOffset = 0;
- GSMYOffset = 0;
- GSMFIELDFix = 0;
-
- // set global settings.
+ gEnableGSM = gGlobalGameCfg.gsm_enable;
+ gGSMVMode = gGlobalGameCfg.gsm_vmode;
+ gGSMXOffset = gGlobalGameCfg.gsm_xoffset;
+ gGSMYOffset = gGlobalGameCfg.gsm_yoffset;
+ gGSMFIELDFix = gGlobalGameCfg.gsm_fieldfix;
gGSMSource = 0;
- configGetInt(configGame, CONFIG_ITEM_ENABLEGSM, &EnableGSM);
- configGetInt(configGame, CONFIG_ITEM_GSMVMODE, &GSMVMode);
- configGetInt(configGame, CONFIG_ITEM_GSMXOFFSET, &GSMXOffset);
- configGetInt(configGame, CONFIG_ITEM_GSMYOFFSET, &GSMYOffset);
- configGetInt(configGame, CONFIG_ITEM_GSMFIELDFIX, &GSMFIELDFix);
-
- // override global with per-game settings if available and selected.
- configGetInt(configSet, CONFIG_ITEM_GSMSOURCE, &gGSMSource);
- if (gGSMSource == SETTINGS_PERGAME) {
- if (!configGetInt(configSet, CONFIG_ITEM_ENABLEGSM, &EnableGSM))
- EnableGSM = 0;
- if (!configGetInt(configSet, CONFIG_ITEM_GSMVMODE, &GSMVMode))
- GSMVMode = 0;
- if (!configGetInt(configSet, CONFIG_ITEM_GSMXOFFSET, &GSMXOffset))
- GSMXOffset = 0;
- if (!configGetInt(configSet, CONFIG_ITEM_GSMYOFFSET, &GSMYOffset))
- GSMYOffset = 0;
- if (!configGetInt(configSet, CONFIG_ITEM_GSMFIELDFIX, &GSMFIELDFix))
- GSMFIELDFix = 0;
+
+ if (pg && pg->gsm_source == SETTINGS_PERGAME) {
+ gGSMSource = SETTINGS_PERGAME;
+ gEnableGSM = pg->gsm_enable;
+ gGSMVMode = pg->gsm_vmode;
+ gGSMXOffset = pg->gsm_xoffset;
+ gGSMYOffset = pg->gsm_yoffset;
+ gGSMFIELDFix = pg->gsm_fieldfix;
}
- // set gui settings.
diaSetInt(diaGSConfig, GSMCFG_GSMSOURCE, gGSMSource);
- diaSetInt(diaGSConfig, GSMCFG_ENABLEGSM, EnableGSM);
- diaSetInt(diaGSConfig, GSMCFG_GSMVMODE, GSMVMode);
- diaSetInt(diaGSConfig, GSMCFG_GSMXOFFSET, GSMXOffset);
- diaSetInt(diaGSConfig, GSMCFG_GSMYOFFSET, GSMYOffset);
- diaSetInt(diaGSConfig, GSMCFG_GSMFIELDFIX, GSMFIELDFix);
+ diaSetInt(diaGSConfig, GSMCFG_ENABLEGSM, gEnableGSM);
+ diaSetInt(diaGSConfig, GSMCFG_GSMVMODE, gGSMVMode);
+ diaSetInt(diaGSConfig, GSMCFG_GSMXOFFSET, gGSMXOffset);
+ diaSetInt(diaGSConfig, GSMCFG_GSMYOFFSET, gGSMYOffset);
+ diaSetInt(diaGSConfig, GSMCFG_GSMFIELDFIX, gGSMFIELDFix);
}
#endif
#ifdef CHEAT
-static void guiGameLoadCheatsConfig(config_set_t *configSet, config_set_t *configGame)
+static void guiGameLoadCheatsConfig(const per_game_cfg_t *pg)
{
- EnableCheat = 0;
- CheatMode = 0;
-
- // set global settings.
+ gEnableCheat = gGlobalGameCfg.cheat_enable;
+ CheatMode = gGlobalGameCfg.cheat_mode;
+ EnableImage = gGlobalGameCfg.cheat_enable_image;
gCheatSource = 0;
- configGetInt(configGame, CONFIG_ITEM_ENABLECHEAT, &EnableCheat);
- configGetInt(configGame, CONFIG_ITEM_CHEATMODE, &CheatMode);
- // override global with per-game settings if available and selected.
- configGetInt(configSet, CONFIG_ITEM_CHEATSSOURCE, &gCheatSource);
- if (gCheatSource == SETTINGS_PERGAME) {
- if (!configGetInt(configSet, CONFIG_ITEM_ENABLECHEAT, &EnableCheat))
- EnableCheat = 0;
- if (!configGetInt(configSet, CONFIG_ITEM_CHEATMODE, &CheatMode))
- CheatMode = 0;
+ if (pg && pg->cheat_source == SETTINGS_PERGAME) {
+ gCheatSource = SETTINGS_PERGAME;
+ gEnableCheat = pg->cheat_enable;
+ CheatMode = pg->cheat_mode;
+ EnableImage = pg->cheat_enable_image;
}
- // set gui settings.
diaSetInt(diaCheatConfig, CHTCFG_CHEATSOURCE, gCheatSource);
- diaSetInt(diaCheatConfig, CHTCFG_ENABLECHEAT, EnableCheat);
+ diaSetInt(diaCheatConfig, CHTCFG_ENABLECHEAT, gEnableCheat);
diaSetInt(diaCheatConfig, CHTCFG_CHEATMODE, CheatMode);
+ diaSetInt(diaCheatConfig, CHTCFG_ENABLEIMAGE, EnableImage);
}
#endif
#ifdef PADEMU
-static void guiGameLoadPadEmuConfig(config_set_t *configSet, config_set_t *configGame)
+static void guiGameLoadPadEmuConfig(const per_game_cfg_t *pg)
{
- EnablePadEmu = 0;
- PadEmuSettings = 0;
-
- // set global settings.
+ gEnablePadEmu = gGlobalGameCfg.pademu_enable;
+ PadEmuSettings = gGlobalGameCfg.pademu_settings;
gPadEmuSource = 0;
- configGetInt(configGame, CONFIG_ITEM_ENABLEPADEMU, &EnablePadEmu);
- configGetInt(configGame, CONFIG_ITEM_PADEMUSETTINGS, &PadEmuSettings);
-
- // override global with per-game settings if available and selected.
- if (!forceGlobalPadEmu) {
- configGetInt(configSet, CONFIG_ITEM_PADEMUSOURCE, &gPadEmuSource);
- if (gPadEmuSource == SETTINGS_PERGAME) {
- if (!configGetInt(configSet, CONFIG_ITEM_ENABLEPADEMU, &EnablePadEmu))
- EnablePadEmu = 0;
- if (!configGetInt(configSet, CONFIG_ITEM_PADEMUSETTINGS, &PadEmuSettings))
- PadEmuSettings = 0;
- }
+
+ if (!forceGlobalPadEmu && pg && pg->pademu_source == SETTINGS_PERGAME) {
+ gPadEmuSource = SETTINGS_PERGAME;
+ gEnablePadEmu = pg->pademu_enable;
+ PadEmuSettings = pg->pademu_settings;
}
- // set gui settings.
+
int PadEmuMtap = (PadEmuSettings >> 24) & 1;
int PadEmuMtapPort = ((PadEmuSettings >> 25) & 1) + 1;
diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_SOURCE, gPadEmuSource);
- diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_ENABLE, EnablePadEmu);
-
+ diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_ENABLE, gEnablePadEmu);
diaSetInt(diaPadEmuConfig, PADCFG_PADPORT, 0);
diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_VIB, (PadEmuSettings >> 16) & 1);
diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_MTAP, PadEmuMtap);
diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_MTAP_PORT, PadEmuMtapPort);
- diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND, ((PadEmuSettings >> 26) & 1));
+ diaSetInt(diaPadEmuConfig, PADCFG_PADEMU_WORKAROUND, (PadEmuSettings >> 26) & 1);
}
-static void guiGameLoadPadMacroConfig(config_set_t *configSet, config_set_t *configGame)
+static void guiGameLoadPadMacroConfig(const per_game_cfg_t *pg)
{
- PadMacroSettings.raw = 0;
+ PadMacroSettings.raw = gGlobalGameCfg.padmacro_settings;
gPadMacroSource = 0;
- configGetInt(configGame, CONFIG_ITEM_PADMACROSETTINGS, &PadMacroSettings.raw);
- // override global with per-game settings if available and selected.
- if (!forceGlobalPadMacro) {
- configGetInt(configSet, CONFIG_ITEM_PADMACROSOURCE, &gPadMacroSource);
- if (gPadMacroSource == SETTINGS_PERGAME) {
- if (!configGetInt(configSet, CONFIG_ITEM_PADMACROSETTINGS, &PadMacroSettings.raw))
- PadMacroSettings.raw = 0;
- }
+ if (!forceGlobalPadMacro && pg && pg->padmacro_source == SETTINGS_PERGAME) {
+ gPadMacroSource = SETTINGS_PERGAME;
+ PadMacroSettings.raw = pg->padmacro_settings;
}
diaSetInt(diaPadMacroConfig, PADMACRO_CFG_SOURCE, gPadMacroSource);
- int slowdown_l_ui = 0, slowdown_r_ui = 0;
- if (PadMacroSettings.l_slowdown_enable) {
- slowdown_l_ui = bit_number_to_button_enum[PadMacroSettings.left_stick_slowdown];
- }
-
- if (PadMacroSettings.r_slowdown_enable) {
- slowdown_r_ui = bit_number_to_button_enum[PadMacroSettings.right_stick_slowdown];
- }
+ int slowdown_l_ui = PadMacroSettings.l_slowdown_enable ? bit_number_to_button_enum[PadMacroSettings.left_stick_slowdown] : 0;
+ int slowdown_r_ui = PadMacroSettings.r_slowdown_enable ? bit_number_to_button_enum[PadMacroSettings.right_stick_slowdown] : 0;
diaSetInt(diaPadMacroConfig, PADMACRO_SLOWDOWN_L, slowdown_l_ui);
diaSetInt(diaPadMacroConfig, PADMACRO_SLOWDOWN_R, slowdown_r_ui);
@@ -1375,14 +1242,15 @@ static int guiGameOSDLanguageUpdater(int modified)
diaGetInt(diaOSDConfig, OSD_LANGUAGE_SOURCE, &gOSDLanguageSource);
// update GUI to display per-game or global settings if changed
- if (previousSource != gOSDLanguageSource && gOSDLanguageSource == SETTINGS_GLOBAL) {
- config_set_t *configSet = gameMenuLoadConfig(diaOSDConfig);
- configRemoveKey(configSet, CONFIG_ITEM_OSD_SETTINGS_SOURCE);
- guiGameLoadOSDLanguageConfig(configSet, configGetByType(CONFIG_GAME));
- } else if (previousSource != gOSDLanguageSource && gOSDLanguageSource == SETTINGS_PERGAME) {
- config_set_t *configSet = gameMenuLoadConfig(diaOSDConfig);
- configSetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_SOURCE, gOSDLanguageSource);
- guiGameLoadOSDLanguageConfig(configSet, configGetByType(CONFIG_GAME));
+ if (previousSource != gOSDLanguageSource) {
+ if (gOSDLanguageSource == SETTINGS_GLOBAL) {
+ guiGameLoadOSDLanguageConfig(NULL);
+ } else if (s_pgcfg) {
+ int saved = s_pgcfg->osd_source;
+ s_pgcfg->osd_source = SETTINGS_PERGAME;
+ guiGameLoadOSDLanguageConfig(s_pgcfg);
+ s_pgcfg->osd_source = saved;
+ }
}
diaGetInt(diaOSDConfig, OSD_LANGUAGE_ENABLE, &gOSDLanguageEnable);
@@ -1409,9 +1277,8 @@ void guiGameShowOSDLanguageConfig(int forceGlobal)
forceGlobalOSDLanguage = forceGlobal;
diaSetEnabled(diaOSDConfig, OSD_LANGUAGE_SOURCE, !forceGlobalOSDLanguage);
- if (forceGlobalOSDLanguage) {
- guiGameLoadOSDLanguageConfig(NULL, configGetByType(CONFIG_GAME));
- }
+ if (forceGlobalOSDLanguage)
+ guiGameLoadOSDLanguageConfig(NULL);
int result = -1;
@@ -1422,60 +1289,43 @@ void guiGameShowOSDLanguageConfig(int forceGlobal)
break;
}
}
-static int guiGameSaveOSDLanguageGameConfig(config_set_t *configSet, int result)
+
+static void guiGameSaveOSDLanguageGameConfig(per_game_cfg_t *pg)
{
if (gOSDLanguageSource == SETTINGS_PERGAME) {
- if ((result = configSetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_SOURCE, gOSDLanguageSource)))
- if ((result = configSetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_ENABLE, gOSDLanguageEnable))) {
- configSetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_LANGID, gOSDLanguageValue);
- configSetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_TV_ASP, gOSDTVAspectRatio);
- result = configSetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_VMODE, gOSDVideOutput);
- }
- } else {
- configRemoveKey(configSet, CONFIG_ITEM_OSD_SETTINGS_LANGID);
- configRemoveKey(configSet, CONFIG_ITEM_OSD_SETTINGS_TV_ASP);
- configRemoveKey(configSet, CONFIG_ITEM_OSD_SETTINGS_VMODE);
- configRemoveKey(configSet, CONFIG_ITEM_OSD_SETTINGS_ENABLE);
- }
-
- return result;
+ pg->osd_source = SETTINGS_PERGAME;
+ pg->osd_enable = gOSDLanguageEnable;
+ pg->osd_langid = gOSDLanguageValue;
+ pg->osd_tv_aspect = gOSDTVAspectRatio;
+ pg->osd_vmode = gOSDVideOutput;
+ } else
+ pg->osd_source = pg->osd_enable = pg->osd_langid = pg->osd_tv_aspect = pg->osd_vmode = 0;
}
-void guiGameSaveOSDLanguageGlobalConfig(config_set_t *configGame)
+void guiGameSaveOSDLanguageGlobalConfig(void)
{
if (gOSDLanguageSource == SETTINGS_GLOBAL) {
- configSetInt(configGame, CONFIG_ITEM_OSD_SETTINGS_ENABLE, gOSDLanguageEnable);
- configSetInt(configGame, CONFIG_ITEM_OSD_SETTINGS_LANGID, gOSDLanguageValue);
- configSetInt(configGame, CONFIG_ITEM_OSD_SETTINGS_TV_ASP, gOSDTVAspectRatio);
- configSetInt(configGame, CONFIG_ITEM_OSD_SETTINGS_VMODE, gOSDVideOutput);
+ gGlobalGameCfg.osd_enable = gOSDLanguageEnable;
+ gGlobalGameCfg.osd_langid = gOSDLanguageValue;
+ gGlobalGameCfg.osd_tv_aspect = gOSDTVAspectRatio;
+ gGlobalGameCfg.osd_vmode = gOSDVideOutput;
}
}
-static void guiGameLoadOSDLanguageConfig(config_set_t *configSet, config_set_t *configGame)
+static void guiGameLoadOSDLanguageConfig(const per_game_cfg_t *pg)
{
- gOSDLanguageValue = 0;
- gOSDLanguageEnable = 0;
+ gOSDLanguageEnable = gGlobalGameCfg.osd_enable;
+ gOSDLanguageValue = gGlobalGameCfg.osd_langid;
+ gOSDTVAspectRatio = gGlobalGameCfg.osd_tv_aspect;
+ gOSDVideOutput = gGlobalGameCfg.osd_vmode;
gOSDLanguageSource = 0;
- gOSDTVAspectRatio = 0;
- gOSDVideOutput = 0;
-
- configGetInt(configGame, CONFIG_ITEM_OSD_SETTINGS_ENABLE, &gOSDLanguageEnable);
- configGetInt(configGame, CONFIG_ITEM_OSD_SETTINGS_LANGID, &gOSDLanguageValue);
- configGetInt(configGame, CONFIG_ITEM_OSD_SETTINGS_TV_ASP, &gOSDTVAspectRatio);
- configGetInt(configGame, CONFIG_ITEM_OSD_SETTINGS_VMODE, &gOSDVideOutput);
- // override global with per-game settings if available and selected.
- if (!forceGlobalOSDLanguage) {
- configGetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_SOURCE, &gOSDLanguageSource);
- if (gOSDLanguageSource == SETTINGS_PERGAME) {
- if (!configGetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_ENABLE, &gOSDLanguageEnable))
- gOSDLanguageEnable = 0;
- if (!configGetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_LANGID, &gOSDLanguageValue))
- gOSDLanguageValue = 0;
- if (!configGetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_TV_ASP, &gOSDTVAspectRatio))
- gOSDTVAspectRatio = 0;
- if (!configGetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_VMODE, &gOSDVideOutput))
- gOSDVideOutput = 0;
- }
+
+ if (!forceGlobalOSDLanguage && pg && pg->osd_source == SETTINGS_PERGAME) {
+ gOSDLanguageSource = SETTINGS_PERGAME;
+ gOSDLanguageEnable = pg->osd_enable;
+ gOSDLanguageValue = pg->osd_langid;
+ gOSDTVAspectRatio = pg->osd_tv_aspect;
+ gOSDVideOutput = pg->osd_vmode;
}
diaSetInt(diaOSDConfig, OSD_LANGUAGE_SOURCE, gOSDLanguageSource);
@@ -1486,56 +1336,53 @@ static void guiGameLoadOSDLanguageConfig(config_set_t *configSet, config_set_t *
}
// OSD Language
-// loads defaults if no config found
-void guiGameLoadConfig(item_list_t *support, config_set_t *configSet)
+void guiGameLoadConfig(item_list_t *support, per_game_cfg_t *pg)
{
- int i;
- config_set_t *configGame = configGetByType(CONFIG_GAME);
+ s_pgcfg = pg;
- configSource[0] = '\0';
- configSourceID = CONFIG_SOURCE_DEFAULT;
- configGetInt(configSet, CONFIG_ITEM_CONFIGSOURCE, &configSourceID);
+ forceGlobalOSDLanguage = 0;
+#ifdef PADEMU
+ forceGlobalPadEmu = 0;
+ forceGlobalPadMacro = 0;
+#endif
- dmaMode = 7; // defaulting to UDMA 4
- if (support->flags & MODE_FLAG_COMPAT_DMA) {
- configGetInt(configSet, CONFIG_ITEM_DMA, &dmaMode);
+ dmaMode = pg ? pg->dma : 7;
+ if (support->flags & MODE_FLAG_COMPAT_DMA)
diaSetInt(diaCompatConfig, COMPAT_DMA, dmaMode);
- } else
+ else
diaSetInt(diaCompatConfig, COMPAT_DMA, 0);
- compatMode = 0;
- configGetInt(configSet, CONFIG_ITEM_COMPAT, &compatMode);
- for (i = 0; i < COMPAT_MODE_COUNT; ++i)
- diaSetInt(diaCompatConfig, COMPAT_MODE_BASE + i, (compatMode & (1 << i)) > 0 ? 1 : 0);
+ compatMode = pg ? pg->compat : 0;
+ for (int i = 0; i < COMPAT_MODE_COUNT; ++i)
+ diaSetInt(diaCompatConfig, COMPAT_MODE_BASE + i, (compatMode & (1 << i)) ? 1 : 0);
- configGetInt(configSet, CONFIG_ITEM_CORE_LOADER, &coreLoader);
+ coreLoader = pg ? guiGameNormaliseCoreLoader(pg->core_loader) : CORE_LOADER_WOPL;
diaSetInt(diaCompatConfig, COMPAT_LOADER, coreLoader);
+
#ifdef GSM
- guiGameLoadGSMConfig(configSet, configGame);
+ guiGameLoadGSMConfig(pg);
#endif
#ifdef CHEAT
- guiGameLoadCheatsConfig(configSet, configGame);
+ guiGameLoadCheatsConfig(pg);
#endif
#ifdef PADEMU
- guiGameLoadPadEmuConfig(configSet, configGame);
- guiGameLoadPadMacroConfig(configSet, configGame);
+ guiGameLoadPadEmuConfig(pg);
+ guiGameLoadPadMacroConfig(pg);
#endif
+ guiGameLoadOSDLanguageConfig(pg);
- guiGameLoadOSDLanguageConfig(configSet, configGame);
+ strncpy(hexid, pg ? pg->dnas : "", sizeof(hexid) - 1);
+ hexid[sizeof(hexid) - 1] = '\0';
- /// Find out the current game ID ///
- hexid[0] = '\0';
- configGetStrCopy(configSet, CONFIG_ITEM_DNAS, hexid, sizeof(hexid));
- diaSetString(diaCompatConfig, COMPAT_GAMEID, hexid);
+ strncpy(altStartup, pg ? pg->alt_startup : "", sizeof(altStartup) - 1);
+ altStartup[sizeof(altStartup) - 1] = '\0';
- altStartup[0] = '\0';
- configGetStrCopy(configSet, CONFIG_ITEM_ALTSTARTUP, altStartup, sizeof(altStartup));
+ diaSetString(diaCompatConfig, COMPAT_GAMEID, hexid);
diaSetString(diaCompatConfig, COMPAT_ALTSTARTUP, altStartup);
- /// VMC ///
- vmc1[0] = '\0';
- configGetVMC(configSet, vmc1, sizeof(vmc1), 0);
+ strncpy(vmc1, pg ? pg->vmc1 : "", sizeof(vmc1) - 1);
+ vmc1[sizeof(vmc1) - 1] = '\0';
- vmc2[0] = '\0';
- configGetVMC(configSet, vmc2, sizeof(vmc2), 1);
+ strncpy(vmc2, pg ? pg->vmc2 : "", sizeof(vmc2) - 1);
+ vmc2[sizeof(vmc2) - 1] = '\0';
}
diff --git a/src/hddsupport.c b/src/hddsupport.c
index e9ecc7d1..ea23d364 100644
--- a/src/hddsupport.c
+++ b/src/hddsupport.c
@@ -9,10 +9,10 @@
#include "include/ioman.h"
#include "include/system.h"
#include "include/extern_irx.h"
+#include "include/tar.h"
#ifdef CHEAT
#include "include/cheatman.h"
#endif
-#include "include/art_tar.h"
#include "modules/iopcore/common/cdvd_config.h"
#include "include/mcemu.h"
#include
@@ -21,6 +21,7 @@
#include
#include "opl-hdd-ioctl.h"
#include "include/initializer.h"
+#include "include/config_wopl.h"
#include
#define NEWLIB_PORT_AWARE
@@ -153,6 +154,8 @@ typedef struct
#define HDL_GAME_DATA_OFFSET 0x100000 // Sector 0x800 in the extended attribute area.
#define HDL_FS_MAGIC 0x1337
+#define WOPL_PARTITION "+" WOPL_CONFIG_NAME
+
extern int probed_fd;
extern u32 probed_lba;
u8 IOBuffer[2048] ALIGNED(64); // one sector
@@ -162,7 +165,6 @@ static unsigned char hddModulesLoaded = 0;
static unsigned char hddHDProKitDetected = 0;
static unsigned char hddModulesLoadCount = 0;
static unsigned char hddSupportModulesLoaded = 0;
-static unsigned char hddLoadedArchivedArt = 0;
static char *hddPrefix = "pfs0:";
static hdl_games_list_t hddGames;
@@ -498,23 +500,28 @@ static int hddGetFileBlockInfo(const char *name, const apa_sub_t *subs, pfs_bloc
static void hddInitModules(void)
{
+ guiSetBootStatusIfActive("Loading HDD modules...");
hddLoadModules();
+
+ guiSetBootStatusIfActive("Mounting HDD...");
hddLoadSupportModules();
// update Themes
char path[256];
+
+ guiSetBootStatusIfActive("Loading HDD themes...");
+
+ sprintf(path, "%sTHM/thm.tar", gHDDPrefix);
+ tarLoadFile(TAR_KIND_THM, path);
+
sprintf(path, "%sTHM", gHDDPrefix);
thmAddElements(path, "/", 1);
+ guiSetBootStatusIfActive("Loading HDD languages...");
sprintf(path, "%sLNG", gHDDPrefix);
lngAddLanguages(path, "/", hddGameList.mode);
- if (!hddLoadedArchivedArt) {
- sprintf(path, "%sART/art.tar", gHDDPrefix);
- loadTarFile(path);
- hddLoadedArchivedArt = 1;
- }
-
+ guiSetBootStatusIfActive("Checking HDD folders...");
sbCreateFolders(gHDDPrefix, 0);
}
@@ -591,7 +598,7 @@ static void hddCheckOPLFolder(const char *mountPoint)
DIR *dir;
char path[32];
- sprintf(path, "%swOPL", mountPoint);
+ sprintf(path, "%s%s", mountPoint, WOPL_CONFIG_NAME);
dir = opendir(path);
if (dir == NULL)
@@ -602,46 +609,55 @@ static void hddCheckOPLFolder(const char *mountPoint)
static void hddFindOPLPartition(void)
{
- static config_set_t *config;
- char name[64];
int fd, ret = 0;
fileXioUmount(hddPrefix);
-
ret = fileXioMount("pfs0:", "hdd0:__common", FIO_MT_RDWR);
if (ret == 0) {
- fd = open("pfs0:wOPL/conf_hdd.cfg", O_RDONLY);
- if (fd >= 0) {
- config = configAlloc(0, NULL, "pfs0:wOPL/conf_hdd.cfg");
- configRead(config);
-
- configGetStrCopy(config, "hdd_partition", name, sizeof(name));
- snprintf(gOPLPart, sizeof(gOPLPart), "hdd0:%s", name);
-
- configFree(config);
- close(fd);
-
- return;
+ const char *paths[] = {
+ "pfs0:" WOPL_CONFIG_NAME "/conf_hdd.cfg",
+ "pfs0:OPL/conf_hdd.cfg", // for OPL Launcher backwards compat
+ NULL};
+
+ for (int i = 0; paths[i]; i++) {
+ fd = open(paths[i], O_RDONLY);
+ if (fd >= 0) {
+ char line[128];
+ int n = read(fd, line, sizeof(line) - 1);
+ close(fd);
+ if (n > 0) {
+ line[n] = '\0';
+ char *val = strchr(line, '=');
+ if (val) {
+ val++;
+ char *cr = strchr(val, '\r');
+ if (cr)
+ *cr = '\0';
+ char *nl = strchr(val, '\n');
+ if (nl)
+ *nl = '\0';
+ snprintf(gOPLPart, sizeof(gOPLPart), "hdd0:%s", val);
+
+ return;
+ }
+ }
+ }
}
+ // not found anywhere.. create in wOPL location with default
hddCheckOPLFolder(hddPrefix);
-
- fd = open("pfs0:wOPL/conf_hdd.cfg", O_CREAT | O_TRUNC | O_WRONLY);
+ char path[256];
+ snprintf(path, sizeof(path), "pfs0:%s/conf_hdd.cfg", WOPL_CONFIG_NAME);
+ fd = open(path, O_CREAT | O_TRUNC | O_WRONLY);
if (fd >= 0) {
- config = configAlloc(0, NULL, "pfs0:wOPL/conf_hdd.cfg");
- configRead(config);
-
- configSetStr(config, "hdd_partition", "+wOPL");
- configWrite(config);
-
- configFree(config);
+ char buf[64];
+ snprintf(buf, sizeof(buf), "hdd_partition=%s\n", WOPL_PARTITION);
+ write(fd, buf, strlen(buf));
close(fd);
}
}
- snprintf(gOPLPart, sizeof(gOPLPart), "hdd0:+wOPL");
-
- return;
+ snprintf(gOPLPart, sizeof(gOPLPart), "hdd0:%s", WOPL_PARTITION);
}
static int hddCreateOPLPartition(const char *name)
@@ -836,7 +852,7 @@ static void hddInit(item_list_t *itemList)
{
LOG("HDDSUPPORT Init\n");
hddForceUpdate = 0; // Use cache at initial startup.
- configGetInt(configGetByType(CONFIG_OPL), "hdd_frames_delay", &hddGameList.delay);
+ itemList->delay = gHDDFramesDelay;
ioPutRequest(IO_CUSTOM_SIMPLEACTION, &hddInitModules);
hddGameList.enabled = 1;
itemList->enabled = 1;
@@ -860,11 +876,15 @@ static int hddUpdateGameList(item_list_t *itemList)
hdl_games_list_t hddGamesNew;
int ret;
+ guiSetBootStatusIfActive("Loading HDD game cache...");
if (((ret = hddLoadGameListCache(&hddGames)) != 0) || (hddForceUpdate)) {
+ guiSetBootStatusIfActive("Scanning HDD games...");
+
hddGamesNew.count = 0;
hddGamesNew.games = NULL;
ret = hddGetHDLGamelist(&hddGamesNew);
if (ret == 0) {
+ guiSetBootStatusIfActive("Updating HDD game cache...");
hddUpdateGameListCache(&hddGames, &hddGamesNew);
hddFreeHDLGamelist(&hddGames);
hddGames = hddGamesNew;
@@ -915,7 +935,7 @@ static void hddRenameGame(item_list_t *itemList, int id, char *newName)
hddForceUpdate = 1;
}
-void hddLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
+void hddLaunchGame(item_list_t *itemList, int id, per_game_cfg_t *pgcfg)
{
int i, size_irx = 0;
int EnablePS2Logo = 0;
@@ -932,100 +952,32 @@ void hddLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
else
game = gAutoLaunchGame;
- apa_sub_t parts[APA_MAXSUB + 1];
- char vmc_name[2][32];
- int part_valid = 0, size_mcemu_irx = 0, nparts;
- hdd_vmc_infos_t hdd_vmc_infos;
- memset(&hdd_vmc_infos, 0, sizeof(hdd_vmc_infos_t));
-
- configGetVMC(configSet, vmc_name[0], sizeof(vmc_name[0]), 0);
- configGetVMC(configSet, vmc_name[1], sizeof(vmc_name[1]), 1);
-
- if (vmc_name[0][0] || vmc_name[1][0]) {
- nparts = hddGetPartitionInfo(gOPLPart, parts);
- if (nparts > 0 && nparts <= 5) {
- for (i = 0; i < nparts; i++) {
- hdd_vmc_infos.parts[i].start = parts[i].start;
- hdd_vmc_infos.parts[i].length = parts[i].length;
- LOG("HDDSUPPORT hdd_vmc_infos.parts[%d].start : 0x%X\n", i, hdd_vmc_infos.parts[i].start);
- LOG("HDDSUPPORT hdd_vmc_infos.parts[%d].length : 0x%X\n", i, hdd_vmc_infos.parts[i].length);
- }
- part_valid = 1;
- }
- }
+ int selectedCore = pgcfg->core_loader == CORE_LOADER_NEUTRINO ? CORE_LOADER_NEUTRINO : CORE_LOADER_WOPL;
+ int isZSO = 0;
+ int size_mcemu_irx = 0;
- if (part_valid) {
- char vmc_path[256];
- int vmc_id, have_error = 0;
- vmc_superblock_t vmc_superblock;
- pfs_blockinfo_t blocks[11];
-
- for (vmc_id = 0; vmc_id < 2; vmc_id++) {
- if (vmc_name[vmc_id][0]) {
- have_error = 1;
- hdd_vmc_infos.active = 0;
- if (sysCheckVMC(gHDDPrefix, "/", vmc_name[vmc_id], 0, &vmc_superblock) > 0) {
- hdd_vmc_infos.flags = vmc_superblock.mc_flag & 0xFF;
- hdd_vmc_infos.flags |= 0x100;
- hdd_vmc_infos.specs.page_size = vmc_superblock.page_size;
- hdd_vmc_infos.specs.block_size = vmc_superblock.pages_per_block;
- hdd_vmc_infos.specs.card_size = vmc_superblock.pages_per_cluster * vmc_superblock.clusters_per_card;
-
- // Check vmc inode block chain (write operation can cause damage)
- snprintf(vmc_path, sizeof(vmc_path), "%sVMC/%s.bin", gHDDPrefix, vmc_name[vmc_id]);
- if ((nparts = hddGetFileBlockInfo(vmc_path, parts, blocks, 11)) > 0) {
- have_error = 0;
- hdd_vmc_infos.active = 1;
- for (i = 0; i < nparts - 1; i++) {
- hdd_vmc_infos.blocks[i].number = blocks[i + 1].number;
- hdd_vmc_infos.blocks[i].subpart = blocks[i + 1].subpart;
- hdd_vmc_infos.blocks[i].count = blocks[i + 1].count;
- LOG("HDDSUPPORT hdd_vmc_infos.blocks[%d].number : 0x%X\n", i, hdd_vmc_infos.blocks[i].number);
- LOG("HDDSUPPORT hdd_vmc_infos.blocks[%d].subpart : 0x%X\n", i, hdd_vmc_infos.blocks[i].subpart);
- LOG("HDDSUPPORT hdd_vmc_infos.blocks[%d].count : 0x%X\n", i, hdd_vmc_infos.blocks[i].count);
- }
- } else { // else VMC file is too fragmented
- LOG("HDDSUPPORT Block Chain NG\n");
- have_error = 2;
- }
- }
+ neutrino_path_t neutrinoPath;
+ char neutrinoVmc0[256];
+ char neutrinoVmc1[256];
- if (have_error) {
- if (gAutoLaunchGame == NULL) {
- char error[256];
- if (have_error == 2) // VMC file is fragmented
- snprintf(error, sizeof(error), _l(_STR_ERR_VMC_FRAGMENTED_CONTINUE), vmc_name[vmc_id], (vmc_id + 1));
- else
- snprintf(error, sizeof(error), _l(_STR_ERR_VMC_CONTINUE), vmc_name[vmc_id], (vmc_id + 1));
- if (!guiMsgBox(error, 1, NULL))
- return;
- } else
- LOG("VMC error\n");
- }
+ neutrinoPath.elf[0] = '\0';
+ neutrinoPath.cwd[0] = '\0';
+ neutrinoVmc0[0] = '\0';
+ neutrinoVmc1[0] = '\0';
- for (i = 0; i < size_hdd_mcemu_irx; i++) {
- if (((u32 *)&hdd_mcemu_irx)[i] == (0xC0DEFAC0 + vmc_id)) {
- if (hdd_vmc_infos.active)
- size_mcemu_irx = size_hdd_mcemu_irx;
- memcpy(&((u32 *)&hdd_mcemu_irx)[i], &hdd_vmc_infos, sizeof(hdd_vmc_infos_t));
- break;
- }
- }
- }
+ if (selectedCore == CORE_LOADER_NEUTRINO) {
+ if (!sbFindNeutrino(&neutrinoPath, gOPLPart)) {
+ guiWarning("Neutrino ELF not found, launching with core", 6);
+ selectedCore = CORE_LOADER_WOPL;
}
}
- if (gRememberLastPlayed) {
- configSetStr(configGetByType(CONFIG_LAST), "last_played", game->startup);
- configSave(CONFIG_LAST, 0);
- }
-
- char gid[5];
- configGetDiscIDBinary(configSet, gid);
+ if (gRememberLastPlayed)
+ wOPLLastSave(game->startup);
int dmaType = 0, dmaMode = 7, compatMode = 0;
- configGetInt(configSet, CONFIG_ITEM_COMPAT, &compatMode);
- configGetInt(configSet, CONFIG_ITEM_DMA, &dmaMode);
+ compatMode = pgcfg->compat;
+ dmaMode = (pgcfg->dma != 7) ? pgcfg->dma : 7;
if (dmaMode < 3)
dmaType = 0x20;
else {
@@ -1047,7 +999,7 @@ void hddLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
irx = &hdd_cdvdman_irx;
}
- sbPrepare(NULL, configSet, size_irx, irx, &i);
+ sbPrepare(NULL, pgcfg, size_irx, irx, &i);
#ifdef CHEAT
if ((result = sbLoadCheats(gHDDPrefix, game->startup)) < 0) {
if (gAutoLaunchGame == NULL) {
@@ -1061,6 +1013,14 @@ void hddLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
} else
LOG("Cheats error\n");
}
+
+ if ((result = sbLoadImage(gHDDPrefix, game->startup)) < 0) {
+ if (gAutoLaunchGame == NULL) {
+ guiWarning(_l(_STR_ERR_IMAGE_LOAD_FAILED), 10);
+ } else {
+ LOG("Image error\n");
+ }
+ }
#endif
settings = (struct cdvdman_settings_hdd *)((u8 *)irx + i);
@@ -1071,16 +1031,17 @@ void hddLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
// patch start_sector
settings->lba_start = game->start_sector;
- if (configGetStrCopy(configSet, CONFIG_ITEM_ALTSTARTUP, filename, sizeof(filename)) == 0)
- strcpy(filename, game->startup);
+ if (pgcfg->alt_startup[0]) {
+ strncpy(filename, pgcfg->alt_startup, sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+ } else {
+ strncpy(filename, game->startup, sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+ }
if (gPS2Logo)
EnablePS2Logo = CheckPS2Logo(0, game->start_sector + OPL_HDD_MODE_PS2LOGO_OFFSET);
- int coreLoader = 0;
- int isZSO = 0;
- configGetInt(configSet, CONFIG_ITEM_CORE_LOADER, &coreLoader);
-
// Check for ZSO to correctly adjust layer1 start
settings->common.layer1_start = 0; // cdvdman will read it from APA header
hddReadSectors(game->start_sector + OPL_HDD_MODE_PS2LOGO_OFFSET, 1, IOBuffer);
@@ -1096,23 +1057,131 @@ void hddLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
isZSO = 1;
}
- const char *neutrinoPath = NULL;
- if (coreLoader) {
- neutrinoPath = sbFileExists(NEUTRINO_PATH) ? NEUTRINO_PATH : (sbFileExists(NEUTRINO_ALT_PATH) ? NEUTRINO_ALT_PATH : NULL);
+ if (selectedCore == CORE_LOADER_NEUTRINO && isZSO) {
+ guiWarning("Neutrino does not support this file format, launching with core", 6);
+ selectedCore = CORE_LOADER_WOPL;
+ }
- if (isZSO) {
- guiWarning("Neutrino does not support this file format, launching with core", 6);
- coreLoader = 0;
- } else if (neutrinoPath == NULL) {
- guiWarning("Neutrino ELF not found, launching with core", 6);
- coreLoader = 0;
+ if (selectedCore == CORE_LOADER_WOPL) {
+ apa_sub_t parts[APA_MAXSUB + 1];
+ char vmc_name[2][32];
+ int part_valid = 0, nparts;
+ hdd_vmc_infos_t hdd_vmc_infos;
+ memset(&hdd_vmc_infos, 0, sizeof(hdd_vmc_infos_t));
+
+ strncpy(vmc_name[0], pgcfg->vmc1, sizeof(vmc_name[0]) - 1);
+ vmc_name[0][sizeof(vmc_name[0]) - 1] = '\0';
+
+ strncpy(vmc_name[1], pgcfg->vmc2, sizeof(vmc_name[1]) - 1);
+ vmc_name[1][sizeof(vmc_name[1]) - 1] = '\0';
+
+ if (vmc_name[0][0] || vmc_name[1][0]) {
+ nparts = hddGetPartitionInfo(gOPLPart, parts);
+ if (nparts > 0 && nparts <= 5) {
+ for (i = 0; i < nparts; i++) {
+ hdd_vmc_infos.parts[i].start = parts[i].start;
+ hdd_vmc_infos.parts[i].length = parts[i].length;
+ LOG("HDDSUPPORT hdd_vmc_infos.parts[%d].start : 0x%X\n", i, hdd_vmc_infos.parts[i].start);
+ LOG("HDDSUPPORT hdd_vmc_infos.parts[%d].length : 0x%X\n", i, hdd_vmc_infos.parts[i].length);
+ }
+ part_valid = 1;
+ }
}
+
+ if (part_valid) {
+ char vmc_path[256];
+ int vmc_id;
+ vmc_superblock_t vmc_superblock;
+ pfs_blockinfo_t blocks[11];
+
+ for (vmc_id = 0; vmc_id < 2; vmc_id++) {
+ int have_error = 0;
+
+ if (vmc_name[vmc_id][0]) {
+ have_error = 1;
+ hdd_vmc_infos.active = 0;
+ if (sysCheckVMC(gHDDPrefix, "/", vmc_name[vmc_id], 0, &vmc_superblock) > 0) {
+ hdd_vmc_infos.flags = vmc_superblock.mc_flag & 0xFF;
+ hdd_vmc_infos.flags |= 0x100;
+ hdd_vmc_infos.specs.page_size = vmc_superblock.page_size;
+ hdd_vmc_infos.specs.block_size = vmc_superblock.pages_per_block;
+ hdd_vmc_infos.specs.card_size = vmc_superblock.pages_per_cluster * vmc_superblock.clusters_per_card;
+
+ // Check vmc inode block chain (write operation can cause damage)
+ snprintf(vmc_path, sizeof(vmc_path), "%sVMC/%s.bin", gHDDPrefix, vmc_name[vmc_id]);
+ if ((nparts = hddGetFileBlockInfo(vmc_path, parts, blocks, 11)) > 0) {
+ have_error = 0;
+ hdd_vmc_infos.active = 1;
+ for (i = 0; i < nparts - 1; i++) {
+ hdd_vmc_infos.blocks[i].number = blocks[i + 1].number;
+ hdd_vmc_infos.blocks[i].subpart = blocks[i + 1].subpart;
+ hdd_vmc_infos.blocks[i].count = blocks[i + 1].count;
+ LOG("HDDSUPPORT hdd_vmc_infos.blocks[%d].number : 0x%X\n", i, hdd_vmc_infos.blocks[i].number);
+ LOG("HDDSUPPORT hdd_vmc_infos.blocks[%d].subpart : 0x%X\n", i, hdd_vmc_infos.blocks[i].subpart);
+ LOG("HDDSUPPORT hdd_vmc_infos.blocks[%d].count : 0x%X\n", i, hdd_vmc_infos.blocks[i].count);
+ }
+ } else { // else VMC file is too fragmented
+ LOG("HDDSUPPORT Block Chain NG\n");
+ have_error = 2;
+ }
+ }
+
+ if (have_error) {
+ if (gAutoLaunchGame == NULL) {
+ char error[256];
+ if (have_error == 2) // VMC file is fragmented
+ snprintf(error, sizeof(error), _l(_STR_ERR_VMC_FRAGMENTED_CONTINUE), vmc_name[vmc_id], (vmc_id + 1));
+ else
+ snprintf(error, sizeof(error), _l(_STR_ERR_VMC_CONTINUE), vmc_name[vmc_id], (vmc_id + 1));
+ if (!guiMsgBox(error, 1, NULL))
+ return;
+ } else
+ LOG("VMC error\n");
+ }
+
+ for (i = 0; i < size_hdd_mcemu_irx; i++) {
+ if (((u32 *)&hdd_mcemu_irx)[i] == (0xC0DEFAC0 + vmc_id)) {
+ if (hdd_vmc_infos.active)
+ size_mcemu_irx = size_hdd_mcemu_irx;
+ memcpy(&((u32 *)&hdd_mcemu_irx)[i], &hdd_vmc_infos, sizeof(hdd_vmc_infos_t));
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ char partitionName[APA_IDMAX + 1];
+ snprintf(partitionName, sizeof(partitionName), "%s", game->partition_name);
+
+ if (selectedCore == CORE_LOADER_NEUTRINO) {
+ sbCreateNeutrinoVMCPath(neutrinoVmc0, sizeof(neutrinoVmc0), gOPLPart, pgcfg->vmc1);
+ sbCreateNeutrinoVMCPath(neutrinoVmc1, sizeof(neutrinoVmc1), gOPLPart, pgcfg->vmc2);
+ }
+
+ if (!(selectedCore == CORE_LOADER_NEUTRINO && sbPathIsMC(neutrinoPath.elf)))
+ sbMMCESendGameId(game->startup);
+
+ int deinitException = NO_EXCEPTION;
+ int deinitMode = HDD_MODE;
+
+ if (selectedCore == CORE_LOADER_NEUTRINO) {
+ int elfDevice = -1;
+ int elfMode = sbGetPathModeAndDevice(neutrinoPath.elf, &elfDevice);
+
+ if (elfMode >= 0) {
+ deinitException = UNMOUNT_EXCEPTION;
+ deinitMode = elfMode;
+ }
+
+ LOG("NEUTRINO ELF MODE=%d DEVICE=%d\n", elfMode, elfDevice);
}
if (gAutoLaunchGame == NULL)
- deinit(NO_EXCEPTION, HDD_MODE); // CAREFUL: deinit will call hddCleanUp, so hddGames/game will be freed
+ deinit(deinitException, deinitMode); // CAREFUL: deinit will call hddCleanUp, so hddGames/game will be freed
else {
- miniDeinit(configSet);
+ miniDeinit();
free(gAutoLaunchGame);
gAutoLaunchGame = NULL;
@@ -1121,9 +1190,9 @@ void hddLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
fileXioDevctl("pfs:", PDIOC_CLOSEALL, NULL, 0, NULL, 0);
}
- if (coreLoader) {
- LOG("partition_name=[%s] name=[%s] startup=[%s]\n", game->partition_name, game->name, game->startup);
- sysLaunchNeutrino("apa", game->partition_name, compatMode, EnablePS2Logo, neutrinoPath);
+ if (selectedCore == CORE_LOADER_NEUTRINO) {
+ LOG("partition_name=[%s]\n", partitionName);
+ sysLaunchNeutrino("apa", partitionName, compatMode, EnablePS2Logo, neutrinoPath.elf, neutrinoPath.cwd, neutrinoVmc0, neutrinoVmc1);
return;
}
@@ -1136,22 +1205,59 @@ void hddLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
sysLaunchLoaderElf(filename, "HDD_MODE", size_irx, irx, size_mcemu_irx, hdd_mcemu_irx, EnablePS2Logo, compatMode);
}
-static config_set_t *hddGetConfig(item_list_t *itemList, int id)
+static void hddGetInfo(item_list_t *itemList, int id, game_info_t *gi)
+{
+ hdl_game_info_t *game = &hddGames.games[id];
+ char info_path[256];
+
+ snprintf(info_path, sizeof(info_path), "%sCFG/%s.info", gHDDPrefix, game->startup);
+
+ wOPLGameInfoLoad(info_path, gi);
+
+ // fallback..
+ if (!gi->title[0]) {
+ strncpy(gi->title, game->name, sizeof(gi->title) - 1);
+ gi->title[sizeof(gi->title) - 1] = '\0';
+ }
+
+ if (!gi->serial[0] && game->startup[0]) {
+ char *dst = gi->serial;
+ for (const char *s = game->startup; *s && (dst - gi->serial) < (int)sizeof(gi->serial) - 1; s++) {
+ if (*s == '_')
+ *dst++ = '-';
+ else if (*s != '.')
+ *dst++ = *s;
+ }
+ *dst = '\0';
+ }
+}
+
+static void hddGetPgCfg(item_list_t *itemList, int id, per_game_cfg_t *cfg)
{
- char path[256];
hdl_game_info_t *game = &hddGames.games[id];
+ char path[256];
snprintf(path, sizeof(path), "%sCFG/%s.cfg", gHDDPrefix, game->startup);
- config_set_t *config = configAlloc(0, NULL, path);
- configRead(config); // Does not matter if the config file exists or not.
- configSetStr(config, CONFIG_ITEM_NAME, game->name);
- configSetInt(config, CONFIG_ITEM_SIZE, game->total_size_in_kb >> 10);
- configSetStr(config, CONFIG_ITEM_FORMAT, "HDL");
- configSetStr(config, CONFIG_ITEM_MEDIA, game->disctype == SCECdPS2CD ? "CD" : "DVD");
- configSetStr(config, CONFIG_ITEM_STARTUP, game->startup);
+ wOPLPerGameLoad(path, cfg);
+
+ if (!cfg->format[0])
+ strcpy(cfg->format, "HDL");
+
+ if (!cfg->media[0])
+ strcpy(cfg->media, game->disctype == SCECdPS2CD ? "CD" : "DVD");
+
+ if (!cfg->size_mb)
+ cfg->size_mb = game->total_size_in_kb >> 10;
+}
+
+static int hddSavePgCfg(item_list_t *itemList, int id, const per_game_cfg_t *cfg)
+{
+ char path[256];
+ hdl_game_info_t *game = &hddGames.games[id];
+ snprintf(path, sizeof(path), "%sCFG/%s.cfg", gHDDPrefix, game->startup);
- return config;
+ return wOPLPerGameSave(path, cfg);
}
static int hddGetImage(item_list_t *itemList, char *folder, int isRelative, char *value, char *suffix, GSTEXTURE *resultTex, short psm)
@@ -1162,7 +1268,7 @@ static int hddGetImage(item_list_t *itemList, char *folder, int isRelative, char
else
snprintf(path, sizeof(path), "%s%s_%s", folder, value, suffix);
- return texDiscoverLoad(resultTex, path, -1, 0);
+ return texDiscoverLoad(resultTex, path, -1, RES_FILESYSTEM);
}
static int hddGetArchivedImage(item_list_t *itemList, char *folder, char *value, char *suffix, GSTEXTURE *resultTex, short psm)
@@ -1171,7 +1277,7 @@ static int hddGetArchivedImage(item_list_t *itemList, char *folder, char *value,
snprintf(path, sizeof(path), "%s_%s", value, suffix);
- return texDiscoverLoad(resultTex, path, -1, 1);
+ return texDiscoverLoad(resultTex, path, -1, RES_TAR_ART);
}
static int hddGetTextId(item_list_t *itemList)
@@ -1181,7 +1287,7 @@ static int hddGetTextId(item_list_t *itemList)
static int hddGetIconId(item_list_t *itemList)
{
- return CATEGORY_HDD_APA_ICON;
+ return HDD_ICON;
}
// This may be called, even if hddInit() was not.
@@ -1351,7 +1457,7 @@ static char *hddGetPrefix(item_list_t *itemList)
static item_list_t hddGameList = {
HDD_MODE, 0, 0, MODE_FLAG_COMPAT_DMA, MENU_MIN_INACTIVE_FRAMES, HDD_MODE_UPDATE_DELAY, NULL, NULL, &hddGetTextId, &hddGetPrefix, &hddInit, &hddNeedsUpdate, &hddUpdateGameList,
&hddGetGameCount, &hddGetGame, &hddGetGameName, &hddGetGameNameLength, &hddGetGameStartup, &hddDeleteGame, &hddRenameGame,
- &hddLaunchGame, &hddGetConfig, &hddGetImage, &hddGetArchivedImage, &hddCleanUp, &hddShutdown, &hddCheckVMC, &hddGetIconId};
+ &hddLaunchGame, &hddGetInfo, &hddGetPgCfg, &hddSavePgCfg, &hddGetImage, &hddGetArchivedImage, &hddCleanUp, &hddShutdown, &hddCheckVMC, &hddGetIconId};
int hddIsPresent()
{
@@ -1363,7 +1469,6 @@ int hddIsPresent()
void autoLaunchHDDGame(char *argv[])
{
char path[256];
- config_set_t *configSet;
miniInit(HDD_MODE);
@@ -1374,9 +1479,9 @@ void autoLaunchHDDGame(char *argv[])
gAutoLaunchGame->start_sector = strtoul(argv[2], NULL, 0);
snprintf(gOPLPart, sizeof(gOPLPart), "hdd0:%s", argv[3]);
+ per_game_cfg_t pgcfg;
snprintf(path, sizeof(path), "%sCFG/%s.cfg", gHDDPrefix, gAutoLaunchGame->startup);
- configSet = configAlloc(0, NULL, path);
- configRead(configSet);
+ wOPLPerGameLoad(path, &pgcfg);
- hddLaunchGame(NULL, -1, configSet);
+ hddLaunchGame(NULL, -1, &pgcfg);
}
\ No newline at end of file
diff --git a/src/initializer.c b/src/initializer.c
index 0d3ce877..81059895 100644
--- a/src/initializer.c
+++ b/src/initializer.c
@@ -12,6 +12,8 @@
#include "include/common.h"
#include "include/sound.h"
+#include "include/config_wopl.h"
+#include "include/config_migration.h" // DELETE_WITH_MIGRATION
#include
#ifdef PADEMU
@@ -21,6 +23,8 @@
extern unsigned int frameCounter;
+#define MC_DIR_CONFIG "mc?:" WOPL_CONFIG_NAME
+
static void initMenuForListSupport(opl_io_module_t *mod)
{
mod->menuItem.icon_id = mod->support->itemIconId(mod->support);
@@ -54,7 +58,6 @@ static void initMenuForListSupport(opl_io_module_t *mod)
guiDeferUpdate(mc);
}
-
void initSupport(item_list_t *itemList, int mode, int force_reinit)
{
opl_io_module_t *mod = &list_support[mode];
@@ -96,6 +99,7 @@ void initSupport(item_list_t *itemList, int mode, int force_reinit)
void initAllSupport(int force_reinit)
{
bdmEnumerateDevices();
+
initSupport(ethGetObject(0), ETH_MODE, force_reinit || (gNetworkStartup >= ERROR_ETH_SMB_CONN));
initSupport(hddGetObject(0), HDD_MODE, force_reinit);
initSupport(appGetObject(0), APP_MODE, force_reinit);
@@ -121,7 +125,7 @@ static void setDefaults(void)
gAutoLaunchDeviceData = NULL;
gOPLPart[0] = '\0';
gHDDPrefix = "pfs0:";
- gBaseMCDir = "mc?:wOPL";
+ gBaseMCDir = MC_DIR_CONFIG;
bdmCacheSize = 16;
hddCacheSize = 8;
@@ -190,7 +194,7 @@ static void setDefaults(void)
gHDDStartMode = START_MODE_DISABLED;
gETHStartMode = START_MODE_DISABLED;
gAPPStartMode = START_MODE_DISABLED;
- gFAVStartMode = START_MODE_MANUAL;
+ gFAVStartMode = START_MODE_DISABLED;
gMMCEStartMode = START_MODE_DISABLED;
gMMCESlot = 2; // Default to first Auto slot
@@ -201,7 +205,7 @@ static void setDefaults(void)
gMMCEAckWaitCycles = 5;
gMMCEUseAlarms = 1;
- gEnableUSB = 0;
+ gEnableUSB = 1;
gEnableILK = 0;
gEnableMX4SIO = 0;
gEnableBdmHDD = 0;
@@ -213,6 +217,11 @@ static void setDefaults(void)
gYOff = 0;
gOverscan = 0;
+ gCoverflowCount = 3;
+ gCoverflowCenterScale = 30;
+ gCoverflowAnimSpeed = 200;
+ gCoverflowDimCovers = 0;
+
setDefaultColors();
// Last Played Auto Start
@@ -229,21 +238,29 @@ void init(void)
padInit(0);
int padStatus = 0;
- configInit(NULL);
+ configInit(NULL); // DELETE_WITH_MIGRATION
rmInit();
lngInit();
thmInit();
guiInit();
+ guiShowBootStatus("Starting wOPL...");
+
+ guiShowBootStatus("Initializing I/O...");
ioInit();
+
+ guiShowBootStatus("Loading menus...");
menuInit();
+ guiShowBootStatus("Starting pads...");
startPads();
bdmInitSemaphore();
// handler for deffered menu updates
ioRegisterHandler(IO_MENU_UPDATE_DEFFERED, &menuDeferredUpdate);
+
+ guiShowBootStatus("Loading cache...");
cacheInit();
gSelectButton = (InitConsoleRegionData() == CONSOLE_REGION_JAPAN) ? KEY_CIRCLE : KEY_CROSS;
@@ -252,13 +269,14 @@ void init(void)
padStatus = startPads();
readPads();
if (!getKeyPressed(KEY_START)) {
- loadConfig(); // only try to restore config if emergency key is not being pressed
+ guiShowBootStatus("Loading settings...");
+ _loadConfig(); // only try to restore config if emergency key is not being pressed
} else {
+ guiShowBootStatus("Skipping settings...");
LOG("--- SKIPPING OPL CONFIG LOADING\n");
configApply(-1, -1, 0);
}
-
// queue deffered init of sound effects, which will take place after the preceding initialization steps within the queue are complete.
ioPutRequest(IO_CUSTOM_SIMPLEACTION, &deferredAudioInit);
}
@@ -285,11 +303,12 @@ void deinit(int exception, int modeSelected)
lngEnd();
thmEnd();
rmEnd();
- configEnd();
+ configEnd(); // DELETE_WITH_MIGRATION
}
void deferredInit(void)
{
+ guiSetBootStatusIfActive("Ready.");
// inform GUI main init part is over
struct gui_update_t *id = guiOpCreate(GUI_INIT_DONE);
@@ -306,6 +325,8 @@ void deferredAudioInit(void)
{
int ret;
+ guiSetBootStatusIfActive("Loading audio...");
+
audioInit();
ret = sfxInit(1);
if (ret < 0)
@@ -320,10 +341,8 @@ void deferredAudioInit(void)
void miniInit(int mode)
{
- int ret;
-
setDefaults();
- configInit(NULL);
+ configInit(NULL); // DELETE_WITH_MIGRATION
ioInit();
LOG_ENABLE();
@@ -343,43 +362,17 @@ void miniInit(int mode)
}
InitConsoleRegionData();
-
- ret = configReadMulti(CONFIG_ALL);
- if (CONFIG_ALL & CONFIG_OPL) {
- if (!(ret & CONFIG_OPL)) {
- if (mode == BDM_MODE)
- ret = configCheckLoadConfigBDM(CONFIG_ALL);
- else if (mode == HDD_MODE)
- ret = configCheckLoadConfigHDD(CONFIG_ALL);
- }
-
- if (ret & CONFIG_OPL) {
- config_set_t *configOPL = configGetByType(CONFIG_OPL);
-
- configGetInt(configOPL, CONFIG_OPL_PS2LOGO, &gPS2Logo);
- configGetStrCopy(configOPL, CONFIG_OPL_EXIT_PATH, gExitPath, sizeof(gExitPath));
- configGetInt(configOPL, CONFIG_OPL_HDD_SPINDOWN, &gHDDSpindown);
- if (mode == BDM_MODE) {
- configGetStrCopy(configOPL, CONFIG_OPL_BDM_PREFIX, gBDMPrefix, sizeof(gBDMPrefix));
- configGetInt(configOPL, CONFIG_OPL_BDM_CACHE, &bdmCacheSize);
- } else if (mode == HDD_MODE) {
- configGetInt(configOPL, CONFIG_OPL_HDD_CACHE, &hddCacheSize);
- } else if (mode == MMCE_MODE) {
- configGetStrCopy(configOPL, CONFIG_OPL_MMCE_PREFIX, gMMCEPrefix, sizeof(gMMCEPrefix));
- }
- }
- }
+ wOPLLoad(NULL, NULL);
}
-void miniDeinit(config_set_t *configSet)
+void miniDeinit()
{
ioBlockOps(1);
#ifdef PADEMU
ds34usb_reset();
ds34bt_reset();
#endif
- configFree(configSet);
ioEnd();
- configEnd();
+ configEnd(); // DELETE_WITH_MIGRATION
}
diff --git a/src/iosupport.c b/src/iosupport.c
index b0e10939..740f6e87 100644
--- a/src/iosupport.c
+++ b/src/iosupport.c
@@ -1,4 +1,3 @@
-
#include "include/common.h"
#include "include/ioman.h"
#include "include/gui.h"
@@ -32,9 +31,9 @@ void itemExecSelect(struct menu_item *curMenu)
if (support) {
if (support->enabled) {
if (curMenu->current) {
- config_set_t *configSet = menuLoadConfig();
+ per_game_cfg_t *pgcfg = menuLoadConfig();
cacheCancelPendingImageLoads();
- support->itemLaunch(support, curMenu->current->item.id, configSet);
+ support->itemLaunch(support, curMenu->current->item.id, pgcfg);
}
} else {
// If we're trying to enable BDM support we need to enable it for all BDM menu slots.
diff --git a/src/lang.c b/src/lang.c
index 2cf1ef89..75167090 100644
--- a/src/lang.c
+++ b/src/lang.c
@@ -165,8 +165,8 @@ int lngAddLanguages(char *path, const char *separator, int mode)
nLanguages += result;
lngRebuildLangNames();
- const char *temp;
- if (configGetStr(configGetByType(CONFIG_OPL), "language_text", &temp)) {
+ const char *temp = wOPLGetLanguageName();
+ if (temp) {
if (lngSetGuiValue(lngFindGuiID(temp)))
moduleUpdateMenu(mode, 0, 1);
}
@@ -200,13 +200,11 @@ int lngSetGuiValue(int langID)
{
if (langID != -1) {
if (guiLangID != langID) {
- bgmMute();
if (langID != 0) {
language_t *currLang = &languages[langID - 1];
if (lngLoadFromFile(currLang->filePath, currLang->name)) {
guiLangID = langID;
thmSetGuiValue(thmGetGuiValue(), 1);
- bgmUnMute();
return 1;
}
}
@@ -215,7 +213,6 @@ int lngSetGuiValue(int langID)
// lang switched back to internalEnglish, reload default font
fntLoadDefault(NULL);
thmSetGuiValue(thmGetGuiValue(), 1);
- bgmUnMute();
}
}
return 0;
diff --git a/src/lwnbd.c b/src/lwnbd.c
index 31025f23..7436dea2 100644
--- a/src/lwnbd.c
+++ b/src/lwnbd.c
@@ -1,7 +1,7 @@
// this need to be on top since some include miss including some headers
#include "include/common.h"
-#include "include/config.h"
+#include "include/config_wopl.h"
#include "include/extern_irx.h"
#include "include/gui.h" // guiExecDeferredOps
#include "include/ioman.h" // ioBlockOps
diff --git a/src/main.c b/src/main.c
index b8dc2e35..e0f649c6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -8,6 +8,8 @@
#include "include/common.h"
+#include "include/pathsupport.h"
+
#include "include/sound.h"
#include "include/tetris.h"
#include "include/xparam.h"
@@ -23,6 +25,7 @@ int main(int argc, char *argv[])
#endif
LOG_INIT();
+ pathSetLaunchPath(argc > 0 ? argv[0] : NULL);
PREINIT_LOG("OPL GUI start!\n");
ChangeThreadPriority(GetThreadId(), 31);
diff --git a/src/menusys.c b/src/menusys.c
index b9bbfd00..8312a381 100644
--- a/src/menusys.c
+++ b/src/menusys.c
@@ -18,6 +18,7 @@
#include "include/sound.h"
#include "include/system.h"
#include "include/themes.h"
+#include "include/config_wopl.h"
#include
#include
@@ -33,6 +34,7 @@ enum MENU_IDs {
MENU_NET_CONFIG,
MENU_START_NBD,
MENU_ABOUT,
+ MENU_CFG_MIGRATION, // DELETE_WITH_MIGRATION
MENU_SAVE_CHANGES,
MENU_EXIT,
MENU_POWER_OFF
@@ -61,7 +63,12 @@ static menu_list_t *selected_item;
static int actionStatus;
static int itemConfigId;
-static config_set_t *itemConfig;
+static item_list_t *itemConfigOwner;
+
+static game_info_t itemGameInfo;
+static per_game_cfg_t itemPgCfg;
+static render_ctx_t itemConfig;
+static render_ctx_t *itemConfigPtr;
static u8 parentalLockCheckEnabled = 1;
@@ -89,9 +96,75 @@ int gAutoRefresh;
extern unsigned char shouldAppsUpdate;
-
#define MENU_GENERAL_UPDATE_DELAY 60
+// DELETE_WITH_MIGRATION v
+int menuGetDevicePaths(char paths[][64], char labels[][80], int maxCount)
+{
+ int count = 0;
+ int i, j;
+
+ for (i = 0; i < MODE_COUNT && count < maxCount; i++) {
+ item_list_t *support = list_support[i].support;
+ if (!support || !support->enabled || !support->itemGetPrefix)
+ continue;
+
+ char *prefix = support->itemGetPrefix(support);
+ if (!prefix || !prefix[0])
+ continue;
+
+ char norm[64];
+ strncpy(norm, prefix, 62);
+ norm[62] = '\0';
+ int len = strlen(norm);
+ if (len > 0 && norm[len - 1] != '/')
+ norm[len] = '/', norm[len + 1] = '\0';
+
+ int dup = 0;
+ for (j = 0; j < count; j++) {
+ if (!strcmp(paths[j], norm)) {
+ dup = 1;
+ break;
+ }
+ }
+ if (dup)
+ continue;
+
+ strncpy(paths[count], norm, 63);
+ paths[count][63] = '\0';
+
+ int textId = support->itemTextId ? support->itemTextId(support) : -1;
+ char devLabel[32];
+
+ if (textId == _STR_USB_GAMES)
+ snprintf(devLabel, sizeof(devLabel), "USB%d", support->mode + 1);
+ else if (textId == _STR_BDM_GAMES)
+ snprintf(devLabel, sizeof(devLabel), "BDM%d", support->mode + 1);
+ else if (textId == _STR_ILINK_GAMES)
+ snprintf(devLabel, sizeof(devLabel), "iLink%d", support->mode + 1);
+ else if (textId == _STR_MX4SIO_GAMES)
+ snprintf(devLabel, sizeof(devLabel), "MX4SIO%d", support->mode + 1);
+ else if (textId == _STR_HDD_GAMES && support->mode <= BDM_MODE4)
+ snprintf(devLabel, sizeof(devLabel), "BDM HDD%d", support->mode + 1);
+ else if (textId == _STR_HDD_GAMES)
+ snprintf(devLabel, sizeof(devLabel), "HDD");
+ else if (textId == _STR_NET_GAMES)
+ snprintf(devLabel, sizeof(devLabel), "Network");
+ else if (textId >= 0) {
+ strncpy(devLabel, _l(textId), sizeof(devLabel) - 1);
+ devLabel[sizeof(devLabel) - 1] = '\0';
+ } else
+ snprintf(devLabel, sizeof(devLabel), "Device%d", support->mode + 1);
+
+ snprintf(labels[count], 80, "%s (%s)", norm, devLabel);
+ labels[count][79] = '\0';
+ count++;
+ }
+
+ return count;
+}
+// DELETE_WITH_MIGRATION ^
+
static void menuRenameGame(submenu_list_t **submenu)
{
if (!selected_item->item->current) {
@@ -191,13 +264,54 @@ static void menuDeleteGame(submenu_list_t **submenu)
guiMsgBox("NULL Support object. Please report", 0, NULL);
}
+static item_list_t *menuGetCurrentConfigOwner(void)
+{
+ item_list_t *list = selected_item->item->userdata;
+
+ if (list->mode == FAV_MODE && selected_item->item->current && selected_item->item->current->item.owner)
+ return (item_list_t *)selected_item->item->current->item.owner;
+
+ return list;
+}
+
static void _menuLoadConfig()
{
+ item_list_t *list;
+ int id;
+ game_info_t gi;
+ per_game_cfg_t pg;
+
WaitSema(menuSemaId);
- if (!itemConfig) {
- item_list_t *list = selected_item->item->userdata;
- itemConfig = list->itemGetConfig(list, itemConfigId);
+ if (itemConfigPtr) {
+ actionStatus = 0;
+ SignalSema(menuSemaId);
+ return;
+ }
+
+ list = itemConfigOwner ? itemConfigOwner : selected_item->item->userdata;
+ id = itemConfigId;
+ SignalSema(menuSemaId);
+
+ memset(&gi, 0, sizeof(gi));
+ memset(&pg, 0, sizeof(pg));
+
+ if (list->itemGetInfo)
+ list->itemGetInfo(list, id, &gi);
+
+ if (list->itemGetPgCfg)
+ list->itemGetPgCfg(list, id, &pg);
+
+ WaitSema(menuSemaId);
+ if (!itemConfigPtr && itemConfigId == id && itemConfigOwner == list) {
+ memcpy(&itemGameInfo, &gi, sizeof(itemGameInfo));
+ memcpy(&itemPgCfg, &pg, sizeof(itemPgCfg));
+
+ itemConfig.gi = &itemGameInfo;
+ itemConfig.pg = &itemPgCfg;
+ itemConfig.uid++;
+ itemConfigPtr = &itemConfig;
}
+
actionStatus = 0;
SignalSema(menuSemaId);
}
@@ -207,8 +321,11 @@ static void _menuSaveConfig()
int result;
WaitSema(menuSemaId);
- result = configWrite(itemConfig);
+ item_list_t *list = itemConfigOwner ? itemConfigOwner : menuGetCurrentConfigOwner();
+ result = list->itemSavePgCfg ? list->itemSavePgCfg(list, itemConfigId, &itemPgCfg) : 1;
itemConfigId = -1; // to invalidate cache and force reload
+ itemConfigOwner = NULL;
+ itemConfigPtr = NULL;
actionStatus = 0;
SignalSema(menuSemaId);
@@ -219,37 +336,43 @@ static void _menuSaveConfig()
static void _menuRequestConfig()
{
WaitSema(menuSemaId);
- if (selected_item->item->current != NULL && itemConfigId != selected_item->item->current->item.id) {
- if (itemConfig) {
- configFree(itemConfig);
- itemConfig = NULL;
- }
- item_list_t *list = selected_item->item->userdata;
- if (itemConfigId == -1 || guiInactiveFrames >= list->delay) {
- itemConfigId = selected_item->item->current->item.id;
+ if (selected_item->item->current != NULL) {
+ item_list_t *owner = menuGetCurrentConfigOwner();
+ int id = selected_item->item->current->item.id;
+
+ if (itemConfigId != id || itemConfigOwner != owner || actionStatus) {
+ if (itemConfigPtr)
+ itemConfigPtr = NULL;
+
+ itemConfigId = id;
+ itemConfigOwner = owner;
ioPutRequest(IO_CUSTOM_SIMPLEACTION, &_menuLoadConfig);
- }
- } else if (itemConfig)
- actionStatus = 0;
+ } else if (itemConfigPtr)
+ actionStatus = 0;
+ }
SignalSema(menuSemaId);
}
-config_set_t *menuLoadConfig()
+per_game_cfg_t *menuLoadConfig()
{
actionStatus = 1;
itemConfigId = -1;
+ itemConfigOwner = NULL;
+ itemConfigPtr = NULL;
guiHandleDeferedIO(&actionStatus, _l(_STR_LOADING_SETTINGS), IO_CUSTOM_SIMPLEACTION, &_menuRequestConfig);
- return itemConfig;
+ return &itemPgCfg;
}
// we don't want a pop up when transitioning to or refreshing Game Menu gui.
-config_set_t *gameMenuLoadConfig(struct UIItem *ui)
+per_game_cfg_t *gameMenuLoadConfig(struct UIItem *ui)
{
actionStatus = 1;
itemConfigId = -1;
+ itemConfigOwner = NULL;
+ itemConfigPtr = NULL;
guiGameHandleDeferedIO(&actionStatus, ui, IO_CUSTOM_SIMPLEACTION, &_menuRequestConfig);
- return itemConfig;
+ return &itemPgCfg;
}
void menuSaveConfig()
@@ -273,6 +396,7 @@ static void menuInitMainMenu(void)
submenuAppendItem(&mainMenu, -1, NULL, MENU_NET_CONFIG, _STR_NETCONFIG, NULL);
submenuAppendItem(&mainMenu, -1, NULL, MENU_START_NBD, _STR_STARTNBD, NULL);
submenuAppendItem(&mainMenu, -1, NULL, MENU_ABOUT, _STR_ABOUT, NULL);
+ submenuAppendItem(&mainMenu, -1, "Config Migration", MENU_CFG_MIGRATION, -1, NULL); // DELETE_WITH_MIGRATION
submenuAppendItem(&mainMenu, -1, NULL, MENU_SAVE_CHANGES, _STR_SAVE_CHANGES, NULL);
submenuAppendItem(&mainMenu, -1, NULL, MENU_EXIT, _STR_EXIT, NULL);
submenuAppendItem(&mainMenu, -1, NULL, MENU_POWER_OFF, _STR_POWEROFF, NULL);
@@ -335,7 +459,11 @@ void menuInit()
menu = NULL;
selected_item = NULL;
itemConfigId = -1;
- itemConfig = NULL;
+ itemConfigOwner = NULL;
+ memset(&itemGameInfo, 0, sizeof(itemGameInfo));
+ memset(&itemPgCfg, 0, sizeof(itemPgCfg));
+ memset(&itemConfig, 0, sizeof(itemConfig));
+ itemConfigPtr = NULL;
mainMenu = NULL;
mainMenuCurrent = NULL;
gameMenu = NULL;
@@ -374,11 +502,6 @@ void menuEnd()
submenuDestroy(&gameMenu);
submenuDestroy(&appMenu);
- if (itemConfig) {
- configFree(itemConfig);
- itemConfig = NULL;
- }
-
DeleteSema(menuSemaId);
DeleteSema(menuListSemaId);
menuListSemaId = -1;
@@ -679,7 +802,6 @@ static void menuNextH()
// If we found a valid menu transition to it.
if (next != NULL) {
selected_item = next;
- itemConfigId = -1;
sfxPlay(SFX_CURSOR);
}
}
@@ -692,7 +814,6 @@ static void menuPrevH()
if (prev != NULL) {
selected_item = prev;
- itemConfigId = -1;
sfxPlay(SFX_CURSOR);
}
}
@@ -702,7 +823,7 @@ static void menuFirstPage()
submenu_list_t *cur = selected_item->item->current;
if (cur) {
if (cur->prev) {
- sfxPlay(SFX_CURSOR);
+ sfxPlay(gTheme->coverflow ? SFX_COVERFLOW : SFX_CURSOR);
}
selected_item->item->current = selected_item->item->submenu;
@@ -715,7 +836,7 @@ static void menuLastPage()
submenu_list_t *cur = selected_item->item->current;
if (cur) {
if (cur->next) {
- sfxPlay(SFX_CURSOR);
+ sfxPlay(gTheme->coverflow ? SFX_COVERFLOW : SFX_CURSOR);
}
while (cur->next)
cur = cur->next; // go to end
@@ -736,10 +857,9 @@ static void menuNextV()
if (cur && cur->next) {
selected_item->item->current = cur->next;
- sfxPlay(SFX_CURSOR);
+ sfxPlay(gTheme->coverflow ? SFX_COVERFLOW : SFX_CURSOR);
- isAnimating = 1;
- animationDirection = 1;
+ thmTriggerCoverflowAnim(-1);
// if the current item is beyond the page start, move the page start one page down
cur = selected_item->item->pagestart;
@@ -752,6 +872,8 @@ static void menuNextV()
selected_item->item->pagestart = selected_item->item->current;
} else { // wrap to start
+ if (cur && (cur->next || cur->prev))
+ thmTriggerCoverflowAnim(-1);
menuFirstPage();
}
}
@@ -762,10 +884,9 @@ static void menuPrevV()
if (cur && cur->prev) {
selected_item->item->current = cur->prev;
- sfxPlay(SFX_CURSOR);
+ sfxPlay(gTheme->coverflow ? SFX_COVERFLOW : SFX_CURSOR);
- isAnimating = 1;
- animationDirection = -1;
+ thmTriggerCoverflowAnim(1);
// if the current item is on the page start, move the page start one page up
if (selected_item->item->pagestart == cur) {
@@ -774,6 +895,8 @@ static void menuPrevV()
selected_item->item->pagestart = selected_item->item->pagestart->prev;
}
} else { // wrap to end
+ if (cur && (cur->next || cur->prev))
+ thmTriggerCoverflowAnim(1);
menuLastPage();
}
}
@@ -784,7 +907,7 @@ static void menuNextPage()
if (cur && cur->next) {
int itms = ((items_list_t *)gTheme->itemsList->extended)->displayedItems + 1;
- sfxPlay(SFX_CURSOR);
+ sfxPlay(gTheme->coverflow ? SFX_COVERFLOW : SFX_CURSOR);
while (--itms && cur->next)
cur = cur->next;
@@ -802,7 +925,7 @@ static void menuPrevPage()
if (cur && cur->prev) {
int itms = ((items_list_t *)gTheme->itemsList->extended)->displayedItems + 1;
- sfxPlay(SFX_CURSOR);
+ sfxPlay(gTheme->coverflow ? SFX_COVERFLOW : SFX_CURSOR);
while (--itms && cur->prev)
cur = cur->prev;
@@ -862,7 +985,7 @@ void menuSetSelectedItem(menu_item_t *item)
void menuRenderMenu()
{
- if (guiDrawBGMain() == 0)
+ if (guiDrawBGSettings() == 0)
guiDrawBGPlasma();
if (!mainMenu)
@@ -909,36 +1032,30 @@ int menuSetParentalLockCheckState(int enabled)
int menuCheckParentalLock(void)
{
- const char *parentalLockPassword;
- char password[CONFIG_KEY_VALUE_LEN];
+ char password[sizeof(gParentalLockPassword)];
int result;
result = 0; // Default to unlocked.
- if (parentalLockCheckEnabled) {
- config_set_t *configOPL = configGetByType(CONFIG_OPL);
+ if (parentalLockCheckEnabled && gParentalLockPassword[0] != '\0') {
+ password[0] = '\0';
// Prompt for password, only if one was set.
- if (configGetStr(configOPL, CONFIG_OPL_PARENTAL_LOCK_PWD, &parentalLockPassword) && (parentalLockPassword[0] != '\0')) {
- password[0] = '\0';
- if (diaShowKeyb(password, CONFIG_KEY_VALUE_LEN, 1, _l(_STR_PARENLOCK_ENTER_PASSWORD_TITLE))) {
- if (strncmp(parentalLockPassword, password, CONFIG_KEY_VALUE_LEN) == 0) {
- result = 0;
- parentalLockCheckEnabled = 0; // Stop asking for the password.
- } else if (strncmp(PARENTAL_LOCK_MASTER_PASS, password, CONFIG_KEY_VALUE_LEN) == 0) {
- guiMsgBox(_l(_STR_PARENLOCK_DISABLE_WARNING), 0, NULL);
-
- configRemoveKey(configOPL, CONFIG_OPL_PARENTAL_LOCK_PWD);
- configSave(CONFIG_OPL, 1);
-
- result = 0;
- parentalLockCheckEnabled = 0; // Stop asking for the password.
- } else {
- guiMsgBox(_l(_STR_PARENLOCK_PASSWORD_INCORRECT), 0, NULL);
- result = EACCES;
- }
- } else // User aborted.
+ if (diaShowKeyb(password, sizeof(password), 1, _l(_STR_PARENLOCK_ENTER_PASSWORD_TITLE))) {
+ if (strncmp(gParentalLockPassword, password, sizeof(password)) == 0) {
+ result = 0;
+ parentalLockCheckEnabled = 0; // Stop asking for the password.
+ } else if (strncmp(PARENTAL_LOCK_MASTER_PASS, password, sizeof(password)) == 0) {
+ guiMsgBox(_l(_STR_PARENLOCK_DISABLE_WARNING), 0, NULL);
+ gParentalLockPassword[0] = '\0';
+ configSave(CONFIG_OPL, 0);
+ result = 0;
+ parentalLockCheckEnabled = 0; // Stop asking for the password.
+ } else {
+ guiMsgBox(_l(_STR_PARENLOCK_PASSWORD_INCORRECT), 0, NULL);
result = EACCES;
- }
+ }
+ } else // User aborted.
+ result = EACCES;
}
return result;
@@ -1001,14 +1118,16 @@ void menuHandleInputMenu()
handleLwnbdSrv();
} else if (id == MENU_ABOUT) {
guiShowAbout();
+ } else if (id == MENU_CFG_MIGRATION) { // DELETE_WITH_MIGRATION
+ guiShowCfgMigration(); // DELETE_WITH_MIGRATION
} else if (id == MENU_SAVE_CHANGES) {
if (menuCheckParentalLock() == 0) {
- guiGameSaveOSDLanguageGlobalConfig(configGetByType(CONFIG_GAME));
+ guiGameSaveOSDLanguageGlobalConfig();
#ifdef PADEMU
- guiGameSavePadEmuGlobalConfig(configGetByType(CONFIG_GAME));
- guiGameSavePadMacroGlobalConfig(configGetByType(CONFIG_GAME));
+ guiGameSavePadEmuGlobalConfig();
+ guiGameSavePadMacroGlobalConfig();
#endif
- configSave(CONFIG_OPL | CONFIG_NETWORK | CONFIG_GAME, 1);
+ configSave(CONFIG_ALL, 1);
menuSetParentalLockCheckState(1); // Re-enable parental lock check.
}
} else if (id == MENU_EXIT) {
@@ -1032,19 +1151,24 @@ void menuHandleInputMenu()
}
}
-static void menuRenderElements(theme_element_t *elem)
+static void menuRenderElements(theme_element_t *elem, int requestConfig)
{
- // selected_item can't be NULL here as we only allow to switch to "Main" rendering when there is at least one device activated
- _menuRequestConfig();
+ render_ctx_t *ctx;
+
+ if (requestConfig)
+ _menuRequestConfig();
WaitSema(menuSemaId);
+ ctx = requestConfig ? itemConfigPtr : NULL;
+
while (elem) {
if (elem->drawElem)
- elem->drawElem(selected_item, selected_item->item->current, itemConfig, elem);
+ elem->drawElem(selected_item, selected_item->item->current, ctx, elem);
elem = elem->next;
}
+
SignalSema(menuSemaId);
}
@@ -1053,13 +1177,13 @@ void menuRenderMain(void)
item_list_t *list = selected_item->item->userdata;
if (list->mode == APP_MODE) {
- menuRenderElements(gTheme->appsMainElems.first);
+ menuRenderElements(gTheme->appsMainElems.first, 0);
gTheme->itemsList = gTheme->appsItemsList;
} else if (list->mode == FAV_MODE) {
- menuRenderElements(gTheme->favsMainElems.first);
+ menuRenderElements(gTheme->favsMainElems.first, 0);
gTheme->itemsList = gTheme->favsItemsList;
} else {
- menuRenderElements(gTheme->mainElems.first);
+ menuRenderElements(gTheme->mainElems.first, 0);
gTheme->itemsList = gTheme->gamesItemsList;
}
}
@@ -1113,60 +1237,44 @@ void menuHandleInputMain()
void menuRenderInfo(void)
{
item_list_t *list = selected_item->item->userdata;
+ item_list_t *source = list;
- if (list->mode == APP_MODE) {
- menuRenderElements(gTheme->appsInfoElems.first);
+ if (list->mode == FAV_MODE && selected_item->item->current && selected_item->item->current->item.owner)
+ source = (item_list_t *)selected_item->item->current->item.owner;
+
+ if (list->mode == APP_MODE)
gTheme->itemsList = gTheme->appsItemsList;
- } else if (list->mode == FAV_MODE) {
- menuRenderElements(gTheme->favsInfoElems.first);
+ else if (list->mode == FAV_MODE)
gTheme->itemsList = gTheme->favsItemsList;
- } else {
- menuRenderElements(gTheme->infoElems.first);
+ else
gTheme->itemsList = gTheme->gamesItemsList;
- }
+
+ if (source->mode == APP_MODE)
+ menuRenderElements(gTheme->appsInfoElems.first, 1);
+ else if (list->mode == FAV_MODE)
+ menuRenderElements(gTheme->favsInfoElems.first, 1);
+ else
+ menuRenderElements(gTheme->infoElems.first, 1);
}
void menuHandleInputInfo()
{
- int navPrev;
- int navNext;
-
- if (gTheme->coverflow == NULL) {
- navPrev = KEY_UP;
- navNext = KEY_DOWN;
- } else {
- navPrev = KEY_LEFT;
- navNext = KEY_RIGHT;
- }
-
if (getKeyOn(KEY_CROSS)) {
if (gSelectButton == KEY_CIRCLE)
guiSwitchScreen(GUI_SCREEN_MAIN);
else
selected_item->item->execCross(selected_item->item);
- } else if (getKey(navPrev)) {
- menuPrevV();
- } else if (getKey(navNext)) {
- menuNextV();
} else if (getKeyOn(KEY_CIRCLE)) {
if (gSelectButton == KEY_CROSS)
guiSwitchScreen(GUI_SCREEN_MAIN);
else
selected_item->item->execCircle(selected_item->item);
- } else if (getKey(KEY_L1)) {
- menuPrevPage();
- } else if (getKey(KEY_R1)) {
- menuNextPage();
- } else if (getKeyOn(KEY_L2)) {
- menuFirstPage();
- } else if (getKeyOn(KEY_R2)) {
- menuLastPage();
}
}
void menuRenderGameMenu()
{
- if (guiDrawBGMain() == 0)
+ if (guiDrawBGSettings() == 0)
guiDrawBGPlasma();
if (!gameMenu)
@@ -1204,11 +1312,7 @@ void menuRenderGameMenu()
int cp = 0; // current position
// game title
- fntRenderString(gTheme->fonts[0], 320, 20, ALIGN_CENTER, 0, 0, selected_item->item->current->item.text, gTheme->selTextColor);
-
- // config source
- char *cfgSource = gameConfigSource();
- fntRenderString(gTheme->fonts[0], 320, 40, ALIGN_CENTER, 0, 0, cfgSource, gTheme->textColor);
+ fntRenderString(gTheme->fonts[0], 320, 40, ALIGN_CENTER, 0, 0, selected_item->item->current->item.text, gTheme->selTextColor);
// settings list
for (it = gameMenu; it; it = it->next, cp++) {
@@ -1255,7 +1359,7 @@ void menuHandleInputGameMenu()
sfxPlay(SFX_CONFIRM);
if (menuID == GAME_COMPAT_SETTINGS) {
- guiGameShowCompatConfig(selected_item->item->current->item.id, selected_item->item->userdata, itemConfig);
+ guiGameShowCompatConfig(selected_item->item->current->item.id, selected_item->item->userdata);
}
#ifdef CHEAT // TODO: Organize this
else if (menuID == GAME_CHEAT_SETTINGS) {
@@ -1278,16 +1382,15 @@ void menuHandleInputGameMenu()
} else if (menuID == GAME_OSD_LANGUAGE_SETTINGS) {
guiGameShowOSDLanguageConfig(0);
} else if (menuID == GAME_SAVE_CHANGES) {
- if (guiGameSaveConfig(itemConfig, selected_item->item->userdata))
- configSetInt(itemConfig, CONFIG_ITEM_CONFIGSOURCE, CONFIG_SOURCE_USER);
+ guiGameSaveConfig(&itemPgCfg, selected_item->item->userdata);
menuSaveConfig();
configSave(CONFIG_GAME, 0);
guiMsgBox(_l(_STR_GAME_SETTINGS_SAVED), 0, NULL);
guiGameLoadConfig(selected_item->item->userdata, gameMenuLoadConfig(NULL));
} else if (menuID == GAME_TEST_CHANGES) {
- guiGameTestSettings(selected_item->item->current->item.id, selected_item->item->userdata, itemConfig);
+ guiGameTestSettings(selected_item->item->current->item.id, selected_item->item->userdata, &itemPgCfg);
} else if (menuID == GAME_REMOVE_CHANGES) {
- if (guiGameShowRemoveSettings(itemConfig, configGetByType(CONFIG_GAME))) {
+ if (guiGameShowRemoveSettings(&itemPgCfg)) {
guiGameLoadConfig(selected_item->item->userdata, gameMenuLoadConfig(NULL));
}
} else if (menuID == GAME_RENAME_GAME) {
@@ -1306,7 +1409,7 @@ void menuHandleInputGameMenu()
void menuRenderAppMenu()
{
- if (guiDrawBGMain() == 0)
+ if (guiDrawBGSettings() == 0)
guiDrawBGPlasma();
if (!appMenu)
@@ -1422,6 +1525,7 @@ void menuClearGameList(opl_io_module_t *mdl)
mdl->menuItem.submenu = NULL;
mdl->menuItem.current = NULL;
mdl->menuItem.pagestart = NULL;
+ mdl->menuItem.last = NULL;
mdl->menuItem.remindLast = 0;
// unlock
@@ -1435,8 +1539,10 @@ static void updateMenuFromGameList(opl_io_module_t *mdl)
menuClearGameList(mdl);
const char *temp = NULL;
- if (gRememberLastPlayed)
- configGetStr(configGetByType(CONFIG_LAST), "last_played", &temp);
+ if (gRememberLastPlayed) {
+ wOPLLastLoad();
+ temp = wOPLLastGet();
+ }
// refresh device icon and text (for bdm)
mdl->menuItem.icon_id = mdl->support->itemIconId(mdl->support);
diff --git a/src/mmcesupport.c b/src/mmcesupport.c
index f3a2f51c..4be570ff 100644
--- a/src/mmcesupport.c
+++ b/src/mmcesupport.c
@@ -8,6 +8,7 @@
#include "include/ioman.h"
#include "include/system.h"
#include "include/extern_irx.h"
+#include "include/tar.h"
#ifdef CHEAT
#include "include/cheatman.h"
#endif
@@ -17,10 +18,10 @@
#include
#include
#include "include/bdmsupport.h"
-#include "include/art_tar.h"
#include
#include
#include "include/common.h"
+#include "include/config_wopl.h"
#include
#define NEWLIB_PORT_AWARE
#include // fileXioIoctl, fileXioDevctl
@@ -68,9 +69,20 @@ void mmceSetPrefix(void)
void mmceLoadModules(void)
{
+ static int mmceModulesLoaded = 0;
+
+ if (mmceModulesLoaded)
+ return;
+
LOG("MMCESUPPORT LoadModules\n");
+
+ guiSetBootStatusIfActive("Loading MMCE modules...");
+
LOG("[MMCEMAN]:\n");
+
sysLoadModuleBuffer(&mmceman_irx, size_mmceman_irx, 0, NULL);
+
+ mmceModulesLoaded = 1;
}
void mmceInit(item_list_t *itemList)
@@ -82,7 +94,7 @@ void mmceInit(item_list_t *itemList)
mmceGameCount = 0;
mmceGames = NULL;
- configGetInt(configGetByType(CONFIG_OPL), "usb_frames_delay", &mmceGameList.delay);
+ itemList->delay = gMMCEFramesDelay;
mmceGameList.updateDelay = -1; // No automatic updates
mmceLoadModules();
@@ -91,12 +103,58 @@ void mmceInit(item_list_t *itemList)
sprintf(mmcePrefix, "mmce0:/");
else if (gMMCESlot == 1)
sprintf(mmcePrefix, "mmce1:/");
- else if (gMMCESlot == 2)
+ else if (gMMCESlot == 2) {
+ guiSetBootStatusIfActive("Detecting MMCE slot...");
mmceDetectSlot();
+ }
mmceGameList.enabled = 1;
}
+void mmceSendGameIdToDevice(int device, const char *gameId)
+{
+ char dev[16];
+
+ if (!gameId || !gameId[0])
+ return;
+
+ if (device != 0 && device != 1) {
+ if (!strncmp(mmcePrefix, "mmce1:", 6))
+ device = 1;
+ else
+ device = 0;
+ }
+
+ snprintf(dev, sizeof(dev), "mmce%d:/", device);
+
+ if (fileXioDevctl(dev, 0x1, NULL, 0, NULL, 0) < 0)
+ return;
+
+ // small delay to let config write before sending game id (remember last game)
+ usleep(200000); // 200 ms
+
+ // send game id to mmce
+ if (fileXioDevctl(dev, 0x8, (void *)gameId, (strlen(gameId) + 1), NULL, 0) < 0)
+ return;
+
+ // wait for busy bit clear 2 seconds 100ms poll
+ for (int i = 0; i < 20; i++) {
+ usleep(100000);
+
+ int status = fileXioDevctl(dev, 0x2, NULL, 0, NULL, 0);
+ if (status < 0)
+ break;
+
+ if ((status & 1) == 0)
+ return; // ready
+ }
+}
+
+void mmceSendGameId(const char *gameId)
+{
+ mmceSendGameIdToDevice(-1, gameId);
+}
+
item_list_t *mmceGetObject(int initOnly)
{
if (initOnly && !mmceGameList.enabled)
@@ -108,7 +166,6 @@ static int mmceNeedsUpdate(item_list_t *itemList)
{
static unsigned char ThemesLoaded = 0;
static unsigned char LanguagesLoaded = 0;
- static unsigned char ArtArchivedLoaded = 0;
char path[256];
int result = 0;
@@ -143,6 +200,11 @@ static int mmceNeedsUpdate(item_list_t *itemList)
// update Themes
if (!ThemesLoaded) {
+ guiSetBootStatusIfActive("Loading MMCE themes...");
+
+ sprintf(path, "%sTHM/thm.tar", mmcePrefix);
+ tarLoadFile(TAR_KIND_THM, path);
+
sprintf(path, "%sTHM", mmcePrefix);
if (thmAddElements(path, "/", 1) > 0)
ThemesLoaded = 1;
@@ -150,17 +212,14 @@ static int mmceNeedsUpdate(item_list_t *itemList)
// update Languages
if (!LanguagesLoaded) {
+ guiSetBootStatusIfActive("Loading MMCE languages...");
+
sprintf(path, "%sLNG", mmcePrefix);
if (lngAddLanguages(path, "/", mmceGameList.mode) > 0)
LanguagesLoaded = 1;
}
- if (!ArtArchivedLoaded) {
- sprintf(path, "%sART/art.tar", mmcePrefix);
- loadTarFile(path);
- ArtArchivedLoaded = 1;
- }
-
+ guiSetBootStatusIfActive("Checking MMCE folders...");
sbCreateFolders(mmcePrefix, 1);
return result;
@@ -168,6 +227,8 @@ static int mmceNeedsUpdate(item_list_t *itemList)
static int mmceUpdateGameList(item_list_t *itemList)
{
+ guiSetBootStatusIfActive("Scanning MMCE games...");
+
sbReadList(&mmceGames, mmcePrefix, &mmceULSizePrev, &mmceGameCount);
return mmceGameCount;
}
@@ -209,7 +270,7 @@ static void mmceRenameGame(item_list_t *itemList, int id, char *newName)
mmceULSizePrev = -2;
}
-void mmceLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
+void mmceLaunchGame(item_list_t *itemList, int id, per_game_cfg_t *pgcfg)
{
int i, index, compatmask = 0;
int EnablePS2Logo = 0;
@@ -223,55 +284,85 @@ void mmceLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
u32 layer1_start, layer1_offset;
unsigned short int layer1_part;
- // No Autolaunch yet
- if (gAutoLaunchBDMGame == NULL)
+ /* No Autolaunch yet
+ if (gAutoLaunchMMCEGame == NULL)
game = &mmceGames[id];
else
- game = gAutoLaunchBDMGame;
+ game = gAutoLaunchMMCEGame;*/
+
+ game = &mmceGames[id];
+
+ int selectedCore = pgcfg->core_loader == CORE_LOADER_NEUTRINO ? CORE_LOADER_NEUTRINO : CORE_LOADER_WOPL;
+
+ neutrino_path_t neutrinoPath;
+ char neutrinoVmc0[256];
+ char neutrinoVmc1[256];
+
+ neutrinoPath.elf[0] = '\0';
+ neutrinoPath.cwd[0] = '\0';
+ neutrinoVmc0[0] = '\0';
+ neutrinoVmc1[0] = '\0';
+
+ if (selectedCore == CORE_LOADER_NEUTRINO) {
+ if (game->format == GAME_FORMAT_USBLD || !strcasecmp(game->extension, ".zso")) {
+ guiWarning("Neutrino does not support this file format, launching with core", 6);
+ selectedCore = CORE_LOADER_WOPL;
+ } else {
+ if (!sbFindNeutrino(&neutrinoPath, mmcePrefix)) {
+ guiWarning("Neutrino ELF not found, launching with core", 6);
+ selectedCore = CORE_LOADER_WOPL;
+ }
+ }
+ }
void *irx = &mmce_cdvdman_irx;
int irx_size = size_mmce_cdvdman_irx;
- compatmask = sbPrepare(game, configSet, irx_size, irx, &index);
+ compatmask = sbPrepare(game, pgcfg, irx_size, irx, &index);
settings = (struct cdvdman_settings_mmce *)((u8 *)irx + index);
if (settings == NULL)
return;
- char vmc_name[32];
- char vmc_path[256];
- int vmc_size_mb;
- int vmc_id, size_mcemu_irx = 0;
- int vmc_fd;
- mmce_vmc_infos_t mmce_vmc_infos;
- vmc_superblock_t vmc_superblock;
-
- for (vmc_id = 0; vmc_id < 2; vmc_id++) {
- memset(&mmce_vmc_infos, 0, sizeof(mmce_vmc_infos));
- configGetVMC(configSet, vmc_name, sizeof(vmc_name), vmc_id);
- if (vmc_name[0]) {
- vmc_size_mb = sysCheckVMC(mmcePrefix, "/", vmc_name, 0, &vmc_superblock);
- if (vmc_size_mb > 0) {
- mmce_vmc_infos.flags = vmc_superblock.mc_flag & 0xFF;
- mmce_vmc_infos.flags |= 0x100;
- mmce_vmc_infos.specs.page_size = vmc_superblock.page_size;
- mmce_vmc_infos.specs.block_size = vmc_superblock.pages_per_block;
- mmce_vmc_infos.specs.card_size = vmc_superblock.pages_per_cluster * vmc_superblock.clusters_per_card;
-
- sprintf(vmc_path, "%sVMC/%s.bin", mmcePrefix, vmc_name);
-
- vmc_fd = fileXioOpen(vmc_path, 0x3, 0666);
- if (vmc_fd >= 0) {
- mmce_vmc_infos.fd = fileXioIoctl2(vmc_fd, 0x80, NULL, 0, NULL, 0);
- mmce_vmc_infos.active = 1;
+ int size_mcemu_irx = 0;
+
+ if (selectedCore == CORE_LOADER_WOPL) {
+ char vmc_name[32];
+ char vmc_path[256];
+ int vmc_size_mb;
+ int vmc_id;
+ int vmc_fd;
+ mmce_vmc_infos_t mmce_vmc_infos;
+ vmc_superblock_t vmc_superblock;
+
+ for (vmc_id = 0; vmc_id < 2; vmc_id++) {
+ memset(&mmce_vmc_infos, 0, sizeof(mmce_vmc_infos));
+ strncpy(vmc_name, vmc_id == 0 ? pgcfg->vmc1 : pgcfg->vmc2, sizeof(vmc_name) - 1);
+ vmc_name[sizeof(vmc_name) - 1] = '\0';
+ if (vmc_name[0]) {
+ vmc_size_mb = sysCheckVMC(mmcePrefix, "/", vmc_name, 0, &vmc_superblock);
+ if (vmc_size_mb > 0) {
+ mmce_vmc_infos.flags = vmc_superblock.mc_flag & 0xFF;
+ mmce_vmc_infos.flags |= 0x100;
+ mmce_vmc_infos.specs.page_size = vmc_superblock.page_size;
+ mmce_vmc_infos.specs.block_size = vmc_superblock.pages_per_block;
+ mmce_vmc_infos.specs.card_size = vmc_superblock.pages_per_cluster * vmc_superblock.clusters_per_card;
+
+ sprintf(vmc_path, "%sVMC/%s.bin", mmcePrefix, vmc_name);
+
+ vmc_fd = fileXioOpen(vmc_path, 0x3, 0666);
+ if (vmc_fd >= 0) {
+ mmce_vmc_infos.fd = fileXioIoctl2(vmc_fd, 0x80, NULL, 0, NULL, 0);
+ mmce_vmc_infos.active = 1;
+ }
}
}
- }
- for (i = 0; i < size_mmce_mcemu_irx; i++) {
- if (((u32 *)&mmce_mcemu_irx)[i] == (0xC0DEFAC0 + vmc_id)) {
- if (mmce_vmc_infos.active)
- size_mcemu_irx = size_mmce_mcemu_irx;
- memcpy(&((u32 *)&mmce_mcemu_irx)[i], &mmce_vmc_infos, sizeof(mmce_vmc_infos_t));
- break;
+ for (i = 0; i < size_mmce_mcemu_irx; i++) {
+ if (((u32 *)&mmce_mcemu_irx)[i] == (0xC0DEFAC0 + vmc_id)) {
+ if (mmce_vmc_infos.active)
+ size_mcemu_irx = size_mmce_mcemu_irx;
+ memcpy(&((u32 *)&mmce_mcemu_irx)[i], &mmce_vmc_infos, sizeof(mmce_vmc_infos_t));
+ break;
+ }
}
}
}
@@ -321,16 +412,26 @@ void mmceLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
} else
LOG("Cheats error\n");
}
-#endif
- if (gRememberLastPlayed) {
- configSetStr(configGetByType(CONFIG_LAST), "last_played", game->startup);
- configSave(CONFIG_LAST, 0);
+ if ((result = sbLoadImage(mmcePrefix, game->startup)) < 0) {
+ if (gAutoLaunchBDMGame == NULL) {
+ guiWarning(_l(_STR_ERR_IMAGE_LOAD_FAILED), 10);
+ } else {
+ LOG("Image error\n");
+ }
}
+#endif
- if (configGetStrCopy(configSet, CONFIG_ITEM_ALTSTARTUP, filename, sizeof(filename)) == 0)
- strcpy(filename, game->startup);
+ if (gRememberLastPlayed)
+ wOPLLastSave(game->startup);
+ if (pgcfg->alt_startup[0]) {
+ strncpy(filename, pgcfg->alt_startup, sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+ } else {
+ strncpy(filename, game->startup, sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+ }
// MMCEDRV settings
if (gMMCESlot == 0)
@@ -356,49 +457,69 @@ void mmceLaunchGame(item_list_t *itemList, int id, config_set_t *configSet)
LOG("name: %s\n", game->name);
LOG("start: %s\n", game->startup);
- // Set gameid and poll card until ready
-#ifdef __DEBUG
- if (gMMCEEnableGameID) {
-#endif
-
- // Send GameID to MMCE
- fileXioDevctl(mmcePrefix, 0x8, game->startup, (strlen(game->startup) + 1), NULL, 0);
-
- for (int i = 0; i < 15; i++) {
- sleep(1);
-
- // Poll MMCE status until busy bit is clear
- if ((fileXioDevctl(mmcePrefix, 0x2, NULL, 0, NULL, 0) & 1) == 0) {
- LOG("Set MMCE GameID to: %s\n", game->startup);
- break;
- }
- }
-#ifdef __DEBUG
+ if (selectedCore == CORE_LOADER_NEUTRINO) {
+ sbCreateNeutrinoVMCPath(neutrinoVmc0, sizeof(neutrinoVmc0), mmcePrefix, pgcfg->vmc1);
+ sbCreateNeutrinoVMCPath(neutrinoVmc1, sizeof(neutrinoVmc1), mmcePrefix, pgcfg->vmc2);
}
-#endif
// mcReset();
// mcInit(MC_TYPE_XMC);
- if (gAutoLaunchBDMGame == NULL)
- deinit(NO_EXCEPTION, MMCE_MODE); // CAREFUL: deinit will call mmceCleanUp, so mmceGames/game will be freed
+ int gameDevice = -1;
+ int elfDevice = -1;
+ int elfMode = -1;
+
+ sbGetPathModeAndDevice(partname, &gameDevice);
+
+ if (selectedCore == CORE_LOADER_NEUTRINO)
+ elfMode = sbGetPathModeAndDevice(neutrinoPath.elf, &elfDevice);
+
+ if (!(selectedCore == CORE_LOADER_NEUTRINO && sbPathIsMC(neutrinoPath.elf)))
+ mmceSendGameIdToDevice(gameDevice, game->startup);
+
+ int deinitException = NO_EXCEPTION;
+ int deinitMode = MMCE_MODE;
+
+ if (selectedCore == CORE_LOADER_NEUTRINO && elfMode >= 0) {
+ deinitException = UNMOUNT_EXCEPTION;
+ deinitMode = elfMode;
+ }
+
+ deinit(deinitException, deinitMode); // CAREFUL: deinit will call mmceCleanUp, so mmceGames/game will be freed
/* No autolaunch yet
+ if (gAutoLaunchMMCEGame == NULL)
+ deinit(deinitException, deinitMode); // CAREFUL: deinit will call mmceCleanUp, so mmceGames/game will be freed
else {
- miniDeinit(configSet);
+ miniDeinit();
- free(gAutoLaunchBDMGame);
- gAutoLaunchBDMGame = NULL;
+ free(gAutoLaunchMMCEGame);
+ gAutoLaunchMMCEGame = NULL;
}*/
+ if (selectedCore == CORE_LOADER_NEUTRINO) {
+ sysLaunchNeutrino("mmce", partname, compatmask, EnablePS2Logo, neutrinoPath.elf, neutrinoPath.cwd, neutrinoVmc0, neutrinoVmc1);
+ return;
+ }
+
settings->common.zso_cache = 0;
sysLaunchLoaderElf(filename, "MMCE_MODE", irx_size, irx, size_mcemu_irx, mmce_mcemu_irx, EnablePS2Logo, compatmask);
}
-static config_set_t *mmceGetConfig(item_list_t *itemList, int id)
+static void mmceGetInfo(item_list_t *itemList, int id, game_info_t *gi)
+{
+ sbPopulateConfig(&mmceGames[id], mmcePrefix, "/", gi, NULL);
+}
+
+static void mmceGetPgCfg(item_list_t *itemList, int id, per_game_cfg_t *cfg)
+{
+ sbPopulateConfig(&mmceGames[id], mmcePrefix, "/", NULL, cfg);
+}
+
+static int mmceSavePgCfg(item_list_t *itemList, int id, const per_game_cfg_t *cfg)
{
- return sbPopulateConfig(&mmceGames[id], mmcePrefix, "/");
+ return sbSaveConfig(&mmceGames[id], mmcePrefix, "/", cfg);
}
static int mmceGetImage(item_list_t *itemList, char *folder, int isRelative, char *value, char *suffix, GSTEXTURE *resultTex, short psm)
@@ -409,7 +530,7 @@ static int mmceGetImage(item_list_t *itemList, char *folder, int isRelative, cha
else
snprintf(path, sizeof(path), "%s%s_%s", folder, value, suffix);
- return texDiscoverLoad(resultTex, path, -1, 0);
+ return texDiscoverLoad(resultTex, path, -1, RES_FILESYSTEM);
}
static int mmceGetArchivedImage(item_list_t *itemList, char *folder, char *value, char *suffix, GSTEXTURE *resultTex, short psm)
@@ -418,7 +539,7 @@ static int mmceGetArchivedImage(item_list_t *itemList, char *folder, char *value
snprintf(path, sizeof(path), "%s_%s", value, suffix);
- return texDiscoverLoad(resultTex, path, -1, 1);
+ return texDiscoverLoad(resultTex, path, -1, RES_TAR_ART);
}
static int mmceGetTextId(item_list_t *itemList)
@@ -430,7 +551,7 @@ static int mmceGetTextId(item_list_t *itemList)
static int mmceGetIconId(item_list_t *itemList)
{
- int mode = CATEGORY_MMCE_ICON;
+ int mode = MMCE_ICON;
return mode;
}
@@ -474,7 +595,7 @@ static char *mmceGetPrefix(item_list_t *itemList)
static item_list_t mmceGameList = {
MMCE_MODE, 2, 0, 0, MENU_MIN_INACTIVE_FRAMES, MMCE_MODE_UPDATE_DELAY, NULL, NULL, &mmceGetTextId, &mmceGetPrefix, &mmceInit, &mmceNeedsUpdate,
&mmceUpdateGameList, &mmceGetGameCount, &mmceGetGame, &mmceGetGameName, &mmceGetGameNameLength, &mmceGetGameStartup, &mmceDeleteGame, &mmceRenameGame,
- &mmceLaunchGame, &mmceGetConfig, &mmceGetImage, &mmceGetArchivedImage, &mmceCleanUp, &mmceShutdown, &mmceCheckVMC, &mmceGetIconId};
+ &mmceLaunchGame, &mmceGetInfo, &mmceGetPgCfg, &mmceSavePgCfg, &mmceGetImage, &mmceGetArchivedImage, &mmceCleanUp, &mmceShutdown, &mmceCheckVMC, &mmceGetIconId};
void mmceInitSemaphore()
{
diff --git a/src/module.c b/src/module.c
index 1e1eaf05..28349d20 100644
--- a/src/module.c
+++ b/src/module.c
@@ -33,20 +33,22 @@ void moduleUpdateMenuInternal(opl_io_module_t *mod, int themeChanged, int langCh
// refresh Hints
menuRemoveHints(&mod->menuItem);
- menuAddHint(&mod->menuItem, _STR_MENU, BUTTON_START_ICON);
+ menuAddHint(&mod->menuItem, _STR_MENU, START_ICON);
if (!mod->support->enabled)
- menuAddHint(&mod->menuItem, _STR_START_DEVICE, gSelectButton == KEY_CIRCLE ? BUTTON_SYMBOL_CIRCLE_ICON : BUTTON_SYMBOL_CROSS_ICON);
+ menuAddHint(&mod->menuItem, _STR_START_DEVICE, gSelectButton == KEY_CIRCLE ? CIRCLE_ICON : CROSS_ICON);
else {
- menuAddHint(&mod->menuItem, _STR_RUN, gSelectButton == KEY_CIRCLE ? BUTTON_SYMBOL_CIRCLE_ICON : BUTTON_SYMBOL_CROSS_ICON);
+ menuAddHint(&mod->menuItem, _STR_RUN, gSelectButton == KEY_CIRCLE ? CIRCLE_ICON : CROSS_ICON);
if (gTheme->infoElems.first)
- menuAddHint(&mod->menuItem, _STR_INFO, BUTTON_SYMBOL_SQUARE_ICON);
+ menuAddHint(&mod->menuItem, _STR_INFO, SQUARE_ICON);
if (!(mod->support->flags & MODE_FLAG_NO_COMPAT) || gEnableWrite)
- menuAddHint(&mod->menuItem, _STR_OPTIONS, BUTTON_SYMBOL_TRIANGLE_ICON);
+ menuAddHint(&mod->menuItem, _STR_OPTIONS, TRIANGLE_ICON);
- menuAddHint(&mod->menuItem, _STR_REFRESH, BUTTON_SELECT_ICON);
- menuAddHint(&mod->menuItem, _STR_FAV_HINT, BUTTON_STICK_R3_ICON);
+ menuAddHint(&mod->menuItem, _STR_REFRESH, SELECT_ICON);
+ if (gFAVStartMode) {
+ menuAddHint(&mod->menuItem, _STR_FAV_HINT, R3_ICON);
+ }
}
// refresh Cache
diff --git a/src/pad.c b/src/pad.c
index 20ead7b6..66043e29 100644
--- a/src/pad.c
+++ b/src/pad.c
@@ -40,9 +40,10 @@ struct pad_data_t
int actuators;
};
-/// current time in miliseconds (last update time)
+/// current CPU tick count from the last pad poll
static u32 curtime = 0;
static u32 time_since_last = 0;
+static int curtime_initialized = 0;
static unsigned short pad_count;
static struct pad_data_t pad_data[MAX_PADS];
@@ -202,6 +203,9 @@ static u32 readLeftJoy(struct pad_data_t *pad, u32 pdata)
u32 padData = pdata;
int xDeadzone, yDeadzone;
+ if (pdata & (PAD_SQUARE | PAD_CROSS | PAD_CIRCLE | PAD_TRIANGLE | PAD_START | PAD_SELECT))
+ return padData;
+
if ((pad->buttons.mode >> 4) == 0x07) {
switch (gXSensitivity) {
case 0:
@@ -215,6 +219,7 @@ static u32 readLeftJoy(struct pad_data_t *pad, u32 pdata)
break;
default:
xDeadzone = 80;
+ break;
}
switch (gYSensitivity) {
@@ -229,6 +234,7 @@ static u32 readLeftJoy(struct pad_data_t *pad, u32 pdata)
break;
default:
yDeadzone = 80;
+ break;
}
if (pad->buttons.ljoy_h < 127 - xDeadzone)
@@ -334,9 +340,13 @@ int readPads()
oldpaddata = paddata;
paddata = 0;
- // in ms.
- u32 newtime = cpu_ticks() / CLOCKS_PER_MILISEC;
- time_since_last = newtime - curtime;
+ u32 newtime = cpu_ticks();
+ if (!curtime_initialized) {
+ time_since_last = 0;
+ curtime_initialized = 1;
+ } else
+ time_since_last = (newtime - curtime) / CLOCKS_PER_MILISEC;
+
curtime = newtime;
int rslt = 0;
diff --git a/src/pathsupport.c b/src/pathsupport.c
new file mode 100644
index 00000000..a2c891b1
--- /dev/null
+++ b/src/pathsupport.c
@@ -0,0 +1,204 @@
+#include
+#include
+#include
+
+#include "include/pathsupport.h"
+
+static char launchPath[256];
+
+static void copy_str(char *dst, const char *src, size_t size)
+{
+ if (!size)
+ return;
+
+ if (!src)
+ src = "";
+
+ strncpy(dst, src, size - 1);
+ dst[size - 1] = '\0';
+}
+
+int pathParseDevicePrefix(const char *path, const char *device, int *index, const char **tail, int requireIndex)
+{
+ const char *suffix;
+ size_t len;
+ int value = -1;
+
+ if (!path || !device)
+ return 0;
+
+ len = strlen(device);
+
+ if (strncmp(path, device, len))
+ return 0;
+
+ suffix = path + len;
+
+ if (*suffix != ':') {
+ if (*suffix < '0' || *suffix > '9')
+ return 0;
+
+ value = 0;
+
+ while (*suffix >= '0' && *suffix <= '9') {
+ value = value * 10 + (*suffix - '0');
+ suffix++;
+ }
+ }
+
+ if (*suffix != ':')
+ return 0;
+
+ if (requireIndex && value < 0)
+ return 0;
+
+ if (index)
+ *index = value;
+
+ if (tail)
+ *tail = suffix + 1;
+
+ return 1;
+}
+
+int pathHasDevicePrefix(const char *path, const char *device)
+{
+ return pathParseDevicePrefix(path, device, NULL, NULL, 0);
+}
+
+void pathSetLaunchPath(const char *path)
+{
+ copy_str(launchPath, path, sizeof(launchPath));
+}
+
+const char *pathGetLaunchPath(void)
+{
+ return launchPath[0] ? launchPath : NULL;
+}
+
+int pathIsDevicePath(const char *path)
+{
+ static const char *devices[] = {
+ "mc",
+ "usb",
+ "mx4sio",
+ "ilink",
+ "ata",
+ "hdd",
+ "mmce",
+ NULL};
+
+ int i;
+
+ if (!path || !path[0])
+ return 0;
+
+ for (i = 0; devices[i] != NULL; i++) {
+ if (pathHasDevicePrefix(path, devices[i]))
+ return 1;
+ }
+
+ return 0;
+}
+
+int pathIsUsbMassCompatPath(const char *path)
+{
+ // wLE seems to use mass: instead of mass0:.. pathHasDevicePrefix() intentionally accepts both forms because the devN is optional
+ return pathHasDevicePrefix(path, "mass");
+}
+
+void pathNormaliseDir(char *dir, size_t dir_len)
+{
+ size_t len;
+
+ if (!dir_len)
+ return;
+
+ dir[dir_len - 1] = '\0';
+ len = strlen(dir);
+
+ if (len > 0 && dir[len - 1] != '/') {
+ if (len + 1 < dir_len) {
+ dir[len] = '/';
+ dir[len + 1] = '\0';
+ }
+ }
+}
+
+static int path_get_dirname(const char *path, char *dir_out, size_t dir_len, int allowUsbMassCompat)
+{
+ const char *slash;
+ const char *colon;
+
+ if (!path || !path[0] || !dir_len)
+ return 0;
+
+ if (!pathIsDevicePath(path) && (!allowUsbMassCompat || !pathIsUsbMassCompatPath(path)))
+ return 0;
+
+ slash = strrchr(path, '/');
+
+ if (!slash) {
+ colon = strchr(path, ':');
+
+ if (!colon)
+ return 0;
+
+ if ((size_t)(colon - path + 1) >= dir_len)
+ return 0;
+
+ memcpy(dir_out, path, colon - path + 1);
+ dir_out[colon - path + 1] = '\0';
+
+ pathNormaliseDir(dir_out, dir_len);
+ return 1;
+ }
+
+ if ((size_t)(slash - path + 1) >= dir_len)
+ return 0;
+
+ memcpy(dir_out, path, slash - path + 1);
+ dir_out[slash - path + 1] = '\0';
+
+ return 1;
+}
+
+int pathGetBootDir(char *dir_out, size_t dir_len)
+{
+ char pwd[128];
+
+ if (!dir_out || !dir_len)
+ return 0;
+
+ // argv0/cwd could still be massN: on old launch paths.. accept it only here
+ if (path_get_dirname(launchPath, dir_out, dir_len, 1))
+ return 1;
+
+ if (getcwd(pwd, sizeof(pwd)) == NULL)
+ return 0;
+
+ if (!pathIsDevicePath(pwd) && !pathIsUsbMassCompatPath(pwd))
+ return 0;
+
+ copy_str(dir_out, pwd, dir_len);
+ pathNormaliseDir(dir_out, dir_len);
+
+ return 1;
+}
+
+int pathJoin(char *out, size_t out_len, const char *dir, const char *name)
+{
+ int len;
+
+ if (!out || !out_len || !dir || !dir[0] || !name)
+ return 0;
+
+ len = snprintf(out, out_len, "%s%s", dir, name);
+
+ if (len < 0 || len >= out_len) {
+ out[0] = '\0';
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/src/renderman.c b/src/renderman.c
index 1c108c7b..7721b047 100644
--- a/src/renderman.c
+++ b/src/renderman.c
@@ -90,8 +90,7 @@ const u64 gColBlack = GS_SETREG_RGBA(0x00, 0x00, 0x00, 0x80); // Alpha 0x80 ->
const u64 gColDarker = GS_SETREG_RGBA(0x00, 0x00, 0x00, 0x60); // Alpha 0x60 -> transparent overlay color
const u64 gColFocus = GS_SETREG_RGBA(0xFF, 0xFF, 0xFF, 0x50); // Alpha 0x50 -> transparent overlay color
-const u64 gDefaultCol = GS_SETREG_RGBA(0x80, 0x80, 0x80, 0x80); // Special color for texture multiplication
-const u64 gDefaultBGCol = GS_SETREG_RGBA(0xBF, 0xBF, 0xBF, 0xBF); // Special color for texture multiplication
+const u64 gDefaultCol = GS_SETREG_RGBA(0x80, 0x80, 0x80, 0x80); // Special color for texture multiplication
const u64 gDefaultAlpha = GS_SETREG_ALPHA(0, 1, 0, 1, 0);
void rmInvalidateTexture(GSTEXTURE *txt)
@@ -357,52 +356,11 @@ void rmSetBackground(GSTEXTURE *txt)
0, 0,
0, 0,
gsGlobal->Width, gsGlobal->Height,
- txt->Width, txt->Height, order, gDefaultBGCol);
+ txt->Width, txt->Height, order, gDefaultCol);
order++;
}
-void rmDrawPixmap(GSTEXTURE *txt, int x, int y, short aligned, int w, int h, short scaled, u64 color)
-{
- rm_quad_t quad;
- rmSetupQuad(txt, x, y, aligned, w, h, scaled, color, &quad);
- rmDrawQuad(&quad);
-}
-
-void rmDrawOverlayPixmap(GSTEXTURE *overlay, int x, int y, short aligned, int w, int h, short scaled, u64 color,
- GSTEXTURE *inlay, int ulx, int uly, int urx, int ury, int blx, int bly, int brx, int bry)
-{
- rm_quad_t quad;
- rmSetupQuad(overlay, x, y, aligned, w, h, scaled, color, &quad);
- ulx = X_SCALE(ulx * iAspectWidth) >> 2;
- urx = X_SCALE(urx * iAspectWidth) >> 2;
- blx = X_SCALE(blx * iAspectWidth) >> 2;
- brx = X_SCALE(brx * iAspectWidth) >> 2;
- uly = Y_SCALE(uly);
- ury = Y_SCALE(ury);
- bly = Y_SCALE(bly);
- bry = Y_SCALE(bry);
-
- if ((inlay->PSM == GS_PSM_CT32) || (inlay->Clut && inlay->ClutPSM == GS_PSM_CT32))
- gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
- else
- gsGlobal->PrimAlphaEnable = GS_SETTING_OFF;
-
- gsKit_TexManager_bind(gsGlobal, inlay);
- gsKit_prim_quad_texture(gsGlobal, inlay,
- quad.ul.x + ulx + fRenderXOff, quad.ul.y + uly + fRenderYOff,
- 0.0f, 0.0f,
- quad.ul.x + urx + fRenderXOff, quad.ul.y + ury + fRenderYOff,
- inlay->Width, 0.0f,
- quad.ul.x + blx + fRenderXOff, quad.ul.y + bly + fRenderYOff,
- 0.0f, inlay->Height,
- quad.ul.x + brx + fRenderXOff, quad.ul.y + bry + fRenderYOff,
- inlay->Width, inlay->Height, order, gDefaultCol);
- order++;
-
- rmDrawQuad(&quad);
-}
-
-void rmDrawPixmapWithReflection(GSTEXTURE *txt, int x, int y, short aligned, int w, int h, short scaled, u64 color)
+void rmDrawPixmap(GSTEXTURE *txt, int x, int y, short aligned, int w, int h, short scaled, u64 color, int reflection)
{
rm_quad_t quad;
rmSetupQuad(txt, x, y, aligned, w, h, scaled, color, &quad);
@@ -424,6 +382,9 @@ void rmDrawPixmapWithReflection(GSTEXTURE *txt, int x, int y, short aligned, int
order, color);
order++;
+ if (!reflection)
+ return;
+
float rowHeight = 1.0f;
float totalHeight = quad.br.y - quad.ul.y;
float alphaStart = 0x20;
@@ -454,8 +415,8 @@ void rmDrawPixmapWithReflection(GSTEXTURE *txt, int x, int y, short aligned, int
}
}
-void rmDrawOverlayPixmapWithReflection(GSTEXTURE *overlay, int x, int y, short aligned, int w, int h, short scaled, u64 color,
- GSTEXTURE *inlay, int ulx, int uly, int urx, int ury, int blx, int bly, int brx, int bry)
+void rmDrawOverlayPixmap(GSTEXTURE *overlay, int x, int y, short aligned, int w, int h, short scaled, u64 color,
+ GSTEXTURE *inlay, int ulx, int uly, int urx, int ury, int blx, int bly, int brx, int bry, int reflection)
{
rm_quad_t quad;
rmSetupQuad(overlay, x, y, aligned, w, h, scaled, color, &quad);
@@ -483,11 +444,14 @@ void rmDrawOverlayPixmapWithReflection(GSTEXTURE *overlay, int x, int y, short a
quad.ul.x + blx + fRenderXOff, quad.ul.y + bly + fRenderYOff,
0.0f, inlay->Height,
quad.ul.x + brx + fRenderXOff, quad.ul.y + bry + fRenderYOff,
- inlay->Width, inlay->Height, order, gDefaultCol);
-
+ inlay->Width, inlay->Height, order, color);
order++;
+
rmDrawQuad(&quad);
+ if (!reflection)
+ return;
+
float rowHeight = 1.0f;
float totalHeight = quad.br.y - quad.ul.y;
float alphaStart = 0x20;
diff --git a/src/sound.c b/src/sound.c
index 24acfcef..97024003 100644
--- a/src/sound.c
+++ b/src/sound.c
@@ -35,6 +35,9 @@ extern unsigned int size_cancel_adp;
extern unsigned char confirm_adp[];
extern unsigned int size_confirm_adp;
+extern unsigned char coverflow_adp[];
+extern unsigned int size_coverflow_adp;
+
extern unsigned char cursor_adp[];
extern unsigned int size_cursor_adp;
@@ -63,6 +66,7 @@ static struct sfxEffect sfx_files[SFX_COUNT] = {
{"boot.adp"},
{"cancel.adp"},
{"confirm.adp"},
+ {"coverflow.adp"},
{"cursor.adp"},
{"message.adp"},
{"transition.adp"},
@@ -143,6 +147,8 @@ static void sfxInitDefaults(void)
sfx_files[SFX_CANCEL].size = size_cancel_adp;
sfx_files[SFX_CONFIRM].buffer = confirm_adp;
sfx_files[SFX_CONFIRM].size = size_confirm_adp;
+ sfx_files[SFX_COVERFLOW].buffer = coverflow_adp;
+ sfx_files[SFX_COVERFLOW].size = size_coverflow_adp;
sfx_files[SFX_CURSOR].buffer = cursor_adp;
sfx_files[SFX_CURSOR].size = size_cursor_adp;
sfx_files[SFX_MESSAGE].buffer = message_adp;
@@ -529,19 +535,6 @@ int isBgmPlaying(void)
return ret;
}
-// HACK: BGM stutters while perfroming certain tasks, mute during these operations and unmute once completed.
-void bgmMute(void)
-{
- if (audio_initialized)
- audsrv_set_volume(0);
-}
-
-void bgmUnMute(void)
-{
- if (audio_initialized)
- audsrv_set_volume(gBGMVolume);
-}
-
/*-- General Audio ------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------*/
diff --git a/src/supportbase.c b/src/supportbase.c
index f5aa7419..55924958 100644
--- a/src/supportbase.c
+++ b/src/supportbase.c
@@ -1,4 +1,3 @@
-
#include "include/common.h"
#include "include/lang.h"
#include "include/util.h"
@@ -15,8 +14,13 @@
#endif
#include "include/ps2cnf.h"
#include "include/gui.h"
+#include "include/guigame.h"
#include "include/bdmsupport.h"
#include "include/hddsupport.h"
+#include "include/mmcesupport.h"
+#include "include/tar.h"
+#include "include/config_wopl.h"
+#include "include/pathsupport.h"
#define NEWLIB_PORT_AWARE
#include // fileXioMount("iso:", ***), fileXioUmount("iso:")
@@ -44,6 +48,7 @@ extern int size_icon_del_icn;
struct game_list_t
{
base_game_info_t gameinfo;
+ char filename[128];
struct game_list_t *next;
};
@@ -63,11 +68,6 @@ int gPadMacroSettings;
int gPadEmuSource;
#endif
-int sbGetmcID(void)
-{
- return mcID;
-}
-
int sbGetFileSize(int fd)
{
int size = lseek(fd, 0, SEEK_END);
@@ -75,7 +75,12 @@ int sbGetFileSize(int fd)
return size;
}
-static int checkMC()
+int sbGetmcID(void)
+{
+ return mcID;
+}
+
+int sbCheckMC(void)
{
int mc0_is_ps2card, mc1_is_ps2card;
int mc0_has_folder, mc1_has_folder;
@@ -95,15 +100,19 @@ static int checkMC()
mc1_is_ps2card = 1;
}
+ char mc0_dir[64];
+ snprintf(mc0_dir, sizeof(mc0_dir), "mc0:%s/", WOPL_CONFIG_NAME);
mc0_has_folder = 0;
- DIR *mc0_opl_dir = opendir("mc0:wOPL/");
+ DIR *mc0_opl_dir = opendir(mc0_dir);
if (mc0_opl_dir != NULL) {
closedir(mc0_opl_dir);
mc0_has_folder = 1;
}
+ char mc1_dir[64];
+ snprintf(mc1_dir, sizeof(mc1_dir), "mc1:%s/", WOPL_CONFIG_NAME);
mc1_has_folder = 0;
- DIR *mc1_opl_dir = opendir("mc1:wOPL/");
+ DIR *mc1_opl_dir = opendir(mc1_dir);
if (mc1_opl_dir != NULL) {
closedir(mc1_opl_dir);
mc1_has_folder = 1;
@@ -132,64 +141,137 @@ static int checkMC()
return mcID;
}
-void sbCheckMCFolder(void)
+static int is_mc_config_path(const char *dir, int *slot)
{
- char path[32];
- int fd;
+ if (!dir || strncmp(dir, "mc", 2) != 0)
+ return 0;
- if (checkMC() < 0) {
- return;
+ if (dir[2] != '0' && dir[2] != '1')
+ return 0;
+
+ if (dir[3] != ':')
+ return 0;
+
+ if (slot)
+ *slot = dir[2] - '0';
+
+ return 1;
+}
+
+static int mc_slot_is_usable(int slot)
+{
+ char path[8];
+ DIR *root;
+
+ snprintf(path, sizeof(path), "mc%d:/", slot);
+
+ root = opendir(path);
+ if (root == NULL)
+ return 0;
+
+ closedir(root);
+ return 1;
+}
+
+static int normalise_mc_config_dir(char *out, int out_size, const char *dir)
+{
+ int len;
+
+ if (!out || out_size <= 0 || !dir)
+ return 0;
+
+ if (snprintf(out, out_size, "%s", dir) >= out_size)
+ return 0;
+
+ len = strlen(out);
+ if (len <= 0)
+ return 0;
+
+ if (out[len - 1] != '/') {
+ if (len + 1 >= out_size)
+ return 0;
+
+ out[len++] = '/';
+ out[len] = '\0';
}
- snprintf(path, sizeof(path), "mc%d:wOPL/", mcID & 1);
- mkdir(path, 0777);
+ return 1;
+}
- snprintf(path, sizeof(path), "mc%d:wOPL/list.icn", mcID & 1);
- fd = open(path, O_RDONLY);
- if (fd < 0) {
- fd = sbOpenFile(path, O_WRONLY | O_CREAT | O_TRUNC);
- if (fd >= 0) {
- write(fd, &icon_icn, size_icon_icn);
- close(fd);
- }
- } else
- close(fd);
+static int ensure_dir_exists(const char *dir)
+{
+ DIR *ldir;
- snprintf(path, sizeof(path), "mc%d:wOPL/copy.icn", mcID & 1);
- fd = open(path, O_RDONLY);
- if (fd < 0) {
- fd = sbOpenFile(path, O_WRONLY | O_CREAT | O_TRUNC);
- if (fd >= 0) {
- write(fd, &icon_cpy_icn, size_icon_cpy_icn);
- close(fd);
- }
- } else
- close(fd);
+ ldir = opendir(dir);
+ if (ldir != NULL) {
+ closedir(ldir);
+ return 1;
+ }
+
+ if (mkdir(dir, 0777) == 0)
+ return 1;
+
+ ldir = opendir(dir);
+ if (ldir != NULL) {
+ closedir(ldir);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void write_file_if_missing(const char *path, const void *data, int size)
+{
+ int fd;
- snprintf(path, sizeof(path), "mc%d:wOPL/del.icn", mcID & 1);
fd = open(path, O_RDONLY);
- if (fd < 0) {
- fd = sbOpenFile(path, O_WRONLY | O_CREAT | O_TRUNC);
- if (fd >= 0) {
- write(fd, &icon_del_icn, size_icon_del_icn);
- close(fd);
- }
- } else
+ if (fd >= 0) {
close(fd);
+ return;
+ }
- snprintf(path, sizeof(path), "mc%d:wOPL/icon.sys", mcID & 1);
- fd = open(path, O_RDONLY);
- if (fd < 0) {
- fd = sbOpenFile(path, O_WRONLY | O_CREAT | O_TRUNC);
- if (fd >= 0) {
- write(fd, &icon_sys, size_icon_sys);
- close(fd);
- }
- } else {
+ fd = sbOpenFile((char *)path, O_WRONLY | O_CREAT | O_TRUNC);
+ if (fd >= 0) {
+ write(fd, data, size);
close(fd);
}
}
+int sbEnsureMCConfigFolder(const char *dir)
+{
+ char folder[128];
+ char path[128];
+ int slot;
+
+ if (!is_mc_config_path(dir, &slot))
+ return 0;
+
+ if (!mc_slot_is_usable(slot))
+ return 0;
+
+ if (!normalise_mc_config_dir(folder, sizeof(folder), dir))
+ return 0;
+
+ if (!ensure_dir_exists(folder))
+ return 0;
+
+ mcID = '0' + slot;
+
+ snprintf(path, sizeof(path), "%slist.icn", folder);
+ write_file_if_missing(path, &icon_icn, size_icon_icn);
+
+ snprintf(path, sizeof(path), "%scopy.icn", folder);
+ write_file_if_missing(path, &icon_cpy_icn, size_icon_cpy_icn);
+
+ snprintf(path, sizeof(path), "%sdel.icn", folder);
+ write_file_if_missing(path, &icon_del_icn, size_icon_del_icn);
+
+ snprintf(path, sizeof(path), "%sicon.sys", folder);
+ write_file_if_missing(path, &icon_sys, size_icon_sys);
+
+ return 1;
+}
+
static int checkFile(char *path, int mode)
{
// check if it is mc
@@ -199,7 +281,7 @@ static int checkFile(char *path, int mode)
if (path[2] == 0x3F) {
// Use default detected card
- if (checkMC() >= 0)
+ if (sbCheckMC() >= 0)
path[2] = mcID;
else
return 0;
@@ -225,7 +307,6 @@ static int checkFile(char *path, int mode)
return 1;
}
-
int sbIsSameSize(const char *prefix, int prevSize)
{
int size = -1;
@@ -519,6 +600,11 @@ void sbCloseFileBuffer(file_buffer_t *fileBuffer)
free(fileBuffer);
}
+void sbMMCESendGameId(const char *gameId)
+{
+ mmceSendGameId(gameId);
+}
+
static int GetStartupExecName(const char *path, char *filename, int maxlength)
{
char ps2disc_boot[CNF_PATH_LEN_MAX] = "";
@@ -724,6 +810,41 @@ static int queryISOGameListCache(const struct game_cache_list *cache, base_game_
return ENOENT;
}
+static void applyISOSizes(char *path, struct game_list_t *glist)
+{
+ iox_dirent_t dirent;
+
+ int fd = fileXioDopen(path);
+ if (fd < 0)
+ return;
+
+ while (fileXioDread(fd, &dirent) > 0) {
+ if (dirent.name[0] == '\0')
+ continue;
+
+ u32 sizeMB = (((u64)dirent.stat.hisize << 32) | dirent.stat.size) >> 20;
+ struct game_list_t *g = glist;
+ while (g) {
+ if (g->gameinfo.format == GAME_FORMAT_USBLD) {
+ const char *fname = dirent.name + 12;
+
+ if (!strncmp(fname, g->gameinfo.startup, strlen(g->gameinfo.startup))) {
+ g->gameinfo.sizeMB += sizeMB;
+ break;
+ }
+ } else {
+ if (strcmp(g->filename, dirent.name) == 0) {
+ g->gameinfo.sizeMB = sizeMB;
+ break;
+ }
+ }
+ g = g->next;
+ }
+ }
+
+ fileXioDclose(fd);
+}
+
static int scanForISO(char *path, char type, struct game_list_t **glist)
{
int count = 0;
@@ -756,6 +877,8 @@ static int scanForISO(char *path, char type, struct game_list_t **glist)
next->next = *glist;
*glist = next;
base_game_info_t *game = &next->gameinfo;
+ strncpy(next->filename, dirent->d_name, sizeof(next->filename) - 1);
+ next->filename[sizeof(next->filename) - 1] = '\0';
memset(game, 0, sizeof(base_game_info_t));
if (format == GAME_FORMAT_OLD_ISO) {
@@ -807,6 +930,8 @@ static int scanForISO(char *path, char type, struct game_list_t **glist)
updateISOGameList(path, NULL, *glist, count);
}
+ applyISOSizes(path, *glist);
+
return count;
}
@@ -825,16 +950,19 @@ int sbReadList(base_game_info_t **list, const char *prefix, int *fsize, int *gam
struct game_list_t *dlist_head = NULL;
// count iso games in "cd" directory
+ guiSetBootStatusIfActive("Scanning CD images...");
snprintf(path, sizeof(path), "%sCD", prefix);
count = scanForISO(path, SCECdPS2CD, &dlist_head);
// count iso games in "dvd" directory
+ guiSetBootStatusIfActive("Scanning DVD images...");
snprintf(path, sizeof(path), "%sDVD", prefix);
if ((result = scanForISO(path, SCECdPS2DVD, &dlist_head)) >= 0) {
count = count < 0 ? result : count + result;
}
// count and process games in ul.cfg
+ guiSetBootStatusIfActive("Checking USBExtreme games...");
snprintf(path, sizeof(path), "%sul.cfg", prefix);
fd = sbOpenFile(path, O_RDONLY);
if (fd >= 0) {
@@ -867,23 +995,6 @@ int sbReadList(base_game_info_t **list, const char *prefix, int *fsize, int *gam
g->media = GameEntry.media;
g->format = GAME_FORMAT_USBLD;
g->sizeMB = 0;
-
- /* TODO: size calculation is very slow
- implmented some caching, or do not touch at all */
-
- // calculate total size for individual game
- /*int ulfd = 1;
- u8 part;
- unsigned int name_checksum = USBA_crc32(g->name);
-
- for (part = 0; part < g->parts && ulfd >= 0; part++) {
- snprintf(path, sizeof(path), "%sul.%08X.%s.%02x", prefix, name_checksum, g->startup, part);
- ulfd = openFile(path, O_RDONLY);
- if (ulfd >= 0) {
- g->sizeMB += (getFileSize(ulfd) >> 20);
- close(ulfd);
- }
- }*/
}
}
}
@@ -991,16 +1102,15 @@ int sbProbeISO9660(const char *path, base_game_info_t *game, u32 layer1_offset)
static const struct cdvdman_settings_common cdvdman_settings_common_sample = CDVDMAN_SETTINGS_DEFAULT_COMMON;
-int sbPrepare(base_game_info_t *game, config_set_t *configSet, int size_cdvdman, void **cdvdman_irx, int *patchindex)
+int sbPrepare(base_game_info_t *game, const per_game_cfg_t *pgcfg, int size_cdvdman, void **cdvdman_irx, int *patchindex)
{
int i;
struct cdvdman_settings_common *settings;
- int compatmask = 0;
- configGetInt(configSet, CONFIG_ITEM_COMPAT, &compatmask);
+ int compatmask = pgcfg ? pgcfg->compat : 0;
char gameid[5];
- configGetDiscIDBinary(configSet, gameid);
+ dnas_to_binary(pgcfg ? pgcfg->dnas : NULL, gameid, sizeof(gameid));
for (i = 0, settings = NULL; i < size_cdvdman; i += 4) {
if (!memcmp((void *)((u8 *)cdvdman_irx + i), &cdvdman_settings_common_sample, sizeof(cdvdman_settings_common_sample))) {
@@ -1042,58 +1152,55 @@ int sbPrepare(base_game_info_t *game, config_set_t *configSet, int size_cdvdman,
settings->fakemodule_flags = 0;
settings->fakemodule_flags |= FAKE_MODULE_FLAG_CDVDFSV;
settings->fakemodule_flags |= FAKE_MODULE_FLAG_CDVDSTM;
+
#ifdef GSM
- InitGSMConfig(configSet);
+ InitGSMConfig(pgcfg);
#endif
#ifdef CHEAT
- InitCheatsConfig(configSet);
+ int cheat_enable = gGlobalGameCfg.cheat_enable;
+ int cheat_mode = gGlobalGameCfg.cheat_mode;
+ int cheat_image = gGlobalGameCfg.cheat_enable_image;
+
+ if (pgcfg && pgcfg->cheat_source == SETTINGS_PERGAME) {
+ cheat_enable = pgcfg->cheat_enable;
+ cheat_mode = pgcfg->cheat_mode;
+ cheat_image = pgcfg->cheat_enable_image;
+ }
+
+ InitCheatsConfig(cheat_enable, cheat_mode, cheat_image);
#endif
- config_set_t *configGame = configGetByType(CONFIG_GAME);
#ifdef PADEMU
- gPadEmuSource = 0;
- gEnablePadEmu = 0;
- gPadEmuSettings = 0;
- gPadMacroSource = 0;
- gPadMacroSettings = 0;
-
- if (configGetInt(configSet, CONFIG_ITEM_PADEMUSOURCE, &gPadEmuSource)) {
- configGetInt(configSet, CONFIG_ITEM_ENABLEPADEMU, &gEnablePadEmu);
- configGetInt(configSet, CONFIG_ITEM_PADEMUSETTINGS, &gPadEmuSettings);
- } else {
- configGetInt(configGame, CONFIG_ITEM_ENABLEPADEMU, &gEnablePadEmu);
- configGetInt(configGame, CONFIG_ITEM_PADEMUSETTINGS, &gPadEmuSettings);
- }
+ gEnablePadEmu = gGlobalGameCfg.pademu_enable;
+ gPadEmuSettings = gGlobalGameCfg.pademu_settings;
+ gPadMacroSettings = gGlobalGameCfg.padmacro_settings;
- if (configGetInt(configSet, CONFIG_ITEM_PADMACROSOURCE, &gPadMacroSource)) {
- configGetInt(configSet, CONFIG_ITEM_PADMACROSETTINGS, &gPadMacroSettings);
- } else {
- configGetInt(configGame, CONFIG_ITEM_PADMACROSETTINGS, &gPadMacroSettings);
+ if (pgcfg && pgcfg->pademu_source == SETTINGS_PERGAME) {
+ gEnablePadEmu = pgcfg->pademu_enable;
+ gPadEmuSettings = pgcfg->pademu_settings;
}
- if (gEnablePadEmu) {
+ if (pgcfg && pgcfg->padmacro_source == SETTINGS_PERGAME)
+ gPadMacroSettings = pgcfg->padmacro_settings;
+
+ if (gEnablePadEmu)
settings->fakemodule_flags |= FAKE_MODULE_FLAG_USBD;
- }
#endif
- // sanitise the settings
+
gOSDLanguageSource = 0;
- gOSDLanguageEnable = 0;
- gOSDLanguageValue = 0;
- gOSDTVAspectRatio = 0;
- gOSDVideOutput = 0;
-
- if (configGetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_SOURCE, &gOSDLanguageSource)) {
- configGetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_ENABLE, &gOSDLanguageEnable);
- configGetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_LANGID, &gOSDLanguageValue);
- configGetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_TV_ASP, &gOSDTVAspectRatio);
- configGetInt(configSet, CONFIG_ITEM_OSD_SETTINGS_VMODE, &gOSDVideOutput);
- } else {
- configGetInt(configGame, CONFIG_ITEM_OSD_SETTINGS_ENABLE, &gOSDLanguageEnable);
- configGetInt(configGame, CONFIG_ITEM_OSD_SETTINGS_LANGID, &gOSDLanguageValue);
- configGetInt(configGame, CONFIG_ITEM_OSD_SETTINGS_TV_ASP, &gOSDTVAspectRatio);
- configGetInt(configGame, CONFIG_ITEM_OSD_SETTINGS_VMODE, &gOSDVideOutput);
+ gOSDLanguageEnable = gGlobalGameCfg.osd_enable;
+ gOSDLanguageValue = gGlobalGameCfg.osd_langid;
+ gOSDTVAspectRatio = gGlobalGameCfg.osd_tv_aspect;
+ gOSDVideOutput = gGlobalGameCfg.osd_vmode;
+
+ if (pgcfg && pgcfg->osd_source == SETTINGS_PERGAME) {
+ gOSDLanguageSource = SETTINGS_PERGAME;
+ gOSDLanguageEnable = pgcfg->osd_enable;
+ gOSDLanguageValue = pgcfg->osd_langid;
+ gOSDTVAspectRatio = pgcfg->osd_tv_aspect;
+ gOSDVideOutput = pgcfg->osd_vmode;
}
*patchindex = i;
@@ -1197,58 +1304,77 @@ void sbRename(base_game_info_t **list, const char *prefix, const char *sep, int
}
}
-config_set_t *sbPopulateConfig(base_game_info_t *game, const char *prefix, const char *sep)
+void sbPopulateConfig(base_game_info_t *game, const char *prefix, const char *sep, game_info_t *gi, per_game_cfg_t *pgcfg)
{
- char path[256];
+ char info_path[256], cfg_path[256];
struct stat st;
- snprintf(path, sizeof(path), "%sCFG%s%s.cfg", prefix, sep, game->startup);
- config_set_t *config = configAlloc(0, NULL, path);
- configRead(config); // Does not matter if the config file could be loaded or not.
-
- // Get game size if not already set
- if (game->sizeMB == 0) {
- char gamepath[256];
-
- if (game->format == GAME_FORMAT_ISO) {
- snprintf(gamepath, sizeof(gamepath), "%s%s%s%s%s%s", prefix, sep, game->media == SCECdPS2CD ? "CD" : "DVD", sep, game->name, game->extension);
-
- if (stat(gamepath, &st) == 0)
- game->sizeMB = st.st_size >> 20;
- } else if (game->format == GAME_FORMAT_OLD_ISO) {
- snprintf(gamepath, sizeof(gamepath), "%s%s%s%s%s.%s%s", prefix, sep, game->media == SCECdPS2CD ? "CD" : "DVD", sep, game->startup, game->name, game->extension);
-
- if (stat(gamepath, &st) == 0)
- game->sizeMB = st.st_size >> 20;
- } else if (game->format == GAME_FORMAT_USBLD) {
- // Calculate total size for multi-part USBLD games
- int part;
- unsigned int name_checksum = USBA_crc32(game->name);
-
- for (part = 0; part < game->parts; part++) {
- snprintf(gamepath, sizeof(gamepath), "%sul.%08X.%s.%02x", prefix, name_checksum, game->startup, part);
- if (stat(gamepath, &st) == 0)
- game->sizeMB += (st.st_size >> 20);
+ snprintf(info_path, sizeof(info_path), "%sCFG%s%s.info", prefix, sep, game->startup);
+ snprintf(cfg_path, sizeof(cfg_path), "%sCFG%s%s.cfg", prefix, sep, game->startup);
+
+ if (gi) {
+ wOPLGameInfoLoad(info_path, gi);
+
+ // fallback if not set.. fill for display.. don't save
+ if (!gi->title[0]) {
+ strncpy(gi->title, game->name, sizeof(gi->title) - 1);
+ gi->title[sizeof(gi->title) - 1] = '\0';
+ }
+
+ if (!gi->serial[0] && game->startup[0]) {
+ char *dst = gi->serial;
+ for (const char *s = game->startup; *s && (dst - gi->serial) < (int)sizeof(gi->serial) - 1; s++) {
+ if (*s == '_')
+ *dst++ = '-';
+ else if (*s != '.')
+ *dst++ = *s;
}
+ *dst = '\0';
}
}
- configSetStr(config, CONFIG_ITEM_NAME, game->name);
- configSetInt(config, CONFIG_ITEM_SIZE, game->sizeMB);
+ if (pgcfg) {
+ wOPLPerGameLoad(cfg_path, pgcfg);
+
+ // auto determine format/media/size if not set
+ if (!pgcfg->format[0]) {
+ if (game->format == GAME_FORMAT_USBLD)
+ strcpy(pgcfg->format, "UL");
+ else if (!strcasecmp(game->extension, ".zso"))
+ strcpy(pgcfg->format, "ZSO");
+ else
+ strcpy(pgcfg->format, "ISO");
+ }
- if (game->format != GAME_FORMAT_USBLD) {
- if (!strcmp(game->extension, ".iso"))
- configSetStr(config, CONFIG_ITEM_FORMAT, "ISO");
- else if (!strcmp(game->extension, ".zso"))
- configSetStr(config, CONFIG_ITEM_FORMAT, "ZSO");
- } else if (game->format == GAME_FORMAT_USBLD)
- configSetStr(config, CONFIG_ITEM_FORMAT, "UL");
+ if (!pgcfg->media[0])
+ strcpy(pgcfg->media, game->media == SCECdPS2CD ? "CD" : "DVD");
+
+ if (!pgcfg->size_mb) {
+ if (game->sizeMB > 0) {
+ pgcfg->size_mb = game->sizeMB;
+ } else {
+ char gamepath[256];
+ if (game->format == GAME_FORMAT_ISO) {
+ snprintf(gamepath, sizeof(gamepath), "%s%s%s%s%s%s", prefix, sep, game->media == SCECdPS2CD ? "CD" : "DVD", sep, game->name, game->extension);
+ if (stat(gamepath, &st) == 0)
+ pgcfg->size_mb = st.st_size >> 20;
+ } else if (game->format == GAME_FORMAT_OLD_ISO) {
+ snprintf(gamepath, sizeof(gamepath), "%s%s%s%s%s.%s%s", prefix, sep, game->media == SCECdPS2CD ? "CD" : "DVD", sep, game->startup, game->name, game->extension);
+ if (stat(gamepath, &st) == 0)
+ pgcfg->size_mb = st.st_size >> 20;
+ }
+ }
+ }
+ }
+}
- configSetStr(config, CONFIG_ITEM_MEDIA, game->media == SCECdPS2CD ? "CD" : "DVD");
+int sbSaveConfig(base_game_info_t *game, const char *prefix, const char *sep, const per_game_cfg_t *cfg)
+{
+ char path[256];
- configSetStr(config, CONFIG_ITEM_STARTUP, game->startup);
+ snprintf(path, sizeof(path), "%sCFG%s%s.cfg", prefix, sep, game->startup);
- return config;
+ return wOPLPerGameSave(path, cfg);
}
static void sbCreateFoldersFromList(const char *path, const char **folders)
@@ -1264,7 +1390,7 @@ static void sbCreateFoldersFromList(const char *path, const char **folders)
void sbCreateFolders(const char *path, int createDiscImgFolders)
{
- const char *basicFolders[] = {"CFG", "THM", "LNG", "ART", "VMC", "CHT", "APPS", NULL};
+ const char *basicFolders[] = {"CFG", "THM", "LNG", "ART", "VMC", "CHT", "APPS", "IMG", NULL};
const char *discImgFolders[] = {"CD", "DVD", NULL};
sbCreateFoldersFromList(path, basicFolders);
@@ -1280,6 +1406,29 @@ int sbLoadCheats(const char *path, const char *file)
int cheatMode = 0;
if (GetCheatsEnabled()) {
+ char filename[32];
+ snprintf(filename, sizeof(filename), "%s.cht", file);
+
+ TarEntryBase *entry = tarFind(TAR_KIND_CHT, filename);
+ if (entry) {
+ LOG("Loading Cheat File from TAR: %s\n", filename);
+
+ void *buf = tarGet(TAR_KIND_CHT, filename);
+ if (buf) {
+ cheatMode = load_cheats_buf((char *)buf, entry->rawSize);
+ free(buf);
+
+ if (cheatMode >= 0) {
+ LOG("Cheats found in TAR\n");
+ if ((gAutoLaunchGame == NULL) && (gAutoLaunchBDMGame == NULL) && (cheatMode == 1))
+ guiManageCheats();
+ return cheatMode;
+ }
+
+ LOG("Error: failed to parse cheats from TAR\n");
+ }
+ }
+
snprintf(cheatfile, sizeof(cheatfile), "%sCHT/%s.cht", path, file);
LOG("Loading Cheat File %s\n", cheatfile);
@@ -1294,4 +1443,260 @@ int sbLoadCheats(const char *path, const char *file)
return cheatMode;
}
-#endif
\ No newline at end of file
+
+int sbLoadImage(const char *path, const char *file)
+{
+ char imgfile[64];
+ int result = 0;
+
+ if (GetImageEnabled()) {
+ snprintf(imgfile, sizeof(imgfile), "%sIMG/%s.img", path, file);
+ LOG("Load image file %s\n", imgfile);
+ if (!LoadImage(imgfile)) {
+ LOG("Image load success\n");
+ } else {
+ result = -1;
+ }
+ }
+ return result;
+}
+#endif
+
+static int sbTryNeutrinoPath(neutrino_path_t *path, const char *cwd)
+{
+ int i;
+ int length;
+ const char *elfNames[] = {
+ "neutrino.elf",
+ "neutrino.ELF",
+ "NEUTRINO.elf",
+ "NEUTRINO.ELF",
+ };
+
+ if (!path || !cwd || !cwd[0])
+ return 0;
+
+ snprintf(path->cwd, sizeof(path->cwd), "%s", cwd);
+
+ length = strlen(path->cwd);
+ if (length <= 0 || length >= sizeof(path->cwd) - 1)
+ return 0;
+
+ if (path->cwd[length - 1] != '/') {
+ path->cwd[length++] = '/';
+ path->cwd[length] = '\0';
+ }
+
+ for (i = 0; i < (int)(sizeof(elfNames) / sizeof(elfNames[0])); i++) {
+ snprintf(path->elf, sizeof(path->elf), "%s%s", path->cwd, elfNames[i]);
+ LOG("SUPPORTBASE: Checking Neutrino ELF '%s'\n", path->elf);
+
+ if (sbFileExists(path->elf)) {
+ LOG("SUPPORTBASE: Neutrino ELF found at '%s'\n", path->elf);
+ return 1;
+ }
+ }
+
+ path->elf[0] = '\0';
+ path->cwd[0] = '\0';
+
+ return 0;
+}
+
+/*
+ * HDD path must not use pfs0: because hddLaunchGame() deinitializes/unmounts it before launching Neutrino
+ * For +wOPL: hdd0:+wOPL/neutrino/neutrino.elf
+ * For __common: hdd0:__common/wOPL/neutrino/neutrino.elf
+ */
+int sbFindNeutrino(neutrino_path_t *path, const char *preferredPrefix)
+{
+ int i;
+ char cwd[256];
+ const char *mcPaths[] = {
+ "mc0:NEUTRINO",
+ "mc1:NEUTRINO",
+ "mc0:/NEUTRINO",
+ "mc1:/NEUTRINO",
+ "mc0:neutrino",
+ "mc1:neutrino",
+ "mc0:/neutrino",
+ "mc1:/neutrino",
+ "mc0:/APPS/neutrino",
+ "mc1:/APPS/neutrino",
+ "mc0:/APPS/NEUTRINO",
+ "mc1:/APPS/NEUTRINO",
+ };
+
+ if (!path)
+ return 0;
+
+ path->elf[0] = '\0';
+ path->cwd[0] = '\0';
+
+ if (preferredPrefix && preferredPrefix[0]) {
+ if (!strncmp(preferredPrefix, "hdd0:", 5)) {
+ if (preferredPrefix[5] != '+') {
+ snprintf(cwd, sizeof(cwd), "%s/%s/neutrino", preferredPrefix, WOPL_CONFIG_NAME);
+ if (sbTryNeutrinoPath(path, cwd))
+ return 1;
+
+ snprintf(cwd, sizeof(cwd), "%s/%s/NEUTRINO", preferredPrefix, WOPL_CONFIG_NAME);
+ if (sbTryNeutrinoPath(path, cwd))
+ return 1;
+ }
+
+ snprintf(cwd, sizeof(cwd), "%s/neutrino", preferredPrefix);
+ if (sbTryNeutrinoPath(path, cwd))
+ return 1;
+
+ snprintf(cwd, sizeof(cwd), "%s/NEUTRINO", preferredPrefix);
+ if (sbTryNeutrinoPath(path, cwd))
+ return 1;
+ } else {
+ snprintf(cwd, sizeof(cwd), "%sneutrino", preferredPrefix);
+ if (sbTryNeutrinoPath(path, cwd))
+ return 1;
+
+ snprintf(cwd, sizeof(cwd), "%sNEUTRINO", preferredPrefix);
+ if (sbTryNeutrinoPath(path, cwd))
+ return 1;
+ }
+ }
+
+ for (i = 0; i < MAX_BDM_DEVICES; i++) {
+ snprintf(cwd, sizeof(cwd), "mass%d:/neutrino", i);
+ if (sbTryNeutrinoPath(path, cwd))
+ return 1;
+
+ snprintf(cwd, sizeof(cwd), "mass%d:neutrino", i);
+ if (sbTryNeutrinoPath(path, cwd))
+ return 1;
+
+ snprintf(cwd, sizeof(cwd), "mass%d:/NEUTRINO", i);
+ if (sbTryNeutrinoPath(path, cwd))
+ return 1;
+
+ snprintf(cwd, sizeof(cwd), "mass%d:NEUTRINO", i);
+ if (sbTryNeutrinoPath(path, cwd))
+ return 1;
+ }
+
+ for (i = 0; i < 2; i++) {
+ snprintf(cwd, sizeof(cwd), "mmce%d:/neutrino", i);
+ if (sbTryNeutrinoPath(path, cwd))
+ return 1;
+
+ snprintf(cwd, sizeof(cwd), "mmce%d:neutrino", i);
+ if (sbTryNeutrinoPath(path, cwd))
+ return 1;
+
+ snprintf(cwd, sizeof(cwd), "mmce%d:/NEUTRINO", i);
+ if (sbTryNeutrinoPath(path, cwd))
+ return 1;
+
+ snprintf(cwd, sizeof(cwd), "mmce%d:NEUTRINO", i);
+ if (sbTryNeutrinoPath(path, cwd))
+ return 1;
+ }
+
+ for (i = 0; i < (int)(sizeof(mcPaths) / sizeof(mcPaths[0])); i++) {
+ if (sbTryNeutrinoPath(path, mcPaths[i]))
+ return 1;
+ }
+
+ return 0;
+}
+
+void sbCreateNeutrinoVMCPath(char *path, int length, const char *prefix, const char *vmc)
+{
+ if (!path || length <= 0)
+ return;
+
+ path[0] = '\0';
+
+ if (!prefix || !prefix[0] || !vmc || !vmc[0])
+ return;
+
+ if (!strncmp(prefix, "hdd0:", 5)) {
+ if (prefix[5] != '+')
+ snprintf(path, length, "%s/%s/VMC/%s.bin", prefix, WOPL_CONFIG_NAME, vmc);
+ else
+ snprintf(path, length, "%s/VMC/%s.bin", prefix, vmc);
+ } else
+ snprintf(path, length, "%sVMC/%s.bin", prefix, vmc);
+}
+
+int sbGetPathModeAndDevice(const char *path, int *device)
+{
+ const char *blkdevnameend;
+ const char *prefixend;
+ int i, blkdevnamelen, prefixlen;
+ int dev;
+ item_list_t *listSupport;
+
+ if (device)
+ *device = -1;
+
+ if (!path || !path[0])
+ return -1;
+
+ if (!strncmp(path, "hdd0:", 5) || !strncmp(path, "pfs0:", 5))
+ return HDD_MODE;
+
+ if (pathParseDevicePrefix(path, "mass", &dev, NULL, 0)) {
+ if (dev < 0)
+ dev = 0;
+
+ if (dev >= MAX_BDM_DEVICES)
+ return -1;
+
+ if (device)
+ *device = dev;
+
+ return BDM_MODE + dev;
+ }
+
+ if (pathParseDevicePrefix(path, "mmce", &dev, NULL, 0)) {
+ if (dev < 0)
+ dev = 0;
+
+ if (device)
+ *device = dev;
+
+ return MMCE_MODE;
+ }
+
+ blkdevnameend = strchr(path, ':');
+ if (blkdevnameend == NULL)
+ return -1;
+
+ blkdevnamelen = (int)(blkdevnameend - path);
+
+ for (i = 0; i < MODE_COUNT; i++) {
+ listSupport = list_support[i].support;
+ if ((listSupport != NULL) && (listSupport->itemGetPrefix != NULL)) {
+ char *prefix = listSupport->itemGetPrefix(listSupport);
+ if (prefix != NULL) {
+ prefixend = strchr(prefix, ':');
+ if (prefixend != NULL) {
+ prefixlen = (int)(prefixend - prefix);
+
+ if (blkdevnamelen == prefixlen && strncmp(path, prefix, blkdevnamelen) == 0)
+ return listSupport->mode;
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+int sbGetPathMode(const char *path)
+{
+ return sbGetPathModeAndDevice(path, NULL);
+}
+
+int sbPathIsMC(const char *path)
+{
+ return path && (!strncmp(path, "mc0:", 4) || !strncmp(path, "mc1:", 4));
+}
diff --git a/src/system.c b/src/system.c
index ee503bdc..8f8cdf1d 100644
--- a/src/system.c
+++ b/src/system.c
@@ -66,6 +66,9 @@ typedef struct
extern unsigned char eecore_elf[];
extern unsigned int size_eecore_elf;
+extern unsigned char neutrino_loader_elf[];
+extern unsigned int size_neutrino_loader_elf;
+
extern unsigned char IOPRP_img[];
extern unsigned int size_IOPRP_img;
@@ -204,6 +207,26 @@ void sysShutdownDev9(void)
}
}
+#ifdef PADEMU
+void sysInitPadEmu(void)
+{
+ if (gEnablePadEmu) {
+ int ds3pads = 1; // only one pad enabled
+
+ ds34usb_deinit();
+ ds34bt_deinit();
+
+ LOG("[DS34_USB]:\n");
+ sysLoadModuleBuffer(&ds34usb_irx, size_ds34usb_irx, 4, (char *)&ds3pads);
+ LOG("[DS34_BT]:\n");
+ sysLoadModuleBuffer(&ds34bt_irx, size_ds34bt_irx, 4, (char *)&ds3pads);
+
+ ds34usb_init();
+ ds34bt_init();
+ }
+}
+#endif
+
void sysReset()
{
#ifdef PADEMU
@@ -273,8 +296,7 @@ void sysReset()
LOG("[POWEROFF]:\n");
sysLoadModuleBuffer(&poweroff_irx, size_poweroff_irx, 0, NULL);
- LOG("[USBD]:\n");
- sysLoadModuleBuffer(&usbd_irx, size_usbd_irx, 0, NULL);
+ bdmLoadModules();
LOG("[ISOFS]:\n");
sysLoadModuleBuffer(&isofs_irx, size_isofs_irx, 0, NULL);
@@ -288,18 +310,7 @@ void sysReset()
sysLoadModuleBuffer(&audsrv_irx, size_audsrv_irx, 0, NULL);
#ifdef PADEMU
- int ds3pads = 1; // only one pad enabled
-
- ds34usb_deinit();
- ds34bt_deinit();
-
- LOG("[DS34_USB]:\n");
- sysLoadModuleBuffer(&ds34usb_irx, size_ds34usb_irx, 4, (char *)&ds3pads);
- LOG("[DS34_BT]:\n");
- sysLoadModuleBuffer(&ds34bt_irx, size_ds34bt_irx, 4, (char *)&ds3pads);
-
- ds34usb_init();
- ds34bt_init();
+ sysInitPadEmu();
#endif
fileXioInit();
@@ -959,6 +970,8 @@ void sysLaunchLoaderElf(const char *filename, const char *mode_str, int size_cdv
config->gCheatList = GetCheatsList();
} else
config->gCheatList = NULL;
+
+ config->gImage = GetImageEnabled() ? (unsigned *)GetImage() : 0;
#endif
sprintf(config->g_ps2_ip, "%u.%u.%u.%u", local_ip_address[0], local_ip_address[1], local_ip_address[2], local_ip_address[3]);
@@ -969,7 +982,7 @@ void sysLaunchLoaderElf(const char *filename, const char *mode_str, int size_cdv
// GSM now.
config->EnableGSMOp = GetGSMEnabled();
if (config->EnableGSMOp) {
- PrepareGSM(NULL, &gsm_config);
+ PrepareGSM(NULL, &gsm_config, gGSMVMode, gGSMXOffset, gGSMYOffset, gGSMFIELDFix);
config->GsmConfig.interlace = gsm_config.interlace;
config->GsmConfig.mode = gsm_config.mode;
config->GsmConfig.ffmd = gsm_config.ffmd;
@@ -1149,22 +1162,23 @@ int sysCheckVMC(const char *prefix, const char *sep, char *name, int createSize,
#include
-static const char *getDeviceName(const char *driver)
+static const char *getDeviceName(const char *device)
{
- if (!strncmp(driver, "usb", 3))
- return "usb";
- else if (!strncmp(driver, "sd", 2))
- return "ilink";
- else if (!strncmp(driver, "sdc", 3))
+ if (!device || !device[0])
+ return "unsupported";
+
+ if (!strncmp(device, "mx4sio", 6) || !strncmp(device, "sdc", 3))
return "mx4sio";
- else if (!strncmp(driver, "ata", 3))
+ else if (!strncmp(device, "ilink", 5) || !strcmp(device, "sd") || !strcmp(device, "sd:"))
+ return "ilink";
+ else if (!strncmp(device, "usb", 3))
+ return "usb";
+ else if (!strncmp(device, "ata", 3))
return "ata";
- else if (!strncmp(driver, "apa", 3))
+ else if (!strncmp(device, "apa", 3))
return "apa";
-#ifdef UDPBD
- else if (!strncmp(driver, "udp", 3))
- return "udpbd";
-#endif
+ else if (!strncmp(device, "mmce", 4))
+ return "mmce";
else
return "unsupported";
}
@@ -1194,21 +1208,79 @@ static int convertCompatmaskToModes(int compatmask)
return atoi(result);
}
-void sysLaunchNeutrino(const char *driver, const char *path, int compatmask, int EnablePS2Logo, const char *neutrinoPath)
+static int LoadNeutrinoELF(const char *filename, int argc, char *argv[])
+{
+ u8 *boot_elf;
+ elf_header_t *eh;
+ elf_pheader_t *eph;
+ void *pdata;
+ char *execArgv[16];
+ int i;
+
+ if (!filename || !filename[0])
+ return -1;
+
+ if (argc + 2 > (int)(sizeof(execArgv) / sizeof(execArgv[0]))) {
+ LOG("NEUTRINO ERROR: too many args\n");
+ return -1;
+ }
+
+ execArgv[0] = "";
+ execArgv[1] = (char *)filename;
+
+ for (i = 0; i < argc; i++)
+ execArgv[i + 2] = argv[i];
+
+ boot_elf = (u8 *)&neutrino_loader_elf;
+ eh = (elf_header_t *)boot_elf;
+ if (*(u32 *)boot_elf != ELF_MAGIC) {
+ LOG("NEUTRINO ERROR: bad loader stub ELF\n");
+ return -1;
+ }
+ eph = (elf_pheader_t *)(boot_elf + eh->phoff);
+
+ memset((void *)0x00084000, 0, 0x00100000 - 0x00084000);
+
+ for (i = 0; i < eh->phnum; i++) {
+ if (eph[i].type != ELF_PT_LOAD)
+ continue;
+ pdata = (void *)(boot_elf + eph[i].offset);
+ memcpy(eph[i].vaddr, pdata, eph[i].filesz);
+ if (eph[i].memsz > eph[i].filesz)
+ memset((u8 *)eph[i].vaddr + eph[i].filesz, 0, eph[i].memsz - eph[i].filesz);
+ }
+
+ LOG("NEUTRINO loader handoff ELF=%s argc=%d\n", filename, argc + 2);
+ for (i = 0; i < argc + 2; i++)
+ LOG("execArgv[%d]=%s\n", i, execArgv[i]);
+
+ SifExitRpc();
+ FlushCache(0);
+ FlushCache(2);
+ return ExecPS2((void *)eh->entry, NULL, argc + 2, execArgv);
+}
+
+void sysLaunchNeutrino(const char *driver, const char *path, int compatmask, int EnablePS2Logo, const char *neutrinoPath, const char *neutrinoCwd, const char *vmc0, const char *vmc1)
{
char device[64];
+ char bsdfs[64];
char filePath[256];
char compatModes[64];
- char *argv[6];
+ char cwd[256 + 5];
+ char mc0[256 + 5];
+ char mc1[256 + 5];
+ char *argv[12];
int argc = 0;
const char *deviceName = getDeviceName(driver);
- if (!strncmp(deviceName, "apa", 3)) {
+ int isHDL = !strcmp(deviceName, "apa");
+
+ if (isHDL) {
snprintf(device, sizeof(device), "-bsd=ata");
argv[argc++] = device;
- snprintf(device, sizeof(device), "-bsdfs=hdl");
- argv[argc++] = device;
+ snprintf(bsdfs, sizeof(bsdfs), "-bsdfs=hdl");
+ argv[argc++] = bsdfs;
snprintf(filePath, sizeof(filePath), "-dvd=hdl:%s", path);
argv[argc++] = filePath;
@@ -1220,17 +1292,42 @@ void sysLaunchNeutrino(const char *driver, const char *path, int compatmask, int
argv[argc++] = filePath;
}
+ if (neutrinoCwd && neutrinoCwd[0]) {
+ snprintf(cwd, sizeof(cwd), "-cwd=%s", neutrinoCwd);
+ argv[argc++] = cwd;
+ }
+
+ if (vmc0 && vmc0[0]) {
+ snprintf(mc0, sizeof(mc0), "-mc0=%s", vmc0);
+ argv[argc++] = mc0;
+ }
+
+ if (vmc1 && vmc1[0]) {
+ snprintf(mc1, sizeof(mc1), "-mc1=%s", vmc1);
+ argv[argc++] = mc1;
+ }
+
snprintf(compatModes, sizeof(compatModes), "-gc=%d", convertCompatmaskToModes(compatmask));
argv[argc++] = compatModes;
- LOG("COMPAT MODE ARG=%s\n", compatModes);
- LOG("FILE PATH=%s\n", filePath);
-
if (gEnableDebug)
argv[argc++] = "-dbc";
- if (EnablePS2Logo)
+ if (!isHDL)
+ argv[argc++] = "-qb";
+ else if (EnablePS2Logo)
argv[argc++] = "-logo";
- LoadELFFromFileWithPartition(neutrinoPath, "", argc, argv);
+ LOG("NEUTRINO ELF=%s\n", neutrinoPath);
+ LOG("NEUTRINO CWD=%s\n", neutrinoCwd ? neutrinoCwd : "");
+ LOG("VMC0=%s\n", vmc0 ? vmc0 : "");
+ LOG("VMC1=%s\n", vmc1 ? vmc1 : "");
+ LOG("COMPAT MODE ARG=%s\n", compatModes);
+ LOG("FILE PATH=%s\n", filePath);
+
+ LOG("Launching Neutrino: argc=%d\n", argc);
+ for (int i = 0; i < argc; i++)
+ LOG("argv[%d]=%s\n", i, argv[i]);
+
+ LoadNeutrinoELF(neutrinoPath, argc, argv);
}
diff --git a/src/tar.c b/src/tar.c
new file mode 100644
index 00000000..845da248
--- /dev/null
+++ b/src/tar.c
@@ -0,0 +1,552 @@
+#include
+#include
+#include
+#include
+#include
+
+#include "include/tar.h"
+
+typedef struct
+{
+ const char *prefix;
+ const char *mountRoot;
+} TarDevice;
+
+typedef struct
+{
+ const char *relPath;
+ const char *cacheName;
+ u32 entrySize;
+} TarKindInfo;
+
+static const TarDevice gDevices[] = {
+ {"mmce0:", "mmce0:/"},
+ {"mmce1:", "mmce1:/"},
+ {"usb0:", "usb0:/"},
+ {"usb1:", "usb1:/"},
+ {"hdd0:", "hdd0:/"},
+ {"hdd1:", "hdd1:/"},
+ {"ata0:", "ata0:/"},
+ {"ata1:", "ata1:/"},
+ {"pfs0:", "pfs0:/"},
+ {"smb0:", "smb0:/"},
+ {NULL, NULL}};
+
+static const TarKindInfo gTarInfo[TAR_KIND_MAX] = {
+ {"ART/art.tar", "art_cache.bin", sizeof(TarEntryArt)},
+ {"CFG/cfg.tar", "cfg_cache.bin", sizeof(TarEntryCfg)},
+ {"CHT/cht.tar", "cht_cache.bin", sizeof(TarEntryCht)},
+ {"THM/thm.tar", "thm_cache.bin", sizeof(TarEntryThm)}};
+
+static void *s_index[TAR_KIND_MAX] = {NULL, NULL, NULL, NULL};
+static u32 s_count[TAR_KIND_MAX] = {0, 0, 0, 0};
+static u32 s_cap[TAR_KIND_MAX] = {0, 0, 0, 0};
+static const TarDevice *s_dev[TAR_KIND_MAX] = {NULL, NULL, NULL, NULL};
+static int s_inactive[TAR_KIND_MAX] = {0, 0, 0, 0};
+
+static const unsigned char s_zeroBlock[TAR_BLOCK_SIZE] __attribute__((aligned(64))) = {0};
+
+static u64 parseOctal(const char *s, int len)
+{
+ u64 val = 0;
+ for (int i = 0; i < len; i++) {
+ char c = s[i];
+ if (c < '0' || c > '7')
+ break;
+ val = (val << 3) + (u64)(c - '0');
+ }
+ return val;
+}
+
+static int isZeroBlock(const unsigned char header[TAR_BLOCK_SIZE])
+{
+ return memcmp(header, s_zeroBlock, TAR_BLOCK_SIZE) == 0;
+}
+
+static void tarCloseInternal(TarKind kind)
+{
+ if (s_index[kind]) {
+ free(s_index[kind]);
+ s_index[kind] = NULL;
+ }
+ s_count[kind] = 0;
+ s_cap[kind] = 0;
+ s_dev[kind] = NULL;
+}
+
+static int buildTarPath(const TarDevice *dev, TarKind kind, char *out, int outSize)
+{
+ int len = snprintf(out, outSize, "%s%s", dev->mountRoot, gTarInfo[kind].relPath);
+
+ return (len > 0 && len < outSize) ? 0 : -1;
+}
+
+static int tarReadCache(const char *cachePath, const char *tarPath, TarKind kind)
+{
+ int fd = open(cachePath, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ struct stat stCache;
+ if (fstat(fd, &stCache) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ int fileSize = stCache.st_size;
+ if (fileSize < (int)sizeof(TarCacheHeader)) {
+ close(fd);
+ return -1;
+ }
+
+ void *buf = memalign(64, fileSize);
+ if (!buf) {
+ close(fd);
+ return -1;
+ }
+
+ if (read(fd, buf, fileSize) != fileSize) {
+ free(buf);
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ TarCacheHeader *hdr = (TarCacheHeader *)buf;
+
+ if (memcmp(hdr->magic, ARC_MAGIC, 4) != 0 || hdr->version != ARC_VERSION) {
+ free(buf);
+ return -1;
+ }
+
+ struct stat stTar;
+ if (stat(tarPath, &stTar) < 0 || (u64)stTar.st_size != hdr->tarSize) {
+ free(buf);
+ return -1;
+ }
+
+ u32 entrySize = gTarInfo[kind].entrySize;
+ int expected = sizeof(TarCacheHeader) + hdr->entryCount * entrySize;
+ if (fileSize < expected) {
+ free(buf);
+ return -1;
+ }
+
+ void *entries = malloc(hdr->entryCount * entrySize);
+ if (!entries) {
+ free(buf);
+ return -1;
+ }
+
+ memcpy(entries, (unsigned char *)buf + sizeof(TarCacheHeader), hdr->entryCount * entrySize);
+
+ s_index[kind] = entries;
+ s_count[kind] = hdr->entryCount;
+ s_cap[kind] = hdr->entryCount;
+
+ free(buf);
+ return 0;
+}
+
+static int tarWriteCache(const char *cachePath, const char *tarPath, TarKind kind)
+{
+ struct stat stTar;
+ if (stat(tarPath, &stTar) < 0)
+ return -1;
+
+ TarCacheHeader hdr;
+ memcpy(hdr.magic, ARC_MAGIC, 4);
+ hdr.version = ARC_VERSION;
+ hdr.tarSize = stTar.st_size;
+ hdr.entryCount = s_count[kind];
+
+ int fd = open(cachePath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd < 0)
+ return -1;
+
+ if (write(fd, &hdr, sizeof(hdr)) != (int)sizeof(hdr)) {
+ close(fd);
+ return -1;
+ }
+
+ u32 entrySize = gTarInfo[kind].entrySize;
+ u32 totalSize = entrySize * s_count[kind];
+ if (totalSize > 0) {
+ if (write(fd, s_index[kind], totalSize) != (int)totalSize) {
+ close(fd);
+ return -1;
+ }
+ }
+
+ close(fd);
+ return 0;
+}
+
+static int tarParseFile(TarKind kind, const char *path)
+{
+ struct stat st;
+ if (stat(path, &st) < 0)
+ return -1;
+
+ char dirPath[256];
+ strncpy(dirPath, path, sizeof(dirPath));
+ dirPath[sizeof(dirPath) - 1] = '\0';
+
+ for (int i = strlen(dirPath) - 1; i >= 0; i--)
+ if (dirPath[i] == '/') {
+ dirPath[i + 1] = '\0';
+ break;
+ }
+
+ char fullCachePath[256];
+ snprintf(fullCachePath, sizeof(fullCachePath), "%s%s", dirPath, gTarInfo[kind].cacheName);
+
+ if (tarReadCache(fullCachePath, path, kind) == 0)
+ return 0;
+
+ int fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ free(s_index[kind]);
+ s_index[kind] = NULL;
+ s_count[kind] = 0;
+ s_cap[kind] = 0;
+
+ u32 entrySize = gTarInfo[kind].entrySize;
+ if (entrySize <= sizeof(TarEntryBase) + 1)
+ goto fail;
+
+ u32 nameMax = entrySize - sizeof(TarEntryBase) - 1;
+
+ while (1) {
+ unsigned char header[TAR_BLOCK_SIZE] __attribute__((aligned(64)));
+ int r = read(fd, header, TAR_BLOCK_SIZE);
+ if (r == 0)
+ break;
+ if (r < 0 || r != TAR_BLOCK_SIZE)
+ goto fail;
+
+ if (isZeroBlock(header))
+ break;
+
+ char typeflag = (char)header[156];
+ u64 rawSize = parseOctal((char *)header + 124, 12);
+ if (rawSize > MAX_FILE_SIZE)
+ rawSize = MAX_FILE_SIZE;
+
+ u64 paddedSize = ((rawSize + 511) / 512) * 512;
+
+ if (typeflag != '\0' && typeflag != '0') {
+ // Skip directory or non‑file entry
+ if (lseek64(fd, paddedSize, SEEK_CUR) == (u64)-1)
+ goto fail;
+ continue;
+ }
+
+ u64 dataOffset = lseek64(fd, 0, SEEK_CUR);
+ if (dataOffset == (u64)-1)
+ goto fail;
+
+ if (s_count[kind] >= s_cap[kind]) {
+ u32 newCap = s_cap[kind] ? (s_cap[kind] + 64) : 64;
+ void *newIndex = realloc(s_index[kind], entrySize * newCap);
+ if (!newIndex)
+ goto fail;
+ s_index[kind] = newIndex;
+ s_cap[kind] = newCap;
+ }
+
+ char *entry = (char *)s_index[kind] + entrySize * s_count[kind];
+ TarEntryBase *base = (TarEntryBase *)entry;
+ base->offset = dataOffset;
+ base->rawSize = rawSize;
+ base->paddedSize = paddedSize;
+
+ char nameBuf[256];
+ memset(nameBuf, 0, sizeof(nameBuf));
+
+ memcpy(nameBuf, header, 100);
+ nameBuf[100] = '\0';
+
+ for (int i = 99; i >= 0; i--) {
+ if (nameBuf[i] == '\0' || nameBuf[i] == ' ')
+ nameBuf[i] = '\0';
+ else
+ break;
+ }
+
+ char prefix[156];
+ memcpy(prefix, header + 345, 155);
+ prefix[155] = '\0';
+
+ for (int i = 154; i >= 0; i--) {
+ if (prefix[i] == '\0' || prefix[i] == ' ')
+ prefix[i] = '\0';
+ else
+ break;
+ }
+
+ if (prefix[0] != '\0') {
+ char full[256];
+ snprintf(full, sizeof(full), "%s/%s", prefix, nameBuf);
+ strncpy(nameBuf, full, sizeof(nameBuf) - 1);
+ nameBuf[sizeof(nameBuf) - 1] = '\0';
+ }
+
+ for (int i = (int)strlen(nameBuf) - 1; i >= 0; i--) {
+ if (nameBuf[i] == '\0' || nameBuf[i] == ' ')
+ nameBuf[i] = '\0';
+ else
+ break;
+ }
+
+ char *fname = entry + sizeof(TarEntryBase);
+ strncpy(fname, nameBuf, nameMax);
+ fname[nameMax - 1] = '\0';
+
+ s_count[kind]++;
+
+ if (lseek64(fd, paddedSize, SEEK_CUR) == (u64)-1)
+ goto fail;
+ }
+
+ close(fd);
+ tarWriteCache(fullCachePath, path, kind);
+ return 0;
+
+fail:
+ close(fd);
+ free(s_index[kind]);
+ s_index[kind] = NULL;
+ s_count[kind] = 0;
+ s_cap[kind] = 0;
+ return -1;
+}
+
+static const TarDevice *detectDevice(const char *path)
+{
+ for (int i = 0; gDevices[i].prefix; i++) {
+ const char *prefix = gDevices[i].prefix;
+ int len = strlen(prefix);
+
+ if (!strncasecmp(path, prefix, len))
+ return &gDevices[i];
+ }
+ return NULL;
+}
+
+int tarLoadFile(TarKind kind, const char *path)
+{
+ const TarDevice *dev = detectDevice(path);
+
+ int r = tarParseFile(kind, path);
+ if (r == 0)
+ s_dev[kind] = dev;
+
+ return r;
+}
+
+int tarLoadFromAnyDevice(TarKind kind)
+{
+ if (s_inactive[kind])
+ return -1;
+
+ if (s_index[kind] && s_dev[kind]) {
+ char tarPath[256];
+ if (buildTarPath(s_dev[kind], kind, tarPath, sizeof(tarPath)) == 0) {
+ int fd = open(tarPath, O_RDONLY);
+ if (fd >= 0) {
+ close(fd);
+ return 0;
+ }
+ }
+ tarCloseInternal(kind);
+ }
+
+ int found = 0;
+
+ for (int i = 0; gDevices[i].prefix; i++) {
+ const TarDevice *dev = &gDevices[i];
+
+ char tarPath[256];
+ if (buildTarPath(dev, kind, tarPath, sizeof(tarPath)) < 0)
+ continue;
+
+ int fd = open(tarPath, O_RDONLY);
+ if (fd < 0)
+ continue;
+ close(fd);
+
+ s_dev[kind] = dev;
+ if (tarParseFile(kind, tarPath) == 0) {
+ found = 1;
+ break;
+ }
+
+ tarCloseInternal(kind);
+ }
+
+ if (!found)
+ s_inactive[kind] = 1;
+
+ return found ? 0 : -1;
+}
+
+int tarClose(TarKind kind)
+{
+ tarCloseInternal(kind);
+ s_inactive[kind] = 0;
+ return 0;
+}
+
+TarEntryBase *tarFind(TarKind kind, const char *filename)
+{
+ if (s_inactive[kind])
+ return NULL;
+
+ if (!s_index[kind] || s_count[kind] == 0)
+ if (tarLoadFromAnyDevice(kind) < 0)
+ return NULL;
+
+ u32 entrySize = gTarInfo[kind].entrySize;
+ char *base = (char *)s_index[kind];
+
+ for (u32 i = 0; i < s_count[kind]; i++) {
+ char *entry = base + entrySize * i;
+ char *fname = entry + sizeof(TarEntryBase);
+ if (strcasecmp(fname, filename) == 0)
+ return (TarEntryBase *)entry;
+ }
+
+ return NULL;
+}
+
+u32 tarRead(TarKind kind, const TarEntryBase *entry, void *dst, u32 dstSize)
+{
+ if (s_inactive[kind])
+ return 0;
+
+ if (!entry || dstSize < entry->rawSize)
+ return 0;
+
+ char tarPath[256];
+ if (buildTarPath(s_dev[kind], kind, tarPath, sizeof(tarPath)) < 0)
+ return 0;
+
+ int fd = open(tarPath, O_RDONLY);
+ if (fd < 0)
+ return 0;
+
+ if (lseek64(fd, entry->offset, SEEK_SET) != entry->offset) {
+ close(fd);
+ return 0;
+ }
+
+ u32 total = 0;
+ while (total < entry->rawSize) {
+ int r = read(fd, (unsigned char *)dst + total, entry->rawSize - total);
+ if (r < 0) {
+ close(fd);
+ return 0;
+ }
+ if (r == 0) {
+ close(fd);
+ return 0;
+ }
+ total += r;
+ }
+
+ close(fd);
+ return total;
+}
+
+void *tarGet(TarKind kind, const char *filename)
+{
+ if (s_inactive[kind])
+ return NULL;
+
+ TarEntryBase *entry = tarFind(kind, filename);
+ if (!entry)
+ return NULL;
+
+ void *buf = memalign(64, entry->rawSize);
+ if (!buf)
+ return NULL;
+
+ if (tarRead(kind, entry, buf, entry->rawSize) != entry->rawSize) {
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
+int tarEnsureLoaded(TarKind kind)
+{
+ if (s_inactive[kind])
+ return -1;
+
+ if (!s_index[kind] || s_count[kind] == 0)
+ return tarLoadFromAnyDevice(kind);
+ return 0;
+}
+
+void tarInvalidate(TarKind kind)
+{
+ tarCloseInternal(kind);
+ s_inactive[kind] = 0;
+}
+
+const char *tarGetDevicePrefix(TarKind kind)
+{
+ return s_dev[kind] ? s_dev[kind]->prefix : NULL;
+}
+
+char **tarGetThemeNames(int *count)
+{
+ *count = 0;
+
+ u32 entrySize = gTarInfo[TAR_KIND_THM].entrySize;
+ char *base = (char *)s_index[TAR_KIND_THM];
+
+ char **names = NULL;
+ int n = 0;
+
+ for (u32 i = 0; i < s_count[TAR_KIND_THM]; i++) {
+ char *entry = base + entrySize * i;
+ char *fname = entry + sizeof(TarEntryBase);
+
+ char *slash = strchr(fname, '/');
+ if (!slash)
+ continue;
+
+ int len = slash - fname;
+ if (len <= 0)
+ continue;
+
+ int exists = 0;
+ for (int j = 0; j < n; j++) {
+ if (strncmp(names[j], fname, len) == 0 && names[j][len] == '\0')
+ exists = 1;
+ }
+ if (exists)
+ continue;
+
+ char *name = malloc(len + 1);
+ memcpy(name, fname, len);
+ name[len] = '\0';
+
+ char **newNames = realloc(names, sizeof(char *) * (n + 1));
+ if (!newNames) {
+ free(name);
+ continue;
+ }
+ names = newNames;
+ names[n] = name;
+ n++;
+ }
+
+ *count = n;
+ return names;
+}
diff --git a/src/textures.c b/src/textures.c
index a3bb5c1a..cea62046 100644
--- a/src/textures.c
+++ b/src/textures.c
@@ -2,12 +2,102 @@
#include "include/textures.h"
#include "include/util.h"
#include "include/ioman.h"
-#include "include/art_tar.h"
+#include "include/tar.h"
#include
#include
#include
#include
+extern void *logo_png;
+
+extern void *load0_png;
+extern void *load1_png;
+extern void *load2_png;
+extern void *load3_png;
+extern void *load4_png;
+extern void *load5_png;
+extern void *load6_png;
+extern void *load7_png;
+extern void *load8_png;
+extern void *usb_png; // Leave BDM Icon as usb.png to maintain theme compat
+extern void *usb_bd_png;
+extern void *ilk_bd_png;
+extern void *m4s_bd_png;
+extern void *hdd_bd_png;
+extern void *mmce_png;
+extern void *hdd_png;
+extern void *eth_png;
+extern void *app_png;
+extern void *fav_png;
+extern void *fav_mark_png;
+extern void *Index_0_png;
+extern void *Index_1_png;
+extern void *Index_2_png;
+extern void *Index_3_png;
+extern void *Index_4_png;
+
+extern void *R3_png;
+extern void *left_png;
+extern void *right_png;
+extern void *cross_png;
+extern void *triangle_png;
+extern void *circle_png;
+extern void *square_png;
+extern void *select_png;
+extern void *start_png;
+extern void *up_png;
+extern void *down_png;
+
+/* currently unused.
+extern void *L1_png;
+extern void *L2_png;
+extern void *L3_png;
+extern void *R1_png;
+extern void *R2_png;*/
+
+extern void *settings_bg_png;
+extern void *info_png;
+extern void *cover_png;
+extern void *cover_app_png;
+extern void *disc_png;
+extern void *screen_png;
+
+extern void *ELF_png;
+extern void *HDL_png;
+extern void *ISO_png;
+extern void *ZSO_png;
+extern void *UL_png;
+extern void *APPS_png;
+extern void *CD_png;
+extern void *DVD_png;
+extern void *Aspect_s_png;
+extern void *Aspect_w_png;
+extern void *Aspect_w1_png;
+extern void *Aspect_w2_png;
+extern void *Region_pal_png;
+extern void *Region_ntsc_png;
+
+extern void *pademu_on_png;
+extern void *pademu_off_png;
+extern void *gsm_on_png;
+extern void *gsm_off_png;
+extern void *cht_on_png;
+extern void *cht_off_png;
+
+extern void *Rating_0_png;
+extern void *Rating_1_png;
+extern void *Rating_2_png;
+extern void *Rating_3_png;
+extern void *Rating_4_png;
+extern void *Rating_5_png;
+
+extern void *case_png;
+extern void *apps_case_png;
+extern void *plank_png;
+extern void *lm_case_png;
+extern void *lm_apps_case_png;
+extern void *lm_case_shadow_png;
+
extern void *logo_01_png;
extern void *logo_02_png;
extern void *logo_03_png;
@@ -30,113 +120,6 @@ extern void *logo_19_png;
extern void *logo_20_png;
extern void *logo_21_png;
-extern void *loading_6_png;
-extern void *loading_7_png;
-extern void *loading_8_png;
-
-extern void *loading_0_png;
-extern void *loading_1_png;
-extern void *loading_2_png;
-extern void *loading_3_png;
-extern void *loading_4_png;
-extern void *loading_5_png;
-extern void *loading_6_png;
-extern void *loading_7_png;
-extern void *loading_8_png;
-extern void *category_empty_bdm_png; // Leave BDM Icon as usb.png to maintain theme compat
-extern void *category_usb_png;
-extern void *category_ilink_png;
-extern void *category_mx4sio_png;
-extern void *category_hdd_bdm_png;
-extern void *category_hdd_apa_png;
-extern void *category_net_smb_png;
-extern void *category_apps_png;
-extern void *category_fav_png;
-extern void *mark_star_png;
-extern void *category_mmce_png;
-extern void *bdm_index_1_png;
-extern void *bdm_index_2_png;
-extern void *bdm_index_3_png;
-extern void *bdm_index_4_png;
-extern void *bdm_index_5_png;
-
-extern void *button_stick_r3_png;
-extern void *button_dpad_left_png;
-extern void *button_dpad_right_png;
-extern void *button_dpad_up_png;
-extern void *button_dpad_down_png;
-extern void *button_symbol_cross_png;
-extern void *button_symbol_triangle_png;
-extern void *button_symbol_circle_png;
-extern void *button_symbol_square_png;
-extern void *button_select_png;
-extern void *button_start_png;
-/* currently unused.
-extern void *L1_png;
-extern void *L2_png;
-extern void *L3_png;
-extern void *R1_png;
-extern void *R2_png;*/
-
-extern void *bg_main_png;
-extern void *bg_info_png;
-extern void *cover_game_png;
-extern void *cover_app_png;
-extern void *disc_png;
-extern void *screenshot_png;
-
-extern void *badge_exec_elf_png;
-extern void *badge_disc_hdl_png;
-extern void *badge_disc_iso_png;
-extern void *badge_disc_zso_png;
-extern void *badge_disc_ul_png;
-extern void *badge_exec_app_png;
-extern void *badge_disc_cd_png;
-extern void *badge_disc_dvd_png;
-extern void *badge_vmode_43_png;
-extern void *badge_vmode_169_png;
-extern void *badge_vmode_169_ps2rd_png;
-extern void *badge_vmode_169_hexiso_png;
-extern void *dev_1_png;
-extern void *dev_2_png;
-extern void *dev_3_png;
-extern void *dev_4_png;
-extern void *dev_5_png;
-extern void *dev_6_png;
-extern void *dev_7_png;
-extern void *dev_8_png;
-extern void *rating_0_png;
-extern void *rating_1_png;
-extern void *rating_2_png;
-extern void *rating_3_png;
-extern void *rating_4_png;
-extern void *rating_5_png;
-extern void *badge_res_240p_png;
-extern void *badge_res_240p_hexiso_png;
-extern void *badge_res_480i_png;
-extern void *badge_res_480p_png;
-extern void *badge_res_480p_xt_png;
-extern void *badge_res_480p_xc_png;
-extern void *badge_res_480p_gsm_png;
-extern void *badge_res_480p_hexiso_png;
-extern void *badge_res_480p_ps2rd_png;
-extern void *badge_res_576i_png;
-extern void *badge_res_576p_gsm_png;
-extern void *badge_res_720p_gsm_png;
-extern void *badge_res_1080i_png;
-extern void *badge_res_1080i_gsm_png;
-extern void *badge_res_1080p_gsm_png; // TODO: Add 1080p support or remove this since gsm doesn´t support 1080p
-extern void *badge_region_multi_png;
-extern void *badge_region_ntsc_png;
-extern void *badge_region_pal_png;
-
-extern void *case_png;
-extern void *apps_case_png;
-extern void *plank_png;
-extern void *discbox_list_games_png;
-extern void *discbox_list_apps_png;
-extern void *discbox_list_shadow_png;
-
// Not related to screen size, just to limit at some point
static int maxSize = 720 * 512 * 4;
@@ -166,124 +149,100 @@ typedef struct
static png_texture_t pngTexture;
static texture_t internalDefault[TEXTURES_COUNT] = {
- {LOGO_1_ICON, "logo_01", &logo_01_png},
- {LOGO_2_ICON, "logo_02", &logo_02_png},
- {LOGO_3_ICON, "logo_03", &logo_03_png},
- {LOGO_4_ICON, "logo_04", &logo_04_png},
- {LOGO_5_ICON, "logo_05", &logo_05_png},
- {LOGO_6_ICON, "logo_06", &logo_06_png},
- {LOGO_7_ICON, "logo_07", &logo_07_png},
- {LOGO_8_ICON, "logo_08", &logo_08_png},
- {LOGO_9_ICON, "logo_09", &logo_09_png},
- {LOGO_10_ICON, "logo_10", &logo_10_png},
- {LOGO_11_ICON, "logo_11", &logo_11_png},
- {LOGO_12_ICON, "logo_12", &logo_12_png},
- {LOGO_13_ICON, "logo_13", &logo_13_png},
- {LOGO_14_ICON, "logo_14", &logo_14_png},
- {LOGO_15_ICON, "logo_15", &logo_15_png},
- {LOGO_16_ICON, "logo_16", &logo_16_png},
- {LOGO_17_ICON, "logo_17", &logo_17_png},
- {LOGO_18_ICON, "logo_18", &logo_18_png},
- {LOGO_19_ICON, "logo_19", &logo_19_png},
- {LOGO_20_ICON, "logo_20", &logo_20_png},
- {LOGO_21_ICON, "logo_21", &logo_21_png},
- {LOADING_1_ICON, "loading_1", &loading_1_png},
- {LOADING_2_ICON, "loading_2", &loading_2_png},
- {LOADING_3_ICON, "loading_3", &loading_3_png},
- {LOADING_4_ICON, "loading_4", &loading_4_png},
- {LOADING_5_ICON, "loading_5", &loading_5_png},
- {LOADING_6_ICON, "loading_6", &loading_6_png},
- {LOADING_7_ICON, "loading_7", &loading_7_png},
- {LOADING_8_ICON, "loading_8", &loading_8_png},
- {CATEGORY_EMPTY_BDM_ICON, "category_empty_bdm", &category_empty_bdm_png},
- {CATEGORY_USB_ICON, "category_usb", &category_usb_png},
- {CATEGORY_ILINK_ICON, "category_ilink", &category_ilink_png},
- {CATEGORY_MX4SIO_ICON, "category_mx4sio", &category_mx4sio_png},
- {CATEGORY_HDD_BDM_ICON, "category_hdd_bdm", &category_hdd_bdm_png},
- {CATEGORY_HDD_APA_ICON, "category_hdd_apa", &category_hdd_apa_png},
- {CATEGORY_NET_SMB_ICON, "category_net_smb", &category_net_smb_png},
- {CATEGORY_APPS_ICON, "category_apps", &category_apps_png},
- {CATEGORY_FAV_ICON, "category_fav", &category_fav_png},
- {MARK_STAR, "mark_star", &mark_star_png},
- {CATEGORY_MMCE_ICON, "category_mmce", &category_mmce_png},
- {BDM_INDEX_1, "bdm_index_1", &bdm_index_1_png},
- {BDM_INDEX_2, "bdm_index_2", &bdm_index_2_png},
- {BDM_INDEX_3, "bdm_index_3", &bdm_index_3_png},
- {BDM_INDEX_4, "bdm_index_4", &bdm_index_4_png},
- {BDM_INDEX_5, "bdm_index_5", &bdm_index_5_png},
- {BUTTON_STICK_R3_ICON, "button_stick_r3", &button_stick_r3_png},
- {BUTTON_DPAD_LEFT_ICON, "button_dpad_left", &button_dpad_left_png},
- {BUTTON_DPAD_RIGHT_ICON, "button_dpad_right", &button_dpad_right_png},
- {BUTTON_SYMBOL_CROSS_ICON, "button_symbol_cross", &button_symbol_cross_png},
- {BUTTON_SYMBOL_TRIANGLE_ICON, "button_symbol_triangle", &button_symbol_triangle_png},
- {BUTTON_SYMBOL_CIRCLE_ICON, "button_symbol_circle", &button_symbol_circle_png},
- {BUTTON_SYMBOL_SQUARE_ICON, "button_symbol_square", &button_symbol_square_png},
- {BUTTON_SELECT_ICON, "button_select", &button_select_png},
- {BUTTON_START_ICON, "button_start", &button_start_png},
- {BUTTON_DPAD_UP_ICON, "button_dpad_up", &button_dpad_up_png},
- {BUTTON_DPAD_DOWN_ICON, "button_dpad_down", &button_dpad_down_png},
- /* currently unused.
- {L1_ICON, "L1", &L1_png},
- {L2_ICON, "L2", &L2_png},
- {L3_ICON, "L3", &L3_png},
- {R1_ICON, "R1", &R1_png},
- {R2_ICON, "R2", &R2_png}, */
- {BG_MAIN, "bg_main", &bg_main_png},
- {BG_INFO, "bg_info", &bg_info_png},
- {COVER_GAMES, "cover_game", &cover_game_png},
- {COVER_APPS, "cover_app", &cover_app_png},
+ {LOAD0_ICON, "load0", &load0_png},
+ {LOAD1_ICON, "load1", &load1_png},
+ {LOAD2_ICON, "load2", &load2_png},
+ {LOAD3_ICON, "load3", &load3_png},
+ {LOAD4_ICON, "load4", &load4_png},
+ {LOAD5_ICON, "load5", &load5_png},
+ {LOAD6_ICON, "load6", &load6_png},
+ {LOAD7_ICON, "load7", &load7_png},
+ {BDM_ICON, "usb", &usb_png},
+ {USB_ICON, "usb_bd", &usb_bd_png},
+ {ILINK_ICON, "ilk_bd", &ilk_bd_png},
+ {MX4SIO_ICON, "m4s_bd", &m4s_bd_png},
+ {HDD_BD_ICON, "hdd_bd", &hdd_bd_png},
+ {HDD_ICON, "hdd", &hdd_png},
+ {ETH_ICON, "eth", ð_png},
+ {APP_ICON, "app", &app_png},
+ {FAV_ICON, "fav", &fav_png},
+ {FAV_MARK, "fav_mark", &fav_mark_png},
+ {MMCE_ICON, "mmce", &mmce_png},
+ {INDEX_0, "Index_0", &Index_0_png},
+ {INDEX_1, "Index_1", &Index_1_png},
+ {INDEX_2, "Index_2", &Index_2_png},
+ {INDEX_3, "Index_3", &Index_3_png},
+ {INDEX_4, "Index_4", &Index_4_png},
+ {R3_ICON, "R3", &R3_png},
+ {LEFT_ICON, "left", &left_png},
+ {RIGHT_ICON, "right", &right_png},
+ {CROSS_ICON, "cross", &cross_png},
+ {TRIANGLE_ICON, "triangle", &triangle_png},
+ {CIRCLE_ICON, "circle", &circle_png},
+ {SQUARE_ICON, "square", &square_png},
+ {SELECT_ICON, "select", &select_png},
+ {START_ICON, "start", &start_png},
+ {UP_ICON, "up", &up_png},
+ {DOWN_ICON, "down", &down_png},
+ {SETTINGS_BG, "settings_bg", &settings_bg_png},
+ {INFO_BG, "info", &info_png},
+ {COVER_DEFAULT, "cover", &cover_png},
+ {APP_COVER_DEFAULT, "cover_app", &cover_app_png},
{DISC_DEFAULT, "disc", &disc_png},
- {SCREENSHOT_DEFAULT, "screenshot", &screenshot_png},
- {BADGE_EXEC_ELF_FORMAT, "badge_exec_elf", &badge_exec_elf_png},
- {BADGE_DISC_HDL_FORMAT, "badge_disc_hdl", &badge_disc_hdl_png},
- {BADGE_DISC_ISO_FORMAT, "badge_disc_iso", &badge_disc_iso_png},
- {BADGE_DISC_ZSO_FORMAT, "badge_disc_zso", &badge_disc_zso_png},
- {BADGE_DISC_UL_FORMAT, "badge_disc_ul", &badge_disc_ul_png},
- {BADGE_EXEC_APP_MEDIA, "badge_exec_app", &badge_exec_app_png},
- {BADGE_DISC_CD_MEDIA, "badge_disc_cd", &badge_disc_cd_png},
- {BADGE_DISC_DVD_MEDIA, "badge_disc_dvd", &badge_disc_dvd_png},
- {BADGE_VMODE_43, "badge_vmode_43", &badge_vmode_43_png},
- {BADGE_VMODE_169, "badge_vmode_169", &badge_vmode_169_png},
- {BADGE_VMODE_169_PS2RD, "badge_vmode_169_ps2rd", &badge_vmode_169_ps2rd_png},
- {BADGE_VMODE_169_HEXISO, "badge_vmode_169_hexiso", &badge_vmode_169_hexiso_png},
- {DEV_1, "dev_1", &dev_1_png},
- {DEV_2, "dev_2", &dev_2_png},
- {DEV_3, "dev_3", &dev_3_png},
- {DEV_4, "dev_4", &dev_4_png},
- {DEV_5, "dev_5", &dev_5_png},
- {DEV_6, "dev_6", &dev_6_png},
- {DEV_7, "dev_7", &dev_7_png},
- {DEV_8, "dev_8", &dev_8_png},
- {RATING_0, "rating_0", &rating_0_png},
- {RATING_1, "rating_1", &rating_1_png},
- {RATING_2, "rating_2", &rating_2_png},
- {RATING_3, "rating_3", &rating_3_png},
- {RATING_4, "rating_4", &rating_4_png},
- {RATING_5, "rating_5", &rating_5_png},
- {BADGE_RES_240P, "badge_res_240p", &badge_res_240p_png},
- {BADGE_RES_240_HEXISO, "badge_res_240p_hexiso", &badge_res_240p_hexiso_png},
- {BADGE_RES_480I, "badge_res_480i", &badge_res_480i_png},
- {BADGE_RES_480P, "badge_res_480p", &badge_res_480p_png},
- {BADGE_RES_480P_XT, "badge_res_480p_xt", &badge_res_480p_xt_png},
- {BADGE_RES_480P_XC, "badge_res_480p_xc", &badge_res_480p_xc_png},
- {BADGE_RES_480P_GSM, "badge_res_480p_gsm", &badge_res_480p_gsm_png},
- {BADGE_RES_480P_PS2RD, "badge_res_480p_ps2rd", &badge_res_480p_ps2rd_png},
- {BADGE_RES_480P_HEXISO, "badge_res_480p_hexiso", &badge_res_480p_hexiso_png},
- {BADGE_RES_576I, "badge_res_576i", &badge_res_576i_png},
- {BADGE_RES_576P_GSM, "badge_res_576p_gsm", &badge_res_576p_gsm_png},
- {BADGE_RES_720P_GSM, "badge_res_720p_gsm", &badge_res_720p_gsm_png},
- {BADGE_RES_1080I, "badge_res_1080i", &badge_res_1080i_png},
- {BADGE_RES_1080I_GSM, "badge_res_1080i_gsm", &badge_res_1080i_gsm_png},
- {BADGE_RES_1080P_GSM, "badge_res_1080p_gsm", &badge_res_1080p_gsm_png},
- {BADGE_REGION_MULTI, "badge_region_multi", &badge_region_multi_png},
- {BADGE_REGION_NTSC, "badge_region_ntsc", &badge_region_ntsc_png},
- {BADGE_REGION_PAL, "badge_region_pal", &badge_region_pal_png},
+ {SCREEN_DEFAULT, "screen", &screen_png},
+ {ELF_FORMAT, "ELF", &ELF_png},
+ {HDL_FORMAT, "HDL", &HDL_png},
+ {ISO_FORMAT, "ISO", &ISO_png},
+ {ZSO_FORMAT, "ZSO", &ZSO_png},
+ {UL_FORMAT, "UL", &UL_png},
+ {APP_MEDIA, "APP", &APPS_png},
+ {CD_MEDIA, "CD", &CD_png},
+ {DVD_MEDIA, "DVD", &DVD_png},
+ {ASPECT_STD, "Aspect_s", &Aspect_s_png},
+ {ASPECT_WIDE, "Aspect_w", &Aspect_w_png},
+ {ASPECT_WIDE1, "Aspect_w1", &Aspect_w1_png},
+ {ASPECT_WIDE2, "Aspect_w2", &Aspect_w2_png},
+ {REGION_PAL, "Region_pal", &Region_pal_png},
+ {REGION_NTSC, "Region_ntsc", &Region_ntsc_png},
+ {PADEMU_ON, "pademu_on", &pademu_on_png},
+ {PADEMU_OFF, "pademu_off", &pademu_off_png},
+ {GSM_ON, "gsm_on", &gsm_on_png},
+ {GSM_OFF, "gsm_off", &gsm_off_png},
+ {CHT_ON, "cht_on", &cht_on_png},
+ {CHT_OFF, "cht_off", &cht_off_png},
+ {RATING_0, "Rating_0", &Rating_0_png},
+ {RATING_1, "Rating_1", &Rating_1_png},
+ {RATING_2, "Rating_2", &Rating_2_png},
+ {RATING_3, "Rating_3", &Rating_3_png},
+ {RATING_4, "Rating_4", &Rating_4_png},
+ {RATING_5, "Rating_5", &Rating_5_png},
+ {LOGO_01, "logo_01", &logo_01_png},
+ {LOGO_02, "logo_02", &logo_02_png},
+ {LOGO_03, "logo_03", &logo_03_png},
+ {LOGO_04, "logo_04", &logo_04_png},
+ {LOGO_05, "logo_05", &logo_05_png},
+ {LOGO_06, "logo_06", &logo_06_png},
+ {LOGO_07, "logo_07", &logo_07_png},
+ {LOGO_08, "logo_08", &logo_08_png},
+ {LOGO_09, "logo_09", &logo_09_png},
+ {LOGO_10, "logo_10", &logo_10_png},
+ {LOGO_11, "logo_11", &logo_11_png},
+ {LOGO_12, "logo_12", &logo_12_png},
+ {LOGO_13, "logo_13", &logo_13_png},
+ {LOGO_14, "logo_14", &logo_14_png},
+ {LOGO_15, "logo_15", &logo_15_png},
+ {LOGO_16, "logo_16", &logo_16_png},
+ {LOGO_17, "logo_17", &logo_17_png},
+ {LOGO_18, "logo_18", &logo_18_png},
+ {LOGO_19, "logo_19", &logo_19_png},
+ {LOGO_20, "logo_20", &logo_20_png},
+ {LOGO_21, "logo_21", &logo_21_png},
{CASE_OVERLAY, "case", &case_png},
{APPS_CASE_OVERLAY, "apps_case", &apps_case_png},
{PLANK, "plank", &plank_png},
- {DISCBOX_LIST_GAMES_OVERLAY, "discbox_list_games", &discbox_list_games_png},
- {DISCBOX_LIST_APPS_OVERLAY, "discbox_list_apps", &discbox_list_apps_png},
- {DISCBOX_LIST_SHADOW_OVERLAY, "discbox_list_shadow", &discbox_list_shadow_png},
+ {LM_CASE_OVERLAY, "lm_case", &lm_case_png},
+ {LM_APPS_CASE_OVERLAY, "lm_apps_case", &lm_apps_case_png},
+ {LM_CASE_SHADOW, "lm_case_shadow", &lm_case_shadow_png},
};
int texLookupInternalTexId(const char *name)
@@ -478,7 +437,7 @@ static void texReadData(GSTEXTURE *texture, png_structp pngPtr, png_infop infoPt
png_read_end(pngPtr, NULL);
}
-static int texLoadAll(GSTEXTURE *texture, const char *filePath, int texId, int archived)
+static int texLoadAll(GSTEXTURE *texture, const char *filePath, int texId, int resourceType)
{
texPrepare(texture);
png_structp pngPtr = NULL;
@@ -487,8 +446,16 @@ static int texLoadAll(GSTEXTURE *texture, const char *filePath, int texId, int a
png_rw_ptr readFunction = NULL;
void *PngFileBufferPtr;
void *pFileBuffer = NULL;
- if (archived) {
- pFileBuffer = getFileFromTar(filePath);
+ if (resourceType == RES_TAR_ART) {
+ pFileBuffer = tarGet(TAR_KIND_ART, filePath);
+ if (!pFileBuffer) {
+ return ERR_BAD_FILE;
+ }
+ PngFileBufferPtr = pFileBuffer;
+ readData = &PngFileBufferPtr;
+ readFunction = &texReadMemFunction;
+ } else if (resourceType == RES_TAR_THM) {
+ pFileBuffer = tarGet(TAR_KIND_THM, filePath);
if (!pFileBuffer) {
return ERR_BAD_FILE;
}
@@ -556,12 +523,15 @@ static int texLoadAll(GSTEXTURE *texture, const char *filePath, int texId, int a
if (bitDepth == 16)
png_set_strip_16(pngPtr);
- if (colorType == PNG_COLOR_TYPE_GRAY || bitDepth < 4) {
+ if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA || bitDepth < 4) {
png_set_expand(pngPtr);
if (png_get_valid(pngPtr, infoPtr, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(pngPtr);
}
+ if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(pngPtr);
+
png_set_filler(pngPtr, 0xff, PNG_FILLER_AFTER);
png_read_update_info(pngPtr, infoPtr);
@@ -617,9 +587,9 @@ static int texLoadAll(GSTEXTURE *texture, const char *filePath, int texId, int a
return texEnd(pngPtr, infoPtr, pFileBuffer, 0);
}
-static int texLoad(GSTEXTURE *texture, const char *filePath, int archived)
+static int texLoad(GSTEXTURE *texture, const char *filePath, int resourceType)
{
- return texLoadAll(texture, filePath, -1, archived);
+ return texLoadAll(texture, filePath, -1, resourceType);
}
int texLoadInternal(GSTEXTURE *texture, int texId)
@@ -627,7 +597,7 @@ int texLoadInternal(GSTEXTURE *texture, int texId)
return texLoadAll(texture, NULL, texId, 0);
}
-int texDiscoverLoad(GSTEXTURE *texture, const char *path, int texId, int archived)
+int texDiscoverLoad(GSTEXTURE *texture, const char *path, int texId, int resourceType)
{
char filePath[256];
@@ -638,16 +608,23 @@ int texDiscoverLoad(GSTEXTURE *texture, const char *path, int texId, int archive
else
snprintf(filePath, sizeof(filePath), "%s.%s", path, "png");
- if (archived) {
- if (findTarEntry(filePath) != NULL) {
- return (texLoad(texture, filePath, archived) >= 0) ? 0 : ERR_BAD_FILE;
+ TarEntryBase *entry = NULL;
+ if (resourceType == RES_TAR_ART) {
+ entry = tarFind(TAR_KIND_ART, filePath);
+ if (entry) {
+ return (texLoad(texture, filePath, RES_TAR_ART) >= 0) ? 0 : ERR_BAD_FILE;
+ }
+ } else if (resourceType == RES_TAR_THM) {
+ entry = tarFind(TAR_KIND_THM, filePath);
+ if (entry) {
+ return (texLoad(texture, filePath, RES_TAR_THM) >= 0) ? 0 : ERR_BAD_FILE;
}
} else {
int fd = open(filePath, O_RDONLY);
if (fd > 0) {
// File found, load it
close(fd);
- return (texLoad(texture, filePath, archived) >= 0) ? 0 : ERR_BAD_FILE;
+ return (texLoad(texture, filePath, RES_FILESYSTEM) >= 0) ? 0 : ERR_BAD_FILE;
}
}
diff --git a/src/themes.c b/src/themes.c
index 39afe8ca..b7384b89 100644
--- a/src/themes.c
+++ b/src/themes.c
@@ -1,4 +1,3 @@
-
#include "include/common.h"
#include "include/themes.h"
#include "include/util.h"
@@ -12,15 +11,20 @@
#include "include/sound.h"
#include "include/supportbase.h"
#include "include/module.h"
+#include "include/guigame.h"
+#include "include/tar.h"
+#include "include/config_wopl.h"
+#include "include/config_migration.h" // DELETE_WITH_MIGRATION
+#include
#include
#include
#include
#include
+#include
-#define MENU_POS_V 50
-#define HINT_HEIGHT 32
-#define DECORATOR_SIZE 20
-#define COVERFLOW_COUNT 3
+#define MENU_POS_V 50
+#define HINT_HEIGHT 32
+#define DECORATOR_SIZE 20
extern const char theme_list_cfg;
extern u16 size_theme_list_cfg;
@@ -52,8 +56,7 @@ enum ELEM_ATTRIBUTE_TYPE {
ELEM_TYPE_MENU_TEXT,
ELEM_TYPE_ITEMS_LIST,
ELEM_TYPE_ITEM_ICON,
- ELEM_TYPE_ITEM_COVER_GAMES,
- ELEM_TYPE_ITEM_COVER_APPS,
+ ELEM_TYPE_ITEM_COVER,
ELEM_TYPE_ITEM_TEXT,
ELEM_TYPE_HINT_TEXT,
ELEM_TYPE_INFO_HINT_TEXT,
@@ -83,8 +86,7 @@ static const char *elementsType[ELEM_TYPE_COUNT] = {
"MenuText",
"ItemsList",
"ItemIcon",
- "ItemCoverGames",
- "ItemCoverApps",
+ "ItemCover",
"ItemText",
"HintText",
"InfoHintText",
@@ -93,6 +95,135 @@ static const char *elementsType[ELEM_TYPE_COUNT] = {
"Coverflow",
};
+static const char *formatInfoImageAttr(const char *prefix, const char *value, char *out, size_t outSize)
+{
+ if (!value || !value[0])
+ return NULL;
+
+ if (strchr(value, '/'))
+ return value;
+
+ snprintf(out, outSize, "%s/%s", prefix, value);
+ return out;
+}
+
+static const char *gameInfoGetAttr(const game_info_t *gi, const per_game_cfg_t *pg, const char *attr)
+{
+ static char s_size[16], s_players[16], s_rating[16];
+ static char s_aspect[32], s_parental[64], s_region[32];
+ int is_app = pg && !strcasecmp(pg->media, "APP");
+
+ if (!attr)
+ return NULL;
+
+ if (attr[0] == '#' || attr[0] == '$')
+ attr++;
+
+ // game_info_t
+ if (gi) {
+ // shared game/app info
+ if (!strcmp(attr, "Title"))
+ return gi->title[0] ? gi->title : NULL;
+ if (!strcasecmp(attr, "Description"))
+ return gi->description[0] ? gi->description : NULL;
+ if (!strcasecmp(attr, "Developer"))
+ return gi->developer[0] ? gi->developer : NULL;
+ if (!strcasecmp(attr, "Release"))
+ return gi->release[0] ? gi->release : NULL;
+
+ // app only info
+ if (!strcasecmp(attr, "Version"))
+ return (is_app && gi->version[0]) ? gi->version : NULL;
+ if (!strcasecmp(attr, "Package"))
+ return (is_app && gi->package[0]) ? gi->package : NULL;
+ if (!strcasecmp(attr, "Source"))
+ return (is_app && gi->source[0]) ? gi->source : NULL;
+
+ // game only info
+ if (!strcasecmp(attr, "Genre"))
+ return (!is_app && gi->genre[0]) ? gi->genre : NULL;
+ if (!strcasecmp(attr, "Publisher"))
+ return (!is_app && gi->publisher[0]) ? gi->publisher : NULL;
+ if (!strcasecmp(attr, "Serial"))
+ return (!is_app && gi->serial[0]) ? gi->serial : NULL;
+
+ if (!strcasecmp(attr, "Parental"))
+ return !is_app ? formatInfoImageAttr("Parental", gi->parental, s_parental, sizeof(s_parental)) : NULL;
+
+ if (!strcasecmp(attr, "Region"))
+ return !is_app ? formatInfoImageAttr("Region", gi->region, s_region, sizeof(s_region)) : NULL;
+
+ if (!strcasecmp(attr, "Players")) {
+ if (is_app || !gi->players)
+ return NULL;
+
+ snprintf(s_players, sizeof(s_players), "Players/%d", gi->players);
+ return s_players;
+ }
+
+ if (!strcasecmp(attr, "Aspect"))
+ return !is_app ? formatInfoImageAttr("Aspect", gi->aspect, s_aspect, sizeof(s_aspect)) : NULL;
+
+ if (!strcasecmp(attr, "UserRating") || !strcasecmp(attr, "Rating")) {
+ if (is_app || !gi->user_rating)
+ return NULL;
+
+ snprintf(s_rating, sizeof(s_rating), "Rating/%d", gi->user_rating);
+ return s_rating;
+ }
+ }
+
+ // per_game_cfg_t
+ if (!pg)
+ return NULL;
+
+ if (!strcasecmp(attr, "Format"))
+ return pg->format[0] ? pg->format : NULL;
+ if (!strcasecmp(attr, "Media"))
+ return pg->media[0] ? pg->media : NULL;
+ if (!strcasecmp(attr, "Size")) {
+ if (!pg->size_mb)
+ return NULL;
+
+ snprintf(s_size, sizeof(s_size), "%d", pg->size_mb);
+ return s_size;
+ }
+
+ // apps dont support extra features.. so dont show the icons
+ if (is_app)
+ return NULL;
+
+#ifdef PADEMU
+ if (!strcasecmp(attr, "PadEmu") ||
+ !strcasecmp(attr, "EnablePadEmu")) { // DELETE_WITH_MIGRATION
+ if (pg->pademu_source == 1)
+ return pg->pademu_enable ? "pademu_on" : "pademu_off";
+
+ return gGlobalGameCfg.pademu_enable ? "pademu_on" : "pademu_off";
+ }
+#endif
+#ifdef CHEAT
+ if (!strcasecmp(attr, "Cheat") ||
+ !strcasecmp(attr, "EnableCheat")) { // DELETE_WITH_MIGRATION
+ if (pg->cheat_source == 1)
+ return pg->cheat_enable ? "cht_on" : "cht_off";
+
+ return gGlobalGameCfg.cheat_enable ? "cht_on" : "cht_off";
+ }
+#endif
+#ifdef GSM
+ if (!strcasecmp(attr, "GSM") ||
+ !strcasecmp(attr, "EnableGSM")) { // DELETE_WITH_MIGRATION
+ if (pg->gsm_source == 1)
+ return pg->gsm_enable ? "gsm_on" : "gsm_off";
+
+ return gGlobalGameCfg.gsm_enable ? "gsm_on" : "gsm_off";
+ }
+#endif
+
+ return NULL;
+}
+
// Common functions for Text ////////////////////////////////////////////////////////////////////////////////////////////////
static void endMutableText(theme_element_t *elem)
@@ -105,31 +236,34 @@ static void endMutableText(theme_element_t *elem)
if (mutableText->alias)
free(mutableText->alias);
+ if (mutableText->currentValue)
+ free(mutableText->currentValue);
+
free(mutableText);
}
free(elem);
}
-static mutable_text_t *initMutableText(const char *themePath, config_set_t *themeConfig, theme_t *theme, const char *name, int type, struct theme_element *elem, const char *value, const char *alias, int displayMode, int sizingMode)
+static mutable_text_t *initMutableText(const char *themePath, config_t *themeConfig, theme_t *theme, const char *name, int type, struct theme_element *elem, const char *value, const char *alias, int displayMode, int sizingMode)
{
mutable_text_t *mutableText = (mutable_text_t *)malloc(sizeof(mutable_text_t));
- mutableText->currentConfigId = 0;
+ mutableText->currentConfigId = -1;
mutableText->currentValue = NULL;
mutableText->alias = NULL;
char elemProp[64];
- snprintf(elemProp, sizeof(elemProp), "%s_display", name);
- configGetInt(themeConfig, elemProp, &displayMode);
+ snprintf(elemProp, sizeof(elemProp), "%s.display", name);
+ cfgGetInt(themeConfig, elemProp, &displayMode);
mutableText->displayMode = displayMode;
int length = strlen(value) + 1;
mutableText->value = (char *)malloc(length * sizeof(char));
memcpy(mutableText->value, value, length);
- snprintf(elemProp, sizeof(elemProp), "%s_wrap", name);
- if (configGetInt(themeConfig, elemProp, &sizingMode)) {
+ snprintf(elemProp, sizeof(elemProp), "%s.wrap", name);
+ if (cfgGetInt(themeConfig, elemProp, &sizingMode)) {
if (sizingMode > 0)
sizingMode = SIZING_WRAP;
}
@@ -148,8 +282,8 @@ static mutable_text_t *initMutableText(const char *themePath, config_set_t *them
mutableText->sizingMode = sizingMode;
if (type == ELEM_TYPE_ATTRIBUTE_TEXT) {
- snprintf(elemProp, sizeof(elemProp), "%s_title", name);
- configGetStr(themeConfig, elemProp, &alias);
+ snprintf(elemProp, sizeof(elemProp), "%s.title", name);
+ cfgGetStr(themeConfig, elemProp, &alias);
if (!alias) {
if (value[0] == '#')
alias = &value[1];
@@ -189,7 +323,7 @@ static mutable_text_t *initMutableText(const char *themePath, config_set_t *them
// StaticText ///////////////////////////////////////////////////////////////////////////////////////////////////////////////
-static void drawStaticText(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem)
+static void drawStaticText(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem)
{
mutable_text_t *mutableText = (mutable_text_t *)elem->extended;
if (mutableText->sizingMode == SIZING_NONE)
@@ -198,13 +332,13 @@ static void drawStaticText(struct menu_list *menu, struct submenu_list *item, co
fntRenderString(elem->font, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, mutableText->value, elem->color);
}
-static void initStaticText(const char *themePath, config_set_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name)
+static void initStaticText(const char *themePath, config_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name)
{
- const char *value;
+ const char *value = NULL;
char elemProp[64];
- snprintf(elemProp, sizeof(elemProp), "%s_value", name);
- configGetStr(themeConfig, elemProp, &value);
+ snprintf(elemProp, sizeof(elemProp), "%s.value", name);
+ cfgGetStr(themeConfig, elemProp, &value);
if (value) {
elem->extended = initMutableText(themePath, themeConfig, theme, name, ELEM_TYPE_STATIC_TEXT, elem, value, NULL, DISPLAY_ALWAYS, SIZING_NONE);
elem->endElem = &endMutableText;
@@ -215,47 +349,75 @@ static void initStaticText(const char *themePath, config_set_t *themeConfig, the
// AttributeText ////////////////////////////////////////////////////////////////////////////////////////////////////////////
-static void drawAttributeText(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem)
+static void drawAttributeText(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem)
{
mutable_text_t *mutableText = (mutable_text_t *)elem->extended;
- if (config) {
- if (mutableText->currentConfigId != config->uid) {
- // force refresh
- mutableText->currentConfigId = config->uid;
- mutableText->currentValue = NULL;
- if (configGetStr(config, mutableText->value, (const char **)&mutableText->currentValue)) {
- if (mutableText->sizingMode == SIZING_WRAP)
- fntFitString(elem->font, mutableText->currentValue, elem->width);
+
+ if (ctx) {
+ if (mutableText->currentConfigId != ctx->uid) {
+ if (mutableText->currentValue) {
+ free(mutableText->currentValue);
+ mutableText->currentValue = NULL;
}
+
+ mutableText->currentConfigId = ctx->uid;
+
+ const char *value = gameInfoGetAttr(ctx->gi, ctx->pg, mutableText->value);
+ if (value)
+ mutableText->currentValue = strdup(value);
}
+
if (mutableText->currentValue) {
- char result[300];
+ char *result;
+ int length;
+
if (mutableText->displayMode == DISPLAY_NEVER) {
if (!strncmp(mutableText->alias, _l(_STR_SIZE), strlen(_l(_STR_SIZE)))) {
- snprintf(result, sizeof(result), "%s MiB", mutableText->currentValue);
- if (mutableText->sizingMode == SIZING_NONE)
- fntRenderString(elem->font, elem->posX, elem->posY, elem->aligned, 0, 0, result, elem->color);
- else
- fntRenderString(elem->font, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, result, elem->color);
+ length = strlen(mutableText->currentValue) + 6;
+ result = (char *)calloc(length, sizeof(char));
+ if (!result)
+ return;
+
+ snprintf(result, length, "%s MiB", mutableText->currentValue);
} else {
- if (mutableText->sizingMode == SIZING_NONE)
- fntRenderString(elem->font, elem->posX, elem->posY, elem->aligned, 0, 0, mutableText->currentValue, elem->color);
- else
- fntRenderString(elem->font, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, mutableText->currentValue, elem->color);
+ length = strlen(mutableText->currentValue) + 1;
+ result = (char *)calloc(length, sizeof(char));
+ if (!result)
+ return;
+
+ snprintf(result, length, "%s", mutableText->currentValue);
}
} else {
- if (!strncmp(mutableText->alias, _l(_STR_SIZE), strlen(_l(_STR_SIZE))))
- snprintf(result, sizeof(result), "%s%s MiB", mutableText->alias, mutableText->currentValue);
- else
- snprintf(result, sizeof(result), "%s%s", mutableText->alias, mutableText->currentValue);
- if (mutableText->sizingMode == SIZING_NONE)
- fntRenderString(elem->font, elem->posX, elem->posY, elem->aligned, 0, 0, result, elem->color);
- else
- fntRenderString(elem->font, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, result, elem->color);
+ if (!strncmp(mutableText->alias, _l(_STR_SIZE), strlen(_l(_STR_SIZE)))) {
+ length = strlen(mutableText->alias) + strlen(mutableText->currentValue) + 6;
+ result = (char *)calloc(length, sizeof(char));
+ if (!result)
+ return;
+
+ snprintf(result, length, "%s%s MiB", mutableText->alias, mutableText->currentValue);
+ } else {
+ length = strlen(mutableText->alias) + strlen(mutableText->currentValue) + 1;
+ result = (char *)calloc(length, sizeof(char));
+ if (!result)
+ return;
+
+ snprintf(result, length, "%s%s", mutableText->alias, mutableText->currentValue);
+ }
}
+
+ if (mutableText->sizingMode == SIZING_WRAP)
+ fntFitString(elem->font, result, elem->width);
+
+ if (mutableText->sizingMode == SIZING_NONE)
+ fntRenderString(elem->font, elem->posX, elem->posY, elem->aligned, 0, 0, result, elem->color);
+ else
+ fntRenderString(elem->font, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, result, elem->color);
+
+ free(result);
return;
}
}
+
if (mutableText->displayMode == DISPLAY_ALWAYS) {
if (mutableText->sizingMode == SIZING_NONE)
fntRenderString(elem->font, elem->posX, elem->posY, elem->aligned, 0, 0, mutableText->alias, elem->color);
@@ -264,13 +426,13 @@ static void drawAttributeText(struct menu_list *menu, struct submenu_list *item,
}
}
-static void initAttributeText(const char *themePath, config_set_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name)
+static void initAttributeText(const char *themePath, config_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name)
{
- const char *attribute;
+ const char *attribute = NULL;
char elemProp[64];
- snprintf(elemProp, sizeof(elemProp), "%s_attribute", name);
- configGetStr(themeConfig, elemProp, &attribute);
+ snprintf(elemProp, sizeof(elemProp), "%s.attribute", name);
+ cfgGetStr(themeConfig, elemProp, &attribute);
if (attribute) {
elem->extended = initMutableText(themePath, themeConfig, theme, name, ELEM_TYPE_ATTRIBUTE_TEXT, elem, attribute, NULL, DISPLAY_ALWAYS, SIZING_NONE);
elem->endElem = &endMutableText;
@@ -281,6 +443,73 @@ static void initAttributeText(const char *themePath, config_set_t *themeConfig,
// Common functions for Image ///////////////////////////////////////////////////////////////////////////////////////////////
+// Get the actual source item_list for a submenu item
+// On the FAV page.. items redirect to their original source list to maintain aspect ratio
+static item_list_t *thmGetItemSource(struct menu_list *menu, struct submenu_list *item)
+{
+ item_list_t *list = (item_list_t *)menu->item->userdata;
+ if (item && item->item.owner && list->mode == FAV_MODE)
+ return (item_list_t *)item->item.owner;
+ return list;
+}
+
+// Find a theme_element_t in an element list by cache suffix
+static theme_element_t *thmFindElemBySuffix(theme_elems_t *elems, const char *suffix)
+{
+ theme_element_t *elem = elems->first;
+ while (elem) {
+ if (elem->type == ELEM_TYPE_GAME_IMAGE || elem->type == ELEM_TYPE_COVERFLOW) {
+ if (elem->extended) {
+ mutable_image_t *img = (mutable_image_t *)elem->extended;
+ if (img->cache && strcmp(img->cache->suffix, suffix) == 0)
+ return elem;
+ }
+ }
+ elem = elem->next;
+ }
+ return NULL;
+}
+
+// Get the appropriate theme_element for an item.
+// On the FAV page.. APP_MODE items use the apps element for correct position, dimensions and overlay.
+static theme_element_t *thmGetElemForItem(theme_element_t *defaultElem, struct menu_list *menu, struct submenu_list *item)
+{
+ if (!item || !item->item.owner)
+ return defaultElem;
+
+ item_list_t *menuList = (item_list_t *)menu->item->userdata;
+ item_list_t *sourceList = (item_list_t *)item->item.owner;
+
+ if (menuList->mode == FAV_MODE && sourceList->mode == APP_MODE) {
+ mutable_image_t *img = (mutable_image_t *)defaultElem->extended;
+ if (img && img->cache) {
+ theme_element_t *appsElem = thmFindElemBySuffix(&gTheme->appsMainElems, img->cache->suffix);
+ if (appsElem)
+ return appsElem;
+ }
+ }
+ return defaultElem;
+}
+
+// Draw a texture with optional overlay, reflection, and overlay offsets
+// offsetX/offsetY are for animated scaling (coverflow).. 0 for normal use
+// scaleFactor is for overlay/inlay scaling (coverflow).. 1.0f for normal use
+static void thmDrawTexture(GSTEXTURE *texture, mutable_image_t *img, int x, int y, short aligned, int w, int h, short scaled, u64 color, int reflection, int offsetX, int offsetY, float scaleFactor)
+{
+ if (img->overlayTexture)
+ rmDrawOverlayPixmap(&img->overlayTexture->source, x, y, aligned, w, h, scaled, color, texture,
+ (int)(img->overlayTexture->upperLeft_x * scaleFactor),
+ (int)(img->overlayTexture->upperLeft_y * scaleFactor),
+ (int)(img->overlayTexture->upperRight_x * scaleFactor) + offsetX,
+ (int)(img->overlayTexture->upperRight_y * scaleFactor),
+ (int)(img->overlayTexture->lowerLeft_x * scaleFactor),
+ (int)(img->overlayTexture->lowerLeft_y * scaleFactor) + offsetY,
+ (int)(img->overlayTexture->lowerRight_x * scaleFactor) + offsetX,
+ (int)(img->overlayTexture->lowerRight_y * scaleFactor) + offsetY, reflection);
+ else
+ rmDrawPixmap(texture, x, y, aligned, w, h, scaled, color, reflection);
+}
+
static void findDuplicate(theme_element_t *first, const char *cachePattern, const char *defaultTexture, const char *overlayTexture, mutable_image_t *target)
{
theme_element_t *elem = first;
@@ -331,25 +560,32 @@ static void freeImageTexture(image_texture_t *texture)
}
}
-static image_texture_t *initImageTexture(const char *themePath, config_set_t *themeConfig, const char *name, const char *imgName, int isOverlay)
+static image_texture_t *initImageTexture(const char *themePath, config_t *themeConfig, const char *name, const char *imgName, int isOverlay)
{
- image_texture_t *texture = (image_texture_t *)malloc(sizeof(image_texture_t));
- texture->name = NULL;
+ image_texture_t *texture = (image_texture_t *)calloc(1, sizeof(image_texture_t));
int texId = -1;
int result = 0;
if (themePath) {
char path[256];
+ char tarLookup[256];
+
snprintf(path, sizeof(path), "%s%s", themePath, imgName);
- if (texDiscoverLoad(&texture->source, path, texId, 0) >= 0)
- ;
- result = 1;
+
+ snprintf(tarLookup, sizeof(tarLookup), "%s.png", path);
+ const TarEntryBase *entry = NULL;
+ entry = tarFind(TAR_KIND_THM, tarLookup);
+
+ if (entry) {
+ result = texDiscoverLoad(&texture->source, path, texId, RES_TAR_THM) >= 0;
+ }
+ if (!result) {
+ result = texDiscoverLoad(&texture->source, path, texId, RES_FILESYSTEM) >= 0;
+ }
} else {
texId = texLookupInternalTexId(imgName);
- if (texLoadInternal(&texture->source, texId) >= 0)
- ;
- result = 1;
+ result = (texId >= 0 && texLoadInternal(&texture->source, texId) >= 0);
}
if (result) {
@@ -360,29 +596,29 @@ static image_texture_t *initImageTexture(const char *themePath, config_set_t *th
if (isOverlay) {
int intValue;
char elemProp[64];
- snprintf(elemProp, sizeof(elemProp), "%s_overlay_ulx", name);
- if (configGetInt(themeConfig, elemProp, &intValue))
+ snprintf(elemProp, sizeof(elemProp), "%s.overlay_ulx", name);
+ if (cfgGetInt(themeConfig, elemProp, &intValue))
texture->upperLeft_x = intValue;
- snprintf(elemProp, sizeof(elemProp), "%s_overlay_uly", name);
- if (configGetInt(themeConfig, elemProp, &intValue))
+ snprintf(elemProp, sizeof(elemProp), "%s.overlay_uly", name);
+ if (cfgGetInt(themeConfig, elemProp, &intValue))
texture->upperLeft_y = intValue;
- snprintf(elemProp, sizeof(elemProp), "%s_overlay_urx", name);
- if (configGetInt(themeConfig, elemProp, &intValue))
+ snprintf(elemProp, sizeof(elemProp), "%s.overlay_urx", name);
+ if (cfgGetInt(themeConfig, elemProp, &intValue))
texture->upperRight_x = intValue;
- snprintf(elemProp, sizeof(elemProp), "%s_overlay_ury", name);
- if (configGetInt(themeConfig, elemProp, &intValue))
+ snprintf(elemProp, sizeof(elemProp), "%s.overlay_ury", name);
+ if (cfgGetInt(themeConfig, elemProp, &intValue))
texture->upperRight_y = intValue;
- snprintf(elemProp, sizeof(elemProp), "%s_overlay_llx", name);
- if (configGetInt(themeConfig, elemProp, &intValue))
+ snprintf(elemProp, sizeof(elemProp), "%s.overlay_llx", name);
+ if (cfgGetInt(themeConfig, elemProp, &intValue))
texture->lowerLeft_x = intValue;
- snprintf(elemProp, sizeof(elemProp), "%s_overlay_lly", name);
- if (configGetInt(themeConfig, elemProp, &intValue))
+ snprintf(elemProp, sizeof(elemProp), "%s.overlay_lly", name);
+ if (cfgGetInt(themeConfig, elemProp, &intValue))
texture->lowerLeft_y = intValue;
- snprintf(elemProp, sizeof(elemProp), "%s_overlay_lrx", name);
- if (configGetInt(themeConfig, elemProp, &intValue))
+ snprintf(elemProp, sizeof(elemProp), "%s.overlay_lrx", name);
+ if (cfgGetInt(themeConfig, elemProp, &intValue))
texture->lowerRight_x = intValue;
- snprintf(elemProp, sizeof(elemProp), "%s_overlay_lry", name);
- if (configGetInt(themeConfig, elemProp, &intValue))
+ snprintf(elemProp, sizeof(elemProp), "%s.overlay_lry", name);
+ if (cfgGetInt(themeConfig, elemProp, &intValue))
texture->lowerRight_y = intValue;
}
} else {
@@ -393,20 +629,19 @@ static image_texture_t *initImageTexture(const char *themePath, config_set_t *th
return texture;
}
-static image_texture_t *initImageInternalTexture(config_set_t *themeConfig, const char *name)
+static image_texture_t *initImageInternalTexture(config_t *themeConfig, const char *name)
{
- image_texture_t *texture = (image_texture_t *)malloc(sizeof(image_texture_t));
- texture->name = NULL;
- int result;
+ image_texture_t *texture = (image_texture_t *)calloc(1, sizeof(image_texture_t));
+ int result = texLookupInternalTexId(name);
- if ((result = texLookupInternalTexId(name)) >= 0) {
+ if (result >= 0)
result = texLoadInternal(&texture->source, result);
+
+ if (result >= 0) {
int length = strlen(name) + 1;
texture->name = (char *)malloc(length * sizeof(char));
memcpy(texture->name, name, length);
- }
-
- if (result < 0) {
+ } else {
freeImageTexture(texture);
texture = NULL;
}
@@ -430,6 +665,9 @@ static void endMutableImage(struct theme_element *elem)
if (mutableImage->overlayTexture && !mutableImage->overlayTextureLinked)
freeImageTexture(mutableImage->overlayTexture);
+ if (mutableImage->currentValue)
+ free(mutableImage->currentValue);
+
free(mutableImage);
}
@@ -439,11 +677,11 @@ static void endMutableImage(struct theme_element *elem)
free(elem);
}
-static mutable_image_t *initMutableImage(const char *themePath, config_set_t *themeConfig, theme_t *theme, const char *name, int type, const char *cachePattern, int cacheCount, const char *defaultTexture, const char *overlayTexture)
+static mutable_image_t *initMutableImage(const char *themePath, config_t *themeConfig, theme_t *theme, const char *name, int type, const char *cachePattern, int cacheCount, const char *defaultTexture, const char *overlayTexture)
{
mutable_image_t *mutableImage = (mutable_image_t *)malloc(sizeof(mutable_image_t));
mutableImage->currentUid = -1;
- mutableImage->currentConfigId = 0;
+ mutableImage->currentConfigId = -1;
mutableImage->currentValue = NULL;
mutableImage->cache = NULL;
mutableImage->cacheLinked = 0;
@@ -455,23 +693,23 @@ static mutable_image_t *initMutableImage(const char *themePath, config_set_t *th
char elemProp[64];
if (type == ELEM_TYPE_ATTRIBUTE_IMAGE) {
- snprintf(elemProp, sizeof(elemProp), "%s_attribute", name);
- configGetStr(themeConfig, elemProp, &cachePattern);
+ snprintf(elemProp, sizeof(elemProp), "%s.attribute", name);
+ cfgGetStr(themeConfig, elemProp, &cachePattern);
LOG("THEMES MutableImage %s: type: %s using cache pattern: %s\n", name, elementsType[type], cachePattern);
} else if ((type == ELEM_TYPE_GAME_IMAGE) || (type == ELEM_TYPE_COVERFLOW) || (type == ELEM_TYPE_BACKGROUND)) {
- snprintf(elemProp, sizeof(elemProp), "%s_pattern", name);
- configGetStr(themeConfig, elemProp, &cachePattern);
- snprintf(elemProp, sizeof(elemProp), "%s_count", name);
- configGetInt(themeConfig, elemProp, &cacheCount);
+ snprintf(elemProp, sizeof(elemProp), "%s.pattern", name);
+ cfgGetStr(themeConfig, elemProp, &cachePattern);
+ snprintf(elemProp, sizeof(elemProp), "%s.count", name);
+ cfgGetInt(themeConfig, elemProp, &cacheCount);
LOG("THEMES MutableImage %s: type: %s using cache pattern: %s count: %d\n", name, elementsType[type], cachePattern, cacheCount);
}
- snprintf(elemProp, sizeof(elemProp), "%s_default", name);
- configGetStr(themeConfig, elemProp, &defaultTexture);
+ snprintf(elemProp, sizeof(elemProp), "%s.default", name);
+ cfgGetStr(themeConfig, elemProp, &defaultTexture);
if (type != ELEM_TYPE_BACKGROUND) {
- snprintf(elemProp, sizeof(elemProp), "%s_overlay", name);
- configGetStr(themeConfig, elemProp, &overlayTexture);
+ snprintf(elemProp, sizeof(elemProp), "%s.overlay", name);
+ cfgGetStr(themeConfig, elemProp, &overlayTexture);
}
findDuplicate(theme->mainElems.first, cachePattern, defaultTexture, overlayTexture, mutableImage);
@@ -503,21 +741,20 @@ static mutable_image_t *initMutableImage(const char *themePath, config_set_t *th
// StaticImage //////////////////////////////////////////////////////////////////////////////////////////////////////////////
-static void drawStaticImage(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem)
+static void drawStaticImage(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem)
{
if (!item && elem->skip)
return;
mutable_image_t *staticImage = (mutable_image_t *)elem->extended;
- if (staticImage->overlayTexture) {
- rmDrawOverlayPixmap(&staticImage->overlayTexture->source, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultBGCol,
- &staticImage->defaultTexture->source, staticImage->overlayTexture->upperLeft_x, staticImage->overlayTexture->upperLeft_y, staticImage->overlayTexture->upperRight_x, staticImage->overlayTexture->upperRight_y,
- staticImage->overlayTexture->lowerLeft_x, staticImage->overlayTexture->lowerLeft_y, staticImage->overlayTexture->lowerRight_x, staticImage->overlayTexture->lowerRight_y);
- } else
- rmDrawPixmap(&staticImage->defaultTexture->source, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultBGCol);
+
+ if (!staticImage || !staticImage->defaultTexture || !staticImage->defaultTexture->source.Mem)
+ return;
+
+ thmDrawTexture(&staticImage->defaultTexture->source, staticImage, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol, 0, 0, 0, 1.0f);
}
-static void initStaticImage(const char *themePath, config_set_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name, const char *imageName)
+static void initStaticImage(const char *themePath, config_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name, const char *imageName)
{
mutable_image_t *mutableImage = initMutableImage(themePath, themeConfig, theme, name, elem->type, NULL, 0, imageName, NULL);
elem->extended = mutableImage;
@@ -538,14 +775,20 @@ static GSTEXTURE *getGameImageTexture(image_cache_t *cache, void *support, struc
return cacheGetTexture(cache, list, &item->cache_id[cache->userId], &item->cache_uid[cache->userId], startup);
}
-static void drawGameImage(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem)
+static void drawGameImage(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem)
{
mutable_image_t *gameImage = (mutable_image_t *)elem->extended;
if (item) {
- GSTEXTURE *texture = getGameImageTexture(gameImage->cache, menu->item->userdata, &item->item);
+ item_list_t *sourceList = thmGetItemSource(menu, item);
+ theme_element_t *drawElem = thmGetElemForItem(elem, menu, item);
+ mutable_image_t *img = (mutable_image_t *)drawElem->extended;
+ if (!gDiscEnableArt && img && img->cache && img->cache->suffix && !strcmp(img->cache->suffix, "ICO"))
+ return;
+
+ GSTEXTURE *texture = getGameImageTexture(img->cache, sourceList, &item->item);
if (!texture || !texture->Mem) {
- if (gameImage->defaultTexture)
- texture = &gameImage->defaultTexture->source;
+ if (img->defaultTexture)
+ texture = &img->defaultTexture->source;
else {
if (elem->type == ELEM_TYPE_BACKGROUND)
guiDrawBGPlasma();
@@ -553,33 +796,18 @@ static void drawGameImage(struct menu_list *menu, struct submenu_list *item, con
}
}
- int x = gWideScreen ? elem->wsX : elem->posX;
-
- if (gameImage->overlayTexture) {
- if (elem->reflection)
- rmDrawOverlayPixmapWithReflection(&gameImage->overlayTexture->source, x, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol,
- texture, gameImage->overlayTexture->upperLeft_x, gameImage->overlayTexture->upperLeft_y, gameImage->overlayTexture->upperRight_x, gameImage->overlayTexture->upperRight_y,
- gameImage->overlayTexture->lowerLeft_x, gameImage->overlayTexture->lowerLeft_y, gameImage->overlayTexture->lowerRight_x, gameImage->overlayTexture->lowerRight_y);
- else
- rmDrawOverlayPixmap(&gameImage->overlayTexture->source, x, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol,
- texture, gameImage->overlayTexture->upperLeft_x, gameImage->overlayTexture->upperLeft_y, gameImage->overlayTexture->upperRight_x, gameImage->overlayTexture->upperRight_y,
- gameImage->overlayTexture->lowerLeft_x, gameImage->overlayTexture->lowerLeft_y, gameImage->overlayTexture->lowerRight_x, gameImage->overlayTexture->lowerRight_y);
- } else {
- if (elem->reflection)
- rmDrawPixmapWithReflection(texture, x, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol);
- else
- rmDrawPixmap(texture, x, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol);
- }
+ int x = gWideScreen ? drawElem->wsX : drawElem->posX;
+ thmDrawTexture(texture, img, x, drawElem->posY, drawElem->aligned, drawElem->width, drawElem->height, drawElem->scaled, gDefaultCol, drawElem->reflection, 0, 0, 1.0f);
} else if (elem->type == ELEM_TYPE_BACKGROUND) {
if (gameImage->defaultTexture)
- rmDrawPixmap(&gameImage->defaultTexture->source, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol);
+ rmDrawPixmap(&gameImage->defaultTexture->source, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol, 0);
else
guiDrawBGPlasma();
}
}
-static void initGameImage(const char *themePath, config_set_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name, const char *pattern, int count, const char *texture, const char *overlay)
+static void initGameImage(const char *themePath, config_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name, const char *pattern, int count, const char *texture, const char *overlay)
{
mutable_image_t *mutableImage = initMutableImage(themePath, themeConfig, theme, name, elem->type, pattern, count, texture, overlay);
elem->extended = mutableImage;
@@ -593,16 +821,22 @@ static void initGameImage(const char *themePath, config_set_t *themeConfig, them
// AttributeImage ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-static void drawAttributeImage(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem)
+static void drawAttributeImage(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem)
{
mutable_image_t *attributeImage = (mutable_image_t *)elem->extended;
- if (config) {
- if (attributeImage->currentConfigId != config->uid) {
- // force refresh
+ if (ctx) {
+ if (attributeImage->currentConfigId != ctx->uid) {
+ if (attributeImage->currentValue) {
+ free(attributeImage->currentValue);
+ attributeImage->currentValue = NULL;
+ }
+
attributeImage->currentUid = -1;
- attributeImage->currentConfigId = config->uid;
- attributeImage->currentValue = NULL;
- configGetStr(config, attributeImage->cache->suffix, (const char **)&attributeImage->currentValue);
+ attributeImage->currentConfigId = ctx->uid;
+
+ const char *value = gameInfoGetAttr(ctx->gi, ctx->pg, attributeImage->cache->suffix);
+ if (value)
+ attributeImage->currentValue = strdup(value);
}
if (attributeImage->currentValue) {
if (IS_DEFAULT_THEME(thmGetGuiValue())) {
@@ -617,30 +851,24 @@ static void drawAttributeImage(struct menu_list *menu, struct submenu_list *item
}
GSTEXTURE *texture = thmGetTexture(texId);
if (texture && texture->Mem)
- rmDrawPixmap(texture, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol);
+ rmDrawPixmap(texture, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol, 0);
return;
} else {
int posZ = 0;
GSTEXTURE *texture = cacheGetTexture(attributeImage->cache, menu->item->userdata, &posZ, &attributeImage->currentUid, attributeImage->currentValue);
if (texture && texture->Mem) {
- if (attributeImage->overlayTexture) {
- rmDrawOverlayPixmap(&attributeImage->overlayTexture->source, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol,
- texture, attributeImage->overlayTexture->upperLeft_x, attributeImage->overlayTexture->upperLeft_y, attributeImage->overlayTexture->upperRight_x, attributeImage->overlayTexture->upperRight_y,
- attributeImage->overlayTexture->lowerLeft_x, attributeImage->overlayTexture->lowerLeft_y, attributeImage->overlayTexture->lowerRight_x, attributeImage->overlayTexture->lowerRight_y);
- } else
- rmDrawPixmap(texture, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol);
-
+ thmDrawTexture(texture, attributeImage, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol, 0, 0, 0, 1.0f);
return;
}
}
}
}
if (attributeImage->defaultTexture)
- rmDrawPixmap(&attributeImage->defaultTexture->source, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol);
+ rmDrawPixmap(&attributeImage->defaultTexture->source, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol, 0);
}
-static void initAttributeImage(const char *themePath, config_set_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name)
+static void initAttributeImage(const char *themePath, config_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name)
{
mutable_image_t *mutableImage = initMutableImage(themePath, themeConfig, theme, name, elem->type, NULL, 1, NULL, NULL);
elem->extended = mutableImage;
@@ -654,6 +882,22 @@ static void initAttributeImage(const char *themePath, config_set_t *themeConfig,
// BasicElement /////////////////////////////////////////////////////////////////////////////////////////////////////////////
+static int thmGetColorSetting(config_t *cfg, const char *key, unsigned char *color)
+{
+ const char *str;
+ unsigned int r, g, b;
+ if (!cfgGetStr(cfg, key, &str))
+ return 0;
+ if (str[0] != '#' || strlen(str) < 7)
+ return 0;
+ if (sscanf(str + 1, "%02X%02X%02X", &r, &g, &b) != 3)
+ return 0;
+ color[0] = (unsigned char)r;
+ color[1] = (unsigned char)g;
+ color[2] = (unsigned char)b;
+ return 1;
+}
+
static void endBasic(theme_element_t *elem)
{
if (elem->extended)
@@ -662,65 +906,72 @@ static void endBasic(theme_element_t *elem)
free(elem);
}
-static theme_element_t *initBasic(const char *themePath, config_set_t *themeConfig, theme_t *theme, const char *name, int type, int x, int y, short aligned, int w, int h, short scaled, u64 color, int font)
+static theme_element_t *initBasic(const char *themePath, config_t *themeConfig, theme_t *theme, const char *name, int type, int x, int y, short aligned, int w, int h, short scaled, u64 color, int font)
{
int intValue;
unsigned char charColor[3];
const char *temp;
char elemProp[64];
-
theme_element_t *elem = (theme_element_t *)malloc(sizeof(theme_element_t));
-
elem->type = type;
elem->extended = NULL;
elem->drawElem = NULL;
elem->endElem = &endBasic;
elem->next = NULL;
-
- snprintf(elemProp, sizeof(elemProp), "%s_x", name);
- if (configGetStr(themeConfig, elemProp, &temp)) {
+ snprintf(elemProp, sizeof(elemProp), "%s.x", name);
+ if (config_lookup_string(themeConfig, elemProp, &temp)) {
if (!strncmp(temp, "POS_MID", 7))
x = screenWidth >> 1;
else
x = atoi(temp);
- }
+ } else if (config_lookup_int(themeConfig, elemProp, &intValue))
+ x = intValue;
+ else
+ cfgCheckExists(themeConfig, elemProp, "string or int");
if (x < 0)
elem->posX = screenWidth + x;
else
elem->posX = x;
-
- snprintf(elemProp, sizeof(elemProp), "%s_y", name);
- if (configGetStr(themeConfig, elemProp, &temp)) {
+ snprintf(elemProp, sizeof(elemProp), "%s.y", name);
+ if (config_lookup_string(themeConfig, elemProp, &temp)) {
if (!strncmp(temp, "POS_MID", 7))
y = screenHeight >> 1;
else
y = atoi(temp);
- }
+ } else if (config_lookup_int(themeConfig, elemProp, &intValue))
+ y = intValue;
+ else
+ cfgCheckExists(themeConfig, elemProp, "string or int");
if (y < 0)
elem->posY = ceil((screenHeight + y) * theme->usedHeight / screenHeight);
else
elem->posY = y;
-
- snprintf(elemProp, sizeof(elemProp), "%s_width", name);
- if (configGetStr(themeConfig, elemProp, &temp)) {
+ snprintf(elemProp, sizeof(elemProp), "%s.width", name);
+ if (config_lookup_string(themeConfig, elemProp, &temp)) {
if (!strncmp(temp, "DIM_INF", 7))
elem->width = screenWidth;
else
elem->width = atoi(temp);
- } else
+ } else if (config_lookup_int(themeConfig, elemProp, &intValue))
+ elem->width = intValue;
+ else {
elem->width = w;
-
- snprintf(elemProp, sizeof(elemProp), "%s_height", name);
- if (configGetStr(themeConfig, elemProp, &temp)) {
+ cfgCheckExists(themeConfig, elemProp, "string or int");
+ }
+ snprintf(elemProp, sizeof(elemProp), "%s.height", name);
+ if (config_lookup_string(themeConfig, elemProp, &temp)) {
if (!strncmp(temp, "DIM_INF", 7))
elem->height = screenHeight;
else
elem->height = atoi(temp);
- } else
+ } else if (config_lookup_int(themeConfig, elemProp, &intValue))
+ elem->height = intValue;
+ else {
elem->height = h;
-
- snprintf(elemProp, sizeof(elemProp), "%s_aligned", name);
- if (configGetInt(themeConfig, elemProp, &intValue)) {
+ cfgCheckExists(themeConfig, elemProp, "string or int");
+ }
+ snprintf(elemProp, sizeof(elemProp), "%s.aligned", name);
+ if (cfgGetInt(themeConfig, elemProp, &intValue)) {
if (intValue == 0)
elem->aligned = ALIGN_NONE;
else if (intValue == 1)
@@ -729,57 +980,50 @@ static theme_element_t *initBasic(const char *themePath, config_set_t *themeConf
elem->aligned = ALIGN_RIGHT;
} else
elem->aligned = aligned;
-
- snprintf(elemProp, sizeof(elemProp), "%s_scaled", name);
- if (configGetInt(themeConfig, elemProp, &intValue))
+ snprintf(elemProp, sizeof(elemProp), "%s.scaled", name);
+ if (cfgGetInt(themeConfig, elemProp, &intValue))
elem->scaled = (intValue == 0) ? SCALING_NONE : SCALING_RATIO;
else
elem->scaled = scaled;
-
- snprintf(elemProp, sizeof(elemProp), "%s_color", name);
- if (configGetColor(themeConfig, elemProp, charColor))
+ snprintf(elemProp, sizeof(elemProp), "%s.color", name);
+ if (thmGetColorSetting(themeConfig, elemProp, charColor))
elem->color = GS_SETREG_RGBA(charColor[0], charColor[1], charColor[2], 0x80);
else
elem->color = color;
-
elem->font = font;
- snprintf(elemProp, sizeof(elemProp), "%s_font", name);
- if (configGetInt(themeConfig, elemProp, &intValue)) {
+ snprintf(elemProp, sizeof(elemProp), "%s.font", name);
+ if (cfgGetInt(themeConfig, elemProp, &intValue)) {
if (intValue > 0 && intValue < THM_MAX_FONTS)
elem->font = theme->fonts[intValue];
}
-
- snprintf(elemProp, sizeof(elemProp), "%s_reflection", name);
- if (configGetInt(themeConfig, elemProp, &intValue))
+ snprintf(elemProp, sizeof(elemProp), "%s.reflection", name);
+ if (cfgGetInt(themeConfig, elemProp, &intValue))
elem->reflection = intValue;
else
elem->reflection = 0;
-
- snprintf(elemProp, sizeof(elemProp), "%s_wsX", name);
- if (configGetInt(themeConfig, elemProp, &intValue)) {
+ snprintf(elemProp, sizeof(elemProp), "%s.wsX", name);
+ if (cfgGetInt(themeConfig, elemProp, &intValue)) {
if (intValue < 0)
elem->wsX = screenWidth + intValue;
else
elem->wsX = intValue;
} else
elem->wsX = elem->posX;
-
- snprintf(elemProp, sizeof(elemProp), "%s_skip", name);
- if (configGetInt(themeConfig, elemProp, &intValue))
+ snprintf(elemProp, sizeof(elemProp), "%s.skip", name);
+ if (cfgGetInt(themeConfig, elemProp, &intValue))
elem->skip = intValue;
else
elem->skip = 0;
-
return elem;
}
// Internal elements ////////////////////////////////////////////////////////////////////////////////////////////////////////
-static void drawBackground(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem)
+static void drawBackground(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem)
{
guiDrawBGPlasma();
}
-static void initBackground(const char *themePath, config_set_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name, const char *pattern, int count, const char *texture)
+static void initBackground(const char *themePath, config_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name, const char *pattern, int count, const char *texture)
{
mutable_image_t *mutableImage = initMutableImage(themePath, themeConfig, theme, name, elem->type, pattern, count, texture, NULL);
elem->extended = mutableImage;
@@ -793,11 +1037,11 @@ static void initBackground(const char *themePath, config_set_t *themeConfig, the
elem->drawElem = &drawBackground;
}
-static void drawMenuIcon(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem)
+static void drawMenuIcon(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem)
{
GSTEXTURE *menuIconTex = thmGetTexture(menu->item->icon_id);
if (menuIconTex && menuIconTex->Mem)
- rmDrawPixmap(menuIconTex, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol);
+ rmDrawPixmap(menuIconTex, elem->posX, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol, 0);
}
static int findMenuNext(struct menu_list *menu)
@@ -818,17 +1062,17 @@ static int findMenuPrev(struct menu_list *menu)
return prev == NULL ? 0 : prev->item->visible;
}
-static void drawMenuText(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem)
+static void drawMenuText(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem)
{
GSTEXTURE *leftIconTex = NULL, *rightIconTex = NULL;
int iconOne, iconTwo;
if (gTheme->coverflow != NULL) {
- iconOne = BUTTON_DPAD_UP_ICON;
- iconTwo = BUTTON_DPAD_DOWN_ICON;
+ iconOne = UP_ICON;
+ iconTwo = DOWN_ICON;
} else {
- iconOne = BUTTON_DPAD_LEFT_ICON;
- iconTwo = BUTTON_DPAD_RIGHT_ICON;
+ iconOne = LEFT_ICON;
+ iconTwo = RIGHT_ICON;
}
if (findMenuPrev(menu) != 0)
@@ -839,19 +1083,19 @@ static void drawMenuText(struct menu_list *menu, struct submenu_list *item, conf
if (elem->aligned) {
int offset = elem->width >> 1;
if (leftIconTex && leftIconTex->Mem)
- rmDrawPixmap(leftIconTex, elem->posX - offset, elem->posY, elem->aligned, 20, 20, elem->scaled, gDefaultCol);
+ rmDrawPixmap(leftIconTex, elem->posX - offset, elem->posY, elem->aligned, 20, 20, elem->scaled, gDefaultCol, 0);
if (rightIconTex && rightIconTex->Mem)
- rmDrawPixmap(rightIconTex, elem->posX + offset, elem->posY, elem->aligned, 20, 20, elem->scaled, gDefaultCol);
+ rmDrawPixmap(rightIconTex, elem->posX + offset, elem->posY, elem->aligned, 20, 20, elem->scaled, gDefaultCol, 0);
} else {
if (leftIconTex && leftIconTex->Mem)
- rmDrawPixmap(leftIconTex, elem->posX - leftIconTex->Width, elem->posY, elem->aligned, 20, 20, elem->scaled, gDefaultCol);
+ rmDrawPixmap(leftIconTex, elem->posX - leftIconTex->Width, elem->posY, elem->aligned, 20, 20, elem->scaled, gDefaultCol, 0);
if (rightIconTex && rightIconTex->Mem)
- rmDrawPixmap(rightIconTex, elem->posX + elem->width, elem->posY, elem->aligned, 20, 20, elem->scaled, gDefaultCol);
+ rmDrawPixmap(rightIconTex, elem->posX + elem->width, elem->posY, elem->aligned, 20, 20, elem->scaled, gDefaultCol, 0);
}
fntRenderString(elem->font, elem->posX, elem->posY, elem->aligned, 0, 0, menuItemGetText(menu->item), elem->color);
}
-static void drawBDMIndex(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem)
+static void drawBDMIndex(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem)
{
item_list_t *itemList = menu->item->userdata;
// Only render for bdm modes and if current mode is visible
@@ -869,10 +1113,10 @@ static void drawBDMIndex(struct menu_list *menu, struct submenu_list *item, conf
GSTEXTURE *indexTex = thmGetTexture(texLookupInternalTexId(&imgName[0]));
if (indexTex && indexTex->Mem)
- rmDrawPixmap(indexTex, x, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol);
+ rmDrawPixmap(indexTex, x, elem->posY, elem->aligned, elem->width, elem->height, elem->scaled, gDefaultCol, 0);
}
-static void drawItemsList(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem)
+static void drawItemsList(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem)
{
if (item) {
items_list_t *itemsList = (items_list_t *)elem->extended;
@@ -883,7 +1127,7 @@ static void drawItemsList(struct menu_list *menu, struct submenu_list *item, con
posY -= elem->height >> 1;
}
- submenu_list_t *ps = menu->item->pagestart;
+ submenu_list_t *ps = (gTheme->coverflow != NULL) ? item : menu->item->pagestart;
int others = 0;
u64 color;
@@ -896,10 +1140,10 @@ static void drawItemsList(struct menu_list *menu, struct submenu_list *item, con
if (itemsList->decoratorImage) {
GSTEXTURE *itemIconTex = getGameImageTexture(itemsList->decoratorImage->cache, menu->item->userdata, &ps->item);
if (itemIconTex && itemIconTex->Mem)
- rmDrawPixmap(itemIconTex, posX, posY, elem->aligned, DECORATOR_SIZE, DECORATOR_SIZE, elem->scaled, gDefaultCol);
+ rmDrawPixmap(itemIconTex, posX, posY, elem->aligned, DECORATOR_SIZE, DECORATOR_SIZE, elem->scaled, gDefaultCol, 0);
else {
if (itemsList->decoratorImage->defaultTexture)
- rmDrawPixmap(&itemsList->decoratorImage->defaultTexture->source, posX, posY, elem->aligned, DECORATOR_SIZE, DECORATOR_SIZE, elem->scaled, gDefaultCol);
+ rmDrawPixmap(&itemsList->decoratorImage->defaultTexture->source, posX, posY, elem->aligned, DECORATOR_SIZE, DECORATOR_SIZE, elem->scaled, gDefaultCol, 0);
}
fntRenderString(elem->font, elem->posX + DECORATOR_SIZE, posY, elem->aligned, elem->width, elem->height, submenuItemGetText(&ps->item), color);
} else
@@ -918,9 +1162,9 @@ static void drawItemsList(struct menu_list *menu, struct submenu_list *item, con
if (itemsList->decoratorImage)
favMarkX += DECORATOR_SIZE;
- GSTEXTURE *favMark = thmGetTexture(MARK_STAR);
+ GSTEXTURE *favMark = thmGetTexture(FAV_MARK);
if (favMark && favMark->Mem)
- rmDrawPixmap(favMark, favMarkX + 2, favMarkPosY, ALIGN_NONE, 8, 8, elem->scaled, gDefaultCol);
+ rmDrawPixmap(favMark, favMarkX + 2, favMarkPosY, ALIGN_NONE, 8, 8, elem->scaled, gDefaultCol, 0);
}
posY += MENU_ITEM_HEIGHT;
@@ -929,7 +1173,7 @@ static void drawItemsList(struct menu_list *menu, struct submenu_list *item, con
}
}
-static void initItemsList(const char *themePath, config_set_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name, const char *decorator)
+static void initItemsList(const char *themePath, config_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name, const char *decorator)
{
char elemProp[64];
@@ -945,8 +1189,8 @@ static void initItemsList(const char *themePath, config_set_t *themeConfig, them
LOG("THEMES ItemsList %s: displaying %d elems, item height: %d\n", name, itemsList->displayedItems, elem->height);
itemsList->decorator = NULL;
- snprintf(elemProp, sizeof(elemProp), "%s_decorator", name);
- configGetStr(themeConfig, elemProp, &decorator);
+ snprintf(elemProp, sizeof(elemProp), "%s.decorator", name);
+ cfgGetStr(themeConfig, elemProp, &decorator);
if (decorator)
itemsList->decorator = decorator; // Will be used later (thmValidate)
@@ -958,7 +1202,7 @@ static void initItemsList(const char *themePath, config_set_t *themeConfig, them
elem->drawElem = &drawItemsList;
}
-static void drawItemText(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem)
+static void drawItemText(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem)
{
if (item) {
item_list_t *support = menu->item->userdata;
@@ -966,7 +1210,7 @@ static void drawItemText(struct menu_list *menu, struct submenu_list *item, conf
}
}
-static void drawHintText(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem)
+static void drawHintText(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem)
{
menu_hint_item_t *hint = menu->item->hints;
if (hint) {
@@ -982,10 +1226,10 @@ static void drawHintText(struct menu_list *menu, struct submenu_list *item, conf
}
}
-static void drawInfoHintText(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem)
+static void drawInfoHintText(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem)
{
int infoHints[2] = {_STR_RUN, _STR_BACK};
- int infoIcons[2] = {BUTTON_SYMBOL_CIRCLE_ICON, BUTTON_SYMBOL_CROSS_ICON};
+ int infoIcons[2] = {CIRCLE_ICON, CROSS_ICON};
int x = elem->posX;
if (elem->aligned)
@@ -998,101 +1242,184 @@ static void drawInfoHintText(struct menu_list *menu, struct submenu_list *item,
// Coverflow ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-int isAnimating = 0; // Animation flag
-int animationDirection = 0; // 1 for right (next), -1 for left (prev)
+static int isAnimating = 0; // Animation flag
+static int animationDirection = 0; // -1 for right (next), 1 for left (prev)
+static clock_t animationStartTime = 0;
+
+int gCoverflowCount = 3;
+int gCoverflowCenterScale = 30;
+int gCoverflowAnimSpeed = 200;
+int gCoverflowDimCovers = 0;
-static void drawCoverFlow(struct menu_list *menu, struct submenu_list *item, config_set_t *config, struct theme_element *elem)
+void thmTriggerCoverflowAnim(int direction)
+{
+ isAnimating = 1;
+ animationDirection = direction;
+ animationStartTime = clock();
+}
+
+static void drawCoverFlow(struct menu_list *menu, struct submenu_list *item, render_ctx_t *ctx, struct theme_element *elem)
{
if (item == NULL)
return;
+ int coverCount = gCoverflowCount;
+ int centerIndex = coverCount / 2;
+
int coverSpacing = 0;
int coverHeight = elem->height;
int coverWidth = gWideScreen ? rmWideScale(elem->width) : elem->width;
- int totalCoversWidth = COVERFLOW_COUNT * coverWidth;
+ int origCoverWidth = coverWidth;
+
+ int coverYOffset = 0;
+ int maxCoverWidth = (screenWidth - (coverCount - 1) * 10) / coverCount;
+ if (coverWidth > maxCoverWidth) {
+ int origHeight = coverHeight;
+ coverHeight = (coverHeight * maxCoverWidth) / coverWidth;
+ coverWidth = maxCoverWidth;
+ coverYOffset = (origHeight - coverHeight) / 2;
+ }
+
+ float coverScaleRatio = (origCoverWidth > 0) ? (float)coverWidth / (float)origCoverWidth : 1.0f;
+
+ int totalCoversWidth = coverCount * coverWidth;
int totalRemainingSpace = screenWidth - totalCoversWidth;
- if (totalRemainingSpace >= 0) {
- coverSpacing = totalRemainingSpace / 4; // Divide by 4 to distribute the space equally (2 spaces between covers + 2 on the sides)
- // If coverSpacing ends up negative set it to a minimum value
- if (coverSpacing < 0)
- coverSpacing = 0;
- }
+ coverSpacing = totalRemainingSpace / (coverCount + 1); // Divide by covercount to distribute the space equally (4 spaces for 3 covers.. 6 spaces for 5)
+ // If coverSpacing ends up negative set it to a minimum value
+ if (coverSpacing < 0)
+ coverSpacing = 0;
if (gWideScreen)
coverSpacing = rmWideScale(coverSpacing);
int coverDistance = coverWidth + coverSpacing;
- int posX = (coverSpacing << 1) + (coverWidth >> 1);
-
- float animationProgress = 0.0f; // Progress of animation (0.0 - 1.0)
- float animationSpeed = 0.05f; // Speed of animation
+ int totalGroupWidth = (coverCount - 1) * coverDistance + coverWidth;
+ int basePosX = (screenWidth - totalGroupWidth) / 2 + (coverWidth >> 1) + (coverWidth * gTheme->coverflowCoverOffset / 256);
struct
{
submenu_list_t *game;
mutable_image_t *cover;
GSTEXTURE *texture;
- } covers[COVERFLOW_COUNT] = {
- {item->prev, NULL, NULL},
- {item, NULL, NULL},
- {item->next, NULL, NULL}};
+ } covers[5];
+
+ int ci;
+ for (ci = 0; ci < coverCount; ci++) {
+ covers[ci].game = NULL;
+ covers[ci].cover = NULL;
+ covers[ci].texture = NULL;
+ }
+ covers[centerIndex].game = item;
+
+ // Populate left, walk backwards from center
+ submenu_list_t *cur = item;
+ for (ci = centerIndex - 1; ci >= 0; ci--) {
+ submenu_list_t *prev = cur->prev;
+ if (prev == NULL && menu->item->last != NULL)
+ prev = menu->item->last;
+ if (prev == NULL || prev == item)
+ break;
+ covers[ci].game = prev;
+ cur = prev;
+ }
+
+ // Populate right, walk forwards from center
+ cur = item;
+ for (ci = centerIndex + 1; ci < coverCount; ci++) {
+ submenu_list_t *next = cur->next;
+ if (next == NULL)
+ next = menu->item->submenu;
+ if (next == NULL || next == item)
+ break;
+ covers[ci].game = next;
+ cur = next;
+ }
+
+ float eased = 1.0f;
+ float animOffset = 0.0f;
if (isAnimating) {
- posX += animationDirection * (animationProgress * coverDistance);
- animationProgress += animationSpeed;
- if (animationProgress >= 1.0f) {
+ if (gCoverflowAnimSpeed <= 0) {
isAnimating = 0;
- animationProgress = 0.0f;
+ } else {
+ clock_t elapsed = clock() - animationStartTime;
+ float t = (float)elapsed / ((float)gCoverflowAnimSpeed * CLOCKS_PER_SEC / 1000);
+
+ if (t >= 1.0f) {
+ t = 1.0f;
+ isAnimating = 0;
+ animationStartTime = 0;
+ }
+
+ float inv = 1.0f - t;
+ eased = 1.0f - inv * inv * inv;
+ animOffset = (float)animationDirection * (float)coverDistance * (eased - 1.0f);
}
}
- int scaling = 30;
- for (int i = 0; i < COVERFLOW_COUNT; i++) {
+ int posX = basePosX + (int)animOffset;
+ int scaling = gCoverflowCenterScale;
+
+ // direction=-1 (next): covers[centerIndex - 1] is visually at center at t=0
+ // direction=1 (prev): covers[centerIndex + 1] is visually at center at t=0
+ int leavingIndex = (animationDirection > 0) ? (centerIndex + 1) : (centerIndex - 1);
+
+ for (int i = 0; i < coverCount; i++) {
int renderPosX = posX;
posX += coverDistance;
if (covers[i].game == NULL)
continue;
+ // Get the appropriate element for this item (handles APP_MODE on FAV page)
+ covers[i].cover = (mutable_image_t *)elem->extended;
+ theme_element_t *coverElem = thmGetElemForItem(elem, menu, covers[i].game);
+ mutable_image_t *img = (mutable_image_t *)coverElem->extended;
+ item_list_t *sourceList = thmGetItemSource(menu, covers[i].game);
+
+ int baseCoverHeight = coverHeight;
+ if (coverElem != elem && coverElem->height != DIM_UNDEF)
+ baseCoverHeight = (int)(coverElem->height * coverScaleRatio);
+
int currentCoverWidth = coverWidth;
- int currentCoverHeight = coverHeight;
+ int currentCoverHeight = baseCoverHeight;
int overlayOffsetY = 0;
int overlayOffsetX = 0;
- if (i == 1) {
- currentCoverWidth += scaling;
- currentCoverHeight += scaling;
- overlayOffsetY = scaling;
- overlayOffsetX = scaling * (gWideScreen ? (4.0f / 3.0f) : 1.0f) - (scaling * ((4.0f / 3.0f) - 1.0f) / 2.0f);
+ // Interpolate scaling.. center cover grows in.. leaving cover shrinks out
+ int currentScaling = 0;
+ if (i == centerIndex) {
+ // New selection.. grows into center as animation progresses
+ float growFactor = isAnimating ? eased : 1.0f;
+ currentScaling = (int)(scaling * growFactor);
+ } else if (isAnimating && i == leavingIndex) {
+ // Cover leaving center.. shrinks as animation progresses
+ currentScaling = (int)(scaling * (1.0f - eased));
}
- covers[i].cover = (mutable_image_t *)elem->extended;
- covers[i].texture = getGameImageTexture(covers[i].cover->cache, menu->item->userdata, &covers[i].game->item);
- if (!covers[i].texture || !covers[i].texture->Mem)
- covers[i].texture = (covers[i].cover->defaultTexture) ? &covers[i].cover->defaultTexture->source : thmGetTexture(COVER_GAMES);
-
- if (covers[i].cover->overlayTexture) {
- if (elem->reflection)
- rmDrawOverlayPixmapWithReflection(&covers[i].cover->overlayTexture->source, renderPosX, elem->posY, ALIGN_CENTER, currentCoverWidth, currentCoverHeight, SCALING_NONE, gDefaultCol,
- covers[i].texture, covers[i].cover->overlayTexture->upperLeft_x, covers[i].cover->overlayTexture->upperLeft_y, covers[i].cover->overlayTexture->upperRight_x + overlayOffsetX,
- covers[i].cover->overlayTexture->upperRight_y, covers[i].cover->overlayTexture->lowerLeft_x, covers[i].cover->overlayTexture->lowerLeft_y + overlayOffsetY,
- covers[i].cover->overlayTexture->lowerRight_x + overlayOffsetX, covers[i].cover->overlayTexture->lowerRight_y + overlayOffsetY);
- else
- rmDrawOverlayPixmap(&covers[i].cover->overlayTexture->source, renderPosX, elem->posY, ALIGN_CENTER, currentCoverWidth, currentCoverHeight, SCALING_NONE, gDefaultCol,
- covers[i].texture, covers[i].cover->overlayTexture->upperLeft_x, covers[i].cover->overlayTexture->upperLeft_y, covers[i].cover->overlayTexture->upperRight_x + overlayOffsetX,
- covers[i].cover->overlayTexture->upperRight_y, covers[i].cover->overlayTexture->lowerLeft_x, covers[i].cover->overlayTexture->lowerLeft_y + overlayOffsetY,
- covers[i].cover->overlayTexture->lowerRight_x + overlayOffsetX, covers[i].cover->overlayTexture->lowerRight_y + overlayOffsetY);
- } else {
- if (elem->reflection)
- rmDrawPixmapWithReflection(covers[i].texture, renderPosX, elem->posY, ALIGN_CENTER, currentCoverWidth, currentCoverHeight, SCALING_NONE, gDefaultCol);
- else
- rmDrawPixmap(covers[i].texture, renderPosX, elem->posY, ALIGN_CENTER, currentCoverWidth, currentCoverHeight, SCALING_NONE, gDefaultCol);
+ if (currentScaling > 0) {
+ currentCoverWidth += currentScaling;
+ currentCoverHeight += currentScaling;
+ overlayOffsetY = currentScaling;
+ overlayOffsetX = currentScaling * (gWideScreen ? (4.0f / 3.0f) : 1.0f) - (currentScaling * ((4.0f / 3.0f) - 1.0f) / 2.0f);
}
+
+ covers[i].texture = getGameImageTexture(img->cache, sourceList, &covers[i].game->item);
+ if (!covers[i].texture || !covers[i].texture->Mem)
+ covers[i].texture = img->defaultTexture ? &img->defaultTexture->source : thmGetTexture(COVER_DEFAULT);
+
+ u64 coverColor = gDefaultCol;
+ if (gCoverflowDimCovers && i != centerIndex)
+ coverColor = GS_SETREG_RGBA(0x80, 0x80, 0x80, 0x40);
+
+ int thisCoverYOffset = (coverElem != elem && coverElem->height != DIM_UNDEF) ? (coverElem->height - baseCoverHeight) / 2 : coverYOffset;
+
+ thmDrawTexture(covers[i].texture, img, renderPosX, coverElem->posY + thisCoverYOffset, ALIGN_CENTER, currentCoverWidth, currentCoverHeight, SCALING_NONE, coverColor, elem->reflection, overlayOffsetX, overlayOffsetY, coverScaleRatio);
}
}
-static void initCoverflow(const char *themePath, config_set_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name, int count, const char *texture, const char *overlay)
+static void initCoverflow(const char *themePath, config_t *themeConfig, theme_t *theme, theme_element_t *elem, const char *name, int count, const char *texture, const char *overlay)
{
mutable_image_t *mutableImage = initMutableImage(themePath, themeConfig, theme, name, ELEM_TYPE_GAME_IMAGE, "COV", count, texture, overlay);
elem->extended = mutableImage;
@@ -1106,7 +1433,7 @@ static void initCoverflow(const char *themePath, config_set_t *themeConfig, them
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-static void validateBackgroundElems(const char *themePath, config_set_t *themeConfig, theme_t *theme, theme_elems_t *mainElems, theme_elems_t *infoElems)
+static void validateBackgroundElems(const char *themePath, config_t *themeConfig, theme_t *theme, theme_elems_t *mainElems, theme_elems_t *infoElems)
{
if (!mainElems->first || (mainElems->first->type != ELEM_TYPE_BACKGROUND)) {
LOG("THEMES No valid background found for main, add default BG_ART\n");
@@ -1127,7 +1454,7 @@ static void validateBackgroundElems(const char *themePath, config_set_t *themeCo
}
}
-static void validateItemsList(const char *themePath, config_set_t *themeConfig, theme_t *theme, theme_element_t *list, theme_elems_t *mainElems)
+static void validateItemsList(const char *themePath, config_t *themeConfig, theme_t *theme, theme_element_t *list, theme_elems_t *mainElems)
{
if (list) {
items_list_t *itemsList = (items_list_t *)list->extended;
@@ -1158,7 +1485,7 @@ static void validateItemsList(const char *themePath, config_set_t *themeConfig,
}
}
-static void validateGUIElems(const char *themePath, config_set_t *themeConfig, theme_t *theme)
+static void validateGUIElems(const char *themePath, config_t *themeConfig, theme_t *theme)
{
// 1. check we have a valid Background elements
validateBackgroundElems(themePath, themeConfig, theme, &theme->mainElems, &theme->infoElems);
@@ -1171,18 +1498,18 @@ static void validateGUIElems(const char *themePath, config_set_t *themeConfig, t
validateItemsList(themePath, themeConfig, theme, theme->favsItemsList, &theme->favsMainElems);
}
-static int addGUIElem(const char *themePath, config_set_t *themeConfig, theme_t *theme, theme_elems_t *elems, const char *type, const char *name)
+static int addGUIElem(const char *themePath, config_t *themeConfig, theme_t *theme, theme_elems_t *elems, const char *type, const char *name)
{
int enabled = 1;
char elemProp[64];
theme_element_t *elem = NULL;
- snprintf(elemProp, sizeof(elemProp), "%s_enabled", name);
- configGetInt(themeConfig, elemProp, &enabled);
+ snprintf(elemProp, sizeof(elemProp), "%s.enabled", name);
+ cfgGetInt(themeConfig, elemProp, &enabled);
if (enabled) {
- snprintf(elemProp, sizeof(elemProp), "%s_type", name);
- configGetStr(themeConfig, elemProp, &type);
+ snprintf(elemProp, sizeof(elemProp), "%s.type", name);
+ cfgGetStr(themeConfig, elemProp, &type);
if (type) {
if (!strcmp(elementsType[ELEM_TYPE_ATTRIBUTE_TEXT], type)) {
elem = initBasic(themePath, themeConfig, theme, name, ELEM_TYPE_ATTRIBUTE_TEXT, 0, 0, ALIGN_CENTER, DIM_UNDEF, DIM_UNDEF, SCALING_RATIO, theme->textColor, theme->fonts[0]);
@@ -1224,13 +1551,10 @@ static int addGUIElem(const char *themePath, config_set_t *themeConfig, theme_t
initItemsList(themePath, themeConfig, theme, elem, name, NULL);
theme->favsItemsList = elem;
}
- } else if (!strcmp(elementsType[ELEM_TYPE_ITEM_ICON], type) && gDiscEnableArt) {
+ } else if (!strcmp(elementsType[ELEM_TYPE_ITEM_ICON], type)) {
elem = initBasic(themePath, themeConfig, theme, name, ELEM_TYPE_GAME_IMAGE, 0, 0, ALIGN_CENTER, 64, 64, SCALING_RATIO, gDefaultCol, theme->fonts[0]);
initGameImage(themePath, themeConfig, theme, elem, name, "ICO", 20, NULL, NULL);
- } else if (!strcmp(elementsType[ELEM_TYPE_ITEM_COVER_GAMES], type)) {
- elem = initBasic(themePath, themeConfig, theme, name, ELEM_TYPE_GAME_IMAGE, 0, 0, ALIGN_CENTER, DIM_UNDEF, DIM_UNDEF, SCALING_RATIO, gDefaultCol, theme->fonts[0]);
- initGameImage(themePath, themeConfig, theme, elem, name, "COV", 10, NULL, NULL);
- } else if (!strcmp(elementsType[ELEM_TYPE_ITEM_COVER_APPS], type)) {
+ } else if (!strcmp(elementsType[ELEM_TYPE_ITEM_COVER], type)) {
elem = initBasic(themePath, themeConfig, theme, name, ELEM_TYPE_GAME_IMAGE, 0, 0, ALIGN_CENTER, DIM_UNDEF, DIM_UNDEF, SCALING_RATIO, gDefaultCol, theme->fonts[0]);
initGameImage(themePath, themeConfig, theme, elem, name, "COV", 10, NULL, NULL);
} else if (!strcmp(elementsType[ELEM_TYPE_ITEM_TEXT], type)) {
@@ -1356,8 +1680,10 @@ static int thmLoadResource(GSTEXTURE *texture, int texId, const char *themePath,
{
int success = -1;
- if (themePath != NULL)
- success = texDiscoverLoad(texture, themePath, texId, 0); // only set success here
+ if (themePath != NULL) {
+ int fromTar = (strchr(themePath, ':') == NULL); // no device -> tar theme
+ success = texDiscoverLoad(texture, themePath, texId, fromTar ? RES_TAR_THM : RES_FILESYSTEM); // only set success here
+ }
if ((success < 0) && useDefault)
texLoadInternal(texture, texId); // we don't mind the result of "default"
@@ -1381,13 +1707,11 @@ static void thmSetColors(theme_t *theme)
}
}
-static void thmLoadFonts(config_set_t *themeConfig, const char *themePath, theme_t *theme)
+static void thmLoadFonts(config_t *themeConfig, const char *themePath, theme_t *theme)
{
- int fntID; // theme side font id, not the fntSys handle
+ int fntID;
for (fntID = 0; fntID < THM_MAX_FONTS; ++fntID) {
- // does the font by the key exist?
char fntKey[16];
-
if (fntID == 0) {
snprintf(fntKey, sizeof(fntKey), "default_font");
theme->fonts[0] = FNT_DEFAULT;
@@ -1397,9 +1721,9 @@ static void thmLoadFonts(config_set_t *themeConfig, const char *themePath, theme
}
const char *fntFile;
- if (configGetStr(themeConfig, fntKey, &fntFile)) {
+ if (cfgGetStr(themeConfig, fntKey, &fntFile)) {
char fullPath[128];
- int fontSize;
+ int fontSize = FNTSYS_DEFAULT_SIZE;
char sizeKey[64];
int fntHandle = FNT_ERROR;
@@ -1408,16 +1732,24 @@ static void thmLoadFonts(config_set_t *themeConfig, const char *themePath, theme
else
snprintf(sizeKey, sizeof(sizeKey), "font%d_size", fntID);
- if (!configGetInt(themeConfig, sizeKey, &fontSize) || fontSize <= 0)
+ cfgGetInt(themeConfig, sizeKey, &fontSize);
+ if (fontSize <= 0)
fontSize = FNTSYS_DEFAULT_SIZE;
if (themePath) {
snprintf(fullPath, sizeof(fullPath), "%s%s", themePath, fntFile);
- fntHandle = fntLoadFile(fullPath, fontSize);
+ const TarEntryBase *entry = tarFind(TAR_KIND_THM, fullPath);
+ if (entry) {
+ void *buf = tarGet(TAR_KIND_THM, fullPath);
+ if (buf) {
+ fntHandle = fntLoadFromBuffer(buf, entry->rawSize, fontSize);
+ // do not free
+ }
+ } else
+ fntHandle = fntLoadFile(fullPath, fontSize);
} else
fntHandle = fntLoadFile(NULL, fontSize);
- // Do we have a valid font? Assign the font handle to the theme font slot
if (fntHandle != FNT_ERROR)
theme->fonts[fntID] = fntHandle;
}
@@ -1452,115 +1784,148 @@ static void thmLoad(const char *themePath, int themeID)
newT->gamesItemsList = NULL;
newT->appsItemsList = NULL;
newT->favsItemsList = NULL;
- newT->logoIcon = NULL;
- newT->logoIconCount = LOGO_21_ICON - LOGO_1_ICON + 1;
newT->loadingIcon = NULL;
- newT->loadingIconCount = LOADING_8_ICON - LOADING_1_ICON + 1;
+ newT->loadingIconCount = LOAD7_ICON - LOAD1_ICON + 1;
newT->coverflow = NULL;
+ newT->coverflowCoverOffset = 0;
+
+ config_t themeConfig;
+ config_init(&themeConfig);
- config_set_t *themeConfig = NULL;
if (!themePath && themeID == 0) {
- // No theme specified. Prepare and load the default theme.
- themeConfig = configAlloc(0, NULL, NULL);
- configReadBuffer(themeConfig, &theme_list_cfg, size_theme_list_cfg);
+ char *buf = malloc(size_theme_list_cfg + 1);
+ if (buf) {
+ memcpy(buf, &theme_list_cfg, size_theme_list_cfg);
+ buf[size_theme_list_cfg] = '\0';
+ config_read_string(&themeConfig, buf);
+ free(buf);
+ }
} else if (!themePath && themeID == 1) {
- themeConfig = configAlloc(0, NULL, NULL);
- configReadBuffer(themeConfig, &theme_coverflow_cfg, size_theme_coverflow_cfg);
+ char *buf = malloc(size_theme_coverflow_cfg + 1);
+ if (buf) {
+ memcpy(buf, &theme_coverflow_cfg, size_theme_coverflow_cfg);
+ buf[size_theme_coverflow_cfg] = '\0';
+ config_read_string(&themeConfig, buf);
+ free(buf);
+ }
} else {
- snprintf(path, sizeof(path), "%sconf_theme.cfg", themePath);
- themeConfig = configAlloc(0, NULL, path);
- configRead(themeConfig); // try to load the theme config file. If it does not exist, defaults will be used.
+ snprintf(path, sizeof(path), "%swopl_theme.cfg", themePath);
+ const TarEntryBase *entry = tarFind(TAR_KIND_THM, path);
+ if (entry) {
+ void *buf = tarGet(TAR_KIND_THM, path);
+ if (buf) {
+ config_read_string(&themeConfig, buf);
+ free(buf);
+ }
+ } else {
+ if (!config_read_file(&themeConfig, path)) {
+ log_config_error(path, &themeConfig);
+
+ // DELETE_WITH_MIGRATION v
+ config_destroy(&themeConfig);
+ config_init(&themeConfig);
+
+ char oldPath[256];
+ snprintf(oldPath, sizeof(oldPath), "%sconf_theme.cfg", themePath);
+
+ if (cfgMigrateLegacyTheme(oldPath, path)) {
+ if (!config_read_file(&themeConfig, path))
+ log_config_error(path, &themeConfig);
+ }
+ // DELETE_WITH_MIGRATION ^
+ }
+ }
}
+ cfgValidateBegin(themePath ? path : (themeID == 1 ? "" : ""));
+
int intValue;
- if (configGetInt(themeConfig, "use_default", &intValue))
+ if (cfgGetInt(&themeConfig, "use_default", &intValue))
newT->useDefault = intValue;
- if (configGetInt(themeConfig, "use_real_height", &intValue)) {
+ if (cfgGetInt(&themeConfig, "use_real_height", &intValue)) {
if (intValue)
newT->usedHeight = screenHeight;
}
- configGetColor(themeConfig, "bg_color", newT->bgColor);
+ thmGetColorSetting(&themeConfig, "bg_color", newT->bgColor);
unsigned char color[3];
- if (configGetColor(themeConfig, "text_color", color))
+ if (thmGetColorSetting(&themeConfig, "text_color", color))
newT->textColor = GS_SETREG_RGBA(color[0], color[1], color[2], 0x80);
- if (configGetColor(themeConfig, "ui_text_color", color))
+ if (thmGetColorSetting(&themeConfig, "ui_text_color", color))
newT->uiTextColor = GS_SETREG_RGBA(color[0], color[1], color[2], 0x80);
- if (configGetColor(themeConfig, "sel_text_color", color))
+ if (thmGetColorSetting(&themeConfig, "sel_text_color", color))
newT->selTextColor = GS_SETREG_RGBA(color[0], color[1], color[2], 0x80);
- if (configGetColor(themeConfig, "plasma_blend_color", color))
+ if (thmGetColorSetting(&themeConfig, "plasma_blend_color", color))
newT->plasBlendColor = GS_SETREG_RGBA(color[0], color[1], color[2], 0x80);
// before loading the element definitions, we have to have the fonts prepared
// for that, we load the fonts and a translation table
- thmLoadFonts(themeConfig, themePath, newT);
+ thmLoadFonts(&themeConfig, themePath, newT);
int i = 1, j;
snprintf(path, sizeof(path), "main0");
- while (addGUIElem(themePath, themeConfig, newT, &newT->mainElems, NULL, path))
+ while (addGUIElem(themePath, &themeConfig, newT, &newT->mainElems, NULL, path))
snprintf(path, sizeof(path), "main%d", i++);
for (j = 0; j < i; j++) {
snprintf(path, sizeof(path), "appsMain%d", j);
- if (addGUIElem(themePath, themeConfig, newT, &newT->appsMainElems, NULL, path))
+ if (addGUIElem(themePath, &themeConfig, newT, &newT->appsMainElems, NULL, path))
continue;
else {
snprintf(path, sizeof(path), "main%d", j);
- addGUIElem(themePath, themeConfig, newT, &newT->appsMainElems, NULL, path);
+ addGUIElem(themePath, &themeConfig, newT, &newT->appsMainElems, NULL, path);
}
}
for (j = 0; j < i; j++) {
snprintf(path, sizeof(path), "favsMain%d", j);
- if (addGUIElem(themePath, themeConfig, newT, &newT->favsMainElems, NULL, path))
+ if (addGUIElem(themePath, &themeConfig, newT, &newT->favsMainElems, NULL, path))
continue;
else {
snprintf(path, sizeof(path), "main%d", j);
- addGUIElem(themePath, themeConfig, newT, &newT->favsMainElems, NULL, path);
+ addGUIElem(themePath, &themeConfig, newT, &newT->favsMainElems, NULL, path);
}
}
i = 1;
snprintf(path, sizeof(path), "info0");
- while (addGUIElem(themePath, themeConfig, newT, &newT->infoElems, NULL, path))
+ while (addGUIElem(themePath, &themeConfig, newT, &newT->infoElems, NULL, path))
snprintf(path, sizeof(path), "info%d", i++);
for (j = 0; j < i; j++) {
snprintf(path, sizeof(path), "appsInfo%d", j);
- if (addGUIElem(themePath, themeConfig, newT, &newT->appsInfoElems, NULL, path))
+ if (addGUIElem(themePath, &themeConfig, newT, &newT->appsInfoElems, NULL, path))
continue;
else {
snprintf(path, sizeof(path), "info%d", j);
- addGUIElem(themePath, themeConfig, newT, &newT->appsInfoElems, NULL, path);
+ addGUIElem(themePath, &themeConfig, newT, &newT->appsInfoElems, NULL, path);
}
}
for (j = 0; j < i; j++) {
snprintf(path, sizeof(path), "favsInfo%d", j);
- if (addGUIElem(themePath, themeConfig, newT, &newT->favsInfoElems, NULL, path))
+ if (addGUIElem(themePath, &themeConfig, newT, &newT->favsInfoElems, NULL, path))
continue;
else {
snprintf(path, sizeof(path), "info%d", j);
- addGUIElem(themePath, themeConfig, newT, &newT->favsInfoElems, NULL, path);
+ addGUIElem(themePath, &themeConfig, newT, &newT->favsInfoElems, NULL, path);
}
}
if (themePath)
- validateGUIElems(themePath, themeConfig, newT);
+ validateGUIElems(themePath, &themeConfig, newT);
newT->itemsList = newT->gamesItemsList;
- configFree(themeConfig);
-
LOG("THEMES Number of cache: %d\n", newT->gameCacheCount);
LOG("THEMES Used height: %d\n", newT->usedHeight);
@@ -1571,12 +1936,11 @@ static void thmLoad(const char *themePath, int themeID)
const char *themePath_temp = themePath;
int customBusy = 0;
- for (i = LOGO_1_ICON; i <= LOGO_21_ICON; i++) {
- thmLoadResource(&newT->textures[i], i, themePath_temp, GS_PSM_CT32, newT->useDefault);
- }
- newT->logoIconCount = i - LOGO_1_ICON;
+ // LOGO, loaded here to avoid flickering during startup with device in AUTO + theme set
+ for (i = LOGO_01; i <= LOGO_21; i++)
+ texLoadInternal(&newT->textures[i], i);
- for (i = LOADING_1_ICON; i <= LOADING_8_ICON; i++) {
+ for (i = LOAD0_ICON; i <= LOAD7_ICON; i++) {
if (thmLoadResource(&newT->textures[i], i, themePath_temp, GS_PSM_CT32, newT->useDefault) >= 0)
customBusy = 1;
else {
@@ -1586,30 +1950,30 @@ static void thmLoad(const char *themePath, int themeID)
themePath_temp = NULL;
}
}
- newT->loadingIconCount = i - LOADING_1_ICON;
+ newT->loadingIconCount = i - LOAD1_ICON;
// Default cover for missing covers in coverflow.
- texLoadInternal(&newT->textures[COVER_GAMES], COVER_GAMES);
+ texLoadInternal(&newT->textures[COVER_DEFAULT], COVER_DEFAULT);
// Customizable icons
- for (i = CATEGORY_EMPTY_BDM_ICON; i <= BUTTON_START_ICON; i++)
+ for (i = BDM_ICON; i <= START_ICON; i++)
thmLoadResource(&newT->textures[i], i, themePath, GS_PSM_CT32, newT->useDefault);
- /* Not customizable icons - currently unused.
- for (i = L1_ICON; i <= R3_ICON; i++)
- thmLoadResource(&newT->textures[i], i, NULL, GS_PSM_CT32, 1); */
-
- if (!themePath)
- for (i = BADGE_EXEC_ELF_FORMAT; i <= BADGE_REGION_PAL; i++)
- thmLoadResource(&newT->textures[i], i, NULL, GS_PSM_CT32, 1);
+ for (i = ELF_FORMAT; i <= RATING_5; i++)
+ thmLoadResource(&newT->textures[i], i, themePath, GS_PSM_CT32, 1);
if (themePath) {
- if (configGetInt(themeConfig, "use_bg_main", &intValue)) {
+ if (cfgGetInt(&themeConfig, "use_settings_bg", &intValue)) {
if (intValue)
- thmLoadResource(&newT->textures[BG_MAIN], BG_MAIN, themePath, GS_PSM_CT32, 0);
+ thmLoadResource(&newT->textures[SETTINGS_BG], SETTINGS_BG, themePath, GS_PSM_CT32, 0);
}
} else
- texLoadInternal(&newT->textures[BG_MAIN], BG_MAIN);
+ texLoadInternal(&newT->textures[SETTINGS_BG], SETTINGS_BG);
+
+ cfgGetInt(&themeConfig, "coverflow_cover_offset", &newT->coverflowCoverOffset);
+
+ cfgValidateEnd();
+ config_destroy(&themeConfig);
gTheme = newT;
thmFree(curT);
@@ -1641,10 +2005,46 @@ int thmAddElements(char *path, const char *separator, int forceRefresh)
result = sbListDir(path, separator, THM_MAX_FILES - nThemes, &thmReadEntry);
nThemes += result;
+
+ int tarCount = 0;
+ char **tarNames = tarGetThemeNames(&tarCount);
+
+ if (tarNames && tarCount > 0) {
+ for (i = 0; i < tarCount; i++) {
+ int exists = 0;
+ for (int j = 0; j < nThemes; j++) {
+ if (strcmp(themes[j].name, tarNames[i]) == 0) {
+ exists = 1;
+ break;
+ }
+ }
+ if (exists)
+ continue;
+
+ if (nThemes >= THM_MAX_FILES)
+ break;
+
+ theme_file_t *currTheme = &themes[nThemes];
+ memset(currTheme, 0, sizeof(theme_file_t));
+
+ currTheme->name = strdup(tarNames[i]);
+
+ int len = strlen(currTheme->name);
+ currTheme->filePath = malloc(len + 2);
+ snprintf(currTheme->filePath, len + 2, "%s/", currTheme->name);
+
+ nThemes++;
+ }
+
+ for (i = 0; i < tarCount; i++)
+ free(tarNames[i]);
+ free(tarNames);
+ }
+
thmRebuildGuiNames();
- const char *temp;
- if (configGetStr(configGetByType(CONFIG_OPL), "theme", &temp)) {
+ const char *temp = wOPLGetThemeName();
+ if (temp) {
LOG("THEMES Trying to set again theme: %s\n", temp);
if (thmSetGuiValue(thmFindGuiID(temp), 0) && forceRefresh) {
for (i = 0; i < MODE_COUNT; i++)
@@ -1652,7 +2052,7 @@ int thmAddElements(char *path, const char *separator, int forceRefresh)
}
}
- return result;
+ return (result + tarCount);
}
void thmInit(void)