diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f1a7805
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/category_lite.elf
+/category_lite.prx
+/category_lite_lang.h
diff --git a/Makefile b/Makefile
index 37debcb..e3f48a3 100644
--- a/Makefile
+++ b/Makefile
@@ -1,47 +1,47 @@
-TARGET = category_lite
-STUBS = imports.o scePaf.o func_stubs.o
-CATEGORY_MODES = multims.o context.o vshitem.o mode.o selection.o
-HELPER = clearcache.o logger.o utils.o config.o filter.o
-OBJS = main.o category.o gcread.o gcpatches.o sysconf.o language.o
-OBJS += $(CATEGORY_MODES) $(HELPER) $(STUBS)
-LIBS = -lpsprtc -lpspreg
-
-EXTRA_TARGETS = category_lang
-EXTRA_CLEAN = category_lite_??.h
-
-ifeq (x$(CONFIG_LANG), x)
-CONFIG_LANG = en
-endif
-
-# use a psp-filer with VSH support or the plugin won't load on PRO firmware
-# edit: 6.20 Pro won't work with this. Bugs, bugs everywhere....
-all: category_lang
-# -psp-packer category_lite.prx
-
-category_lang:
- bin2c lang/category_lite_$(CONFIG_LANG).txt category_lite_lang.h category_lite_lang
-
-EXTRA_WARNS= -Wextra -Wfloat-equal -Wundef -Wshadow -Wpointer-arith -Wwrite-strings -Wunreachable-code
-CFLAGS =-O2 -Wall -G0 -std=c99 -fshort-wchar $(EXTRA_WARNS)
-
-ifeq ($(DEBUG), 1)
-CFLAGS+=-DDEBUG
-endif
-
-ifeq ($(BENCHMARK), 1)
-CFLAGS+=-DBENCHMARK
-endif
-
-ASFLAGS = $(CFLAGS)
-LDFLAGS = -mno-crt0 -nostartfiles
-
-BUILD_PRX = 1
-PRX_EXPORTS = exports.exp
-
-USE_USER_LIBS = 1
-USE_USER_LIBC = 1
-
-PSP_FW_VERSION=620
-
-PSPSDK=$(shell psp-config --pspsdk-path)
-include $(PSPSDK)/lib/build.mak
+TARGET = category_lite
+STUBS = imports.o scePaf.o func_stubs.o
+CATEGORY_MODES = multims.o context.o vshitem.o mode.o selection.o
+HELPER = clearcache.o logger.o utils.o config.o filter.o
+OBJS = main.o category.o gcread.o gcpatches.o sysconf.o language.o
+OBJS += $(CATEGORY_MODES) $(HELPER) $(STUBS)
+LIBS = -lpsprtc -lpspreg
+
+EXTRA_TARGETS = category_lang
+EXTRA_CLEAN = category_lite_??.h
+
+ifeq (x$(CONFIG_LANG), x)
+CONFIG_LANG = en
+endif
+
+# use a psp-filer with VSH support or the plugin won't load on PRO firmware
+# edit: 6.20 Pro won't work with this. Bugs, bugs everywhere....
+all: category_lang
+# -psp-packer category_lite.prx
+
+category_lang:
+ bin2c lang/category_lite_$(CONFIG_LANG).txt category_lite_lang.h category_lite_lang
+
+EXTRA_WARNS= -Wextra -Wfloat-equal -Wundef -Wshadow -Wpointer-arith -Wwrite-strings -Wunreachable-code
+CFLAGS =-O2 -Wall -G0 -std=c99 -fshort-wchar $(EXTRA_WARNS)
+
+ifeq ($(DEBUG), 1)
+CFLAGS+=-DDEBUG
+endif
+
+ifeq ($(BENCHMARK), 1)
+CFLAGS+=-DBENCHMARK
+endif
+
+ASFLAGS = $(CFLAGS)
+LDFLAGS = -nostartfiles
+
+BUILD_PRX = 1
+PRX_EXPORTS = exports.exp
+
+USE_USER_LIBS = 1
+USE_USER_LIBC = 1
+
+PSP_FW_VERSION=620
+
+PSPSDK=$(shell psp-config --pspsdk-path)
+include $(PSPSDK)/lib/build.mak
diff --git a/categories_lite.h b/categories_lite.h
index 7620725..e099477 100644
--- a/categories_lite.h
+++ b/categories_lite.h
@@ -1,228 +1,228 @@
-/*
- * this file is part of Game Categories Lite
- *
- * Copyright (C) 2011 Bubbletune
- * Copyright (C) 2011 Codestation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#ifndef CATEGORY_LITE_
-#define CATEGORY_LITE_
-
-#include
-#include "gcpatches.h"
-
-int sceKernelGetCompiledSdkVersion();
-
-#define MAKE_CALL(a, f) _sw(0x0C000000 | (((u32)(f) >> 2) & 0x03ffffff), a);
-#define MAKE_JUMP(a, f) _sw(0x08000000 | (((u32)(f) & 0x0ffffffc) >> 2), a);
-#define MAKE_STUB(a, f) {u32 addr = a; _sw(0x08000000 | (((u32)(f) & 0x0ffffffc) >> 2), addr); _sw(0, addr+4); }
-#define U_EXTRACT_CALL(x) ((((u32)_lw((u32)x)) & ~0x0C000000) << 2)
-#define ClearCachesForUser sceKernelGetCompiledSdkVersion
-#define NOP_OPCODE 0
-#define ISSET(v, m) (((v) & (m)) == (m))
-
-// useful to know who is the caller
-#define GET_RA(x) __asm__("move %0,$ra" : "=r"((x)))
-
-#define UNUSED __attribute__((unused))
-
-#define ITEMSOF(arr) (sizeof(arr) / sizeof(0[arr]))
-
-// uncomment this once the oldplugin of Pro is gone
-//#define DEVICE_MEMORY_STICK "ms0:"
-//#define DEVICE_INTERNAL_STORAGE "ef0:"
-
-/* Avoid that "Old Plugin Support (PSP-Go only)" screw us */
-#define XOR_KEY 0xDEADC0DE
-#define XORED_MEMORY_STICK 0xE49DB3B3
-#define XORED_INTERNAL_STORAGE 0xE49DA6BB
-
-#define PSPGO_MULTIMS_SENTINEL 1000
-#define PSPMS_MULTIMS_SENTINEL 100
-
-#define PSPGO_UNCAT_SENTINEL 2000
-#define PSPMS_UNCAT_SENTINEL 200
-
-#define PSPGO_CONTEXT_SENTINEL 0x90000
-#define PSPMS_CONTEXT_SENTINEL 0x70000
-
-typedef struct
-{
- void *next;
- int location;
- u64 mtime;
- char letter;
- char name;
-} Category;
-
-typedef struct
-{
- void *unk; // 0
- int id; // 4
- const char *regkey; // 8
- const char *text; // 12
- const char *subtitle; // 16
- const char *page; // 20
-} SceSysconfItem; // 24
-
-typedef struct
-{
- char text[48];
- int play_sound;
- int action;
- int action_arg;
-} SceContextItem; // 60
-
-typedef struct
-{
- int id; // 0
- int relocate; // 4
- int action; // 8
- int action_arg; // 12
- SceContextItem *context; // 16
- char *subtitle; // 20
- int unk; // 24
- char play_sound; // 28
- char memstick; // 29
- char umd_icon; // 30
- char image[4]; // 31
- char image_shadow[4]; // 35
- char image_glow[4]; // 39
- char text[37]; // 43
-} SceVshItem; // 80
-
-typedef struct {
- char *name;
- void *callback;
- u32 unknown;
-} SceCallbackItem;
-
-typedef struct
-{
- u8 id; //00
- u8 type; //01
- u16 unk1; //02
- u32 label; //04
- u32 param; //08
- u32 first_child; //0c
- int child_count; //10
- u32 next_entry; // 14
- u32 prev_entry; //18
- u32 parent; //1c
- u32 unknown[2]; //20
-} SceRcoEntry;
-
-typedef struct
-{
- void *unknown;
- int option;
- const char *text;
-} SceGameContext;
-
-typedef struct {
- u8 unk0[0xF0];
- char gametitle[0x80];
- u32 unk1;
- char gamecode[0xA];
- u8 unk2[0x5E];
- char firmware[0x5];
- u8 unk3[0x3];
- char category[0x3];
- u8 unk4;
-} SfoInfo620;
-
-typedef struct {
- u8 unk0[0x68];
- char gametitle[0x80];
- u32 unk1;
- char gamecode[0xA];
- u8 unk2[0x5E];
- char firmware[0x5];
- u8 unk3[0x3];
- char category[0x3];
- u8 unk4;
-} SfoInfo630;
-
-typedef union _sfo {
- SfoInfo620 sfo620;
- SfoInfo630 sfo630;
-} SfoInfo;
-
-enum CategoryLocation {
- MEMORY_STICK,
- INTERNAL_STORAGE,
- INVALID = -1,
-};
-
-enum GameCategoriesModes {
- MODE_MULTI_MS,
- MODE_CONTEXT_MENU,
- MODE_FOLDER,
-};
-
-typedef union _dpath {
- u32 *device;
- const char *path;
-} dpath;
-
-#define SET_DEVICENAME(p, l) { \
- dpath __d; \
- __d.path = (p); \
- *__d.device = XOR_KEY; \
- *__d.device ^= (l) == MEMORY_STICK ? XORED_MEMORY_STICK : XORED_INTERNAL_STORAGE; \
-}
-
-// Functions in: multims.c
-void PatchVshmain(u32 text_addr);
-void PatchGameText(u32 text_addr);
-
-// Functions in: vshitem.c
-void PatchPaf(u32 text_addr);
-void PatchVshCommonGui(u32 text_addr);
-
-// Functions in: sysconf.c
-void PatchSysconf(u32 text_addr);
-void PatchVshmainForSysconf(u32 text_addr);
-void PatchPafForSysconf(u32 text_addr);
-void PatchExecuteActionForSysconf(int action, int action_arg);
-
-// Functions in: context.c
-void PatchVshmainForContext(u32 text_addr);
-
-// Functions in: gcread.c
-void PatchGamePluginForGCread(u32 text_addr);
-
-// Functions in: selection.c
-void PatchSelection(u32 text_addr);
-
-// Functions in: category.c
-int CountCategories(Category *head[], int location);
-void ClearCategories(Category *head[], int location);
-int AddCategory(Category *head[], const char *category, u64 mtime, int location);
-Category *GetNextCategory(Category *head[], Category *prev, int location);
-Category *FindCategory(Category *head[], const char *category, int location);
-void IndexCategories(Category *head[], const char *path, int location);
-int is_game_folder(const char *base, const char *path);
-int has_directories(const char *base, const char *path);
-
-// Functions in: gcpatches.c
-void ResolveNIDs();
-GCPatches *GetPatches(int fw_group);
-
-// Functions in: clearcache.S
-void ClearCaches();
-
-#endif /* CATEGORY_LITE_ */
+/*
+ * this file is part of Game Categories Lite
+ *
+ * Copyright (C) 2011 Bubbletune
+ * Copyright (C) 2011 Codestation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CATEGORY_LITE_
+#define CATEGORY_LITE_
+
+#include
+#include "gcpatches.h"
+
+int sceKernelGetCompiledSdkVersion();
+
+#define MAKE_CALL(a, f) _sw(0x0C000000 | (((u32)(f) >> 2) & 0x03ffffff), a);
+#define MAKE_JUMP(a, f) _sw(0x08000000 | (((u32)(f) & 0x0ffffffc) >> 2), a);
+#define MAKE_STUB(a, f) {u32 addr = a; _sw(0x08000000 | (((u32)(f) & 0x0ffffffc) >> 2), addr); _sw(0, addr+4); }
+#define U_EXTRACT_CALL(x) ((((u32)_lw((u32)x)) & ~0x0C000000) << 2)
+#define ClearCachesForUser sceKernelGetCompiledSdkVersion
+#define NOP_OPCODE 0
+#define ISSET(v, m) (((v) & (m)) == (m))
+
+// useful to know who is the caller
+#define GET_RA(x) __asm__("move %0,$ra" : "=r"((x)))
+
+#define UNUSED __attribute__((unused))
+
+#define ITEMSOF(arr) (sizeof(arr) / sizeof(0[arr]))
+
+// uncomment this once the oldplugin of Pro is gone
+//#define DEVICE_MEMORY_STICK "ms0:"
+//#define DEVICE_INTERNAL_STORAGE "ef0:"
+
+/* Avoid that "Old Plugin Support (PSP-Go only)" screw us */
+#define XOR_KEY 0xDEADC0DE
+#define XORED_MEMORY_STICK 0xE49DB3B3
+#define XORED_INTERNAL_STORAGE 0xE49DA6BB
+
+#define PSPGO_MULTIMS_SENTINEL 1000
+#define PSPMS_MULTIMS_SENTINEL 100
+
+#define PSPGO_UNCAT_SENTINEL 2000
+#define PSPMS_UNCAT_SENTINEL 200
+
+#define PSPGO_CONTEXT_SENTINEL 0x90000
+#define PSPMS_CONTEXT_SENTINEL 0x70000
+
+typedef struct
+{
+ void *next;
+ int location;
+ u64 mtime;
+ char letter;
+ char name;
+} Category;
+
+typedef struct
+{
+ void *unk; // 0
+ int id; // 4
+ const char *regkey; // 8
+ const char *text; // 12
+ const char *subtitle; // 16
+ const char *page; // 20
+} SceSysconfItem; // 24
+
+typedef struct
+{
+ char text[48];
+ int play_sound;
+ int action;
+ int action_arg;
+} SceContextItem; // 60
+
+typedef struct
+{
+ int id; // 0
+ int relocate; // 4
+ int action; // 8
+ int action_arg; // 12
+ SceContextItem *context; // 16
+ char *subtitle; // 20
+ int unk; // 24
+ char play_sound; // 28
+ char memstick; // 29
+ char umd_icon; // 30
+ char image[4]; // 31
+ char image_shadow[4]; // 35
+ char image_glow[4]; // 39
+ char text[37]; // 43
+} SceVshItem; // 80
+
+typedef struct {
+ char *name;
+ void *callback;
+ u32 unknown;
+} SceCallbackItem;
+
+typedef struct
+{
+ u8 id; //00
+ u8 type; //01
+ u16 unk1; //02
+ u32 label; //04
+ u32 param; //08
+ u32 first_child; //0c
+ int child_count; //10
+ u32 next_entry; // 14
+ u32 prev_entry; //18
+ u32 parent; //1c
+ u32 unknown[2]; //20
+} SceRcoEntry;
+
+typedef struct
+{
+ void *unknown;
+ int option;
+ const char *text;
+} SceGameContext;
+
+typedef struct {
+ u8 unk0[0xF0];
+ char gametitle[0x80];
+ u32 unk1;
+ char gamecode[0xA];
+ u8 unk2[0x5E];
+ char firmware[0x5];
+ u8 unk3[0x3];
+ char category[0x3];
+ u8 unk4;
+} SfoInfo620;
+
+typedef struct {
+ u8 unk0[0x68];
+ char gametitle[0x80];
+ u32 unk1;
+ char gamecode[0xA];
+ u8 unk2[0x5E];
+ char firmware[0x5];
+ u8 unk3[0x3];
+ char category[0x3];
+ u8 unk4;
+} SfoInfo630;
+
+typedef union _sfo {
+ SfoInfo620 sfo620;
+ SfoInfo630 sfo630;
+} SfoInfo;
+
+enum CategoryLocation {
+ MEMORY_STICK,
+ INTERNAL_STORAGE,
+ INVALID = -1,
+};
+
+enum GameCategoriesModes {
+ MODE_MULTI_MS,
+ MODE_CONTEXT_MENU,
+ MODE_FOLDER,
+};
+
+typedef union _dpath {
+ u32 *device;
+ const char *path;
+} dpath;
+
+#define SET_DEVICENAME(p, l) { \
+ dpath __d; \
+ __d.path = (p); \
+ *__d.device = XOR_KEY; \
+ *__d.device ^= (l) == MEMORY_STICK ? XORED_MEMORY_STICK : XORED_INTERNAL_STORAGE; \
+}
+
+// Functions in: multims.c
+void PatchVshmain(u32 text_addr);
+void PatchGameText(u32 text_addr);
+
+// Functions in: vshitem.c
+void PatchPaf(u32 text_addr);
+void PatchVshCommonGui(u32 text_addr);
+
+// Functions in: sysconf.c
+void PatchSysconf(u32 text_addr);
+void PatchVshmainForSysconf(u32 text_addr);
+void PatchPafForSysconf(u32 text_addr);
+void PatchExecuteActionForSysconf(int action, int action_arg);
+
+// Functions in: context.c
+void PatchVshmainForContext(u32 text_addr);
+
+// Functions in: gcread.c
+void PatchGamePluginForGCread(u32 text_addr);
+
+// Functions in: selection.c
+void PatchSelection(u32 text_addr);
+
+// Functions in: category.c
+int CountCategories(Category *head[], int location);
+void ClearCategories(Category *head[], int location);
+int AddCategory(Category *head[], const char *category, u64 mtime, int location);
+Category *GetNextCategory(Category *head[], Category *prev, int location);
+Category *FindCategory(Category *head[], const char *category, int location);
+void IndexCategories(Category *head[], const char *path, int location);
+int is_game_folder(const char *base, const char *path);
+int has_directories(const char *base, const char *path);
+
+// Functions in: gcpatches.c
+void ResolveNIDs();
+GCPatches *GetPatches(int fw_group);
+
+// Functions in: clearcache.S
+void ClearCaches();
+
+#endif /* CATEGORY_LITE_ */
diff --git a/category.c b/category.c
index 8607a02..10738ed 100644
--- a/category.c
+++ b/category.c
@@ -28,25 +28,39 @@
static const char *eboot_types[] = { "EBOOT.PBP", "PARAM.PBP", "PBOOT.PBP" };
Category *GetNextCategory(Category *head[], Category *prev, int location) {
- u64 time = 0, last;
- Category *newest = NULL;
- if (prev) {
- last = prev->mtime;
- } else {
- last = (u64) -1;
- }
+ Category *newest = NULL;
Category *p = (Category *) head[location];
- while (p) {
- if (p->mtime < last) {
- if (p->mtime > time) {
- time = p->mtime;
- newest = p;
+ if(config.catsort) {
+ char *name = NULL;
+ char *last = prev ? &prev->name : NULL;
+
+ while (p) {
+ if (!last || sce_paf_private_strcmp(&p->name, last) > 0) {
+ if(!name || sce_paf_private_strcmp(&p->name, name) < 0) {
+ name = &p->name;
+ newest = p;
+ }
}
+
+ p = p->next;
}
- p = p->next;
+ } else {
+ u64 time = 0;
+ u64 last = prev ? prev->mtime : (u64)-1;
+
+ while (p) {
+ if (p->mtime < last) {
+ if (p->mtime > time) {
+ time = p->mtime;
+ newest = p;
+ }
+ }
+
+ p = p->next;
+ }
}
return newest;
diff --git a/config.c b/config.c
index 4b9c9d9..b7ddef3 100644
--- a/config.c
+++ b/config.c
@@ -23,7 +23,7 @@
#include "logger.h"
CategoryConfig config;
-static CategoryConfig prev_conf = {-1, -1, -1, -1};
+static CategoryConfig prev_conf = {-1, -1, -1, -1, -1};
extern int model;
@@ -57,7 +57,7 @@ int save_config() {
char device[12];
if(sce_paf_private_memcmp(&config, &prev_conf, sizeof(CategoryConfig)) != 0) {
- if(prev_conf.mode != config.mode || prev_conf.prefix != config.prefix || prev_conf.uncategorized != config.uncategorized) {
+ if(prev_conf.mode != config.mode || prev_conf.prefix != config.prefix || prev_conf.uncategorized != config.uncategorized || prev_conf.catsort != config.catsort) {
reset = 1;
} else {
reset = 0;
diff --git a/config.h b/config.h
index 623fcb7..ffa1549 100644
--- a/config.h
+++ b/config.h
@@ -27,6 +27,7 @@ typedef struct {
u32 prefix;
u32 uncategorized;
u32 selection;
+ u32 catsort;
} CategoryConfig;
enum uncat {
diff --git a/exports.exp b/exports.exp
index 30896f6..43b7f97 100644
--- a/exports.exp
+++ b/exports.exp
@@ -1,8 +1,8 @@
-PSP_BEGIN_EXPORTS
-
-PSP_EXPORT_START(syslib, 0, 0x8000)
-PSP_EXPORT_FUNC_HASH(module_start)
-PSP_EXPORT_VAR_HASH(module_info)
+PSP_BEGIN_EXPORTS
+
+PSP_EXPORT_START(syslib, 0, 0x8000)
+PSP_EXPORT_FUNC_HASH(module_start)
+PSP_EXPORT_VAR_HASH(module_info)
PSP_EXPORT_END
-
-PSP_END_EXPORTS
+
+PSP_END_EXPORTS
diff --git a/gcpatches.c b/gcpatches.c
index 17bbbd2..4b210bb 100644
--- a/gcpatches.c
+++ b/gcpatches.c
@@ -1,180 +1,180 @@
-/*
- * this file is part of Game Categories Lite
- *
- * Copyright (C) 2011 Bubbletune
- * Copyright (C) 2011 Codestation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#include
-#include
-#include
-#include
-#include
-#include "gcpatches.h"
-#include "categories_lite.h"
-
-extern SceModuleInfo module_info;
-
-typedef struct {
- u32 nid620;
- u32 nid630;
- u32 nid660;
-} nid;
-
-static nid nids[] =
-{
- { 0xE5A74996, 0x8F95CC01, 0x726DFBA9 }, // sce_paf_private_strcpy
- { 0x4F487FBC, 0xD38E62C6, 0x706ABBFF }, // sce_paf_private_strncpy
- { 0x5E7610DF, 0x726776D7, 0x7B7133D5 }, // sce_paf_private_snprintf
- { 0x4900119B, 0x1B952318, 0x4CF09BA2 }, // sce_paf_private_strcmp
- { 0xE00E38F8, 0x9DF5623C, 0xE0B32AE8 }, // sce_paf_private_strncmp
- { 0x16789955, 0x861C4627, 0xB05D9677 }, // sce_paf_private_memcmp
- { 0x23C8DAB5, 0xE281261E, 0xF7C46E37 }, // sce_paf_private_memmove
- { 0xF0D98BD1, 0x9E9FFBFB, 0x5E909060 }, // sce_paf_private_malloc
- { 0xE0E8820F, 0xB0363C2E, 0xDB1612F1 }, // sce_paf_private_free
- { 0x58189108, 0x49A72E5D, 0xD7DCB972 }, // sce_paf_private_strlen
- { 0x0C962B6E, 0x5612DE15, 0xA4B8A4E3 }, // sce_paf_private_strtoul
- { 0xF200AF8E, 0xE1C930B5, 0x02119936 }, // scePafSetSelectedItem
-// { 0xE8473E80, 0x4C386F3C, 0xA138A376 }, // sce_paf_private_sprintf
-
-// { 0x03A0E8C2, 0xDE69A6CD, 0xAF067FA2 }, // sce_paf_private_wcslen
-// { 0xF7A832C8, 0xDCE3B13E, 0xB4652CFE }, // sce_paf_private_memcpy
-// { 0xBF48C1FC, 0x5870455C, 0xD9E2D6E1 }, // sce_paf_private_memset
-// { 0x3029535C, 0xFD6C776F, 0xC3B2D310 }, // sce_paf_private_strchr
-// { 0x42D04DD2, 0xCFC81D9F, 0x22420CC7 }, // sce_paf_private_strrchr
-// { 0x2C433251, 0x4853DF6E, 0x4CE9C8D7 }, // sce_paf_private_strpbrk
-
-// { 0x39E9B515, 0xBF2046E2, 0xC907E7CF }, // scePafGetPageChild
-// { 0x62D2266B, 0x9CFBB2D9, 0x4918DB1A }, // scePafGetPageString
-// { 0xCB608DE5, 0x70082F6F, 0x3874A5F8 }, // scePafGetText
-// { 0x511991AE, 0xE03A5C26, 0x50DCB767 }, // PAF_Resource_GetPageNodeByID
-// { 0xC8C59436, 0xFDAFC9E9, 0x64A31513 }, // PAF_Resource_ResolveRefWString
-
-// { 0xE73C355B, 0x3A370539, 0x6C582683 }, // vshGetRegistryValue
-// { 0x2375A440, 0xCD3AF2EC, 0x858291A3 }, // vshSetRegistryValue
-};
-
-GCPatches patches =
-{
- /** main.c (sceSystemMemoryManager) */
- { 0x88009B28, 0x88009A08, 0x880098CC }, // get_compiled_sdk_version
-
- /** gcread.c (game_plugin_module) */
- { 0x28930, 0x2A5F0, 0x2A894 }, // io_dopen_stub
- { 0x28940, 0x2A600, 0x2A8A4 }, // io_dread_stub
- { 0x28948, 0x2A608, 0x2A8AC }, // io_dclose_stub
-
- { 0x28958, 0x2A618, 0x2A8BC }, // io_open_stub
- { 0x28928, 0x2A5E8, 0x2A88C }, // io_getstat_stub
- { 0x28938, 0x2A5F8, 0x2A89C }, // io_chstat_stub
- { 0x28950, 0x2A610, 0x2A8B4 }, // io_remove_stub
- { 0x28960, 0x2A620, 0x2A8C4 }, // io_rmdir_stub
-
- { 0x1FB4C, 0x21310, 0x215B4 }, // base_path
- { 0x1FB50, 0x21314, 0x215B8 }, // base_path_arg
-
- { { 0x1CD24, 0x1CD18 }, { 0x1E42C, 0x1E420 }, {0x1E6A8, 0x1E69C} }, // snprintf_call_arg_1
- { { 0x1F8F0, 0x1F8B4 }, { 0x2109C, 0x21060 }, {0x21340, 0x21304} }, // snprintf_call_arg_2
-
- //{ { 0x10EFC, 0x11C68 }, { 0x1176C, 0x123A4 } }, // sce_paf_get_text_call
-
- /** vshitem.c (vshmain) */
-
- // { 0x1F3BC, 0x1FB70 }, // RegisterCallbacks
- { 0x2348C, 0x23C7C, 0x23CE8 }, // AddVshItem
- { 0x21D68, 0x22558, 0x22598 }, // GetBackupVshItem
- //{ { 0x1631C, 0x2FF8C }, { 0x16984, 0x30828 } }, // ExecuteAction
- //{ 0x16514, 0x16B7C// UnloadModule
-
- { 0x21E18, 0x22608, 0x22648 }, // AddVshItemOffset
- { 0x16340, 0x169A8, 0x16A70 }, // ExecuteActionOffset
- { 0x16734, 0x16D9C, 0x16E64 }, // UnloadModuleOffset
-
- //{ 0x23BE0, 0x243D0 }, // sce_paf_get_text_call
-
- /** sysconf.c (sysconf_plugin_module) */
- { 0x1C4A8, 0x1CD18, 0x1D150 }, // AddSysconfItem
- { 0x02934, 0x02A28, 0x02A28 }, // GetSysconfItem
-
- /** sysconf.c (scePaf) */
- { 0x6750C, 0x674D4, 0x676F4 }, // GetPageNodeByIDOffset
- { 0x677EC, 0x677B4, 0x679D4 }, // ResolveRefWStringOffset
-
- /** sysconf.c (vshmain) */
- { 0x03908, 0x03998, 0x03998 }, // vshGetRegistryValueOffset
- { 0x03A38, 0x03AC8, 0x03AC8 }, // vshSetRegistryValueOffset
-
- /** vshitem.c (scePaf) */
- { 0x3C404, 0x3C3CC, 0x3C5EC }, // scePafGetTextOffset
- /** vshitem.c (commonGui) */
- { 0x03C54, 0x03C54, 0x03C54 }, // CommonGuiDisplayContextOffset
-
- /** context.c (vshmain) */
- { 0x16284, 0x168EC, 0x169B4 }, // OnXmbPush
- { 0x15D38, 0x163A0, 0x16468 }, // OnXmbContextMenu
- //{ 0x0DEAD, 0x0DEAD, 0x1643C }, // OnMenuListScrollIn
-
- //{0xDEAD, 0xDEAD, 0x66E48}, // scePafGetPageChildOffset
-
- // folder categories patches (game_plugin_module)
- { 0x2C8A8, 0x2E61C, 0x2E94C }, // current_mode
- { 0x19B5C, 0x1ABF4, 0x1AE10 }, // categorize_game
- //{ 0x28BF0, 0x2A800, 0x2AC04 }, // play_sound
- { 0x119A4, 0x12300, 0x124E0 }, // play_sound_call
- { (52/4), (56/4), (56/4) }, // array_index
- { { 0x10680, 0x11750 }, { 0x11348, 0x11FD0 }, {0x114D8, 0x12178} }, // add_game_context_call
- { 0x26B38, 0x28560, 0x28804 }, // add_game_context
- { { 0xC704, 0xC708 }, { 0x8B8C, 0x8B90 }, {0x8C94, 0x8C90 } }, // setmode_call_arg_1
- { { 0xC638, 0xC630 }, { 0, 0 }, { 0, 0 } }, // setmode_call_arg_2
- { 0x02203821, 0x02003821, 0x02003821 }, // setmode_arg_opcode
- { 0xC458, 0xCE44, 0xCFC8 }, // setmode
- { 0x2C290, 0x2DFB0, 0x2E2E4 }, // on_push_folder_options_call
- { 0x117DC, 0x120B4, 0x12294 }, // on_push_folder_options
- { 0x2C218, 0x2DF44, 0x2E278 }, // on_push_options_call
- { 0x1070C, 0x1142C, 0x115F4 }, // on_push_options
- { 0x1B0F0, 0x1C5D0, 0x1C84C }, // get_selection
- { 0x2CFCC, 0x2ED8C, 0x2F0BC }, // get_selection_arg
- { {0, 0}, { 0x113B8, 0x12040 }, {0x11548, 0x121E8 } }, // set_selection_call
- { 2, 3, 3 }, // OPTION_BY_CATEGORY
- { 1, 2, 2 }, // MODE_BY_EXPIRE_DATE
- {0x2D1D4, 0x2EF84, 0x2F2B4 }, // struct_addr
- {1, 0, 0 }, // index
-};
-
-void ResolveNIDs(int fw_ver) {
- u32 stub_top = (u32) module_info.stub_top;
- u32 stub_end = (u32) module_info.stub_end;
-
- while (stub_top < stub_end) {
- SceLibraryStubTable *stub = (SceLibraryStubTable *) stub_top;
- if (strcmp(stub->libname, "scePaf") == 0) {
- for (u32 i = 0; i < stub->stubcount; i++) {
- for (u32 x = 0; x < ITEMSOF(nids); x++) {
- if (stub->nidtable[i] == nids[x].nid630) {
- if(fw_ver == FW_620) {
- stub->nidtable[i] = nids[x].nid620;
- } else if (fw_ver == FW_630) {
- stub->nidtable[i] = nids[x].nid630;
- } else if (fw_ver == FW_660) {
- stub->nidtable[i] = nids[x].nid660;
- }
- }
- }
- }
- }
- stub_top += (u32)(stub->len * 4);
- }
-}
+/*
+ * this file is part of Game Categories Lite
+ *
+ * Copyright (C) 2011 Bubbletune
+ * Copyright (C) 2011 Codestation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include "gcpatches.h"
+#include "categories_lite.h"
+
+extern SceModuleInfo module_info;
+
+typedef struct {
+ u32 nid620;
+ u32 nid630;
+ u32 nid660;
+} nid;
+
+static nid nids[] =
+{
+ { 0xE5A74996, 0x8F95CC01, 0x726DFBA9 }, // sce_paf_private_strcpy
+ { 0x4F487FBC, 0xD38E62C6, 0x706ABBFF }, // sce_paf_private_strncpy
+ { 0x5E7610DF, 0x726776D7, 0x7B7133D5 }, // sce_paf_private_snprintf
+ { 0x4900119B, 0x1B952318, 0x4CF09BA2 }, // sce_paf_private_strcmp
+ { 0xE00E38F8, 0x9DF5623C, 0xE0B32AE8 }, // sce_paf_private_strncmp
+ { 0x16789955, 0x861C4627, 0xB05D9677 }, // sce_paf_private_memcmp
+ { 0x23C8DAB5, 0xE281261E, 0xF7C46E37 }, // sce_paf_private_memmove
+ { 0xF0D98BD1, 0x9E9FFBFB, 0x5E909060 }, // sce_paf_private_malloc
+ { 0xE0E8820F, 0xB0363C2E, 0xDB1612F1 }, // sce_paf_private_free
+ { 0x58189108, 0x49A72E5D, 0xD7DCB972 }, // sce_paf_private_strlen
+ { 0x0C962B6E, 0x5612DE15, 0xA4B8A4E3 }, // sce_paf_private_strtoul
+ { 0xF200AF8E, 0xE1C930B5, 0x02119936 }, // scePafSetSelectedItem
+// { 0xE8473E80, 0x4C386F3C, 0xA138A376 }, // sce_paf_private_sprintf
+
+// { 0x03A0E8C2, 0xDE69A6CD, 0xAF067FA2 }, // sce_paf_private_wcslen
+// { 0xF7A832C8, 0xDCE3B13E, 0xB4652CFE }, // sce_paf_private_memcpy
+// { 0xBF48C1FC, 0x5870455C, 0xD9E2D6E1 }, // sce_paf_private_memset
+// { 0x3029535C, 0xFD6C776F, 0xC3B2D310 }, // sce_paf_private_strchr
+// { 0x42D04DD2, 0xCFC81D9F, 0x22420CC7 }, // sce_paf_private_strrchr
+// { 0x2C433251, 0x4853DF6E, 0x4CE9C8D7 }, // sce_paf_private_strpbrk
+
+// { 0x39E9B515, 0xBF2046E2, 0xC907E7CF }, // scePafGetPageChild
+// { 0x62D2266B, 0x9CFBB2D9, 0x4918DB1A }, // scePafGetPageString
+// { 0xCB608DE5, 0x70082F6F, 0x3874A5F8 }, // scePafGetText
+// { 0x511991AE, 0xE03A5C26, 0x50DCB767 }, // PAF_Resource_GetPageNodeByID
+// { 0xC8C59436, 0xFDAFC9E9, 0x64A31513 }, // PAF_Resource_ResolveRefWString
+
+// { 0xE73C355B, 0x3A370539, 0x6C582683 }, // vshGetRegistryValue
+// { 0x2375A440, 0xCD3AF2EC, 0x858291A3 }, // vshSetRegistryValue
+};
+
+GCPatches patches =
+{
+ /** main.c (sceSystemMemoryManager) */
+ { 0x88009B28, 0x88009A08, 0x880098CC }, // get_compiled_sdk_version
+
+ /** gcread.c (game_plugin_module) */
+ { 0x28930, 0x2A5F0, 0x2A894 }, // io_dopen_stub
+ { 0x28940, 0x2A600, 0x2A8A4 }, // io_dread_stub
+ { 0x28948, 0x2A608, 0x2A8AC }, // io_dclose_stub
+
+ { 0x28958, 0x2A618, 0x2A8BC }, // io_open_stub
+ { 0x28928, 0x2A5E8, 0x2A88C }, // io_getstat_stub
+ { 0x28938, 0x2A5F8, 0x2A89C }, // io_chstat_stub
+ { 0x28950, 0x2A610, 0x2A8B4 }, // io_remove_stub
+ { 0x28960, 0x2A620, 0x2A8C4 }, // io_rmdir_stub
+
+ { 0x1FB4C, 0x21310, 0x215B4 }, // base_path
+ { 0x1FB50, 0x21314, 0x215B8 }, // base_path_arg
+
+ { { 0x1CD24, 0x1CD18 }, { 0x1E42C, 0x1E420 }, {0x1E6A8, 0x1E69C} }, // snprintf_call_arg_1
+ { { 0x1F8F0, 0x1F8B4 }, { 0x2109C, 0x21060 }, {0x21340, 0x21304} }, // snprintf_call_arg_2
+
+ //{ { 0x10EFC, 0x11C68 }, { 0x1176C, 0x123A4 } }, // sce_paf_get_text_call
+
+ /** vshitem.c (vshmain) */
+
+ // { 0x1F3BC, 0x1FB70 }, // RegisterCallbacks
+ { 0x2348C, 0x23C7C, 0x23CE8 }, // AddVshItem
+ { 0x21D68, 0x22558, 0x22598 }, // GetBackupVshItem
+ //{ { 0x1631C, 0x2FF8C }, { 0x16984, 0x30828 } }, // ExecuteAction
+ //{ 0x16514, 0x16B7C// UnloadModule
+
+ { 0x21E18, 0x22608, 0x22648 }, // AddVshItemOffset
+ { 0x16340, 0x169A8, 0x16A70 }, // ExecuteActionOffset
+ { 0x16734, 0x16D9C, 0x16E64 }, // UnloadModuleOffset
+
+ //{ 0x23BE0, 0x243D0 }, // sce_paf_get_text_call
+
+ /** sysconf.c (sysconf_plugin_module) */
+ { 0x1C4A8, 0x1CD18, 0x1D150 }, // AddSysconfItem
+ { 0x02934, 0x02A28, 0x02A28 }, // GetSysconfItem
+
+ /** sysconf.c (scePaf) */
+ { 0x6750C, 0x674D4, 0x676F4 }, // GetPageNodeByIDOffset
+ { 0x677EC, 0x677B4, 0x679D4 }, // ResolveRefWStringOffset
+
+ /** sysconf.c (vshmain) */
+ { 0x03908, 0x03998, 0x03998 }, // vshGetRegistryValueOffset
+ { 0x03A38, 0x03AC8, 0x03AC8 }, // vshSetRegistryValueOffset
+
+ /** vshitem.c (scePaf) */
+ { 0x3C404, 0x3C3CC, 0x3C5EC }, // scePafGetTextOffset
+ /** vshitem.c (commonGui) */
+ { 0x03C54, 0x03C54, 0x03C54 }, // CommonGuiDisplayContextOffset
+
+ /** context.c (vshmain) */
+ { 0x16284, 0x168EC, 0x169B4 }, // OnXmbPush
+ { 0x15D38, 0x163A0, 0x16468 }, // OnXmbContextMenu
+ //{ 0x0DEAD, 0x0DEAD, 0x1643C }, // OnMenuListScrollIn
+
+ //{0xDEAD, 0xDEAD, 0x66E48}, // scePafGetPageChildOffset
+
+ // folder categories patches (game_plugin_module)
+ { 0x2C8A8, 0x2E61C, 0x2E94C }, // current_mode
+ { 0x19B5C, 0x1ABF4, 0x1AE10 }, // categorize_game
+ //{ 0x28BF0, 0x2A800, 0x2AC04 }, // play_sound
+ { 0x119A4, 0x12300, 0x124E0 }, // play_sound_call
+ { (52/4), (56/4), (56/4) }, // array_index
+ { { 0x10680, 0x11750 }, { 0x11348, 0x11FD0 }, {0x114D8, 0x12178} }, // add_game_context_call
+ { 0x26B38, 0x28560, 0x28804 }, // add_game_context
+ { { 0xC704, 0xC708 }, { 0x8B8C, 0x8B90 }, {0x8C94, 0x8C90 } }, // setmode_call_arg_1
+ { { 0xC638, 0xC630 }, { 0, 0 }, { 0, 0 } }, // setmode_call_arg_2
+ { 0x02203821, 0x02003821, 0x02003821 }, // setmode_arg_opcode
+ { 0xC458, 0xCE44, 0xCFC8 }, // setmode
+ { 0x2C290, 0x2DFB0, 0x2E2E4 }, // on_push_folder_options_call
+ { 0x117DC, 0x120B4, 0x12294 }, // on_push_folder_options
+ { 0x2C218, 0x2DF44, 0x2E278 }, // on_push_options_call
+ { 0x1070C, 0x1142C, 0x115F4 }, // on_push_options
+ { 0x1B0F0, 0x1C5D0, 0x1C84C }, // get_selection
+ { 0x2CFCC, 0x2ED8C, 0x2F0BC }, // get_selection_arg
+ { {0, 0}, { 0x113B8, 0x12040 }, {0x11548, 0x121E8 } }, // set_selection_call
+ { 2, 3, 3 }, // OPTION_BY_CATEGORY
+ { 1, 2, 2 }, // MODE_BY_EXPIRE_DATE
+ {0x2D1D4, 0x2EF84, 0x2F2B4 }, // struct_addr
+ {1, 0, 0 }, // index
+};
+
+void ResolveNIDs(int fw_ver) {
+ u32 stub_top = (u32) module_info.stub_top;
+ u32 stub_end = (u32) module_info.stub_end;
+
+ while (stub_top < stub_end) {
+ SceLibraryStubTable *stub = (SceLibraryStubTable *) stub_top;
+ if (strcmp(stub->libname, "scePaf") == 0) {
+ for (u32 i = 0; i < stub->stubcount; i++) {
+ for (u32 x = 0; x < ITEMSOF(nids); x++) {
+ if (stub->nidtable[i] == nids[x].nid630) {
+ if(fw_ver == FW_620) {
+ stub->nidtable[i] = nids[x].nid620;
+ } else if (fw_ver == FW_630) {
+ stub->nidtable[i] = nids[x].nid630;
+ } else if (fw_ver == FW_660) {
+ stub->nidtable[i] = nids[x].nid660;
+ }
+ }
+ }
+ }
+ }
+ stub_top += (u32)(stub->len * 4);
+ }
+}
diff --git a/gcpatches.h b/gcpatches.h
index af5552c..4f817aa 100644
--- a/gcpatches.h
+++ b/gcpatches.h
@@ -1,121 +1,121 @@
-/*
- * this file is part of Game Categories Lite
- *
- * Copyright (C) 2011 Bubbletune
- * Copyright (C) 2011 Codestation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#ifndef GCPATCHES_H_
-#define GCPATCHES_H_
-
-#include
-
-typedef struct {
- /** main.c (sceSystemMemoryManager) */
- u32 get_compiled_sdk_version[3];
-
- /** gcread.c (game_plugin_module) */
- u32 io_dopen_stub[3];
- u32 io_dread_stub[3];
- u32 io_dclose_stub[3];
-
- u32 io_open_stub[3];
- u32 io_getstat_stub[3];
- u32 io_chstat_stub[3];
- u32 io_remove_stub[3];
- u32 io_rmdir_stub[3];
-
- u32 base_path[3];
- u32 base_path_arg[3];
-
- u32 snprintf_call_arg_1[3][2];
- u32 snprintf_call_arg_2[3][2];
-
- //u32 sce_paf_get_text_call[2];
-
- /** vshitem.c (vshmain) */
- //u32 RegisterCallbacks[3];
- u32 AddVshItem[3];
- u32 GetBackupVshItem[3];
- //u32 ExecuteAction[3][2];
- //u32 UnloadModule[3];
-
- u32 AddVshItemOffset[3];
- u32 ExecuteActionOffset[3];
- u32 UnloadModuleOffset[3];
-
- //u32 sce_paf_get_text_call;
-
- /** sysconf.c (sysconf_plugin_module) */
- u32 AddSysconfItem[3];
- u32 GetSysconfItem[3];
-
- /** sysconf.c (scePaf) */
- u32 GetPageNodeByIDOffset[3];
- u32 ResolveRefWStringOffset[3];
-
- /** sysconf.c (vshmain) */
- u32 vshGetRegistryValueOffset[3];
- u32 vshSetRegistryValueOffset[3];
-
- /** vshitem.c (scePaf) */
- u32 scePafGetTextOffset[3];
- /** vshitem.c (commonGui) */
- u32 CommonGuiDisplayContextOffset[3];
-
- /** context.c */
- u32 OnXmbPush[3];
- u32 OnXmbContextMenu[3];
- //u32 OnMenuListScrollIn[3];
-
- //u32 scePafGetPageChildContext[3];
-
- // folder categories patches
- u32 current_mode[3];
- u32 categorize_game[3];
- //u32 play_sound[3];
- u32 play_sound_call[3];
- u32 array_index[3];
- u32 add_game_context_call[3][2];
- u32 add_game_context[3];
- u32 setmode_call_arg_1[3][2];
- u32 setmode_call_arg_2[3][2];
- u32 setmode_arg_opcode[3];
- u32 setmode[3];
- u32 on_push_folder_options_call[3];
- u32 on_push_folder_options[3];
- u32 on_push_options_call[3];
- u32 on_push_options[3];
- u32 get_selection[3];
- u32 get_selection_arg[3];
- u32 set_selection_call[3][2];
- int OPTION_BY_CATEGORY[3];
- u32 MODE_BY_EXPIRE_DATE[3];
- u32 struct_addr[3];
- u32 index[3];
-
-} GCPatches;
-
-
-enum fw_ver { FW_620, FW_630, FW_660};
-
-extern GCPatches patches;
-extern int patch_index;
-
-void ResolveNIDs();
-GCPatches *GetPatches(int fw_group);
-
-#endif /* GCPATCHES_H_ */
+/*
+ * this file is part of Game Categories Lite
+ *
+ * Copyright (C) 2011 Bubbletune
+ * Copyright (C) 2011 Codestation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef GCPATCHES_H_
+#define GCPATCHES_H_
+
+#include
+
+typedef struct {
+ /** main.c (sceSystemMemoryManager) */
+ u32 get_compiled_sdk_version[3];
+
+ /** gcread.c (game_plugin_module) */
+ u32 io_dopen_stub[3];
+ u32 io_dread_stub[3];
+ u32 io_dclose_stub[3];
+
+ u32 io_open_stub[3];
+ u32 io_getstat_stub[3];
+ u32 io_chstat_stub[3];
+ u32 io_remove_stub[3];
+ u32 io_rmdir_stub[3];
+
+ u32 base_path[3];
+ u32 base_path_arg[3];
+
+ u32 snprintf_call_arg_1[3][2];
+ u32 snprintf_call_arg_2[3][2];
+
+ //u32 sce_paf_get_text_call[2];
+
+ /** vshitem.c (vshmain) */
+ //u32 RegisterCallbacks[3];
+ u32 AddVshItem[3];
+ u32 GetBackupVshItem[3];
+ //u32 ExecuteAction[3][2];
+ //u32 UnloadModule[3];
+
+ u32 AddVshItemOffset[3];
+ u32 ExecuteActionOffset[3];
+ u32 UnloadModuleOffset[3];
+
+ //u32 sce_paf_get_text_call;
+
+ /** sysconf.c (sysconf_plugin_module) */
+ u32 AddSysconfItem[3];
+ u32 GetSysconfItem[3];
+
+ /** sysconf.c (scePaf) */
+ u32 GetPageNodeByIDOffset[3];
+ u32 ResolveRefWStringOffset[3];
+
+ /** sysconf.c (vshmain) */
+ u32 vshGetRegistryValueOffset[3];
+ u32 vshSetRegistryValueOffset[3];
+
+ /** vshitem.c (scePaf) */
+ u32 scePafGetTextOffset[3];
+ /** vshitem.c (commonGui) */
+ u32 CommonGuiDisplayContextOffset[3];
+
+ /** context.c */
+ u32 OnXmbPush[3];
+ u32 OnXmbContextMenu[3];
+ //u32 OnMenuListScrollIn[3];
+
+ //u32 scePafGetPageChildContext[3];
+
+ // folder categories patches
+ u32 current_mode[3];
+ u32 categorize_game[3];
+ //u32 play_sound[3];
+ u32 play_sound_call[3];
+ u32 array_index[3];
+ u32 add_game_context_call[3][2];
+ u32 add_game_context[3];
+ u32 setmode_call_arg_1[3][2];
+ u32 setmode_call_arg_2[3][2];
+ u32 setmode_arg_opcode[3];
+ u32 setmode[3];
+ u32 on_push_folder_options_call[3];
+ u32 on_push_folder_options[3];
+ u32 on_push_options_call[3];
+ u32 on_push_options[3];
+ u32 get_selection[3];
+ u32 get_selection_arg[3];
+ u32 set_selection_call[3][2];
+ int OPTION_BY_CATEGORY[3];
+ u32 MODE_BY_EXPIRE_DATE[3];
+ u32 struct_addr[3];
+ u32 index[3];
+
+} GCPatches;
+
+
+enum fw_ver { FW_620, FW_630, FW_660};
+
+extern GCPatches patches;
+extern int patch_index;
+
+void ResolveNIDs();
+GCPatches *GetPatches(int fw_group);
+
+#endif /* GCPATCHES_H_ */
diff --git a/gcread.c b/gcread.c
index b0d77ea..07af0f3 100644
--- a/gcread.c
+++ b/gcread.c
@@ -1,333 +1,333 @@
-/*
- * this file is part of Game Categories Lite
- *
- * Copyright (C) 2011 Codestation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#include
-#include
-#include
-#include
-#include "categories_lite.h"
-#include "psppaf.h"
-#include "gcpatches.h"
-#include "pspdefs.h"
-#include "vshitem.h"
-#include "config.h"
-#include "filter.h"
-#include "language.h"
-#include "logger.h"
-
-#define GAME_FOLDER "/PSP/GAME"
-
-//extern variables
-extern Category *folder_list[2];
-
-// global vars
-char category[52];
-
-// unit vars
-static SceUID game_dfd = -1;
-static SceUID fakefd = -1;
-static SceUID realfd = -1;
-static SceUID openfd = -1;
-static int uncategorized;
-static char user_buffer[256];
-static char mod_base[128];
-static char opened_path[128];
-
-#ifdef BENCHMARK
-u64 start_mtime;
-int display_flag;
-#endif
-
-inline void trim(char *str) {
- int i = sce_paf_private_strlen(str);
- while(str[i-1] == ' ') {
- --i;
- }
- if(str[i] == ' ') {
- str[i] = '\0';
- }
-}
-
-int is_category_folder(SceIoDirent *dir) {
- kprintf("checking %s\n", dir->d_name);
- if(FIO_S_ISDIR(dir->d_stat.st_mode)) {
- if(!*category) {
- if(config.mode == MODE_FOLDER) {
- kprintf("base: %s\n", opened_path);
- if(!config.prefix && (*opened_path && !is_game_folder(opened_path, dir->d_name)) && !FindCategory(folder_list, dir->d_name, global_pos)) {
- return 1;
- }
- } else {
- if(!config.prefix && FindCategory(cat_list, dir->d_name, global_pos)) {
- return 1;
- }
- }
- if(config.prefix && sce_paf_private_strncmp(dir->d_name, "CAT_", 4) == 0) {
- return 1;
- }
- }
- if(!config.prefix && sce_paf_private_strcmp(dir->d_name, category) == 0) {
- return 1;
- }
- if(config.prefix && sce_paf_private_strcmp(dir->d_name + 4, category) == 0) {
- return 1;
- }
- }
- return 0;
-}
-
-
-SceUID sceIoDopenPatched(const char *path) {
- SceUID fd = sceIoDopen(path);
-
- // only make a backup of the opened path if the game folder is opened in uncategorized mode
- if (!*category && sce_paf_private_strcmp(path + 4, GAME_FOLDER) == 0) {
- sce_paf_private_strcpy(opened_path, path);
- } else {
- *opened_path = '\0';
- }
-
- if(config.mode == MODE_FOLDER && sce_paf_private_strcmp(path + 4, GAME_FOLDER) == 0) {
-#ifdef BENCHMARK
- display_flag = 0;
- sceRtcGetCurrentTick(&start_mtime);
-#endif
- sce_paf_private_strcpy(opened_path, path);
- ClearCategories(folder_list, global_pos);
- uncategorized = 0;
- game_dfd = fd;
- }
-
- kprintf("opened dir, path: [%s], fd: %08X\n", path, fd);
- // we are receiving a kernel mode file descriptor
- if(fd > 0xFFFF) {
- realfd = fd;
- sce_paf_private_strncpy(user_buffer, path, 13);
- user_buffer[13]= '\0';
- // lets return a dummy fd instead
- fakefd = sceIoDopen(user_buffer);
- kprintf("Opening fake dir: [%s], fd: %08X\n", user_buffer, fakefd);
- fd = fakefd;
- }
- return fd;
-}
-
-int sceIoDreadPatchedFolder(SceUID fd, SceIoDirent *dir) {
- int res;
-
- if (fd == game_dfd) {
- while (1) {
- if (openfd >= 0) {
- res = sceIoDread(openfd, dir);
- if (res > 0) {
- if (dir->d_name[0] != '.' && !check_filter(dir->d_name)) {
- sce_paf_private_strcpy(dir->d_name + 128, dir->d_name);
- sce_paf_private_snprintf(dir->d_name, 128, "%s/%s", user_buffer + 14, dir->d_name + 128);
- if (dir->d_private) {
- sce_paf_private_strcpy((char *)dir->d_private + 13, dir->d_name);
- }
- kprintf("B) exit, dir: [%s]\n", dir->d_name);
- return res;
- } else {
- kprintf("C) ignoring [%s]\n", dir->d_name);
- continue;
- }
- } else {
- sceIoDclose(openfd);
- openfd = -1;
- }
- }
-
- res = sceIoDread(fd, dir);
-
- if (res > 0) {
- kprintf("checking %s\n", dir->d_name);
- if (dir->d_name[0] != '.') {
- if(is_category_folder(dir) && has_directories(opened_path, dir->d_name)) {
- u64 mtime;
-
- kprintf("category match: %s\n", dir->d_name);
- sceRtcGetTick((pspTime *) &dir->d_stat.st_mtime, &mtime);
- kprintf("Adding %s\n", dir->d_name);
- if(AddCategory(folder_list, dir->d_name, mtime, global_pos)) {
- sce_paf_private_snprintf(user_buffer, 128, "%s/%s", opened_path, dir->d_name);
- openfd = sceIoDopen(user_buffer);
- }
- continue;
- } else {
- if (!global_pos && (config.uncategorized & ONLY_MS)) {
- uncategorized = 1;
- } else if (global_pos && (config.uncategorized & ONLY_IE)) {
- uncategorized = 1;
- } else {
- kprintf("A) ignoring [%s]\n", dir->d_name);
- continue; // ignore this Dread
- }
- if(dir->d_name[0] == '.' || check_filter(dir->d_name) || (*opened_path && !is_game_folder(opened_path, dir->d_name))) { // ignore non game folders
- kprintf("B) ignoring [%s]\n", dir->d_name);
- continue;
- }
- }
- }
- }
- if(res > 0) {
- kprintf("A) exit, dir: [%s]\n", dir->d_name);
- } else {
- kprintf("exit, end of directory\n");
- }
- return res;
- }
- }
-
- return sceIoDread(fd, dir);
-}
-
-int sceIoDreadPatched(SceUID fd, SceIoDirent *dir) {
- SceUID ret;
-
- //if our fake fd is being used then replace it with the kernel one
- if(fd == fakefd) {
- kprintf("Replacing fakefd: %08X with realfd: %08X\n", fakefd, realfd);
- fd = realfd;
- }
-
- while((ret = sceIoDread(fd, dir)) > 0) {
- kprintf("read dir: [%s]\n", dir->d_name);
- if(!check_filter(dir->d_name) && (*category || !*opened_path || dir->d_name[0] == '.' || is_game_folder(opened_path, dir->d_name))) {
- break;
- }
- }
- if(ret <= 0) {
- kprintf("end of directory reached\n");
- }
- return ret;
-}
-
-int gcGetStatIso(SceIoStat *stat) {
- if(config.prefix) {
- sce_paf_private_strcpy(user_buffer, "xxx:/ISO/CAT_");
- sce_paf_private_strcpy(user_buffer + 13, category);
- } else {
- sce_paf_private_strcpy(user_buffer, "xxx:/ISO/");
- sce_paf_private_strcpy(user_buffer + 9, category);
- }
- SET_DEVICENAME(user_buffer, global_pos);
-
- // trim away the ME marker (5 spaces at the end of the category)
- trim(user_buffer);
-
- sce_paf_private_memset(stat, 0, sizeof(SceIoStat));
- kprintf("opening [%s]\n", user_buffer);
- return sceIoGetstat(user_buffer, stat);
-}
-
-int sceIoGetstatPatched(char *file, SceIoStat *stat) {
- int ret;
-
- kprintf("checking [%s]\n", file);
- ret = sceIoGetstat(file, stat);
- if(ret < 0 && *category) {
- // lets verify if it was trying to open a ISO category
- sce_paf_private_strcpy(user_buffer, GAME_FOLDER);
- user_buffer[9] = '/';
- if(config.prefix) {
- sce_paf_private_strcpy(user_buffer + 10, "CAT_");
- sce_paf_private_strcpy(user_buffer + 14, category);
- } else {
- sce_paf_private_strcpy(user_buffer + 10, category);
- }
- kprintf("comparing to [%s]\n", user_buffer);
- if(sce_paf_private_strcmp(user_buffer, file + 4) == 0) {
- kprintf("tried to open a iso category, retry\n");
- // check if the category exists in /ISO
- ret = gcGetStatIso(stat);
- kprintf("gcGetStatIso result: %08X\n", ret);
- }
- }
- return ret;
-}
-
-char *ReturnBasePathPatched(char *base) {
- kprintf("orig base: [%s]\n", base);
- // only do the patch if a category is being accessed
- if(*category && base && sce_paf_private_strcmp(base + 4, GAME_FOLDER) == 0) {
- sce_paf_private_strcpy(mod_base, base);
- //append the category dir if is available and the original path is /PSP/GAME
- if(config.prefix) {
- sce_paf_private_strcpy(mod_base + 13, "/CAT_");
- sce_paf_private_strcpy(mod_base + 18, category);
- } else {
- mod_base[13] = '/';
- sce_paf_private_strcpy(mod_base + 14, category);
- }
- // force the device name
- SET_DEVICENAME(mod_base, global_pos);
- kprintf("modified base: [%s]\n", mod_base);
- base = mod_base;
- }
- return base;
-}
-
-int sceIoDclosePatched(SceUID fd) {
- kprintf("closing dir, fd: %08X\n", fd);
- if(config.mode == MODE_FOLDER && fd == game_dfd) {
- // add the uncategorized content in folder mode
- if(uncategorized) {
- AddCategory(folder_list, lang_container.msg_uncategorized, 1, 0);
- }
- game_dfd = -1;
- }
- // close the kernel descriptor along the fake one
- if(fd == fakefd) {
- kprintf("closing realfd: %08X\n", realfd);
- sceIoDclose(realfd);
- fakefd = -1;
- }
- return sceIoDclose(fd);
-}
-
-int sce_paf_private_snprintf_patched(char *a0, int a1, const char *a2, void *a3, void *t0) {
- sce_paf_private_strcpy((char *)a1, (char *)t0);
- return sce_paf_private_snprintf(a0, 291, a2, a3, t0);
-}
-
-
-void PatchGamePluginForGCread(u32 text_addr) {
- // hook some sceIo funcs
- MAKE_STUB(text_addr + patches.io_dread_stub[patch_index], config.mode == MODE_FOLDER ? sceIoDreadPatchedFolder : sceIoDreadPatched);
- MAKE_STUB(text_addr + patches.io_dopen_stub[patch_index], sceIoDopenPatched);
- MAKE_STUB(text_addr + patches.io_dclose_stub[patch_index], sceIoDclosePatched);
- MAKE_STUB(text_addr + patches.io_getstat_stub[patch_index], sceIoGetstatPatched);
-
- // hook the base path creation
- MAKE_JUMP(text_addr + patches.base_path[patch_index], ReturnBasePathPatched);
- _sw(0x00602021, text_addr + patches.base_path_arg[patch_index]); // move $a0, $v1
-
- /* SCE renames folders before removal, but it doesn't handle
- categories in doing so. It will try to move things out of
- the category with the rename function... just get rid of
- renaming to fix this lame bug. */
- // #1
- MAKE_CALL(text_addr+patches.snprintf_call_arg_1[patch_index][0], sce_paf_private_snprintf_patched);
- _sw(0x02402821, text_addr+patches.snprintf_call_arg_1[patch_index][1]); // li $a1, 291 -> move $a1, $s2
-
- // #2
- MAKE_CALL(text_addr+patches.snprintf_call_arg_2[patch_index][0], sce_paf_private_snprintf_patched);
- _sw(0x02002821, text_addr+patches.snprintf_call_arg_2[patch_index][1]); // li $a1, 291 -> move $a1, $s0
-}
+/*
+ * this file is part of Game Categories Lite
+ *
+ * Copyright (C) 2011 Codestation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include "categories_lite.h"
+#include "psppaf.h"
+#include "gcpatches.h"
+#include "pspdefs.h"
+#include "vshitem.h"
+#include "config.h"
+#include "filter.h"
+#include "language.h"
+#include "logger.h"
+
+#define GAME_FOLDER "/PSP/GAME"
+
+//extern variables
+extern Category *folder_list[2];
+
+// global vars
+char category[52];
+
+// unit vars
+static SceUID game_dfd = -1;
+static SceUID fakefd = -1;
+static SceUID realfd = -1;
+static SceUID openfd = -1;
+static int uncategorized;
+static char user_buffer[256];
+static char mod_base[128];
+static char opened_path[128];
+
+#ifdef BENCHMARK
+u64 start_mtime;
+int display_flag;
+#endif
+
+inline void trim(char *str) {
+ int i = sce_paf_private_strlen(str);
+ while(str[i-1] == ' ') {
+ --i;
+ }
+ if(str[i] == ' ') {
+ str[i] = '\0';
+ }
+}
+
+int is_category_folder(SceIoDirent *dir) {
+ kprintf("checking %s\n", dir->d_name);
+ if(FIO_S_ISDIR(dir->d_stat.st_mode)) {
+ if(!*category) {
+ if(config.mode == MODE_FOLDER) {
+ kprintf("base: %s\n", opened_path);
+ if(!config.prefix && (*opened_path && !is_game_folder(opened_path, dir->d_name)) && !FindCategory(folder_list, dir->d_name, global_pos)) {
+ return 1;
+ }
+ } else {
+ if(!config.prefix && FindCategory(cat_list, dir->d_name, global_pos)) {
+ return 1;
+ }
+ }
+ if(config.prefix && sce_paf_private_strncmp(dir->d_name, "CAT_", 4) == 0) {
+ return 1;
+ }
+ }
+ if(!config.prefix && sce_paf_private_strcmp(dir->d_name, category) == 0) {
+ return 1;
+ }
+ if(config.prefix && sce_paf_private_strcmp(dir->d_name + 4, category) == 0) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+SceUID sceIoDopenPatched(const char *path) {
+ SceUID fd = sceIoDopen(path);
+
+ // only make a backup of the opened path if the game folder is opened in uncategorized mode
+ if (!*category && sce_paf_private_strcmp(path + 4, GAME_FOLDER) == 0) {
+ sce_paf_private_strcpy(opened_path, path);
+ } else {
+ *opened_path = '\0';
+ }
+
+ if(config.mode == MODE_FOLDER && sce_paf_private_strcmp(path + 4, GAME_FOLDER) == 0) {
+#ifdef BENCHMARK
+ display_flag = 0;
+ sceRtcGetCurrentTick(&start_mtime);
+#endif
+ sce_paf_private_strcpy(opened_path, path);
+ ClearCategories(folder_list, global_pos);
+ uncategorized = 0;
+ game_dfd = fd;
+ }
+
+ kprintf("opened dir, path: [%s], fd: %08X\n", path, fd);
+ // we are receiving a kernel mode file descriptor
+ if(fd > 0xFFFF) {
+ realfd = fd;
+ sce_paf_private_strncpy(user_buffer, path, 13);
+ user_buffer[13]= '\0';
+ // lets return a dummy fd instead
+ fakefd = sceIoDopen(user_buffer);
+ kprintf("Opening fake dir: [%s], fd: %08X\n", user_buffer, fakefd);
+ fd = fakefd;
+ }
+ return fd;
+}
+
+int sceIoDreadPatchedFolder(SceUID fd, SceIoDirent *dir) {
+ int res;
+
+ if (fd == game_dfd) {
+ while (1) {
+ if (openfd >= 0) {
+ res = sceIoDread(openfd, dir);
+ if (res > 0) {
+ if (dir->d_name[0] != '.' && !check_filter(dir->d_name)) {
+ sce_paf_private_strcpy(dir->d_name + 128, dir->d_name);
+ sce_paf_private_snprintf(dir->d_name, 128, "%s/%s", user_buffer + 14, dir->d_name + 128);
+ if (dir->d_private) {
+ sce_paf_private_strcpy((char *)dir->d_private + 13, dir->d_name);
+ }
+ kprintf("B) exit, dir: [%s]\n", dir->d_name);
+ return res;
+ } else {
+ kprintf("C) ignoring [%s]\n", dir->d_name);
+ continue;
+ }
+ } else {
+ sceIoDclose(openfd);
+ openfd = -1;
+ }
+ }
+
+ res = sceIoDread(fd, dir);
+
+ if (res > 0) {
+ kprintf("checking %s\n", dir->d_name);
+ if (dir->d_name[0] != '.') {
+ if(is_category_folder(dir) && has_directories(opened_path, dir->d_name)) {
+ u64 mtime;
+
+ kprintf("category match: %s\n", dir->d_name);
+ sceRtcGetTick((pspTime *) &dir->d_stat.st_mtime, &mtime);
+ kprintf("Adding %s\n", dir->d_name);
+ if(AddCategory(folder_list, dir->d_name, mtime, global_pos)) {
+ sce_paf_private_snprintf(user_buffer, 128, "%s/%s", opened_path, dir->d_name);
+ openfd = sceIoDopen(user_buffer);
+ }
+ continue;
+ } else {
+ if (!global_pos && (config.uncategorized & ONLY_MS)) {
+ uncategorized = 1;
+ } else if (global_pos && (config.uncategorized & ONLY_IE)) {
+ uncategorized = 1;
+ } else {
+ kprintf("A) ignoring [%s]\n", dir->d_name);
+ continue; // ignore this Dread
+ }
+ if(dir->d_name[0] == '.' || check_filter(dir->d_name) || (*opened_path && !is_game_folder(opened_path, dir->d_name))) { // ignore non game folders
+ kprintf("B) ignoring [%s]\n", dir->d_name);
+ continue;
+ }
+ }
+ }
+ }
+ if(res > 0) {
+ kprintf("A) exit, dir: [%s]\n", dir->d_name);
+ } else {
+ kprintf("exit, end of directory\n");
+ }
+ return res;
+ }
+ }
+
+ return sceIoDread(fd, dir);
+}
+
+int sceIoDreadPatched(SceUID fd, SceIoDirent *dir) {
+ SceUID ret;
+
+ //if our fake fd is being used then replace it with the kernel one
+ if(fd == fakefd) {
+ kprintf("Replacing fakefd: %08X with realfd: %08X\n", fakefd, realfd);
+ fd = realfd;
+ }
+
+ while((ret = sceIoDread(fd, dir)) > 0) {
+ kprintf("read dir: [%s]\n", dir->d_name);
+ if(!check_filter(dir->d_name) && (*category || !*opened_path || dir->d_name[0] == '.' || is_game_folder(opened_path, dir->d_name))) {
+ break;
+ }
+ }
+ if(ret <= 0) {
+ kprintf("end of directory reached\n");
+ }
+ return ret;
+}
+
+int gcGetStatIso(SceIoStat *stat) {
+ if(config.prefix) {
+ sce_paf_private_strcpy(user_buffer, "xxx:/ISO/CAT_");
+ sce_paf_private_strcpy(user_buffer + 13, category);
+ } else {
+ sce_paf_private_strcpy(user_buffer, "xxx:/ISO/");
+ sce_paf_private_strcpy(user_buffer + 9, category);
+ }
+ SET_DEVICENAME(user_buffer, global_pos);
+
+ // trim away the ME marker (5 spaces at the end of the category)
+ trim(user_buffer);
+
+ sce_paf_private_memset(stat, 0, sizeof(SceIoStat));
+ kprintf("opening [%s]\n", user_buffer);
+ return sceIoGetstat(user_buffer, stat);
+}
+
+int sceIoGetstatPatched(char *file, SceIoStat *stat) {
+ int ret;
+
+ kprintf("checking [%s]\n", file);
+ ret = sceIoGetstat(file, stat);
+ if(ret < 0 && *category) {
+ // lets verify if it was trying to open a ISO category
+ sce_paf_private_strcpy(user_buffer, GAME_FOLDER);
+ user_buffer[9] = '/';
+ if(config.prefix) {
+ sce_paf_private_strcpy(user_buffer + 10, "CAT_");
+ sce_paf_private_strcpy(user_buffer + 14, category);
+ } else {
+ sce_paf_private_strcpy(user_buffer + 10, category);
+ }
+ kprintf("comparing to [%s]\n", user_buffer);
+ if(sce_paf_private_strcmp(user_buffer, file + 4) == 0) {
+ kprintf("tried to open a iso category, retry\n");
+ // check if the category exists in /ISO
+ ret = gcGetStatIso(stat);
+ kprintf("gcGetStatIso result: %08X\n", ret);
+ }
+ }
+ return ret;
+}
+
+char *ReturnBasePathPatched(char *base) {
+ kprintf("orig base: [%s]\n", base);
+ // only do the patch if a category is being accessed
+ if(*category && base && sce_paf_private_strcmp(base + 4, GAME_FOLDER) == 0) {
+ sce_paf_private_strcpy(mod_base, base);
+ //append the category dir if is available and the original path is /PSP/GAME
+ if(config.prefix) {
+ sce_paf_private_strcpy(mod_base + 13, "/CAT_");
+ sce_paf_private_strcpy(mod_base + 18, category);
+ } else {
+ mod_base[13] = '/';
+ sce_paf_private_strcpy(mod_base + 14, category);
+ }
+ // force the device name
+ SET_DEVICENAME(mod_base, global_pos);
+ kprintf("modified base: [%s]\n", mod_base);
+ base = mod_base;
+ }
+ return base;
+}
+
+int sceIoDclosePatched(SceUID fd) {
+ kprintf("closing dir, fd: %08X\n", fd);
+ if(config.mode == MODE_FOLDER && fd == game_dfd) {
+ // add the uncategorized content in folder mode
+ if(uncategorized) {
+ AddCategory(folder_list, lang_container.msg_uncategorized, 1, 0);
+ }
+ game_dfd = -1;
+ }
+ // close the kernel descriptor along the fake one
+ if(fd == fakefd) {
+ kprintf("closing realfd: %08X\n", realfd);
+ sceIoDclose(realfd);
+ fakefd = -1;
+ }
+ return sceIoDclose(fd);
+}
+
+int sce_paf_private_snprintf_patched(char *a0, int a1, const char *a2, void *a3, void *t0) {
+ sce_paf_private_strcpy((char *)a1, (char *)t0);
+ return sce_paf_private_snprintf(a0, 291, a2, a3, t0);
+}
+
+
+void PatchGamePluginForGCread(u32 text_addr) {
+ // hook some sceIo funcs
+ MAKE_STUB(text_addr + patches.io_dread_stub[patch_index], config.mode == MODE_FOLDER ? sceIoDreadPatchedFolder : sceIoDreadPatched);
+ MAKE_STUB(text_addr + patches.io_dopen_stub[patch_index], sceIoDopenPatched);
+ MAKE_STUB(text_addr + patches.io_dclose_stub[patch_index], sceIoDclosePatched);
+ MAKE_STUB(text_addr + patches.io_getstat_stub[patch_index], sceIoGetstatPatched);
+
+ // hook the base path creation
+ MAKE_JUMP(text_addr + patches.base_path[patch_index], ReturnBasePathPatched);
+ _sw(0x00602021, text_addr + patches.base_path_arg[patch_index]); // move $a0, $v1
+
+ /* SCE renames folders before removal, but it doesn't handle
+ categories in doing so. It will try to move things out of
+ the category with the rename function... just get rid of
+ renaming to fix this lame bug. */
+ // #1
+ MAKE_CALL(text_addr+patches.snprintf_call_arg_1[patch_index][0], sce_paf_private_snprintf_patched);
+ _sw(0x02402821, text_addr+patches.snprintf_call_arg_1[patch_index][1]); // li $a1, 291 -> move $a1, $s2
+
+ // #2
+ MAKE_CALL(text_addr+patches.snprintf_call_arg_2[patch_index][0], sce_paf_private_snprintf_patched);
+ _sw(0x02002821, text_addr+patches.snprintf_call_arg_2[patch_index][1]); // li $a1, 291 -> move $a1, $s0
+}
diff --git a/lang/category_lite_bg.txt b/lang/category_lite_bg.txt
index 280c083..59a52a3 100644
--- a/lang/category_lite_bg.txt
+++ b/lang/category_lite_bg.txt
@@ -13,5 +13,9 @@
Мемори Карта ™
Вътрешна памет
И двете
+Sort categories
+Allows sorting categories using CAT_XX or XXhomebrew.
+No
+Yes
Некатегоризирани
По категория
diff --git a/lang/category_lite_ch1.txt b/lang/category_lite_ch1.txt
index 46a9d41..8701c8f 100644
--- a/lang/category_lite_ch1.txt
+++ b/lang/category_lite_ch1.txt
@@ -1,17 +1,21 @@
-分類模式
-選擇分類的模式與外觀
-記憶卡圖示
-文字選單
-文件夾
-CAT字頭分類
-以「CAT_」作為資料夾開頭命名進行的分類
-不使用
-使用
-未分類顯示
-顯示或隱藏尚未分類的內容
-不顯示
-顯示記憶卡
-顯示主機硬碟
-顯示全部
-未分類
-依原有分類排列
+分類模式
+選擇分類的模式與外觀
+記憶卡圖示
+文字選單
+文件夾
+CAT字頭分類
+以「CAT_」作為資料夾開頭命名進行的分類
+不使用
+使用
+未分類顯示
+顯示或隱藏尚未分類的內容
+不顯示
+顯示記憶卡
+顯示主機硬碟
+顯示全部
+Sort categories
+Allows sorting categories using CAT_XX or XXhomebrew.
+No
+Yes
+未分類
+依原有分類排列
diff --git a/lang/category_lite_ch2.txt b/lang/category_lite_ch2.txt
index 48c80ab..77872a7 100644
--- a/lang/category_lite_ch2.txt
+++ b/lang/category_lite_ch2.txt
@@ -1,17 +1,21 @@
-分类模式
-选择显示的分类模式。
-图标
-文字
-文件夹
-文件夹前缀名
-隐藏文件夹"CAT_"前缀名。
-不使用
-使用
-未分类显示方式
-隐藏未分类的内容。
-不显示
-仅显示记忆棒
-仅显示内部存储
-全部显示
-未分类
-按类别
+分类模式
+选择显示的分类模式。
+图标
+文字
+文件夹
+文件夹前缀名
+隐藏文件夹"CAT_"前缀名。
+不使用
+使用
+未分类显示方式
+隐藏未分类的内容。
+不显示
+仅显示记忆棒
+仅显示内部存储
+全部显示
+Sort categories
+Allows sorting categories using CAT_XX or XXhomebrew.
+No
+Yes
+未分类
+按类别
diff --git a/lang/category_lite_de.txt b/lang/category_lite_de.txt
index 1f7d8af..caf5a81 100644
--- a/lang/category_lite_de.txt
+++ b/lang/category_lite_de.txt
@@ -13,5 +13,9 @@ Nein
Nur MS™
Nur interner Speicher
Alle Medien
+Sort categories
+Allows sorting categories using CAT_XX or XXhomebrew.
+No
+Yes
Unsortierte
Nach Kategorie
diff --git a/lang/category_lite_en.txt b/lang/category_lite_en.txt
index 0c74a53..5bf224a 100644
--- a/lang/category_lite_en.txt
+++ b/lang/category_lite_en.txt
@@ -13,5 +13,9 @@ No
Only Memory Stick™
Only Internal Storage
Both
+Sort categories
+Allows sorting categories using CAT_XX or XXhomebrew.
+No
+Yes
Uncategorized
By Category
diff --git a/lang/category_lite_es.txt b/lang/category_lite_es.txt
index 80d931f..68cbd90 100644
--- a/lang/category_lite_es.txt
+++ b/lang/category_lite_es.txt
@@ -13,5 +13,9 @@ No
Solo Memory Stick™
Solo memoria interna
Ambos
+Ordenar categorias
+Permite organizar categorias usando CAT_XX o XXhomebrew.
+No
+Si
Sin categorizar
Por categorias
diff --git a/lang/category_lite_it.txt b/lang/category_lite_it.txt
index fcad71f..6181d6d 100644
--- a/lang/category_lite_it.txt
+++ b/lang/category_lite_it.txt
@@ -1,4 +1,4 @@
-Modo visualizzazione
+Modo visualizzazione
Permette di visualizzare le sottocartelle presenti nella cartella PSP/GAME in diversi modi.
Multi MS
Menu contestuale
@@ -13,5 +13,9 @@ No
Solo Memory Stick™
Solo Internal Storage
Entrambe
+Sort categories
+Allows sorting categories using CAT_XX or XXhomebrew.
+No
+Yes
Senza Categoria
Per categoria
diff --git a/lang/category_lite_ja.txt b/lang/category_lite_ja.txt
index 9276533..fd6e8e1 100644
--- a/lang/category_lite_ja.txt
+++ b/lang/category_lite_ja.txt
@@ -13,5 +13,9 @@ CAT_を使用する
メモリースティックのみ
内部ストレージのみ
両方
+Sort categories
+Allows sorting categories using CAT_XX or XXhomebrew.
+No
+Yes
未分類
カテゴリで
diff --git a/lang/category_lite_pl.txt b/lang/category_lite_pl.txt
index 35d7db2..b14f4f0 100644
--- a/lang/category_lite_pl.txt
+++ b/lang/category_lite_pl.txt
@@ -13,5 +13,9 @@ Nie pokazuj
Karta pamieci
Pamiec wewnetrzna
Karta i pamiec
+Sort categories
+Allows sorting categories using CAT_XX or XXhomebrew.
+No
+Yes
Nieskategoryzowane
Wedlug kategorii
diff --git a/lang/category_lite_ru.txt b/lang/category_lite_ru.txt
index 532e452..9d4c6e7 100644
--- a/lang/category_lite_ru.txt
+++ b/lang/category_lite_ru.txt
@@ -1,17 +1,21 @@
Режим отображения
-Selects the the display mode for the Games/Homebrew.
+Выберите режим отображения категорий в меню Игра
Карты Памяти
Контекстное Меню
Папки
-Префикс для категорий
-Use the "CAT_" prefix to recognize the categories.
+Префикс для Категорий
+Использование префикса "CAT_" для опознавания категорий
+Да
Нет
-Использовать
-Показать без категории
-Allows hiding the uncategorized content.
+Контент вне Категорий
+Отображение приложений, не попавших в категории
+Не показывать
+Карта Памяти
+Память Системы
+Оба вида
+Сортировка Категорий
+Сортировка по префиксу CAT_XX или XXhomebrew в названиях категорий
Нет
-Только Memory Stick™
-Только Память Системы
-Оба
-Без Категории
-К категориям
+Да
+Без Категорий
+По Категориям
diff --git a/language.c b/language.c
index cdf98da..aa3c5c5 100644
--- a/language.c
+++ b/language.c
@@ -48,8 +48,9 @@ int LoadLanguageContainer(void *data, int size) {
sce_paf_private_free(((char **) &lang_container)[i]);
}
for (j = 0; j < sce_paf_private_strlen(line); j++) {
- if (line[j] == 0x5c)
+ if (line[j] == 0x5c) {
line[j] = '\n';
+ }
}
((char **) &lang_container)[i] = sce_paf_private_malloc(sce_paf_private_strlen(line) + 1);
diff --git a/language.h b/language.h
index d7c2003..7e22dfb 100644
--- a/language.h
+++ b/language.h
@@ -33,6 +33,10 @@ typedef struct {
char *msg_show_sub;
char *show[4];
+ char *msg_sort;
+ char *msg_sort_sub;
+ char *sort[2];
+
char *msg_uncategorized;
char *by_category;
diff --git a/main.c b/main.c
index 0ace3c2..c80123d 100644
--- a/main.c
+++ b/main.c
@@ -1,139 +1,139 @@
-/*
- * this file is part of Game Categories Lite
- *
- * Copyright (C) 2011 Bubbletune
- * Copyright (C) 2011 Codestation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#include
-#include
-#include
-#include "categories_lite.h"
-#include "psppaf.h"
-#include "gcpatches.h"
-#include "pspdefs.h"
-#include "config.h"
-#include "logger.h"
-
-// change the module name back to GCLite once PRO stops doing weird things with plugins
-PSP_MODULE_INFO("Game_Categories_Light", 0x0807, 1, 5);
-PSP_NO_CREATE_MAIN_THREAD();
-
-/* Global variables */
-int patch_index;
-int model;
-int game_plug = 0;
-int sysconf_plug = 0;
-
-char currfw[5];
-
-//TODO: remove it from here
-u32 text_addr_game;
-u32 text_size_game;
-
-static STMOD_HANDLER previous;
-
-int OnModuleStart(SceModule2 *mod) {
- //kprintf(">> %s: loading %s, text_addr: %08X\n", __func__, mod->modname, mod->text_addr);
- if (sce_paf_private_strcmp(mod->modname, "game_plugin_module") == 0) {
-
- kprintf("loading %s, text_addr: %08X\n", mod->modname, mod->text_addr);
- game_plug = 1;
-
- //TODO: remove it from here
- text_addr_game = mod->text_addr;
- text_size_game = mod->text_size;
-
- PatchGamePluginForGCread(mod->text_addr);
- if(config.mode == MODE_FOLDER) {
- PatchSelection(mod->text_addr);
- }
- ClearCaches();
-
- } else if (sce_paf_private_strcmp(mod->modname, "vsh_module") == 0) {
-
- kprintf("loading %s, text_addr: %08X\n", mod->modname, mod->text_addr);
- PatchVshmain(mod->text_addr);
- PatchVshmainForSysconf(mod->text_addr);
- PatchVshmainForContext(mod->text_addr);
-
- /* Make sceKernelGetCompiledSdkVersion clear the caches,
- so that we don't have to create a kernel module just
- to be able to clear the caches from user mode.*/
-
- //6.20: 0xFC114573 [0x00009B0C] - SysMemUserForUser_FC114573
- //6.35: 0xFC114573 [0x000099EC] - SysMemUserForUser_FC114573
- //6.60: 0xFC114573 [0x000098B0] - SysMemUserForUser_FC114573
- kprintf("Patching sceKernelGetCompiledSdkVersion, index: %i\n", patch_index);
- MAKE_JUMP(patches.get_compiled_sdk_version[patch_index], ClearCaches);
- ClearCaches();
-
- } else if (sce_paf_private_strcmp(mod->modname, "sysconf_plugin_module") == 0) {
-
- kprintf("loading %s, text_addr: %08X\n", mod->modname, mod->text_addr);
- sysconf_plug = 1;
- PatchSysconf(mod->text_addr);
- ClearCaches();
-
- } else if (sce_paf_private_strcmp(mod->modname, "scePaf_Module") == 0) {
-
- kprintf("loading %s, text_addr: %08X\n", mod->modname, mod->text_addr);
- PatchPaf(mod->text_addr);
- PatchPafForSysconf(mod->text_addr);
- ClearCaches();
-
- } else if (sce_paf_private_strcmp(mod->modname, "sceVshCommonGui_Module") == 0) {
-
- kprintf("loading %s, text_addr: %08X\n", mod->modname, mod->text_addr);
- PatchVshCommonGui(mod->text_addr);
- ClearCaches();
- }
-
- return previous ? previous(mod) : 0;
-}
-
-int module_start(SceSize args UNUSED, void *argp UNUSED) {
- const char *src = "xx0:/category_lite.log";
- char *dest = filebuf;
-
- model = kuKernelGetModel();
- while((*dest++ = *src++));
- SET_DEVICENAME(filebuf, model == 4 ? INTERNAL_STORAGE : MEMORY_STICK);
- // paf isn't loaded yet
- kwrite(filebuf, "GCLite 1.5 starting\n", 20);
- // Determine fw group
- u32 devkit = sceKernelDevkitVersion();
- if (devkit == 0x06020010) {
- patch_index = FW_620;
- ResolveNIDs(FW_620);
- } else if (devkit >= 0x06030010 && devkit < 0x06040010) {
- patch_index = FW_630;
- } else if (devkit >= 0x06060010 && devkit < 0x06070010) {
- patch_index = FW_660;
- ResolveNIDs(FW_660);
- } else {
- return 1;
- }
-
- currfw[0] = ((devkit >> 24) & 0xF) + '0';
- currfw[1] = '.';
- currfw[2] = ((devkit >> 16) & 0xF) + '0';
- currfw[3] = ((devkit >> 8) & 0xF) + '0';
- currfw[4] = 0;
-
- previous = sctrlHENSetStartModuleHandler(OnModuleStart);
- return 0;
-}
+/*
+ * this file is part of Game Categories Lite
+ *
+ * Copyright (C) 2011 Bubbletune
+ * Copyright (C) 2011 Codestation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include "categories_lite.h"
+#include "psppaf.h"
+#include "gcpatches.h"
+#include "pspdefs.h"
+#include "config.h"
+#include "logger.h"
+
+// change the module name back to GCLite once PRO stops doing weird things with plugins
+PSP_MODULE_INFO("Game_Categories_Light", 0x0807, 1, 5);
+PSP_NO_CREATE_MAIN_THREAD();
+
+/* Global variables */
+int patch_index;
+int model;
+int game_plug = 0;
+int sysconf_plug = 0;
+
+char currfw[5];
+
+//TODO: remove it from here
+u32 text_addr_game;
+u32 text_size_game;
+
+static STMOD_HANDLER previous;
+
+int OnModuleStart(SceModule2 *mod) {
+ //kprintf(">> %s: loading %s, text_addr: %08X\n", __func__, mod->modname, mod->text_addr);
+ if (sce_paf_private_strcmp(mod->modname, "game_plugin_module") == 0) {
+
+ kprintf("loading %s, text_addr: %08X\n", mod->modname, mod->text_addr);
+ game_plug = 1;
+
+ //TODO: remove it from here
+ text_addr_game = mod->text_addr;
+ text_size_game = mod->text_size;
+
+ PatchGamePluginForGCread(mod->text_addr);
+ if(config.mode == MODE_FOLDER) {
+ PatchSelection(mod->text_addr);
+ }
+ ClearCaches();
+
+ } else if (sce_paf_private_strcmp(mod->modname, "vsh_module") == 0) {
+
+ kprintf("loading %s, text_addr: %08X\n", mod->modname, mod->text_addr);
+ PatchVshmain(mod->text_addr);
+ PatchVshmainForSysconf(mod->text_addr);
+ PatchVshmainForContext(mod->text_addr);
+
+ /* Make sceKernelGetCompiledSdkVersion clear the caches,
+ so that we don't have to create a kernel module just
+ to be able to clear the caches from user mode.*/
+
+ //6.20: 0xFC114573 [0x00009B0C] - SysMemUserForUser_FC114573
+ //6.35: 0xFC114573 [0x000099EC] - SysMemUserForUser_FC114573
+ //6.60: 0xFC114573 [0x000098B0] - SysMemUserForUser_FC114573
+ kprintf("Patching sceKernelGetCompiledSdkVersion, index: %i\n", patch_index);
+ MAKE_JUMP(patches.get_compiled_sdk_version[patch_index], ClearCaches);
+ ClearCaches();
+
+ } else if (sce_paf_private_strcmp(mod->modname, "sysconf_plugin_module") == 0) {
+
+ kprintf("loading %s, text_addr: %08X\n", mod->modname, mod->text_addr);
+ sysconf_plug = 1;
+ PatchSysconf(mod->text_addr);
+ ClearCaches();
+
+ } else if (sce_paf_private_strcmp(mod->modname, "scePaf_Module") == 0) {
+
+ kprintf("loading %s, text_addr: %08X\n", mod->modname, mod->text_addr);
+ PatchPaf(mod->text_addr);
+ PatchPafForSysconf(mod->text_addr);
+ ClearCaches();
+
+ } else if (sce_paf_private_strcmp(mod->modname, "sceVshCommonGui_Module") == 0) {
+
+ kprintf("loading %s, text_addr: %08X\n", mod->modname, mod->text_addr);
+ PatchVshCommonGui(mod->text_addr);
+ ClearCaches();
+ }
+
+ return previous ? previous(mod) : 0;
+}
+
+int module_start(SceSize args UNUSED, void *argp UNUSED) {
+ const char *src = "xx0:/category_lite.log";
+ char *dest = filebuf;
+
+ model = kuKernelGetModel();
+ while((*dest++ = *src++));
+ SET_DEVICENAME(filebuf, model == 4 ? INTERNAL_STORAGE : MEMORY_STICK);
+ // paf isn't loaded yet
+ kwrite(filebuf, "Game Categories Lite v1.7-js1 starting\n", 20);
+ // Determine fw group
+ u32 devkit = sceKernelDevkitVersion();
+ if (devkit == 0x06020010) {
+ patch_index = FW_620;
+ ResolveNIDs(FW_620);
+ } else if (devkit >= 0x06030010 && devkit < 0x06040010) {
+ patch_index = FW_630;
+ } else if (devkit >= 0x06060010 && devkit < 0x06070010) {
+ patch_index = FW_660;
+ ResolveNIDs(FW_660);
+ } else {
+ return 1;
+ }
+
+ currfw[0] = ((devkit >> 24) & 0xF) + '0';
+ currfw[1] = '.';
+ currfw[2] = ((devkit >> 16) & 0xF) + '0';
+ currfw[3] = ((devkit >> 8) & 0xF) + '0';
+ currfw[4] = 0;
+
+ previous = sctrlHENSetStartModuleHandler(OnModuleStart);
+ return 0;
+}
diff --git a/mode.c b/mode.c
index 91ac6dc..6c117a8 100644
--- a/mode.c
+++ b/mode.c
@@ -1,394 +1,399 @@
-/*
- Game Categories Light v 1.3
- Copyright (C) 2011, Bubbletune
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
- */
-
-#include
-#include
-#include
-#include "psppaf.h"
-#include
-#include "categories_lite.h"
-#include "vshitem.h"
-#include "utils.h"
-#include "logger.h"
-
-typedef struct {
- u32 addr;
- u32 opcode;
-} ToggleCategoryPatch;
-
-/* Function pointers */
-static int (*CategorizeGame)(void *unk, int folder, int unk2);
-
-/* Global variables */
-extern char currfw[5];
-int by_category_mode;
-extern u32 text_addr_game, text_size_game;
-//static void *GetSelectionArg;
-static void *class_buffer = NULL;
-extern char user_buffer[256];
-
-Category *folder_list[2] = { NULL, NULL };
-
-#ifdef BENCHMARK
-extern u64 start_mtime;
-u64 now_mtime;
-double benchmark_result;
-extern int display_flag;
-#endif
-
-static int (*scePafAddGameItems)(void *unk, int count, void *unk2);
-
-/* Functions */
-int CategorizeGamePatched(void *unk, int folder, int unk2) {
- int i;
- u32 *array = (u32 *) *(u32 *) ((*(u32 *) (text_addr_game + patches.struct_addr[patch_index])) + ((u32) folder << 2));
- char *title = (char *) array[68 / 4];
- kprintf("called\n");
- Category *p = GetNextCategory(folder_list, NULL, global_pos);
-
- for (i = patches.index[patch_index]; p; i++) {
- char *name = &p->name;
- kprintf("name: %s\n", name);
- int len = sce_paf_private_strlen(name);
-
- if (sce_paf_private_strncmp(name, title, len) == 0) {
- if (title[len] == '/') {
- return CategorizeGame(unk, i, unk2);
- }
- }
-
- p = GetNextCategory(folder_list, p, 0);
- }
-
- /* uncategorized */
- return CategorizeGame(unk, i - 1, unk2);
-}
-
-int scePafAddGameItemsPatched(void *unk, int count, void *unk2) {
- kprintf("called, count: %i\n", count);
- if(count == 3) {
- count = CountCategories(folder_list, global_pos);
- }
- return scePafAddGameItems(unk, count, unk2);
-}
-
-wchar_t* GetGameSubtitle(void *arg0 UNUSED, SfoInfo *sfo) {
- const char *game_type;
- char subtitle[128];
- char firmware[5];
- char *sfofirm, *sfocat, *sfocode;
-
- sfofirm = patch_index ? sfo->sfo630.firmware : sfo->sfo620.firmware;
- sfocat = patch_index ? sfo->sfo630.category : sfo->sfo620.category;
- sfocode = patch_index ? sfo->sfo630.gamecode: sfo->sfo620.gamecode;
-
- kprintf("called\n");
-
- sce_paf_private_strcpy(firmware, sfofirm);
-
- if (sce_paf_private_strcmp(sfocat, "EG") == 0) {
- game_type = "PSN Game";
-
- if (sfofirm[0] == 0) {
- sce_paf_private_strcpy(firmware, "5.00");
- }
- } else if (sce_paf_private_strcmp(sfocat, "ME") == 0) {
- game_type = "PS1 Game";
-
- if (sfofirm[0] == 0) {
- sce_paf_private_strcpy(firmware, "3.03");
- }
- } else {
- if (sfocode[0] == 0 || sce_paf_private_strcmp(sfocode, "UCJS10041") == 0) {
- game_type = "Homebrew Game";
- sce_paf_private_strcpy(firmware, "2.71");
- } else {
- game_type = "Game";
-
- if (sfofirm[0] == 0) {
- sce_paf_private_strcpy(firmware, "1.00");
- }
- }
- }
-
- if (firmware[0] >= currfw[0] && firmware[2] >= currfw[2] && firmware[3] >= currfw[3]) {
- sce_paf_private_snprintf(subtitle, 128, "%s (requires %s)", game_type, firmware);
- } else {
- sce_paf_private_snprintf(subtitle, 128, "%s (for %s - %s)", game_type, firmware, currfw);
- }
-
-#ifdef BENCHMARK
- sce_paf_private_snprintf(subtitle, 128, "Benchmark result: %.4f seconds", benchmark_result);
-#endif
-
- kprintf("Returning %s\n", subtitle);
- gc_utf8_to_unicode((wchar_t*)user_buffer, subtitle);
- return (wchar_t*)user_buffer;
-}
-
-wchar_t *GetCategoryTitle(int number) {
- char *name;
-
-#ifdef BENCHMARK
- if(!display_flag) {
- sceRtcGetCurrentTick(&now_mtime);
- benchmark_result = (double)(now_mtime - start_mtime) / sceRtcGetTickResolution();
- display_flag = 1;
- }
-#endif
-
- kprintf("called, number: %i\n", number);
- Category *p = GetNextCategory(folder_list, NULL, global_pos);
-
- for (int i = patches.index[patch_index]; p; i++) {
- if (i == number) {
- name = &p->name;
- kprintf("Found category: %s\n", name);
- if(sce_paf_private_strncmp(&p->name, "CAT_", 4) == 0) {
- name += 4;
- }
- gc_utf8_to_unicode((wchar_t *) user_buffer, name);
- return (wchar_t *) user_buffer;
- }
-
- p = GetNextCategory(folder_list, p, global_pos);
- }
- kprintf("Cannot find title\n");
- return NULL;
-}
-
-void HijackGameClass(int items) {
- if (patch_index) {
- // SCE made it a little more dynamic in 6.30+, so this hack is no longer needed :)
- return;
- }
-
- /* There's a class inside game_plugin_module that is way to small for
- all the crap we want to do. We need to take control of it to supply
- a buffer of the size we really want. */
-
- u32 text_addr = text_addr_game;
- u32 text_end = text_addr + text_size_game;
- int size = 816 + (items * 12);
-
- /* Allocate the buffer */
- if (class_buffer) {
- sce_paf_private_free(class_buffer);
- }
-
- class_buffer = sce_paf_private_malloc(size);
-
- void *original = (void *) (text_addr + 0x2C8E8);
-
- /* Copy the original to the buffer */
- sce_paf_private_memset(class_buffer, 0, size);
-
- /* Set a pointer to the buffer */
- *(void **) original = class_buffer;
-
- /* Patch the size */
- _sh(items - 1, text_addr + 0x197D4);
- _sh(items - 1, text_addr + 0x19ECC);
-
- /* Hijack the opcodes */
- u32 code = 0x24000000 | ((u32) original & 0xFFFF);
-
- for (; text_addr < text_end; text_addr += 4) {
- u32 read = _lw(text_addr);
-
- /* Check for addiu */
- if ((read & 0xFC00FFFF) == code) {
- /* Check if the registers are not $zero */
- if ((read & 0x03E00000) && (read & 0x001F0000)) {
- /* addiu -> lw */
- _sw((read & 0x03FFFFFF) | 0x8C000000, text_addr);
- }
- }
- }
-}
-
-ToggleCategoryPatch ToggleCategoryPatches_620[] = {
- /* Force the branch to "msgvideoms_info_expired" */
- { 0x0000EBF0, 0x100000C7 }, // beq $s2, $v0, loc_EF10 -> b loc_EF10
- { 0x00011EA4, 0x10000065 }, // beq $v1, $v0, loc_1203C -> b loc_1203C
-
- /* Move a value we need later to a callee-saved register */
- { 0x00012040, 0x00608821 }, // lw $a0, 4($v0) -> move $s1, $v1
-
- /* Patch the call of scePafGetText to GetCategoryTitle */
- { 0x0000EF1C, (u32) GetCategoryTitle }, // jal scePaf_CB608DE5 -> jal GetCategoryTitle
- { 0x0000EF20, 0x26440001 }, // addiu $a1, $a1, -21896 -> addiu $a0, $s2, 1
- { 0x00012048, (u32) GetCategoryTitle }, // jal scePaf_CB608DE5 -> jal GetCategoryTitle
- { 0x0001204C, 0x26240001 }, // addiu $a1, $a1, -21896 -> addiu $a0, $s1, 1
-
- /* Patch a usually hardcoded value to a dynamic one from earlier in the code */
- { 0x0000EF34, 0x26450001 }, // li $a1, 2 -> addiu $a1, $s2, 1
- { 0x00012060, 0x26250001 }, // li $a1, 2 -> addiu $a1, $s1, 1
-
- /* Force a branch to be taken regardless of the timelimit situation */
- { 0x00001524, 0x10000012 }, // beqz $v0, loc_1570 -> b loc_1570
-
- /* Change a call for hardcoded organization to our own category-based one */
- { 0x0001570, (u32) CategorizeGamePatched }, // jal sub_19B5C -> jal CategorizeGamePatched
- { 0x0001528, 0x8E050004 }, // move $a1, $zr -> lw $a1, 4($s0)
-
- /* Force a branch to be taken regardless of the timelimit situation */
- { 0x0000A52C, 0x100000DC }, // beqz $v0, loc_A8A0 -> b loc_A8A0
-
- /* Patch the call of scePafGetText to GetGameSubtitle */
- { 0x0000A8AC, (u32) GetGameSubtitle }, // jal scePaf_CB608DE5 -> jal GetGameSubtitle
- { 0x0000A8B0, 0x02402821 }, // addiu $a1, $a1, -21952 -> move $a1, $s2
-
- /* Patch the call of scePafAddGameItems to change the number */
- { 0x0000DC38, (u32) scePafAddGameItemsPatched }, // jal scePaf_FBC4392D -> jal scePafAddGameItemsPatched
-
- /* Patch some checks regarding the number of folders */
- { 0x00019940, 0x00000000 }, { 0x00019A18, 0x00000000 }, { 0x00019AE8, 0x00000000 }, { 0x00019B94, 0x10000006 },
-};
-
-ToggleCategoryPatch ToggleCategoryPatches_63x[] = {
- /* Change the mode to 'All' in order to avoid all the mess and get to categorizing immediatly */
- { 0x000014C8, 0x10000027 }, // beqz $v1, loc_1568 -> b loc_1568
-
- /* Change a call for hardcoded organization to our own category-based one */
- { 0x00001568, (u32) CategorizeGamePatched }, // jal sub_1ABF4 -> jal CategorizeGamePatched
- { 0x000014CC, 0x8E050004 }, // li $a1, -1 -> lw $a1, 4($s0)
-
- /* Patch the call of scePafAddGameItems to change the number */
- { 0x0000E98C, (u32) scePafAddGameItemsPatched }, // jal scePaf_FBC4392D -> jal scePafAddGameItemsPatched
-
- /* Force the branch to "msgvideoms_info_expired" */
- { 0x0000FDE4, 0x10000019 }, // beq $s3, $v0, loc_FE4C -> b loc_FE4C
- { 0x0001288C, 0x1000001B }, // beq $a0, $v0, loc_128FC -> b loc_128FC
-
- /* Move a value we need later to a callee-saved register */
- { 0x00012900, 0x00809821 }, // lw $a0, 4($v0) -> move $s3, $a0
-
- /* Patch the call of scePafGetText to GetCategoryTitle */
- { 0x00012908, (u32) GetCategoryTitle }, // jal scePaf_70082F6F -> jal GetCategoryTitle
- { 0x0001290C, 0x02602021 }, // addiu $a1, $a1, -13004 -> move $a0, $s3
- { 0x0000FE54, (u32) GetCategoryTitle }, // jal scePaf_70082F6F -> jal GetCategoryTitle
- { 0x0000FE58, 0x02602021 }, // addiu $a1, $a1, -13716 -> move $a0, $s3
-
- /* Patch a usually hardcoded value to a dynamic one from earlier in the code */
- /* Where it gets subtitle from? ;-) */
- { 0x00012920, 0x02602821 }, // li $a1, 1 -> move $a1, $s3
- { 0x0000FE6C, 0x02602821 }, // li $a1, 1 -> move $a1, $s3
-
- /* Force a branch to be taken regardless of the timelimit situation */
- { 0x0000A0AC, 0x100000D8 }, // beqz $v0, loc_A410 -> b loc_A410
-
- /* Patch the call of scePafGetText to GetGameSubtitle */
- { 0x0000A420, (u32) GetGameSubtitle }, // jal scePaf_CB608DE5 -> jal GetGameSubtitle
- { 0x0000A424, 0x02602821 }, // addiu $a1, $a1, -21952 -> move $a1, $s3
-};
-
-ToggleCategoryPatch ToggleCategoryPatches_66x[] = {
- /* Change the mode to 'All' in order to avoid all the mess and get to categorizing immediatly */
- { 0x000014C8, 0x10000027 }, // beqz $v1, loc_1568 -> b loc_1568
-
- /* Change a call for hardcoded organization to our own category-based one */
- { 0x00001568, (u32) CategorizeGamePatched }, // jal sub_1AE10 -> jal CategorizeGamePatched
- { 0x000014CC, 0x8E050004 }, // li $a1, -1 -> lw $a1, 4($s0)
-
- /* Patch the call of scePafAddGameItems to change the number */
- { 0x0000EB10, (u32) scePafAddGameItemsPatched }, // jal scePaf_E219FD72 -> jal scePafAddGameItemsPatched
-
- /* Force the branch to "msgvideoms_info_expired" */
- { 0x0000FF68, 0x10000019 }, // beq $s3, $v0, loc_FE4C -> b loc_FFD0
- { 0x00012A6C, 0x1000001B }, // beq $a0, $v0, loc_128FC -> b loc_12ADC
-
- /* Move a value we need later to a callee-saved register */
- { 0x00012AE0, 0x00809821 }, // lw $a0, 4($v0) -> move $s3, $a0
-
- /* Patch the call of scePafGetText to GetCategoryTitle */
- { 0x00012AE8, (u32) GetCategoryTitle }, // jal scePaf_3874A5F8 -> jal GetCategoryTitle
- { 0x00012AEC, 0x02602021 }, // addiu $a1, $a1, -12268 -> move $a0, $s3
- { 0x0000FFD8, (u32) GetCategoryTitle }, // jal scePaf_3874A5F8 -> jal GetCategoryTitle
- { 0x0000FFDC, 0x02602021 }, // addiu $a1, $a1, -13828 -> move $a0, $s3
-
- /* Patch a usually hardcoded value to a dynamic one from earlier in the code */
- /* Where it gets subtitle from? ;-) */
- { 0x00012B00, 0x02602821 }, // li $a1, 1 -> move $a1, $s3
- { 0x0000FFF0, 0x02602821 }, // li $a1, 1 -> move $a1, $s3
-
- /* Force a branch to be taken regardless of the timelimit situation */
- { 0x0000A230, 0x100000D8 }, // beqz $v0, loc_A594 -> b loc_A594
-
- /* Patch the call of scePafGetText to GetGameSubtitle */
- { 0x0000A5A4, (u32) GetGameSubtitle }, // jal scePaf_3874A5F8 -> jal GetGameSubtitle
- { 0x0000A5A8, 0x02602821 }, // addiu $a1, $a1, -13876 -> move $a1, $s3
-};
-
-static u32 backup[sizeof(ToggleCategoryPatches_620) / sizeof(ToggleCategoryPatch)];
-
-int ToggleCategoryMode(int mode) {
- int total;
- ToggleCategoryPatch *ToggleCategoryPatches;
-
- kprintf("called, mode: %i\n", mode);
-
- if (patch_index == 0) {
- ToggleCategoryPatches = ToggleCategoryPatches_620;
- total = sizeof(ToggleCategoryPatches_620) / sizeof(ToggleCategoryPatch);
- } else if (patch_index == 1) {
- ToggleCategoryPatches = ToggleCategoryPatches_63x;
- total = sizeof(ToggleCategoryPatches_63x) / sizeof(ToggleCategoryPatch);
- } else if (patch_index == 2) {
- ToggleCategoryPatches = ToggleCategoryPatches_66x;
- total = sizeof(ToggleCategoryPatches_66x) / sizeof(ToggleCategoryPatch);
-
- } else {
- return -1;
- }
-
- kprintf("text_addr: %08X\n", text_addr_game);
- if (by_category_mode == 0 && mode == 1) {
- by_category_mode = 1;
- for (int i = 0; i < total; i++) {
- u32 addr = text_addr_game + ToggleCategoryPatches[i].addr;
- u32 opcode = ToggleCategoryPatches[i].opcode;
- backup[i] = _lw(addr);
- if ((opcode & 0xFF000000) == 0x08000000) {
- if(opcode == (u32)scePafAddGameItemsPatched) {
- scePafAddGameItems = (void *)U_EXTRACT_CALL(addr);
- } else if(opcode == (u32)CategorizeGamePatched) {
- CategorizeGame = (void *)U_EXTRACT_CALL(addr);
- }
- MAKE_CALL(addr, opcode);
- } else {
- _sw(opcode, addr);
- }
- }
- ClearCachesForUser();
- return 0;
- }
-
- else if (by_category_mode == 1 && mode == 0) {
- by_category_mode = 0;
-
- for (int i = 0; i < total; i++) {
- kprintf("restoring backup[%i] == %08X, to addr %08X\n", i, backup[i], text_addr_game + ToggleCategoryPatches[i].addr);
- _sw(backup[i], text_addr_game + ToggleCategoryPatches[i].addr);
- }
- ClearCachesForUser();
-
- return 0;
- }
- return -1;
-}
+/*
+ Game Categories Light v 1.3
+ Copyright (C) 2011, Bubbletune
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include "psppaf.h"
+#include
+#include "categories_lite.h"
+#include "vshitem.h"
+#include "utils.h"
+#include "logger.h"
+#include "config.h"
+
+typedef struct {
+ u32 addr;
+ u32 opcode;
+} ToggleCategoryPatch;
+
+/* Function pointers */
+static int (*CategorizeGame)(void *unk, int folder, int unk2);
+
+/* Global variables */
+extern char currfw[5];
+int by_category_mode;
+extern u32 text_addr_game, text_size_game;
+//static void *GetSelectionArg;
+static void *class_buffer = NULL;
+extern char user_buffer[256];
+
+Category *folder_list[2] = { NULL, NULL };
+
+#ifdef BENCHMARK
+extern u64 start_mtime;
+u64 now_mtime;
+double benchmark_result;
+extern int display_flag;
+#endif
+
+static int (*scePafAddGameItems)(void *unk, int count, void *unk2);
+
+/* Functions */
+int CategorizeGamePatched(void *unk, int folder, int unk2) {
+ int i;
+ u32 *array = (u32 *) *(u32 *) ((*(u32 *) (text_addr_game + patches.struct_addr[patch_index])) + ((u32) folder << 2));
+ char *title = (char *) array[68 / 4];
+ kprintf("called\n");
+ Category *p = GetNextCategory(folder_list, NULL, global_pos);
+
+ for (i = patches.index[patch_index]; p; i++) {
+ char *name = &p->name;
+ kprintf("name: %s\n", name);
+ int len = sce_paf_private_strlen(name);
+
+ if (sce_paf_private_strncmp(name, title, len) == 0) {
+ if (title[len] == '/') {
+ return CategorizeGame(unk, i, unk2);
+ }
+ }
+
+ p = GetNextCategory(folder_list, p, 0);
+ }
+
+ /* uncategorized */
+ return CategorizeGame(unk, i - 1, unk2);
+}
+
+int scePafAddGameItemsPatched(void *unk, int count, void *unk2) {
+ kprintf("called, count: %i\n", count);
+ if(count == 3) {
+ count = CountCategories(folder_list, global_pos);
+ }
+ return scePafAddGameItems(unk, count, unk2);
+}
+
+wchar_t* GetGameSubtitle(void *arg0 UNUSED, SfoInfo *sfo) {
+ const char *game_type;
+ char subtitle[128];
+ char firmware[5];
+ char *sfofirm, *sfocat, *sfocode;
+
+ sfofirm = patch_index ? sfo->sfo630.firmware : sfo->sfo620.firmware;
+ sfocat = patch_index ? sfo->sfo630.category : sfo->sfo620.category;
+ sfocode = patch_index ? sfo->sfo630.gamecode: sfo->sfo620.gamecode;
+
+ kprintf("called\n");
+
+ sce_paf_private_strcpy(firmware, sfofirm);
+
+ if (sce_paf_private_strcmp(sfocat, "EG") == 0) {
+ game_type = "PSN Game";
+
+ if (sfofirm[0] == 0) {
+ sce_paf_private_strcpy(firmware, "5.00");
+ }
+ } else if (sce_paf_private_strcmp(sfocat, "ME") == 0) {
+ game_type = "PS1 Game";
+
+ if (sfofirm[0] == 0) {
+ sce_paf_private_strcpy(firmware, "3.03");
+ }
+ } else {
+ if (sfocode[0] == 0 || sce_paf_private_strcmp(sfocode, "UCJS10041") == 0) {
+ game_type = "Homebrew Game";
+ sce_paf_private_strcpy(firmware, "2.71");
+ } else {
+ game_type = "Game";
+
+ if (sfofirm[0] == 0) {
+ sce_paf_private_strcpy(firmware, "1.00");
+ }
+ }
+ }
+
+ if (firmware[0] >= currfw[0] && firmware[2] >= currfw[2] && firmware[3] >= currfw[3]) {
+ sce_paf_private_snprintf(subtitle, 128, "%s (requires %s)", game_type, firmware);
+ } else {
+ sce_paf_private_snprintf(subtitle, 128, "%s (for %s - %s)", game_type, firmware, currfw);
+ }
+
+#ifdef BENCHMARK
+ sce_paf_private_snprintf(subtitle, 128, "Benchmark result: %.4f seconds", benchmark_result);
+#endif
+
+ kprintf("Returning %s\n", subtitle);
+ gc_utf8_to_unicode((wchar_t*)user_buffer, subtitle);
+ return (wchar_t*)user_buffer;
+}
+
+wchar_t *GetCategoryTitle(int number) {
+ char *name;
+
+#ifdef BENCHMARK
+ if(!display_flag) {
+ sceRtcGetCurrentTick(&now_mtime);
+ benchmark_result = (double)(now_mtime - start_mtime) / sceRtcGetTickResolution();
+ display_flag = 1;
+ }
+#endif
+
+ kprintf("called, number: %i\n", number);
+ Category *p = GetNextCategory(folder_list, NULL, global_pos);
+
+ for (int i = patches.index[patch_index]; p; i++) {
+ if (i == number) {
+ name = &p->name;
+ kprintf("Found category: %s\n", name);
+ if(sce_paf_private_strncmp(&p->name, "CAT_", 4) == 0) {
+ name += 4;
+ }
+ if(config.catsort && p->mtime != 1) {
+ gc_utf8_to_unicode((wchar_t *) user_buffer, name+2);
+ } else {
+ gc_utf8_to_unicode((wchar_t *) user_buffer, name);
+ }
+ return (wchar_t *) user_buffer;
+ }
+
+ p = GetNextCategory(folder_list, p, global_pos);
+ }
+ kprintf("Cannot find title\n");
+ return NULL;
+}
+
+void HijackGameClass(int items) {
+ if (patch_index) {
+ // SCE made it a little more dynamic in 6.30+, so this hack is no longer needed :)
+ return;
+ }
+
+ /* There's a class inside game_plugin_module that is way to small for
+ all the crap we want to do. We need to take control of it to supply
+ a buffer of the size we really want. */
+
+ u32 text_addr = text_addr_game;
+ u32 text_end = text_addr + text_size_game;
+ int size = 816 + (items * 12);
+
+ /* Allocate the buffer */
+ if (class_buffer) {
+ sce_paf_private_free(class_buffer);
+ }
+
+ class_buffer = sce_paf_private_malloc(size);
+
+ void *original = (void *) (text_addr + 0x2C8E8);
+
+ /* Copy the original to the buffer */
+ sce_paf_private_memset(class_buffer, 0, size);
+
+ /* Set a pointer to the buffer */
+ *(void **) original = class_buffer;
+
+ /* Patch the size */
+ _sh(items - 1, text_addr + 0x197D4);
+ _sh(items - 1, text_addr + 0x19ECC);
+
+ /* Hijack the opcodes */
+ u32 code = 0x24000000 | ((u32) original & 0xFFFF);
+
+ for (; text_addr < text_end; text_addr += 4) {
+ u32 read = _lw(text_addr);
+
+ /* Check for addiu */
+ if ((read & 0xFC00FFFF) == code) {
+ /* Check if the registers are not $zero */
+ if ((read & 0x03E00000) && (read & 0x001F0000)) {
+ /* addiu -> lw */
+ _sw((read & 0x03FFFFFF) | 0x8C000000, text_addr);
+ }
+ }
+ }
+}
+
+ToggleCategoryPatch ToggleCategoryPatches_620[] = {
+ /* Force the branch to "msgvideoms_info_expired" */
+ { 0x0000EBF0, 0x100000C7 }, // beq $s2, $v0, loc_EF10 -> b loc_EF10
+ { 0x00011EA4, 0x10000065 }, // beq $v1, $v0, loc_1203C -> b loc_1203C
+
+ /* Move a value we need later to a callee-saved register */
+ { 0x00012040, 0x00608821 }, // lw $a0, 4($v0) -> move $s1, $v1
+
+ /* Patch the call of scePafGetText to GetCategoryTitle */
+ { 0x0000EF1C, (u32) GetCategoryTitle }, // jal scePaf_CB608DE5 -> jal GetCategoryTitle
+ { 0x0000EF20, 0x26440001 }, // addiu $a1, $a1, -21896 -> addiu $a0, $s2, 1
+ { 0x00012048, (u32) GetCategoryTitle }, // jal scePaf_CB608DE5 -> jal GetCategoryTitle
+ { 0x0001204C, 0x26240001 }, // addiu $a1, $a1, -21896 -> addiu $a0, $s1, 1
+
+ /* Patch a usually hardcoded value to a dynamic one from earlier in the code */
+ { 0x0000EF34, 0x26450001 }, // li $a1, 2 -> addiu $a1, $s2, 1
+ { 0x00012060, 0x26250001 }, // li $a1, 2 -> addiu $a1, $s1, 1
+
+ /* Force a branch to be taken regardless of the timelimit situation */
+ { 0x00001524, 0x10000012 }, // beqz $v0, loc_1570 -> b loc_1570
+
+ /* Change a call for hardcoded organization to our own category-based one */
+ { 0x0001570, (u32) CategorizeGamePatched }, // jal sub_19B5C -> jal CategorizeGamePatched
+ { 0x0001528, 0x8E050004 }, // move $a1, $zr -> lw $a1, 4($s0)
+
+ /* Force a branch to be taken regardless of the timelimit situation */
+ { 0x0000A52C, 0x100000DC }, // beqz $v0, loc_A8A0 -> b loc_A8A0
+
+ /* Patch the call of scePafGetText to GetGameSubtitle */
+ { 0x0000A8AC, (u32) GetGameSubtitle }, // jal scePaf_CB608DE5 -> jal GetGameSubtitle
+ { 0x0000A8B0, 0x02402821 }, // addiu $a1, $a1, -21952 -> move $a1, $s2
+
+ /* Patch the call of scePafAddGameItems to change the number */
+ { 0x0000DC38, (u32) scePafAddGameItemsPatched }, // jal scePaf_FBC4392D -> jal scePafAddGameItemsPatched
+
+ /* Patch some checks regarding the number of folders */
+ { 0x00019940, 0x00000000 }, { 0x00019A18, 0x00000000 }, { 0x00019AE8, 0x00000000 }, { 0x00019B94, 0x10000006 },
+};
+
+ToggleCategoryPatch ToggleCategoryPatches_63x[] = {
+ /* Change the mode to 'All' in order to avoid all the mess and get to categorizing immediatly */
+ { 0x000014C8, 0x10000027 }, // beqz $v1, loc_1568 -> b loc_1568
+
+ /* Change a call for hardcoded organization to our own category-based one */
+ { 0x00001568, (u32) CategorizeGamePatched }, // jal sub_1ABF4 -> jal CategorizeGamePatched
+ { 0x000014CC, 0x8E050004 }, // li $a1, -1 -> lw $a1, 4($s0)
+
+ /* Patch the call of scePafAddGameItems to change the number */
+ { 0x0000E98C, (u32) scePafAddGameItemsPatched }, // jal scePaf_FBC4392D -> jal scePafAddGameItemsPatched
+
+ /* Force the branch to "msgvideoms_info_expired" */
+ { 0x0000FDE4, 0x10000019 }, // beq $s3, $v0, loc_FE4C -> b loc_FE4C
+ { 0x0001288C, 0x1000001B }, // beq $a0, $v0, loc_128FC -> b loc_128FC
+
+ /* Move a value we need later to a callee-saved register */
+ { 0x00012900, 0x00809821 }, // lw $a0, 4($v0) -> move $s3, $a0
+
+ /* Patch the call of scePafGetText to GetCategoryTitle */
+ { 0x00012908, (u32) GetCategoryTitle }, // jal scePaf_70082F6F -> jal GetCategoryTitle
+ { 0x0001290C, 0x02602021 }, // addiu $a1, $a1, -13004 -> move $a0, $s3
+ { 0x0000FE54, (u32) GetCategoryTitle }, // jal scePaf_70082F6F -> jal GetCategoryTitle
+ { 0x0000FE58, 0x02602021 }, // addiu $a1, $a1, -13716 -> move $a0, $s3
+
+ /* Patch a usually hardcoded value to a dynamic one from earlier in the code */
+ /* Where it gets subtitle from? ;-) */
+ { 0x00012920, 0x02602821 }, // li $a1, 1 -> move $a1, $s3
+ { 0x0000FE6C, 0x02602821 }, // li $a1, 1 -> move $a1, $s3
+
+ /* Force a branch to be taken regardless of the timelimit situation */
+ { 0x0000A0AC, 0x100000D8 }, // beqz $v0, loc_A410 -> b loc_A410
+
+ /* Patch the call of scePafGetText to GetGameSubtitle */
+ { 0x0000A420, (u32) GetGameSubtitle }, // jal scePaf_CB608DE5 -> jal GetGameSubtitle
+ { 0x0000A424, 0x02602821 }, // addiu $a1, $a1, -21952 -> move $a1, $s3
+};
+
+ToggleCategoryPatch ToggleCategoryPatches_66x[] = {
+ /* Change the mode to 'All' in order to avoid all the mess and get to categorizing immediatly */
+ { 0x000014C8, 0x10000027 }, // beqz $v1, loc_1568 -> b loc_1568
+
+ /* Change a call for hardcoded organization to our own category-based one */
+ { 0x00001568, (u32) CategorizeGamePatched }, // jal sub_1AE10 -> jal CategorizeGamePatched
+ { 0x000014CC, 0x8E050004 }, // li $a1, -1 -> lw $a1, 4($s0)
+
+ /* Patch the call of scePafAddGameItems to change the number */
+ { 0x0000EB10, (u32) scePafAddGameItemsPatched }, // jal scePaf_E219FD72 -> jal scePafAddGameItemsPatched
+
+ /* Force the branch to "msgvideoms_info_expired" */
+ { 0x0000FF68, 0x10000019 }, // beq $s3, $v0, loc_FE4C -> b loc_FFD0
+ { 0x00012A6C, 0x1000001B }, // beq $a0, $v0, loc_128FC -> b loc_12ADC
+
+ /* Move a value we need later to a callee-saved register */
+ { 0x00012AE0, 0x00809821 }, // lw $a0, 4($v0) -> move $s3, $a0
+
+ /* Patch the call of scePafGetText to GetCategoryTitle */
+ { 0x00012AE8, (u32) GetCategoryTitle }, // jal scePaf_3874A5F8 -> jal GetCategoryTitle
+ { 0x00012AEC, 0x02602021 }, // addiu $a1, $a1, -12268 -> move $a0, $s3
+ { 0x0000FFD8, (u32) GetCategoryTitle }, // jal scePaf_3874A5F8 -> jal GetCategoryTitle
+ { 0x0000FFDC, 0x02602021 }, // addiu $a1, $a1, -13828 -> move $a0, $s3
+
+ /* Patch a usually hardcoded value to a dynamic one from earlier in the code */
+ /* Where it gets subtitle from? ;-) */
+ { 0x00012B00, 0x02602821 }, // li $a1, 1 -> move $a1, $s3
+ { 0x0000FFF0, 0x02602821 }, // li $a1, 1 -> move $a1, $s3
+
+ /* Force a branch to be taken regardless of the timelimit situation */
+ { 0x0000A230, 0x100000D8 }, // beqz $v0, loc_A594 -> b loc_A594
+
+ /* Patch the call of scePafGetText to GetGameSubtitle */
+ { 0x0000A5A4, (u32) GetGameSubtitle }, // jal scePaf_3874A5F8 -> jal GetGameSubtitle
+ { 0x0000A5A8, 0x02602821 }, // addiu $a1, $a1, -13876 -> move $a1, $s3
+};
+
+static u32 backup[sizeof(ToggleCategoryPatches_620) / sizeof(ToggleCategoryPatch)];
+
+int ToggleCategoryMode(int mode) {
+ int total;
+ ToggleCategoryPatch *ToggleCategoryPatches;
+
+ kprintf("called, mode: %i\n", mode);
+
+ if (patch_index == 0) {
+ ToggleCategoryPatches = ToggleCategoryPatches_620;
+ total = sizeof(ToggleCategoryPatches_620) / sizeof(ToggleCategoryPatch);
+ } else if (patch_index == 1) {
+ ToggleCategoryPatches = ToggleCategoryPatches_63x;
+ total = sizeof(ToggleCategoryPatches_63x) / sizeof(ToggleCategoryPatch);
+ } else if (patch_index == 2) {
+ ToggleCategoryPatches = ToggleCategoryPatches_66x;
+ total = sizeof(ToggleCategoryPatches_66x) / sizeof(ToggleCategoryPatch);
+
+ } else {
+ return -1;
+ }
+
+ kprintf("text_addr: %08X\n", text_addr_game);
+ if (by_category_mode == 0 && mode == 1) {
+ by_category_mode = 1;
+ for (int i = 0; i < total; i++) {
+ u32 addr = text_addr_game + ToggleCategoryPatches[i].addr;
+ u32 opcode = ToggleCategoryPatches[i].opcode;
+ backup[i] = _lw(addr);
+ if ((opcode & 0xFF000000) == 0x08000000) {
+ if(opcode == (u32)scePafAddGameItemsPatched) {
+ scePafAddGameItems = (void *)U_EXTRACT_CALL(addr);
+ } else if(opcode == (u32)CategorizeGamePatched) {
+ CategorizeGame = (void *)U_EXTRACT_CALL(addr);
+ }
+ MAKE_CALL(addr, opcode);
+ } else {
+ _sw(opcode, addr);
+ }
+ }
+ ClearCachesForUser();
+ return 0;
+ }
+
+ else if (by_category_mode == 1 && mode == 0) {
+ by_category_mode = 0;
+
+ for (int i = 0; i < total; i++) {
+ kprintf("restoring backup[%i] == %08X, to addr %08X\n", i, backup[i], text_addr_game + ToggleCategoryPatches[i].addr);
+ _sw(backup[i], text_addr_game + ToggleCategoryPatches[i].addr);
+ }
+ ClearCachesForUser();
+
+ return 0;
+ }
+ return -1;
+}
diff --git a/readme.txt b/readme.txt
index 6a80e89..0035bfe 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,76 +1,83 @@
-Game Categories Lite v1.5 - Codestation
-
-This plugin is based and uses source code from Game Categories Revised v12 (GCR)
-and Game Categories Light v1.3 (GCL), both created by Bubbletune. Compatible with
-6.20, 6.3x and 6.60 CFW.
-
-I changed the name of the project (doing a fork in the process) to avoid confusion
-with GCL and GCR (the plugin is based heavily in GCL since it doesn't use a
-kernel/user approach).
-
-You can configure the folder prefix (use CAT_ for folders or not), showing uncategorized
-content and change the category mode in system settings.
-
-If you want to hide certain homebrew/game/dlc from the category listing just create a file
-named gclite_filter.txt and add the folders name, one per line (make sure that a newline is
-added at the end of the file). Next, put the file in your seplugins folder.
-
-If you want a translation of the visible options then edit the file category_lite_en.txt
-and save it using the language code that first you (e.g. "es" for spanish).
-
-Note: you must use UTF-8 encoding for the translation files (without unicode BOM).
-
-Languages supported: "ja", "en", "fr", "es", "de", "it", "nl", "pt", "ru", "ko", "ch1", "ch2"
-
-The source code is also available in https://github.com/codestation/gclite
-
-Notes: make sure that this is the 1st plugin listed in vsh.txt
-
-Known issues:
->> Unknown if fixable:
-* Change of category in the PSPGo requires a VSH reset.
-
->> Unrelated to gclite:
-* ME doesn't merge the categories with the same name between /ISO and /PSP/GAME.
-
->> Folder mode limitations/bugs:
-* Max categories: 8 (included uncategorized).
-* Folder name + homebrew folder name: 30 character, e.g.: My homebrews/AwesomeBigHomebrew
- is valid but My homebrews/AwesomeBigHomebrews isn't (i am not counting the "/").
- Note: the japanese and other non-ascii characters are 2 bytes wide, e.g.: カラフル counts
- as 8 chars.
-
-Changelog
-[!]Fix duplicated entries on iso category in folder mode (PRO).
-v1.5:
-[+]Support for categories in folder mode like Bubbletune's GCL (thx Nekmo for betatesting).
-[+]Support to hide certain homebrews/games/dlc from the categories.
-[+]Added subtitles to the config options.
-[+]Empty categories are hidden by default.
-[+]Non game folders are hidden by default on uncategorized content.
-[+]Added folder mode benchmark (compile with BENCHMARK=1)
-[+]Added bulgarian translation by Xian Nox.
-[+]Added simple chinese translation by phoe-nix.
-[+]Added Traditional-Chinese translation by Raiyou.
-[+]Added Russian translation by Frostegater.
-[+]Added italian translation by stevealexanderames.
-[!]Force the uncategorized content to be the last item by default.
-[!]Fixed UMD icon malfunction bug introduced in 1.4-r2.
-v1.4:
-[+]6.60 firmware support
-[+]Allow the uncategorized folder to be sorted with your favorite app.
-[+]Multiple language support.
-[+]Added ja translation by popsdeco.
-[+]Added de translation by KOlle and The Z.
-v1.3:
-[+]Support for categories in contextual menu.
-[+]Support for plugin configuration in system settings.
-[+]Added runtime detection for ME, so category games are now shown.
-[!]Fixed issues with PSPGo (big thanks to raing3 to help me with the debugging).
-v1.2:
-[!]Fixed PSPGo categories, again (thx RUSTII for the tests).
-[!]Fixed the free space display when the psp returns from sleep.
-v1.1:
-[!]Fixed PSPGo categories (thx RUSTII for the tests).
-v1.0:
-[+]First release.
+Game Categories Lite
+
+This plugin is based and uses source code from Game Categories Revised v12 (GCR)
+and Game Categories Light v1.3 (GCL), both created by Bubbletune. Compatible with
+6.20, 6.3x and 6.60 CFW.
+
+I changed the name of the project (doing a fork in the process) to avoid confusion
+with GCL and GCR (the plugin is based heavily in GCL since it doesn't use a
+kernel/user approach).
+
+You can configure the folder prefix (use CAT_ for folders or not), showing uncategorized
+content and change the category mode in system settings.
+
+If you want to hide certain homebrew/game/dlc from the category listing just create a file
+named gclite_filter.txt and add the folders name, one per line (make sure that a newline is
+added at the end of the file). Next, put the file in your seplugins folder.
+
+If you want a translation of the visible options then edit the file category_lite_en.txt
+and save it using the language code that first you (e.g. "es" for spanish).
+
+Note: you must use UTF-8 encoding for the translation files (without unicode BOM).
+
+Languages supported: "ja", "en", "fr", "es", "de", "it", "nl", "pt", "ru", "ko", "ch1", "ch2"
+
+The source code is also available in https://github.com/codestation/gclite
+
+Notes: make sure that this is the 1st plugin listed in vsh.txt
+
+Known issues:
+>> Unknown if fixable:
+* Change of category in the PSPGo requires a VSH reset.
+
+>> Unrelated to gclite:
+* ME doesn't merge the categories with the same name between /ISO and /PSP/GAME.
+
+>> Folder mode limitations/bugs:
+* Max categories: 8 (included uncategorized).
+* Folder name + homebrew folder name: 30 character, e.g.: My homebrews/AwesomeBigHomebrew
+ is valid but My homebrews/AwesomeBigHomebrews isn't (i am not counting the "/").
+ Note: the japanese and other non-ascii characters are 2 bytes wide, e.g.: カラフル counts
+ as 8 chars.
+
+Changelog
+v1.7-js1 (October 17, 2017):
+[!]Fix labels not showing on PSP go internal storage.
+v1.6:
+[+]Added new option to sort categories: Use CAT_XX or XXcategory_name (XX between 00 and 99).
+v1.5-r4
+[+]Added polish translation.
+v1.5-r3
+[!]Fix duplicated entries on iso category in folder mode (PRO).
+v1.5:
+[+]Support for categories in folder mode like Bubbletune's GCL (thx Nekmo for betatesting).
+[+]Support to hide certain homebrews/games/dlc from the categories.
+[+]Added subtitles to the config options.
+[+]Empty categories are hidden by default.
+[+]Non game folders are hidden by default on uncategorized content.
+[+]Added folder mode benchmark (compile with BENCHMARK=1)
+[+]Added bulgarian translation by Xian Nox.
+[+]Added simple chinese translation by phoe-nix.
+[+]Added Traditional-Chinese translation by Raiyou.
+[+]Added Russian translation by Frostegater.
+[+]Added italian translation by stevealexanderames.
+[!]Force the uncategorized content to be the last item by default.
+[!]Fixed UMD icon malfunction bug introduced in 1.4-r2.
+v1.4:
+[+]6.60 firmware support
+[+]Allow the uncategorized folder to be sorted with your favorite app.
+[+]Multiple language support.
+[+]Added ja translation by popsdeco.
+[+]Added de translation by KOlle and The Z.
+v1.3:
+[+]Support for categories in contextual menu.
+[+]Support for plugin configuration in system settings.
+[+]Added runtime detection for ME, so category games are now shown.
+[!]Fixed issues with PSPGo (big thanks to raing3 to help me with the debugging).
+v1.2:
+[!]Fixed PSPGo categories, again (thx RUSTII for the tests).
+[!]Fixed the free space display when the psp returns from sleep.
+v1.1:
+[!]Fixed PSPGo categories (thx RUSTII for the tests).
+v1.0:
+[+]First release.
diff --git a/selection.c b/selection.c
index 2dc58ae..1edb84e 100644
--- a/selection.c
+++ b/selection.c
@@ -1,311 +1,311 @@
-/*
- Game Categories Light v 1.3
- Copyright (C) 2011, Bubbletune
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
- */
-
-#include
-#include
-#include
-#include "psppaf.h"
-#include
-#include "categories_lite.h"
-#include "logger.h"
-
-/* Global variables */
-static int already_in_foldermode = 1;
-extern int by_category_mode;
-extern u32 text_addr_game;
-static void *GetSelectionArg;
-static u32 sound_call_addr;
-
-static int defaulted;
-
-int ToggleCategoryMode(int mode);
-void HijackGameClass(int items);
-
-/* Function pointers */
-int (*AddGameContext)(void *unk, SceGameContext **item);
-SceGameContext *(*GetSelection)(void *arg0, u32 arg1);
-int (*SetMode)(void *arg0, void *arg1, void *arg2);
-int (*OnPushFolderOptionListCascade)(void *arg0, u32 *arg1);
-int (*OnPushOptionListCascade)(void *arg0, u32 *arg1);
-
-int (*scePafSetSelection)(void *arg0, int selection);
-int (*vsh_function)(void *arg);
-
-/* Functions */
-void ToggleSound(int toggle) {
- kprintf("called, toggle: %i\n", toggle);
- if (toggle == 0) {
- sound_call_addr = U_EXTRACT_CALL(text_addr_game + patches.play_sound_call[patch_index]);
- _sw(NOP_OPCODE, text_addr_game + patches.play_sound_call[patch_index]);
- }
-
- else {
- MAKE_CALL(text_addr_game+patches.play_sound_call[patch_index], sound_call_addr);
- }
-
- ClearCachesForUser();
-}
-
-int AddGameContextPatched(void *unk, SceGameContext **item) {
- /* Allocate buffer for "By Category" */
- SceGameContext *newitem = sce_paf_private_malloc(sizeof(SceGameContext));
- sce_paf_private_memcpy(newitem, *item, sizeof(SceGameContext));
- kprintf("called\n");
- /* Modify buffer for "By Category */
- newitem->text = "msg_by_category";
- newitem->option = patches.OPTION_BY_CATEGORY[patch_index];
-
- /* Add the option */
- AddGameContext(unk, &newitem);
-
- /* Add the original */
- return AddGameContext(unk, item);
-}
-
-#define MODE_ALL 0
-#define OPTION_BY_EXPIRE_DATE 0
-
-int SetModePatched(void *arg0, void *arg1, void *arg2, u32 *info) {
- /** Square-button cycling **/
- kprintf("called\n");
- /* What's the current mode? */
- if (info[patches.array_index[patch_index]] == MODE_ALL) {
- /* All */
- /* Next stop: By Expire Date */
- info[patches.array_index[patch_index]] = patches.MODE_BY_EXPIRE_DATE[patch_index];
- already_in_foldermode = 1;
- } else if (info[patches.array_index[patch_index]] == patches.MODE_BY_EXPIRE_DATE[patch_index]) {
- if (!by_category_mode) {
- /* By Expire Date */
- /* Next stop: By Category */
- ToggleCategoryMode(1);
- } else {
- /* By Category */
- /* Next stop: All */
- ToggleCategoryMode(0);
- info[patches.array_index[patch_index]] = MODE_ALL;
-
- already_in_foldermode = 0;
- }
- }
- return SetMode(arg0, arg1, arg2);
-}
-
-void QuickSwitchToAll(SceGameContext *selection, void *arg0, u32 *arg1) {
- kprintf("called\n");
- u32 backup = selection->option;
-
- /* Switch to "All" */
- selection->option = 1;
-
- /* Disable the click sound */
- ToggleSound(0);
-
- /* Call the function */
- OnPushFolderOptionListCascade(arg0, arg1);
-
- /* Enable the click sound */
- ToggleSound(1);
-
- /* Restore the original option */
- selection->option = backup;
-}
-
-int OnPushFolderOptionListCascadePatched(void *arg0, u32 *arg1) {
- kprintf("called\n");
- SceGameContext *selection = GetSelection(GetSelectionArg, arg1[3]);
- kprintf("GetSelection returned, selection: %08X\n", selection);
- /* We're in one of the folder modes in case this function is being used... */
- /* Where do we want to go? */
- if (selection->option == OPTION_BY_EXPIRE_DATE) {
- kprintf("OPTION_BY_EXPIRE_DATE\n");
- /* Check if we're already in there */
- if (by_category_mode) {
- kprintf("toggle category to 0\n");
- /* Toggle "By Category" off */
- ToggleCategoryMode(0);
-
- /* We're not... */
- if (already_in_foldermode) {
- kprintf("calling QuickSwitchToAll #1\n");
- /* Switch to "All" for a brief second */
- QuickSwitchToAll(selection, arg0, arg1);
- } else {
- /* We are now! */
- already_in_foldermode = 1;
- }
- }
- }
-
- else if (selection->option == patches.OPTION_BY_CATEGORY[patch_index]) {
- int res;
- kprintf("OPTION_BY_CATEGORY\n");
- /* Check if we're already in there */
- if (!by_category_mode) {
- /* We're not... */
- if (already_in_foldermode) {
- kprintf("calling QuickSwitchToAll #2\n");
- /* Switch to "All" for a brief second */
- QuickSwitchToAll(selection, arg0, arg1);
- } else {
- /* We are now! */
- already_in_foldermode = 1;
- }
- kprintf("toggle category to 1\n");
- /* Toggle "By Category" on */
- ToggleCategoryMode(1);
-
- /** HINT: This mode doesn't *really* exist, so we need to simulate "By Expire Date" */
- /* Switch to "By Expire Date" */
- selection->option = OPTION_BY_EXPIRE_DATE;
- kprintf("calling OnPushOptionListCascade\n");
- /* Call the function */
- res = OnPushOptionListCascade(arg0, arg1);
-
- /* Restore the original */
- selection->option = patches.OPTION_BY_CATEGORY[patch_index];
- } else {
- /* We are... */
- /* But darn, you user! You opened a lame context menu! ): */
- /* Now we need to fake another item just to make it dissappear */
- kprintf("faking item\n");
- /* Switch to "By Expire Date" */
- selection->option = OPTION_BY_EXPIRE_DATE;
- kprintf("calling OnPushFolderOptionListCascade\n");
- /* Call the function */
- res = OnPushFolderOptionListCascade(arg0, arg1);
-
- /* Restore the original */
- selection->option = patches.OPTION_BY_CATEGORY[patch_index];
- }
-
- return res;
- } else { // OPTION_ALL && OPTION_BY_FORMAT
- already_in_foldermode = 0;
- }
- kprintf("going out\n");
- return OnPushFolderOptionListCascade(arg0, arg1);
-}
-
-int OnPushOptionListCascadePatched(void *arg0, u32 *arg1) {
- kprintf("called\n");
- SceGameContext *selection = GetSelection(GetSelectionArg, arg1[3]);
-
- /* We're in the "All" mode in case this function is being used... */
- /* Where do we want to go? */
- if (selection->option == OPTION_BY_EXPIRE_DATE) {
- already_in_foldermode = 1;
-
- /* Toggle "By Category" off */
- ToggleCategoryMode(0);
- } else if (selection->option == patches.OPTION_BY_CATEGORY[patch_index]) {
- already_in_foldermode = 1;
-
- /* Toggle "By Category" on */
- ToggleCategoryMode(1);
-
- /** HINT: This mode doesn't *really* exist, so we need to simulate "By Expire Date" */
- /* Switch to "By Expire Date" */
- selection->option = OPTION_BY_EXPIRE_DATE;
-
- kprintf("Calling OnPushOptionListCascade\n");
- /* Call the function */
- int res = OnPushOptionListCascade(arg0, arg1);
- kprintf("Called OnPushOptionListCascade, res: %i\n", res);
- /* Restore the original */
- selection->option = patches.OPTION_BY_CATEGORY[patch_index];
-
- return res;
- } else {
- already_in_foldermode = 0;
- }
-
- return OnPushOptionListCascade(arg0, arg1);
-}
-
-int scePafSetSelectionPatched(void *arg0, int selection) {
- kprintf("called\n");
- /* "By Expire Date" */
- if (selection == 1) {
- /* Should it be "By Category"? */
- if (by_category_mode) {
- selection = 2; // yes
- }
- }
-
- return scePafSetSelection(arg0, selection);
-}
-
-int vsh_function_patched(void *arg) {
- if (!defaulted) {
- ToggleCategoryMode(1);
-
- _sw(patches.MODE_BY_EXPIRE_DATE[patch_index], text_addr_game + patches.current_mode[patch_index] + 56);
- ClearCachesForUser();
-
- defaulted = 1;
- }
-
- return vsh_function(arg);
-}
-
-void PatchSelection(u32 text_addr) {
- /* Patch AddGameContext */
- MAKE_CALL(text_addr+patches.add_game_context_call[patch_index][0], AddGameContextPatched);
- MAKE_CALL(text_addr+patches.add_game_context_call[patch_index][1], AddGameContextPatched);
- AddGameContext = (void *) (text_addr + patches.add_game_context[patch_index]);
-
- /* Patch calls to SetMode */
- MAKE_CALL(text_addr+patches.setmode_call_arg_1[patch_index][0], SetModePatched);
- _sw(patches.setmode_arg_opcode[patch_index], text_addr + patches.setmode_call_arg_1[patch_index][1]);
-
- if (patches.setmode_call_arg_2[patch_index][0]) {
- kprintf("patching SetMode\n");
- MAKE_CALL(text_addr+patches.setmode_call_arg_2[patch_index][0], SetModePatched);
- _sw(patches.setmode_arg_opcode[patch_index], text_addr + patches.setmode_call_arg_2[patch_index][1]);
- }
-
- /* Patch OnPushFolderOptionListCascade */
- _sw((u32) OnPushFolderOptionListCascadePatched, text_addr + patches.on_push_folder_options_call[patch_index]);
- OnPushFolderOptionListCascade = (void *) (text_addr + patches.on_push_folder_options[patch_index]);
-
- /* Patch OnPushOptionListCascade */
- _sw((u32) OnPushOptionListCascadePatched, text_addr + patches.on_push_options_call[patch_index]);
- OnPushOptionListCascade = (void *) (text_addr + patches.on_push_options[patch_index]);
-
- /* Additional function pointers */
- GetSelection = (void *) (text_addr + patches.get_selection[patch_index]);
- SetMode = (void *) (text_addr + patches.setmode[patch_index]);
-
- /* Argument required for GetSelection */
- GetSelectionArg = (void *) (text_addr + patches.get_selection_arg[patch_index]);
-
- if (patches.set_selection_call[patch_index][0]) {
- /* Patch scePafSetSelection */
- scePafSetSelection = (void *)U_EXTRACT_CALL(text_addr+patches.set_selection_call[patch_index][0]);
- MAKE_CALL(text_addr+patches.set_selection_call[patch_index][0], scePafSetSelectionPatched);
- MAKE_CALL(text_addr+patches.set_selection_call[patch_index][1], scePafSetSelectionPatched);
- }
-
- defaulted = 0;
- by_category_mode = 0;
- vsh_function = (void *)U_EXTRACT_CALL(text_addr+0x12E0);
- MAKE_CALL(text_addr+0x12E0, vsh_function_patched);
-
- HijackGameClass(32);
-}
+/*
+ Game Categories Light v 1.3
+ Copyright (C) 2011, Bubbletune
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include "psppaf.h"
+#include
+#include "categories_lite.h"
+#include "logger.h"
+
+/* Global variables */
+static int already_in_foldermode = 1;
+extern int by_category_mode;
+extern u32 text_addr_game;
+static void *GetSelectionArg;
+static u32 sound_call_addr;
+
+static int defaulted;
+
+int ToggleCategoryMode(int mode);
+void HijackGameClass(int items);
+
+/* Function pointers */
+int (*AddGameContext)(void *unk, SceGameContext **item);
+SceGameContext *(*GetSelection)(void *arg0, u32 arg1);
+int (*SetMode)(void *arg0, void *arg1, void *arg2);
+int (*OnPushFolderOptionListCascade)(void *arg0, u32 *arg1);
+int (*OnPushOptionListCascade)(void *arg0, u32 *arg1);
+
+int (*scePafSetSelection)(void *arg0, int selection);
+int (*vsh_function)(void *arg);
+
+/* Functions */
+void ToggleSound(int toggle) {
+ kprintf("called, toggle: %i\n", toggle);
+ if (toggle == 0) {
+ sound_call_addr = U_EXTRACT_CALL(text_addr_game + patches.play_sound_call[patch_index]);
+ _sw(NOP_OPCODE, text_addr_game + patches.play_sound_call[patch_index]);
+ }
+
+ else {
+ MAKE_CALL(text_addr_game+patches.play_sound_call[patch_index], sound_call_addr);
+ }
+
+ ClearCachesForUser();
+}
+
+int AddGameContextPatched(void *unk, SceGameContext **item) {
+ /* Allocate buffer for "By Category" */
+ SceGameContext *newitem = sce_paf_private_malloc(sizeof(SceGameContext));
+ sce_paf_private_memcpy(newitem, *item, sizeof(SceGameContext));
+ kprintf("called\n");
+ /* Modify buffer for "By Category */
+ newitem->text = "msg_by_category";
+ newitem->option = patches.OPTION_BY_CATEGORY[patch_index];
+
+ /* Add the option */
+ AddGameContext(unk, &newitem);
+
+ /* Add the original */
+ return AddGameContext(unk, item);
+}
+
+#define MODE_ALL 0
+#define OPTION_BY_EXPIRE_DATE 0
+
+int SetModePatched(void *arg0, void *arg1, void *arg2, u32 *info) {
+ /** Square-button cycling **/
+ kprintf("called\n");
+ /* What's the current mode? */
+ if (info[patches.array_index[patch_index]] == MODE_ALL) {
+ /* All */
+ /* Next stop: By Expire Date */
+ info[patches.array_index[patch_index]] = patches.MODE_BY_EXPIRE_DATE[patch_index];
+ already_in_foldermode = 1;
+ } else if (info[patches.array_index[patch_index]] == patches.MODE_BY_EXPIRE_DATE[patch_index]) {
+ if (!by_category_mode) {
+ /* By Expire Date */
+ /* Next stop: By Category */
+ ToggleCategoryMode(1);
+ } else {
+ /* By Category */
+ /* Next stop: All */
+ ToggleCategoryMode(0);
+ info[patches.array_index[patch_index]] = MODE_ALL;
+
+ already_in_foldermode = 0;
+ }
+ }
+ return SetMode(arg0, arg1, arg2);
+}
+
+void QuickSwitchToAll(SceGameContext *selection, void *arg0, u32 *arg1) {
+ kprintf("called\n");
+ u32 backup = selection->option;
+
+ /* Switch to "All" */
+ selection->option = 1;
+
+ /* Disable the click sound */
+ ToggleSound(0);
+
+ /* Call the function */
+ OnPushFolderOptionListCascade(arg0, arg1);
+
+ /* Enable the click sound */
+ ToggleSound(1);
+
+ /* Restore the original option */
+ selection->option = backup;
+}
+
+int OnPushFolderOptionListCascadePatched(void *arg0, u32 *arg1) {
+ kprintf("called\n");
+ SceGameContext *selection = GetSelection(GetSelectionArg, arg1[3]);
+ kprintf("GetSelection returned, selection: %08X\n", selection);
+ /* We're in one of the folder modes in case this function is being used... */
+ /* Where do we want to go? */
+ if (selection->option == OPTION_BY_EXPIRE_DATE) {
+ kprintf("OPTION_BY_EXPIRE_DATE\n");
+ /* Check if we're already in there */
+ if (by_category_mode) {
+ kprintf("toggle category to 0\n");
+ /* Toggle "By Category" off */
+ ToggleCategoryMode(0);
+
+ /* We're not... */
+ if (already_in_foldermode) {
+ kprintf("calling QuickSwitchToAll #1\n");
+ /* Switch to "All" for a brief second */
+ QuickSwitchToAll(selection, arg0, arg1);
+ } else {
+ /* We are now! */
+ already_in_foldermode = 1;
+ }
+ }
+ }
+
+ else if (selection->option == patches.OPTION_BY_CATEGORY[patch_index]) {
+ int res;
+ kprintf("OPTION_BY_CATEGORY\n");
+ /* Check if we're already in there */
+ if (!by_category_mode) {
+ /* We're not... */
+ if (already_in_foldermode) {
+ kprintf("calling QuickSwitchToAll #2\n");
+ /* Switch to "All" for a brief second */
+ QuickSwitchToAll(selection, arg0, arg1);
+ } else {
+ /* We are now! */
+ already_in_foldermode = 1;
+ }
+ kprintf("toggle category to 1\n");
+ /* Toggle "By Category" on */
+ ToggleCategoryMode(1);
+
+ /** HINT: This mode doesn't *really* exist, so we need to simulate "By Expire Date" */
+ /* Switch to "By Expire Date" */
+ selection->option = OPTION_BY_EXPIRE_DATE;
+ kprintf("calling OnPushOptionListCascade\n");
+ /* Call the function */
+ res = OnPushOptionListCascade(arg0, arg1);
+
+ /* Restore the original */
+ selection->option = patches.OPTION_BY_CATEGORY[patch_index];
+ } else {
+ /* We are... */
+ /* But darn, you user! You opened a lame context menu! ): */
+ /* Now we need to fake another item just to make it dissappear */
+ kprintf("faking item\n");
+ /* Switch to "By Expire Date" */
+ selection->option = OPTION_BY_EXPIRE_DATE;
+ kprintf("calling OnPushFolderOptionListCascade\n");
+ /* Call the function */
+ res = OnPushFolderOptionListCascade(arg0, arg1);
+
+ /* Restore the original */
+ selection->option = patches.OPTION_BY_CATEGORY[patch_index];
+ }
+
+ return res;
+ } else { // OPTION_ALL && OPTION_BY_FORMAT
+ already_in_foldermode = 0;
+ }
+ kprintf("going out\n");
+ return OnPushFolderOptionListCascade(arg0, arg1);
+}
+
+int OnPushOptionListCascadePatched(void *arg0, u32 *arg1) {
+ kprintf("called\n");
+ SceGameContext *selection = GetSelection(GetSelectionArg, arg1[3]);
+
+ /* We're in the "All" mode in case this function is being used... */
+ /* Where do we want to go? */
+ if (selection->option == OPTION_BY_EXPIRE_DATE) {
+ already_in_foldermode = 1;
+
+ /* Toggle "By Category" off */
+ ToggleCategoryMode(0);
+ } else if (selection->option == patches.OPTION_BY_CATEGORY[patch_index]) {
+ already_in_foldermode = 1;
+
+ /* Toggle "By Category" on */
+ ToggleCategoryMode(1);
+
+ /** HINT: This mode doesn't *really* exist, so we need to simulate "By Expire Date" */
+ /* Switch to "By Expire Date" */
+ selection->option = OPTION_BY_EXPIRE_DATE;
+
+ kprintf("Calling OnPushOptionListCascade\n");
+ /* Call the function */
+ int res = OnPushOptionListCascade(arg0, arg1);
+ kprintf("Called OnPushOptionListCascade, res: %i\n", res);
+ /* Restore the original */
+ selection->option = patches.OPTION_BY_CATEGORY[patch_index];
+
+ return res;
+ } else {
+ already_in_foldermode = 0;
+ }
+
+ return OnPushOptionListCascade(arg0, arg1);
+}
+
+int scePafSetSelectionPatched(void *arg0, int selection) {
+ kprintf("called\n");
+ /* "By Expire Date" */
+ if (selection == 1) {
+ /* Should it be "By Category"? */
+ if (by_category_mode) {
+ selection = 2; // yes
+ }
+ }
+
+ return scePafSetSelection(arg0, selection);
+}
+
+int vsh_function_patched(void *arg) {
+ if (!defaulted) {
+ ToggleCategoryMode(1);
+
+ _sw(patches.MODE_BY_EXPIRE_DATE[patch_index], text_addr_game + patches.current_mode[patch_index] + 56);
+ ClearCachesForUser();
+
+ defaulted = 1;
+ }
+
+ return vsh_function(arg);
+}
+
+void PatchSelection(u32 text_addr) {
+ /* Patch AddGameContext */
+ MAKE_CALL(text_addr+patches.add_game_context_call[patch_index][0], AddGameContextPatched);
+ MAKE_CALL(text_addr+patches.add_game_context_call[patch_index][1], AddGameContextPatched);
+ AddGameContext = (void *) (text_addr + patches.add_game_context[patch_index]);
+
+ /* Patch calls to SetMode */
+ MAKE_CALL(text_addr+patches.setmode_call_arg_1[patch_index][0], SetModePatched);
+ _sw(patches.setmode_arg_opcode[patch_index], text_addr + patches.setmode_call_arg_1[patch_index][1]);
+
+ if (patches.setmode_call_arg_2[patch_index][0]) {
+ kprintf("patching SetMode\n");
+ MAKE_CALL(text_addr+patches.setmode_call_arg_2[patch_index][0], SetModePatched);
+ _sw(patches.setmode_arg_opcode[patch_index], text_addr + patches.setmode_call_arg_2[patch_index][1]);
+ }
+
+ /* Patch OnPushFolderOptionListCascade */
+ _sw((u32) OnPushFolderOptionListCascadePatched, text_addr + patches.on_push_folder_options_call[patch_index]);
+ OnPushFolderOptionListCascade = (void *) (text_addr + patches.on_push_folder_options[patch_index]);
+
+ /* Patch OnPushOptionListCascade */
+ _sw((u32) OnPushOptionListCascadePatched, text_addr + patches.on_push_options_call[patch_index]);
+ OnPushOptionListCascade = (void *) (text_addr + patches.on_push_options[patch_index]);
+
+ /* Additional function pointers */
+ GetSelection = (void *) (text_addr + patches.get_selection[patch_index]);
+ SetMode = (void *) (text_addr + patches.setmode[patch_index]);
+
+ /* Argument required for GetSelection */
+ GetSelectionArg = (void *) (text_addr + patches.get_selection_arg[patch_index]);
+
+ if (patches.set_selection_call[patch_index][0]) {
+ /* Patch scePafSetSelection */
+ scePafSetSelection = (void *)U_EXTRACT_CALL(text_addr+patches.set_selection_call[patch_index][0]);
+ MAKE_CALL(text_addr+patches.set_selection_call[patch_index][0], scePafSetSelectionPatched);
+ MAKE_CALL(text_addr+patches.set_selection_call[patch_index][1], scePafSetSelectionPatched);
+ }
+
+ defaulted = 0;
+ by_category_mode = 0;
+ vsh_function = (void *)U_EXTRACT_CALL(text_addr+0x12E0);
+ MAKE_CALL(text_addr+0x12E0, vsh_function_patched);
+
+ HijackGameClass(32);
+}
diff --git a/sysconf.c b/sysconf.c
index e00144d..89a45a1 100644
--- a/sysconf.c
+++ b/sysconf.c
@@ -36,14 +36,34 @@
char user_buffer[256];
static u32 backup[4] = { 0, 0, 0, 0 };
int context_mode = 0;
-static SceSysconfItem *sysconf_item[] = { NULL, NULL, NULL };
+static SceSysconfItem *sysconf_item[] = { NULL, NULL, NULL, NULL };
extern int sysconf_plug;
extern int model;
-static const char *sysconf_str[] = {"gc0", "gc1" , "gc2"};
-static const char *sysconf_sub[] = {"gcs0", "gcs1" , "gcs2"};
+#define GC_SYSCONF_MODE "gc0"
+#define GC_SYSCONF_MODE_SUB "gcs0"
+#define GC_SYSCONF_PREFIX "gc1"
+#define GC_SYSCONF_PREFIX_SUB "gcs1"
+#define GC_SYSCONF_SHOW "gc2"
+#define GC_SYSCONF_SHOW_SUB "gcs2"
+#define GC_SYSCONF_SORT "gc3"
+#define GC_SYSCONF_SORT_SUB "gcs3"
+
+static const char *sysconf_str[] = {
+ GC_SYSCONF_MODE,
+ GC_SYSCONF_PREFIX,
+ GC_SYSCONF_SHOW,
+ GC_SYSCONF_SORT
+};
+
+static const char *sysconf_sub[] = {
+ GC_SYSCONF_MODE_SUB,
+ GC_SYSCONF_PREFIX_SUB,
+ GC_SYSCONF_SHOW_SUB,
+ GC_SYSCONF_SORT_SUB
+};
void (*AddSysconfItem)(u32 *option, SceSysconfItem **item);
SceSysconfItem *(*GetSysconfItem)(void *arg0, void *arg1);
@@ -108,8 +128,12 @@ void HijackContext(SceRcoEntry *src, char **options, int n) {
item_param[0] = 0xDEAD;
item_param[1] = (u32)options[i];
- if(i != 0) item->prev_entry = item->next_entry;
- if(i == n - 1) item->next_entry = 0;
+ if(i != 0) {
+ item->prev_entry = item->next_entry;
+ }
+ if(i == n - 1) {
+ item->next_entry = 0;
+ }
item = (SceRcoEntry *)((u32)item + base->next_entry);
item_param = (u32 *)((u32)item + base->param);
@@ -160,6 +184,9 @@ int vshGetRegistryValuePatched(u32 *option, char *name, void *arg2, int size, in
case 2:
*value = config.uncategorized;
return 0;
+ case 3:
+ *value = config.catsort;
+ return 0;
default:
*value = 0;
return 0;
@@ -186,6 +213,9 @@ int vshSetRegistryValuePatched(u32 *option, char *name, int size, int *value) {
case 2:
cfg = &config.uncategorized;
break;
+ case 3:
+ cfg = &config.catsort;
+ break;
default:
cfg = NULL;
break;
@@ -231,6 +261,9 @@ int GetPageNodeByIDPatched(void *resource, char *name, SceRcoEntry **child) {
case 3:
HijackContext(*child, lang_container.show, ITEMSOF(lang_container.show));
break;
+ case 4:
+ HijackContext(*child, lang_container.sort, ITEMSOF(lang_container.sort));
+ break;
}
}
}
diff --git a/vshitem.c b/vshitem.c
index 0598b16..b82d4ac 100644
--- a/vshitem.c
+++ b/vshitem.c
@@ -45,8 +45,22 @@ int global_pos = 0;
Category *cat_list[2] = { NULL, NULL };
-static const char *cat_str[] = { "gc", "gc0", "gc1", "gc2", "gc4", "gc5", "gcv_", "gcw_" };
-static const char *cat_sub[] = {"gcs0", "gcs1" , "gcs2"};
+static const char* GC_PREFIX = "gc";
+
+static const char* GC_SYSCONF_MODE = "gc0";
+static const char* GC_SYSCONF_MODE_SUB = "gcs0";
+static const char* GC_SYSCONF_PREFIX = "gc1";
+static const char* GC_SYSCONF_PREFIX_SUB = "gcs1";
+static const char* GC_SYSCONF_SHOW = "gc2";
+static const char* GC_SYSCONF_SHOW_SUB = "gcs2";
+static const char* GC_SYSCONF_SORT = "gc3";
+static const char* GC_SYSCONF_SORT_SUB = "gcs3";
+
+static const char* GC_UNCATEGORIZED_MS = "gc4";
+static const char* GC_UNCATEGORIZED_INTERNAL = "gc5";
+static const char* GC_CATEGORY_PREFIX_MS = "gcv_";
+static const char* GC_CATEGORY_PREFIX_INTERNAL = "gcw_";
+
int vsh_id[2] = { -1, -1 };
int vsh_action_arg[2] = { -1, -1 };
@@ -181,50 +195,66 @@ int UnloadModulePatched(int skip) {
}
wchar_t* scePafGetTextPatched(void *arg, char *name) {
- if (name && sce_paf_private_strncmp(name, cat_str[0], 2) == 0) {
+ if (name && sce_paf_private_strncmp(name, GC_PREFIX, 2) == 0) {
kprintf("match name: %s\n", name);
//TODO: optimize this code
// sysconf 1
- if (sce_paf_private_strcmp(name, cat_str[1]) == 0) {
+ if (sce_paf_private_strcmp(name, GC_SYSCONF_MODE) == 0) {
gc_utf8_to_unicode((wchar_t *)user_buffer, lang_container.msg_mode);
return (wchar_t *) user_buffer;
// sysconf 2
- } else if (sce_paf_private_strcmp(name, cat_str[2]) == 0) {
+ } else if (sce_paf_private_strcmp(name, GC_SYSCONF_PREFIX) == 0) {
gc_utf8_to_unicode((wchar_t *)user_buffer, lang_container.msg_prefix);
return (wchar_t *) user_buffer;
// sysconf 3
- } else if (sce_paf_private_strcmp(name, cat_str[3]) == 0) {
+ } else if (sce_paf_private_strcmp(name, GC_SYSCONF_SHOW) == 0) {
gc_utf8_to_unicode((wchar_t *)user_buffer, lang_container.msg_show);
return (wchar_t *) user_buffer;
- // sysconf subtitle 1
- }else if (sce_paf_private_strcmp(name, cat_sub[0]) == 0) {
+ // sysconf 4
+ } else if (sce_paf_private_strcmp(name, GC_SYSCONF_SORT) == 0) {
+ gc_utf8_to_unicode((wchar_t *)user_buffer, lang_container.msg_sort);
+ return (wchar_t *) user_buffer;
+ // sysconf subtitle 1
+ } else if (sce_paf_private_strcmp(name, GC_SYSCONF_MODE_SUB) == 0) {
gc_utf8_to_unicode((wchar_t *)user_buffer, lang_container.msg_mode_sub);
return (wchar_t *) user_buffer;
// sysconf subtitle 2
- } else if (sce_paf_private_strcmp(name, cat_sub[1]) == 0) {
+ } else if (sce_paf_private_strcmp(name, GC_SYSCONF_PREFIX_SUB) == 0) {
gc_utf8_to_unicode((wchar_t *)user_buffer, lang_container.msg_prefix_sub);
return (wchar_t *) user_buffer;
// sysconf subtitle 3
- } else if (sce_paf_private_strcmp(name, cat_sub[2]) == 0) {
+ } else if (sce_paf_private_strcmp(name, GC_SYSCONF_SHOW_SUB) == 0) {
gc_utf8_to_unicode((wchar_t *)user_buffer, lang_container.msg_show_sub);
return (wchar_t *) user_buffer;
- // Memory Stick
- } else if (sce_paf_private_strncmp(name, cat_str[6], 4) == 0) {
+ // sysconf subtitle 4
+ } else if (sce_paf_private_strcmp(name, GC_SYSCONF_SORT_SUB) == 0) {
+ gc_utf8_to_unicode((wchar_t *)user_buffer, lang_container.msg_sort_sub);
+ return (wchar_t *) user_buffer;
+ // Memory Stick
+ } else if (sce_paf_private_strncmp(name, GC_CATEGORY_PREFIX_MS, 4) == 0) {
Category *p = (Category *) sce_paf_private_strtoul(name + 4, NULL, 16);
- gc_utf8_to_unicode((wchar_t *) user_buffer, &p->name);
+ if(config.catsort) {
+ gc_utf8_to_unicode((wchar_t *) user_buffer, &p->name+2);
+ } else {
+ gc_utf8_to_unicode((wchar_t *) user_buffer, &p->name);
+ }
fix_text_padding((wchar_t *) user_buffer, scePafGetText(arg, "msgshare_ms"), 'M', 0x2122);
return (wchar_t *) user_buffer;
- } else if (sce_paf_private_strcmp(name, cat_str[4]) == 0) {
+ } else if (sce_paf_private_strcmp(name, GC_UNCATEGORIZED_MS) == 0) {
gc_utf8_to_unicode((wchar_t *) user_buffer, lang_container.msg_uncategorized);
fix_text_padding((wchar_t *) user_buffer, scePafGetText(arg, "msgshare_ms"), 'M', 0x2122);
return (wchar_t *) user_buffer;
// Internal Storage
- } else if (sce_paf_private_strncmp(name, cat_str[7], 4) == 0) {
+ } else if (sce_paf_private_strncmp(name, GC_CATEGORY_PREFIX_INTERNAL, 4) == 0) {
Category *p = (Category *) sce_paf_private_strtoul(name + 4, NULL, 16);
- gc_utf8_to_unicode((wchar_t *) user_buffer, &p->name);
+ if(config.catsort) {
+ gc_utf8_to_unicode((wchar_t *) user_buffer, &p->name+2);
+ } else {
+ gc_utf8_to_unicode((wchar_t *) user_buffer, &p->name);
+ }
fix_text_padding((wchar_t *) user_buffer, scePafGetText(arg, "msg_em"), 'M', 0x2122);
return (wchar_t *) user_buffer;
- } else if (sce_paf_private_strcmp(name, cat_str[5]) == 0) {
+ } else if (sce_paf_private_strcmp(name, GC_UNCATEGORIZED_INTERNAL) == 0) {
gc_utf8_to_unicode((wchar_t *) user_buffer, lang_container.msg_uncategorized);
fix_text_padding((wchar_t *) user_buffer, scePafGetText(arg, "msg_em"), 'M', 0x2122);
return (wchar_t *) user_buffer;