aboutsummaryrefslogtreecommitdiff
path: root/project/src/lib-staticr.c
diff options
context:
space:
mode:
authorroot <root@dc13.lemmi>2020-07-21 10:00:46 +0200
committerroot <root@dc13.lemmi>2020-07-21 10:00:46 +0200
commit1192c4575ea7afdf38e3fa0df4587a08620af11e (patch)
tree6910a62487c282d0840d2e668aa7eb40b7c8a3fd /project/src/lib-staticr.c
parent76de8a89f7c1d1b4522eb66e8f89d248e8c1781f (diff)
SZS v2.18a
Diffstat (limited to '')
-rw-r--r--project/src/lib-staticr.c6886
1 files changed, 6886 insertions, 0 deletions
diff --git a/project/src/lib-staticr.c b/project/src/lib-staticr.c
new file mode 100644
index 0000000..2125714
--- /dev/null
+++ b/project/src/lib-staticr.c
@@ -0,0 +1,6886 @@
+
+/***************************************************************************
+ * _______ _______ _______ *
+ * | ___ |____ | ___ | *
+ * | | |_| / /| | |_| *
+ * | |_____ / / | |_____ *
+ * |_____ | / / |_____ | *
+ * _ | | / / _ | | *
+ * | |___| |/ /____| |___| | *
+ * |_______|_______|_______| *
+ * *
+ * Wiimms SZS Tools *
+ * https://szs.wiimm.de/ *
+ * *
+ ***************************************************************************
+ * *
+ * This file is part of the SZS project. *
+ * Visit https://szs.wiimm.de/ for project details and sources. *
+ * *
+ * Copyright (c) 2011-2020 by Dirk Clemens <wiimm@wiimm.de> *
+ * *
+ ***************************************************************************
+ * *
+ * 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 2 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. *
+ * *
+ * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt *
+ * *
+ ***************************************************************************/
+
+#include "lib-staticr.h"
+#include "crypt.h"
+#include "db-dol.h"
+#include "db-mkw.h"
+#include "lib-mkw.h"
+#include "db-ctcode.h"
+
+#include "libbz2/bzlib.h"
+#include "lib-bzip2.h"
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// data: tracks + arenas ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static const u32 pal_track_offset[] =
+{
+ 0x37fcd0,
+ // 0x380ee0, // original has an alternative order here
+ 0x384f58,
+ 0x397da8,
+ 0x398130,
+ 0x39d040,
+ 0
+};
+
+static const u32 pal_arena_offset[] =
+{
+ 0x384fd8,
+ 0x3974f8,
+ 0x397780,
+ 0x39c8e0,
+ 0
+};
+
+//-----------------------------------------------------------------------------
+
+static const u32 usa_track_offset[] =
+{
+ 0x37fbe0,
+ // 0x380b70, // original has an alternative order here
+ 0x3839e0,
+ 0x397450,
+ 0x3977d8,
+ 0x39b300,
+ 0
+};
+
+static const u32 usa_arena_offset[] =
+{
+ 0x383a60,
+ 0x39d130,
+ 0x39d3b8,
+ 0x39e988,
+ 0
+};
+
+//-----------------------------------------------------------------------------
+
+static const u32 jap_track_offset[] =
+{
+ 0x37f9a0,
+ // 0x380bb0, // original has an alternative order here
+ 0x384c28,
+ 0x397588,
+ 0x397910,
+ 0x39c820,
+ 0
+};
+
+static const u32 jap_arena_offset[] =
+{
+ 0x384ca8,
+ 0x396cd8,
+ 0x396f60,
+ 0x39c0c0,
+ 0
+};
+
+//-----------------------------------------------------------------------------
+
+static const u32 kor_track_offset[] =
+{
+ 0x3800b0,
+ // 0x3812c8, // original has an alternative order here
+ 0x385348,
+ 0x3981e8,
+ 0x398570,
+ 0x39d480,
+ 0
+};
+
+static const u32 kor_arena_offset[] =
+{
+ 0x3853c8,
+ 0x397938,
+ 0x397bc0,
+ 0x39cd20,
+ 0
+};
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// data: region ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+typedef enum rpat_mode_t // bit field
+{
+ RPAT_FF = 1, // mask value by 0xff
+ RPAT_TEST = 2, // test mode, only patched on 'opt_x'
+}
+rpat_mode_t;
+
+typedef struct region_patch_t
+{
+ rpat_mode_t patch_mode; // bit field, see above
+ u32 offset[STR_ZBI_N]; // offsets for all 4 DVD types
+ u32 orig; // original value
+ u32 patch; // patch base value
+ ccp info; // info about impact
+}
+region_patch_t;
+
+static const region_patch_t vs_region_patch[] =
+{
+ {0,{0x143364,0x14325c,0x143050,0x14365c}, 0x80a30084,0x38a00000, "vs: locstring status" },
+ {1,{0x149d54,0x149c4c,0x149a40,0x14a04c}, 0x80840084,0x38800000, "vs: check locstring, masked by 0xff" },
+ {0,{0x148f2c,0x148e24,0x148c18,0x149224}, 0x80e50084,0x38e00000, "'vs_#' at regional matches." },
+ {0,{0x149444,0x14933c,0x149130,0x14973c}, 0x80e50084,0x38e00000, "'vs_#' after friend connect" },
+ {0,{0,0,0,0},0,0,0}
+};
+
+static const region_patch_t bt_region_patch[] =
+{
+ {0,{0x1433d0,0x1432c8,0x1430bc,0x1436c8}, 0x80c30084,0x38c00000, "bt: locstring status" },
+ {1,{0x149da0,0x149c98,0x149a8c,0x14a098}, 0x80840084,0x38800000, "bt: check locstring, masked by 0xff" },
+ {0,{0x148f80,0x148e78,0x148c6c,0x149278}, 0x80e50084,0x38e00000, "'bt_#' at regional matches." },
+ {0,{0x149498,0x149390,0x149184,0x149790}, 0x80e50084,0x38e00000, "'bt_#' after friend connect" },
+ {0,{0,0,0,0},0,0,0}
+};
+
+enum
+{
+ N_TAB_VS_REGION_PATCH = sizeof(vs_region_patch) / sizeof(*vs_region_patch) - 1,
+ N_VS_REGION_PATCH = N_TAB_VS_REGION_PATCH < MAX_REGION_PATCH
+ ? N_TAB_VS_REGION_PATCH : MAX_REGION_PATCH,
+
+ N_TAB_BT_REGION_PATCH = sizeof(bt_region_patch) / sizeof(*bt_region_patch) - 1,
+ N_BT_REGION_PATCH = N_TAB_BT_REGION_PATCH < MAX_REGION_PATCH
+ ? N_TAB_BT_REGION_PATCH : MAX_REGION_PATCH,
+};
+
+//-----------------------------------------------------------------------------
+
+typedef struct region_info_t
+{
+ int * region; // vector with regions, -1=orig
+ const
+ region_patch_t * patch_tab; // patch table
+ uint max_patch; // N_(VS|BT)_REGION_PATCH
+ uint flag_known; // STR_S_(VS|BT)_REGION_KNOWN
+ uint flag_differ; // STR_S_(VS|BT)_REGION_KNOWN_DIFF
+ uint flag_unknown; // STR_S_(VS|BT)_REGION_UNKNOWN
+ ccp name; // "versus" or "battle"
+
+ int *opt_region; // pointer to option
+ bool *opt_x; // pointer to x-mode
+ bool *opt_t; // pointer to t-mode
+}
+region_info_t;
+
+void SetupRegionInfo ( region_info_t *ri, staticr_t * str, bool is_battle )
+{
+ DASSERT(ri);
+
+ if (is_battle)
+ {
+ ri->region = str ? str->bt_region : 0;
+ ri->patch_tab = bt_region_patch;
+ ri->max_patch = N_BT_REGION_PATCH;
+ ri->flag_known = STR_S_BT_REGION_KNOWN;
+ ri->flag_differ = STR_S_BT_REGION_KNOWN_DIFF;
+ ri->flag_unknown= STR_S_BT_REGION_UNKNOWN;
+ ri->name = "battle";
+
+ ri->opt_region = &opt_str_bt_region;
+ ri->opt_x = &opt_str_xbt_region;
+ ri->opt_t = &opt_str_tbt_region;
+ }
+ else
+ {
+ ri->region = str ? str->vs_region : 0;
+ ri->patch_tab = vs_region_patch;
+ ri->max_patch = N_VS_REGION_PATCH;
+ ri->flag_known = STR_S_VS_REGION_KNOWN;
+ ri->flag_differ = STR_S_VS_REGION_KNOWN_DIFF;
+ ri->flag_unknown= STR_S_VS_REGION_UNKNOWN;
+ ri->name = "versus";
+
+ ri->opt_region = &opt_str_vs_region;
+ ri->opt_x = &opt_str_xvs_region;
+ ri->opt_t = &opt_str_tvs_region;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static char all_ranks_orig[] = "%s >= %d and %s <= %d";
+static char all_ranks_patch1[] = "(%s > %d or %s <= %d)";
+static char all_ranks_patch2[] = "(%s >= %d or %s < %d)";
+static int all_ranks_size = sizeof(all_ranks_orig);
+
+static const u32 pal_all_ranks_offset[] =
+{
+ 0x389f36,
+ 0x389f52,
+ 0
+};
+
+static const u32 usa_all_ranks_offset[] =
+{
+ 0x389be6,
+ 0x389c02,
+ 0
+};
+
+static const u32 jap_all_ranks_offset[] =
+{
+ 0x389716,
+ 0x389732,
+ 0
+};
+
+static const u32 kor_all_ranks_offset[] =
+{
+ 0x38a34e,
+ 0x38a36a,
+ 0
+};
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// data: vs/bat ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static const u32 pal_vs_offset[] =
+{
+ 0x389f1b,
+ 0x389f24,
+ 0x389f9e,
+ 0x389fa7,
+ 0
+};
+
+static const u32 usa_vs_offset[] =
+{
+ 0x389bcb,
+ 0x389bd4,
+ 0x389c4e,
+ 0x389c57,
+ 0
+};
+
+static const u32 jap_vs_offset[] =
+{
+ 0x3896fb,
+ 0x389704,
+ 0x38977e,
+ 0x389787,
+ 0
+};
+
+static const u32 kor_vs_offset[] =
+{
+ 0x38a333,
+ 0x38a33c,
+ 0x38a3b6,
+ 0x38a3bf,
+ 0
+};
+
+//-----------------------------------------------------------------------------
+
+static const u32 pal_bt_offset[] =
+{
+ 0x389f2a,
+ 0x389f33,
+ 0x389fad,
+ 0x389fb6,
+ 0
+};
+
+static const u32 usa_bt_offset[] =
+{
+ 0x389bda,
+ 0x389be3,
+ 0x389c5d,
+ 0x389c66,
+ 0
+};
+
+static const u32 jap_bt_offset[] =
+{
+ 0x38970a,
+ 0x389713,
+ 0x38978d,
+ 0x389796,
+ 0
+};
+
+static const u32 kor_bt_offset[] =
+{
+ 0x38a342,
+ 0x38a34b,
+ 0x38a3c5,
+ 0x38a3ce,
+ 0
+};
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// data: cannons ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#define CANNON_N 3
+#define CANNON_SIZE 16
+#define CANNON_N_PARAM 4
+
+typedef struct cannon_param_t
+{
+ bool valid; // TRUE: params are valid
+ float param[CANNON_N_PARAM]; // parameters
+}
+cannon_param_t;
+
+static cannon_param_t cannon_param[CANNON_N] = {{0}};
+
+//-----------------------------------------------------------------------------
+
+static u32 GetCannonOffset ( const staticr_t * str )
+{
+ ASSERT( sizeof(CannonDataLEX)-4 == sizeof(str->cannon_data) );
+ ASSERT( sizeof(CannonDataLEX)-4 == CANNON_SIZE * CANNON_N );
+
+ if ( str && str->is_staticr )
+ {
+ switch (str->mode)
+ {
+ case STR_M_PAL: return 0x3a5a08;
+ case STR_M_USA: return 0x3a54c8;
+ case STR_M_JAP: return 0x3a51e8;
+ case STR_M_KOR: return 0x3a5e60;
+ default: return 0;
+ }
+ }
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// data: /Scene/UI/MenuSingle ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static const u32 pal_menu_offset[] =
+{
+ 0x387b64, // /Scene/UI/MenuSingle
+ 0x387b9d, // /Scene/UI/MenuSingle
+ 0x387bea, // /Scene/UI/MenuSingle
+ 0x387c48, // /Scene/UI/MenuSingle
+ 0x387cd9, // /Scene/UI/MenuSingle
+
+ 0x387b79, // /Scene/UI/MenuMulti
+ 0x387bb2, // /Scene/UI/MenuMulti
+ 0x387bd6, // /Scene/UI/MenuMulti
+ 0x387bff, // /Scene/UI/MenuMulti
+
+ 0
+};
+
+static const u32 usa_menu_offset[] =
+{
+ 0x38513c, // /Scene/UI/MenuSingle
+ 0x385175, // /Scene/UI/MenuSingle
+ 0x3851c2, // /Scene/UI/MenuSingle
+ 0x385220, // /Scene/UI/MenuSingle
+ 0x3852b1, // /Scene/UI/MenuSingle
+
+ 0x385151, // /Scene/UI/MenuMulti
+ 0x38518a, // /Scene/UI/MenuMulti
+ 0x3851ae, // /Scene/UI/MenuMulti
+ 0x3851d7, // /Scene/UI/MenuMulti
+
+ 0
+};
+
+static const u32 jap_menu_offset[] =
+{
+ 0x387834, // /Scene/UI/MenuSingle
+ 0x38786d, // /Scene/UI/MenuSingle
+ 0x3878ba, // /Scene/UI/MenuSingle
+ 0x387918, // /Scene/UI/MenuSingle
+ 0x3879a9, // /Scene/UI/MenuSingle
+
+ 0x387849, // /Scene/UI/MenuMulti
+ 0x387882, // /Scene/UI/MenuMulti
+ 0x3878a6, // /Scene/UI/MenuMulti
+ 0x3878cf, // /Scene/UI/MenuMulti
+
+ 0
+};
+
+static const u32 kor_menu_offset[] =
+{
+ 0x38846c, // /Scene/UI/MenuSingle
+ 0x3884a5, // /Scene/UI/MenuSingle
+ 0x3884f2, // /Scene/UI/MenuSingle
+ 0x388550, // /Scene/UI/MenuSingle
+ 0x3885e1, // /Scene/UI/MenuSingle
+
+ 0x388481, // /Scene/UI/MenuMulti
+ 0x3884ba, // /Scene/UI/MenuMulti
+ 0x3884de, // /Scene/UI/MenuMulti
+ 0x388507, // /Scene/UI/MenuMulti
+
+ 0
+};
+
+static const int meno_patch_idx[] =
+{
+ 1, // Single: Global VS + Battle
+ 2, // Single: Room
+ 7, // Multi: Global VS + Battle
+ 8, // Multi: Room
+ -1
+};
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// data: servers ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+const str_server_list_t ServerList[SERV__N+1] =
+{
+ { SERV_NONE, "NONE", 0, 0, "" },
+ { SERV_NASW, "NASW", 90, 23, "naswii.nintendowifi.net" },
+ { SERV_NASW_DEV, "NASW_DEV", 91, 27, "naswii.dev.nintendowifi.net" },
+ { SERV_NASW_TEST, "NASW_TEST", 92, 28, "naswii.test.nintendowifi.net" },
+ { SERV_MKW_RACE, "MKW_RACE", 93, 37, "mariokartwii.race.gs.nintendowifi.net" },
+ { SERV_SAKE, "SAKE", 0, 37, "mariokartwii.sake.gs.nintendowifi.net" },
+ { SERV_S_SAKE, "S_SAKE", 0, 27, "%s.sake.gs.nintendowifi.net" },
+ { SERV_GS, "GS", 0, 19, "gs.nintendowifi.net" },
+ {0,0,0,0,0}
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+const str_server_patch_t ServerPatch[217+1] =
+{
+ { STR_M_PAL,0, 2,SERV_NONE, 0x38aa32, 5, "" },
+ { STR_M_PAL,0, 2,SERV_NONE, 0x38ab4c, 5, "" },
+ { STR_M_PAL,2, 2,SERV_MKW_RACE, 0x38a38c, 75, "/raceservice/maindl_%s_%s.ashx" },
+ { STR_M_PAL,2, 2,SERV_MKW_RACE, 0x38a458, 78, "/raceservice/messagedl_%s_%s.ashx" },
+ { STR_M_PAL,0, 2,SERV_MKW_RACE, 0x38a8f0, 84, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_PAL,0, 1,SERV_MKW_RACE, 0x38a945, 83, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_PAL,0, 2,SERV_SAKE, 0x38a3d8, 100, "/SakeFileServer/ghostdownload.aspx?gameid=1687&region=0" },
+ { STR_M_PAL,0, 0,SERV_GS, 0x38a9bc, 117, "/SakeFileServer/ghostupload.aspx?gameid=%d&regionid=%d&courseid=%d&score=%d&pid=%d&playerinfo=%s%s" },
+ { STR_M_PAL,0, 0,SERV_GS, 0x38aaef, 92, "/SakeFileServer/ghostdownload.aspx?gameid=1687&region=0&p0=%d&c0=%d&t0=%d" },
+ { STR_M_PAL,0, 0,SERV_GS, 0x38ac9c, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_PAL,0, 0,SERV_GS, 0x38ae05, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_PAL,0, 0,SERV_GS, 0x38af33, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_PAL,0, 0,SERV_GS, 0x38b014, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_PAL,0, 0,SERV_GS, 0x38b109, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_PAL,0, 0,SERV_GS, 0x38b21e, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_PAL,0, 0,SERV_GS, 0x38b330, 58, "/RaceService/NintendoRacingService.asmx" },
+
+ { STR_M_PAL,9, 2,SERV_NONE, 0x2797f8, 8, "" }, // "https://"
+ { STR_M_PAL,9, 2,SERV_NONE, 0x279808, 8, "" }, // "https://"
+ { STR_M_PAL,9, 2,SERV_NONE, 0x279a78, 8, "" }, // "https://"
+ { STR_M_PAL,9, 2,SERV_NONE, 0x29d568, 8, "" }, // "https://"
+ { STR_M_PAL,9, 2,SERV_NONE, 0x29e388, 8, "" }, // "https://"
+
+ { STR_M_PAL,5, 2,SERV_NASW, 0x276528, 34, "/ac" },
+ { STR_M_PAL,1, 2,SERV_NASW, 0x2765a8, 34, "/pr" },
+ { STR_M_PAL,5, 2,SERV_NASW_DEV, 0x27654c, 38, "/ac" },
+ { STR_M_PAL,1, 2,SERV_NASW_DEV, 0x2765cc, 38, "/pr" },
+ { STR_M_PAL,5, 2,SERV_NASW_TEST, 0x276500, 39, "/ac" },
+ { STR_M_PAL,1, 2,SERV_NASW_TEST, 0x276580, 39, "/pr" },
+ { STR_M_PAL,1, 2,SERV_S_SAKE, 0x275e58, 72, "/SakeStorageServer/StorageServer.asmx" },
+ { STR_M_PAL,1, 2,SERV_S_SAKE, 0x275ea4, 62, "/SakeFileServer/upload.aspx" },
+ { STR_M_PAL,1, 2,SERV_S_SAKE, 0x275ee4, 64, "/SakeFileServer/download.aspx" },
+ { STR_M_PAL,1, 1,SERV_S_SAKE, 0x27a1a0, 71, "/SakeStorageServer/StorageServer.asmx" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x2718be, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x272635, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x272651, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x272672, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x272693, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x2726b5, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x2726d7, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x2726f7, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x272717, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x272736, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x272759, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x27277e, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x27279c, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x276e65, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x2775d5, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x277fed, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x27918e, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x279b82, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x279da8, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x279dc4, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x279de0, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x279f30, 19, "" },
+ { STR_M_PAL,1, 0,SERV_GS, 0x279fb4, 19, "" },
+
+ { STR_M_USA,0, 2,SERV_NONE, 0x38a3ba, 5, "" },
+ { STR_M_USA,0, 2,SERV_NONE, 0x38a4d4, 5, "" },
+ { STR_M_USA,2, 2,SERV_MKW_RACE, 0x389d14, 75, "/raceservice/maindl_%s_%s.ashx" },
+ { STR_M_USA,2, 2,SERV_MKW_RACE, 0x389de0, 78, "/raceservice/messagedl_%s_%s.ashx" },
+ { STR_M_USA,0, 2,SERV_MKW_RACE, 0x38a278, 84, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_USA,0, 1,SERV_MKW_RACE, 0x38a2cd, 83, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_USA,0, 2,SERV_SAKE, 0x389d60, 100, "/SakeFileServer/ghostdownload.aspx?gameid=1687&region=0" },
+ { STR_M_USA,0, 0,SERV_GS, 0x38a344, 117, "/SakeFileServer/ghostupload.aspx?gameid=%d&regionid=%d&courseid=%d&score=%d&pid=%d&playerinfo=%s%s" },
+ { STR_M_USA,0, 0,SERV_GS, 0x38a477, 92, "/SakeFileServer/ghostdownload.aspx?gameid=1687&region=0&p0=%d&c0=%d&t0=%d" },
+ { STR_M_USA,0, 0,SERV_GS, 0x38a604, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_USA,0, 0,SERV_GS, 0x38a76d, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_USA,0, 0,SERV_GS, 0x38a89b, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_USA,0, 0,SERV_GS, 0x38a97c, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_USA,0, 0,SERV_GS, 0x38aa71, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_USA,0, 0,SERV_GS, 0x38ab86, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_USA,0, 0,SERV_GS, 0x38ac98, 58, "/RaceService/NintendoRacingService.asmx" },
+
+ { STR_M_USA,9, 2,SERV_NONE, 0x275498, 8, "" }, // "https://"
+ { STR_M_USA,9, 2,SERV_NONE, 0x2754a8, 8, "" }, // "https://"
+ { STR_M_USA,9, 2,SERV_NONE, 0x275718, 8, "" }, // "https://"
+ { STR_M_USA,9, 2,SERV_NONE, 0x299208, 8, "" }, // "https://"
+ { STR_M_USA,9, 2,SERV_NONE, 0x29a028, 8, "" }, // "https://"
+
+ { STR_M_USA,5, 2,SERV_NASW, 0x2721c8, 34, "/ac" },
+ { STR_M_USA,1, 2,SERV_NASW, 0x272248, 34, "/pr" },
+ { STR_M_USA,5, 2,SERV_NASW_DEV, 0x2721ec, 38, "/ac" },
+ { STR_M_USA,1, 2,SERV_NASW_DEV, 0x27226c, 38, "/pr" },
+ { STR_M_USA,5, 2,SERV_NASW_TEST, 0x2721a0, 39, "/ac" },
+ { STR_M_USA,1, 2,SERV_NASW_TEST, 0x272220, 39, "/pr" },
+ { STR_M_USA,1, 2,SERV_S_SAKE, 0x271af8, 72, "/SakeStorageServer/StorageServer.asmx" },
+ { STR_M_USA,1, 2,SERV_S_SAKE, 0x271b44, 62, "/SakeFileServer/upload.aspx" },
+ { STR_M_USA,1, 2,SERV_S_SAKE, 0x271b84, 64, "/SakeFileServer/download.aspx" },
+ { STR_M_USA,1, 1,SERV_S_SAKE, 0x275e40, 71, "/SakeStorageServer/StorageServer.asmx" },
+ { STR_M_USA,1, 0,SERV_GS, 0x26d55e, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x26e2d5, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x26e2f1, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x26e312, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x26e333, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x26e355, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x26e377, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x26e397, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x26e3b7, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x26e3d6, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x26e3f9, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x26e41e, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x26e43c, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x272b05, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x273275, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x273c8d, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x274e2e, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x275822, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x275a48, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x275a64, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x275a80, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x275bd0, 19, "" },
+ { STR_M_USA,1, 0,SERV_GS, 0x275c54, 19, "" },
+
+ { STR_M_JAP,0, 2,SERV_NONE, 0x38a212, 5, "" },
+ { STR_M_JAP,0, 2,SERV_NONE, 0x38a32c, 5, "" },
+ { STR_M_JAP,2, 2,SERV_MKW_RACE, 0x389b6c, 75, "/raceservice/maindl_%s_%s.ashx" },
+ { STR_M_JAP,2, 2,SERV_MKW_RACE, 0x389c38, 78, "/raceservice/messagedl_%s_%s.ashx" },
+ { STR_M_JAP,0, 2,SERV_MKW_RACE, 0x38a0d0, 84, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_JAP,0, 1,SERV_MKW_RACE, 0x38a125, 83, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_JAP,0, 2,SERV_SAKE, 0x389bb8, 100, "/SakeFileServer/ghostdownload.aspx?gameid=1687&region=0" },
+ { STR_M_JAP,0, 0,SERV_GS, 0x38a19c, 117, "/SakeFileServer/ghostupload.aspx?gameid=%d&regionid=%d&courseid=%d&score=%d&pid=%d&playerinfo=%s%s" },
+ { STR_M_JAP,0, 0,SERV_GS, 0x38a2cf, 92, "/SakeFileServer/ghostdownload.aspx?gameid=1687&region=0&p0=%d&c0=%d&t0=%d" },
+ { STR_M_JAP,0, 0,SERV_GS, 0x38a47c, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_JAP,0, 0,SERV_GS, 0x38a5e5, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_JAP,0, 0,SERV_GS, 0x38a713, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_JAP,0, 0,SERV_GS, 0x38a7f4, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_JAP,0, 0,SERV_GS, 0x38a8e9, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_JAP,0, 0,SERV_GS, 0x38a9fe, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_JAP,0, 0,SERV_GS, 0x38ab10, 58, "/RaceService/NintendoRacingService.asmx" },
+
+ { STR_M_JAP,9, 2,SERV_NONE, 0x279178, 8, "" }, // "https://"
+ { STR_M_JAP,9, 2,SERV_NONE, 0x279188, 8, "" }, // "https://"
+ { STR_M_JAP,9, 2,SERV_NONE, 0x2793f8, 8, "" }, // "https://"
+ { STR_M_JAP,9, 2,SERV_NONE, 0x29cee8, 8, "" }, // "https://"
+ { STR_M_JAP,9, 2,SERV_NONE, 0x29dd08, 8, "" }, // "https://"
+
+ { STR_M_JAP,5, 2,SERV_NASW, 0x275ea8, 34, "/ac" },
+ { STR_M_JAP,1, 2,SERV_NASW, 0x275f28, 34, "/pr" },
+ { STR_M_JAP,5, 2,SERV_NASW_DEV, 0x275ecc, 38, "/ac" },
+ { STR_M_JAP,1, 2,SERV_NASW_DEV, 0x275f4c, 38, "/pr" },
+ { STR_M_JAP,5, 2,SERV_NASW_TEST, 0x275e80, 39, "/ac" },
+ { STR_M_JAP,1, 2,SERV_NASW_TEST, 0x275f00, 39, "/pr" },
+ { STR_M_JAP,1, 2,SERV_S_SAKE, 0x2757d8, 72, "/SakeStorageServer/StorageServer.asmx" },
+ { STR_M_JAP,1, 2,SERV_S_SAKE, 0x275824, 62, "/SakeFileServer/upload.aspx" },
+ { STR_M_JAP,1, 2,SERV_S_SAKE, 0x275864, 64, "/SakeFileServer/download.aspx" },
+ { STR_M_JAP,1, 1,SERV_S_SAKE, 0x279b20, 71, "/SakeStorageServer/StorageServer.asmx" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x27123e, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x271fb5, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x271fd1, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x271ff2, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x272013, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x272035, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x272057, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x272077, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x272097, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x2720b6, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x2720d9, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x2720fe, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x27211c, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x2767e5, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x276f55, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x27796d, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x278b0e, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x279502, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x279728, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x279744, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x279760, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x2798b0, 19, "" },
+ { STR_M_JAP,1, 0,SERV_GS, 0x279934, 19, "" },
+
+ { STR_M_KOR,2, 2,SERV_MKW_RACE, 0x38a7a4, 75, "/raceservice/maindl_%s_%s.ashx" },
+ { STR_M_KOR,2, 2,SERV_MKW_RACE, 0x38a858, 78, "/raceservice/messagedl_%s_%s.ashx" },
+ { STR_M_KOR,0, 2,SERV_MKW_RACE, 0x38ad10, 84, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_KOR,0, 2,SERV_MKW_RACE, 0x38ad65, 89, "/RaceServiceStage/NintendoRacingService.asmx" },
+ { STR_M_KOR,0, 2,SERV_SAKE, 0x38a7f0, 100, "/SakeFileServer/ghostdownload.aspx?gameid=1687&region=0" },
+ { STR_M_KOR,0, 2,SERV_SAKE, 0x38adcb, 131, "/%s/ghostupload.aspx?gameid=%d&regionid=%d&courseid=%d&score=%d&pid=%d&playerinfo=%s%s" },
+ { STR_M_KOR,0, 2,SERV_SAKE, 0x38af0b, 106, "/%s/ghostdownload.aspx?gameid=1687&region=0&p0=%d&c0=%d&t0=%d" },
+ { STR_M_KOR,0, 0,SERV_GS, 0x38b0dc, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_KOR,0, 0,SERV_GS, 0x38b245, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_KOR,0, 0,SERV_GS, 0x38b373, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_KOR,0, 0,SERV_GS, 0x38b454, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_KOR,0, 0,SERV_GS, 0x38b549, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_KOR,0, 0,SERV_GS, 0x38b65e, 58, "/RaceService/NintendoRacingService.asmx" },
+ { STR_M_KOR,0, 0,SERV_GS, 0x38b770, 58, "/RaceService/NintendoRacingService.asmx" },
+
+ { STR_M_KOR,9, 2,SERV_NONE, 0x2676a8, 8, "" }, // "https://"
+ { STR_M_KOR,9, 2,SERV_NONE, 0x2676b8, 8, "" }, // "https://"
+ { STR_M_KOR,9, 2,SERV_NONE, 0x267928, 8, "" }, // "https://"
+ { STR_M_KOR,9, 2,SERV_NONE, 0x28b570, 8, "" }, // "https://"
+ { STR_M_KOR,9, 2,SERV_NONE, 0x28c390, 8, "" }, // "https://"
+
+ { STR_M_KOR,5, 2,SERV_NASW, 0x2643d8, 34, "/ac" },
+ { STR_M_KOR,1, 2,SERV_NASW, 0x264458, 34, "/pr" },
+ { STR_M_KOR,5, 2,SERV_NASW_DEV, 0x2643fc, 38, "/ac" },
+ { STR_M_KOR,1, 2,SERV_NASW_DEV, 0x26447c, 38, "/pr" },
+ { STR_M_KOR,5, 2,SERV_NASW_TEST, 0x2643b0, 39, "/ac" },
+ { STR_M_KOR,1, 2,SERV_NASW_TEST, 0x264430, 39, "/pr" },
+ { STR_M_KOR,1, 2,SERV_S_SAKE, 0x263c38, 72, "/SakeStorageServer/StorageServer.asmx" },
+ { STR_M_KOR,1, 2,SERV_S_SAKE, 0x263c84, 62, "/SakeFileServer/upload.aspx" },
+ { STR_M_KOR,1, 2,SERV_S_SAKE, 0x263cc4, 64, "/SakeFileServer/download.aspx" },
+ { STR_M_KOR,1, 2,SERV_S_SAKE, 0x263d08, 75, "/SakeStorageServerDev/StorageServer.asmx" },
+ { STR_M_KOR,1, 2,SERV_S_SAKE, 0x263d54, 65, "/SakeFileServerDev/upload.aspx" },
+ { STR_M_KOR,1, 2,SERV_S_SAKE, 0x263d98, 67, "/SakeFileServerDev/download.aspx" },
+ { STR_M_KOR,1, 1,SERV_S_SAKE, 0x268050, 71, "/SakeStorageServer/StorageServer.asmx" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x25f69e, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x260415, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x260431, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x260452, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x260473, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x260495, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x2604b7, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x2604d7, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x2604f7, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x260516, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x260539, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x26055e, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x26057c, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x264d15, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x265485, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x265e9d, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x26703e, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x267a32, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x267c58, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x267c74, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x267c90, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x267de0, 19, "" },
+ { STR_M_KOR,1, 0,SERV_GS, 0x267e64, 19, "" },
+
+ {0,0,0,0,0,0,0}
+};
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// data access ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+ccp GetStrModeName ( str_mode_t mode )
+{
+ switch (mode)
+ {
+ case STR_M_UNKNOWN: return "---";
+ case STR_M_PAL: return "PAL";
+ case STR_M_USA: return "USA";
+ case STR_M_JAP: return "JAP";
+ case STR_M_KOR: return "KOR";
+ case STR_M__N: return "???";
+ }
+ return "???";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+char GetStrModeRegionChar ( str_mode_t mode )
+{
+ switch (mode)
+ {
+ case STR_M_PAL: return 'P';
+ case STR_M_USA: return 'E';
+ case STR_M_JAP: return 'J';
+ case STR_M_KOR: return 'K';
+ default: return 0;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static u32 GetIdAddress ( const staticr_t * str )
+{
+ if (str)
+ {
+ switch (str->mode)
+ {
+ case STR_M_PAL: return 0x80276054;
+ case STR_M_USA: return 0x80271d14;
+ case STR_M_JAP: return 0x802759f4;
+ case STR_M_KOR: return 0x80263e34;
+ default: return 0;
+ }
+ }
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+static char * GetIdPointer ( const staticr_t * str )
+{
+ const u32 addr = GetIdAddress(str);
+ if (addr)
+ {
+ const dol_header_t *dol_head = (dol_header_t*)str->data;
+ const u32 off = GetDolOffsetByAddr(dol_head,addr,PATCHED_BY_SIZE,0);
+ if (off)
+ return (char*)str->data + off;
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const u32 * GetTrackOffsetTab ( const staticr_t * str )
+{
+ if (str)
+ {
+ switch (str->mode)
+ {
+ case STR_M_PAL: return pal_track_offset;
+ case STR_M_USA: return usa_track_offset;
+ case STR_M_JAP: return jap_track_offset;
+ case STR_M_KOR: return kor_track_offset;
+ default: return 0;
+ }
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const u32 * GetArenaOffsetTab ( const staticr_t * str )
+{
+ if (str)
+ {
+ switch (str->mode)
+ {
+ case STR_M_PAL: return pal_arena_offset;
+ case STR_M_USA: return usa_arena_offset;
+ case STR_M_JAP: return jap_arena_offset;
+ case STR_M_KOR: return kor_arena_offset;
+ default: return 0;
+ }
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const u32 * GetAllRanksOffsetTab ( const staticr_t * str )
+{
+ if (str)
+ {
+ switch (str->mode)
+ {
+ case STR_M_PAL: return pal_all_ranks_offset;
+ case STR_M_USA: return usa_all_ranks_offset;
+ case STR_M_JAP: return jap_all_ranks_offset;
+ case STR_M_KOR: return kor_all_ranks_offset;
+ default: return 0;
+ }
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const u32 * GetVsBtOffsetTab ( const staticr_t * str, bool battle )
+{
+ if (str)
+ {
+ if (battle)
+ {
+ switch (str->mode)
+ {
+ case STR_M_PAL: return pal_bt_offset;
+ case STR_M_USA: return usa_bt_offset;
+ case STR_M_JAP: return jap_bt_offset;
+ case STR_M_KOR: return kor_bt_offset;
+ default: return 0;
+ }
+ }
+ else
+ {
+ switch (str->mode)
+ {
+ case STR_M_PAL: return pal_vs_offset;
+ case STR_M_USA: return usa_vs_offset;
+ case STR_M_JAP: return jap_vs_offset;
+ case STR_M_KOR: return kor_vs_offset;
+ default: return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const u32 * GetMenuOffsetTab ( str_mode_t mode )
+{
+ switch (mode)
+ {
+ case STR_M_PAL: return pal_menu_offset;
+ case STR_M_USA: return usa_menu_offset;
+ case STR_M_JAP: return jap_menu_offset;
+ case STR_M_KOR: return kor_menu_offset;
+ default: return 0;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ORIG_VBI_VALUE 0x4e800020
+
+static u32 GetVbiAddress ( const staticr_t * str )
+{
+ DASSERT(str);
+ switch (str->mode)
+ {
+ case STR_M_PAL: return 0x801bab20;
+ case STR_M_USA: return 0x801baa80;
+ case STR_M_JAP: return 0x801baa40;
+ case STR_M_KOR: return 0x801bae7c;
+ default: return FindDolAddressOfVBI(str->data,str->data_size);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define ORIG_SDK_VERS "001000\0" // one more '\0' is added by compiler
+
+static u32 GetSdkverAddress ( str_mode_t mode )
+{
+ // return address of string '001000\0\0'
+ // string 'sdkver\0\0' follows with offset +8
+
+ switch (mode)
+ {
+ case STR_M_PAL: return 0x80384fd0;
+ case STR_M_USA: return 0x80380c50;
+ case STR_M_JAP: return 0x80384950;
+ case STR_M_KOR: return 0x80372fd0;
+ default: return 0;
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// second host at nas login ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static u32 GetSecondHostAddress ( str_mode_t mode, u32 *ret_orig )
+{
+ // PAL: Offset 800ed868, original 0x480eb86d, neu 0x60000000
+ // USA: Offset 800ed7c8, original 0x480eb86d, neu 0x60000000
+ // JAP: Offset 800ed788, original 0x480eb86d, neu 0x60000000
+ // KOR: Offset 800ed8e0, original 0x480ebb51, neu 0x60000000
+
+ if (ret_orig)
+ *ret_orig = mode == STR_M_KOR ? 0x480ebb51 : 0x480eb86d;
+
+ switch (mode)
+ {
+ case STR_M_PAL: return 0x800ed868;
+ case STR_M_USA: return 0x800ed7c8;
+ case STR_M_JAP: return 0x800ed788;
+ case STR_M_KOR: return 0x800ed8e0;
+ default: return 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+static u32 * GetSecondHostPointer ( const staticr_t * str, u32 *ret_orig )
+{
+ const u32 addr = GetSecondHostAddress(str->mode,ret_orig);
+ if (addr)
+ {
+ const dol_header_t *dol_head = (dol_header_t*)str->data;
+ const u32 off = GetDolOffsetByAddr(dol_head,addr,4,0);
+ if (off)
+ return (u32*)(str->data + off);
+ }
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// wiimmfi codes ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static const u8 wiimmfi_code_pal_bz2[] =
+{
+ // 4 bytes decompressed len
+ 0x00,0x00,0x01,0x18,
+
+ // bzipped data
+ 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xe0,0xa4, 0xf4,0x97,0x00,0x00,
+ 0x27,0x7f,0xfd,0xff, 0xd7,0xcd,0x42,0xe6, 0x56,0x00,0x5c,0x74, 0x65,0x84,0x00,0x60,
+ 0x42,0x00,0x24,0x7c, 0x42,0x44,0x80,0x6d, 0x6c,0x00,0x04,0x54, 0x30,0x09,0x00,0xc4,
+ 0x5c,0xb0,0x00,0xd9, 0x8c,0x70,0xd3,0x4c, 0x10,0xc8,0x69,0xa6, 0x46,0x4c,0x20,0x1a,
+ 0x68,0x03,0x09,0xa3, 0x4c,0x98,0x00,0x40, 0xd0,0x6a,0x69,0x46, 0xd4,0xd0,0xd1,0xa4,
+ 0xc0,0x00,0x32,0x00, 0x00,0x8c,0x0d,0x00, 0x34,0x32,0x4c,0xc9, 0xa1,0x04,0x4d,0x4d,
+ 0x06,0x80,0x00,0x06, 0x80,0x34,0x0d,0x01, 0xa1,0x90,0x34,0x0c, 0x80,0x03,0x12,0x46,
+ 0x06,0xbe,0x9a,0x92, 0xa6,0x1b,0x60,0xf3, 0x6f,0x35,0x10,0x01, 0xa1,0xd6,0xd4,0x64,
+ 0x10,0xcb,0x04,0x73, 0x81,0xc8,0x82,0x20, 0x03,0x02,0xca,0xcd, 0x3d,0x4c,0xe4,0xb8,
+ 0xac,0x91,0xdc,0xa1, 0x90,0xe1,0xa8,0x81, 0xa2,0x9c,0x05,0xc6, 0xa0,0xf2,0x82,0xaa,
+ 0x02,0x49,0x45,0x0c, 0x04,0x26,0x10,0x90, 0xdc,0xf7,0x57,0x43, 0x99,0x8c,0x29,0x26,
+ 0x3f,0x8e,0x05,0x82, 0x03,0xa9,0x1a,0xe8, 0xde,0x7b,0x29,0x22, 0xfc,0x74,0x21,0x91,
+ 0x50,0x43,0x29,0x9d, 0xf4,0x8c,0x69,0xff, 0xb1,0x09,0xa4,0x5f, 0xd0,0xfc,0x66,0x24,
+ 0xd5,0x53,0x6c,0x59, 0x0d,0xb6,0x07,0xe0, 0x1b,0x98,0xcf,0xa9, 0xf1,0xa0,0x4b,0x23,
+ 0x2e,0xbf,0xcd,0xce, 0x59,0x65,0xdf,0x8d, 0xcd,0xa4,0xac,0x46, 0xc0,0xac,0x91,0xab,
+ 0xf8,0x3b,0x2b,0x49, 0x4c,0x24,0x1a,0x22, 0x3f,0x99,0xed,0x48, 0x28,0x06,0xad,0x2d,
+ 0x19,0x7b,0x07,0x61, 0x05,0x5c,0xf6,0x43, 0x93,0x95,0x54,0x58, 0xc0,0xe1,0xfb,0xba,
+ 0xd7,0x72,0x1a,0x68, 0x3a,0x9e,0x81,0xde, 0xa2,0xee,0x07,0x9e, 0x5a,0x9b,0x3c,0x04,
+ 0x9f,0xf8,0xbb,0x92, 0x29,0xc2,0x84,0x87, 0x05,0x27,0xa4,0xb8,
+ // [300 Bytes]
+};
+
+static BZ2Manager_t wiimmfi_code_pal_mgr
+ = { wiimmfi_code_pal_bz2, sizeof(wiimmfi_code_pal_bz2), 0, 0 };
+
+//-----------------------------------------------------------------------------
+
+static const u8 wiimmfi_code_usa_bz2[] =
+{
+ // 4 bytes decompressed len
+ 0x00,0x00,0x01,0x18,
+
+ // bzipped data
+ 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x24,0x09, 0x21,0x7b,0x00,0x00,
+ 0x28,0x7f,0xfd,0xff, 0xd7,0xcd,0x42,0xe6, 0x56,0x00,0x5c,0x70, 0x65,0x84,0x44,0x60,
+ 0x42,0x00,0x24,0x7c, 0x42,0x44,0x80,0x6d, 0x6c,0x00,0x04,0x54, 0x30,0x09,0x00,0xc0,
+ 0x18,0xb0,0x00,0xd9, 0xb4,0x22,0x50,0x64, 0x68,0x30,0x98,0x40, 0x68,0x18,0x8c,0x02,
+ 0x00,0x68,0xc8,0x00, 0xc2,0x61,0x04,0x53, 0xd2,0x32,0x9a,0x32, 0x68,0x00,0x34,0x0c,
+ 0x80,0x1a,0x06,0x81, 0xa0,0x00,0x1a,0x68, 0xc4,0xf5,0x06,0xa6, 0xa7,0xa9,0xa8,0x00,
+ 0x06,0x80,0x00,0x03, 0x40,0x01,0xa1,0x90, 0x00,0x69,0xa0,0x0e, 0x65,0x49,0x81,0x7c,
+ 0xa8,0xd3,0x51,0x16, 0x82,0x77,0xf6,0x80, 0x90,0x34,0xb7,0xc7, 0x83,0x21,0x2c,0xb0,
+ 0x46,0x60,0xc8,0x84, 0x40,0x18,0x43,0x2b, 0xb4,0xf1,0xd9,0x96, 0x24,0x2e,0xa9,0xf7,
+ 0xa1,0x98,0xc6,0xc2, 0x0d,0x14,0xd0,0x5d, 0x2a,0xa3,0xac,0x1a, 0x89,0x0b,0x14,0x51,
+ 0x84,0x90,0xa0,0x0e, 0x12,0xdc,0xa7,0xf8, 0xc6,0x96,0xd8,0x51, 0x0a,0xbb,0x40,0xe8,
+ 0x80,0x5c,0x10,0x0f, 0x42,0x83,0x41,0xcc, 0x97,0x4a,0x6c,0x73, 0x0c,0x67,0x42,0xc0,
+ 0x28,0x34,0x5e,0xda, 0x0d,0xa6,0xff,0x18, 0xc9,0x15,0xa7,0x12, 0x0d,0x02,0x9a,0xca,
+ 0x2a,0x64,0x9f,0x40, 0x39,0x96,0x00,0xf7, 0x4d,0xdc,0x7a,0xfb, 0xa8,0x2a,0xd6,0xe8,
+ 0x09,0x44,0xe7,0xed, 0x3e,0x7c,0x91,0x47, 0x1d,0xe9,0x8c,0xb2, 0x58,0x06,0x31,0x84,
+ 0xa4,0x54,0x60,0x48, 0xea,0xa0,0x30,0x7c, 0x11,0x22,0xcb,0xbd, 0x2c,0x62,0x20,0xc4,
+ 0xe3,0x36,0xe8,0x45, 0x05,0xa1,0x80,0x43, 0xa4,0x35,0x99,0x55, 0x94,0x01,0x10,0x2b,
+ 0x8c,0x40,0xa4,0x42, 0x91,0x57,0xc0,0x87, 0x5c,0xe7,0x3a,0x38, 0x8d,0xef,0xf8,0xbb,
+ 0x92,0x29,0xc2,0x84, 0x81,0x20,0x49,0x0b, 0xd8,
+ // [297 Bytes]
+};
+
+static BZ2Manager_t wiimmfi_code_usa_mgr
+ = { wiimmfi_code_usa_bz2, sizeof(wiimmfi_code_usa_bz2), 0, 0 };
+
+//-----------------------------------------------------------------------------
+
+static const u8 wiimmfi_code_jap_bz2[] =
+{
+ // 4 bytes decompressed len
+ 0x00,0x00,0x01,0x18,
+
+ // bzipped data
+ 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x19,0x2d, 0x5f,0x73,0x00,0x00,
+ 0x27,0x7f,0xfd,0xff, 0xd7,0xcd,0x46,0xe6, 0x56,0x00,0x5c,0x70, 0x65,0x84,0x00,0x64,
+ 0x42,0x00,0x24,0x7c, 0x42,0x44,0x80,0x6d, 0x6c,0x00,0x04,0x54, 0x24,0x11,0x00,0xc0,
+ 0x18,0xb0,0x00,0xd9, 0x34,0x35,0x10,0x6a, 0x68,0x34,0xd0,0x34, 0x00,0x34,0x34,0x64,
+ 0x34,0xd0,0x00,0xd0, 0x01,0xa6,0x26,0x80, 0x22,0x9e,0x94,0x64, 0x19,0x0d,0x1a,0x32,
+ 0x06,0x8d,0x34,0x00, 0x34,0x1a,0x62,0x34, 0x00,0x01,0x8d,0x47, 0xa8,0x41,0x13,0x53,
+ 0x40,0x00,0xd0,0x01, 0xa0,0x0d,0x03,0x40, 0xf5,0x06,0x40,0xd0, 0x32,0x00,0x32,0xa8,
+ 0x60,0xea,0xb1,0xaa, 0x2e,0x61,0xb8,0x0f, 0xb8,0x2b,0x09,0x00, 0x1a,0x5e,0x73,0x83,
+ 0x40,0x86,0x92,0x47, 0x38,0x1c,0x88,0x23, 0x80,0x24,0x5a,0x61, 0xb7,0xea,0x9c,0x99,
+ 0x16,0x14,0x3b,0xb4, 0xb3,0x1c,0x36,0x10, 0x36,0x54,0x00,0xb9, 0xd6,0x1e,0x91,0x17,
+ 0x40,0x2c,0x52,0x03, 0x11,0x09,0x84,0x25, 0xb9,0x45,0xe6,0x50, 0xea,0x73,0x9c,0x53,
+ 0xb5,0x58,0x24,0xfc, 0x88,0x0c,0x14,0x0e, 0xe5,0x23,0x43,0x89, 0xcc,0xa2,0x66,0xd5,
+ 0x31,0x9d,0x16,0x84, 0x72,0xd3,0x79,0xc1, 0x99,0x2f,0x5b,0xe3, 0x42,0x66,0xf6,0xa0,
+ 0x34,0x95,0x25,0x24, 0x27,0x32,0x1f,0x20, 0x72,0x96,0x07,0x0d, 0x6e,0x20,0x48,0x8a,
+ 0x94,0x96,0x17,0x5f, 0xef,0x2f,0xac,0x40, 0x7b,0x2f,0xd0,0xf6, 0xa2,0x6c,0x00,0xb2,
+ 0x24,0xb8,0xf1,0xce, 0xad,0x39,0x20,0x88, 0x7c,0x44,0x90,0x55, 0xf6,0xa0,0xc8,0x39,
+ 0x85,0x10,0xd3,0xe4, 0x25,0x04,0xd2,0xc4, 0x21,0xcc,0xb6,0x11, 0xb4,0x70,0x10,0x23,
+ 0xeb,0x63,0xe9,0x51, 0x92,0x81,0x12,0x8b, 0x00,0xc6,0x35,0x73, 0x1b,0xf1,0xed,0x1a,
+ 0x0f,0xe2,0xee,0x48, 0xa7,0x0a,0x12,0x03, 0x25,0xab,0xee,0x60,
+ // [300 Bytes]
+};
+
+static BZ2Manager_t wiimmfi_code_jap_mgr
+ = { wiimmfi_code_jap_bz2, sizeof(wiimmfi_code_jap_bz2), 0, 0 };
+
+//-----------------------------------------------------------------------------
+
+static const u8 wiimmfi_code_kor_bz2[] =
+{
+ // 4 bytes decompressed len
+ 0x00,0x00,0x01,0x18,
+
+ // bzipped data
+ 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xc2,0x0b, 0xe6,0xb5,0x00,0x00,
+ 0x27,0x7f,0xfd,0xff, 0xd7,0xcd,0x42,0xe4, 0x76,0x00,0x5c,0x70, 0x61,0xd4,0x40,0x60,
+ 0x46,0x00,0x24,0x7c, 0x42,0x44,0x80,0x6d, 0x6c,0x00,0x04,0x54, 0x28,0x05,0x00,0xc0,
+ 0x18,0xb0,0x00,0xd9, 0xb5,0x11,0x28,0x34, 0x68,0x06,0x9a,0x69, 0xa6,0x4d,0x34,0xd1,
+ 0x91,0xa6,0x9a,0x1a, 0x18,0x20,0x0d,0x00, 0x68,0xda,0x43,0x6a, 0x0d,0x53,0xd2,0x99,
+ 0xa2,0x19,0x00,0xd0, 0x03,0x40,0x00,0x68, 0x00,0x00,0x00,0x34, 0x62,0x68,0x35,0x34,
+ 0x4d,0x46,0x80,0x00, 0x03,0x40,0x00,0x00, 0xd1,0xa0,0xd0,0x1a, 0x06,0x40,0x02,0xea,
+ 0x1c,0x46,0xbc,0x75, 0xa8,0x9c,0xd6,0x03, 0xad,0xfd,0x5f,0xa4, 0x0d,0x0e,0x35,0xc1,
+ 0x90,0x96,0x58,0x24, 0x20,0x20,0x88,0x44, 0x01,0x80,0xc9,0x5a, 0xd6,0xab,0xb4,0x16,
+ 0x13,0x5b,0x51,0x0d, 0xac,0xba,0x06,0xc2, 0x0c,0x94,0xd0,0x5c, 0xea,0x1b,0xa4,0x0a,
+ 0x69,0x12,0x0a,0x28, 0xc0,0x46,0x39,0x84, 0x76,0xa7,0x39,0x55, 0x2d,0xe6,0x38,0xf0,
+ 0x49,0x55,0x07,0x9f, 0x01,0x60,0x80,0xf1, 0xf6,0xf0,0xec,0x88, 0x16,0xae,0xa8,0xca,
+ 0xe7,0x3d,0x30,0xa9, 0x4c,0x6b,0x50,0xc4, 0x9b,0x7b,0x9f,0xed, 0x3b,0x3a,0x51,0x19,
+ 0x49,0x48,0x4a,0xad, 0x17,0xe5,0x60,0x0d, 0xab,0x70,0xe3,0xab, 0x67,0x91,0x81,0x42,
+ 0x51,0x19,0x42,0xc5, 0x18,0x03,0x12,0x73, 0x93,0x26,0x68,0x86, 0x3b,0x9f,0x8d,0xab,
+ 0x18,0x02,0xe5,0x09, 0x88,0xa5,0xfa,0x5e, 0xc9,0x60,0x90,0xf8, 0x44,0x65,0xa8,0xb5,
+ 0x18,0x93,0x42,0xb2, 0xe7,0x91,0xe8,0x3f, 0x14,0xa1,0xba,0xe1, 0x0e,0xae,0xb0,0xad,
+ 0x54,0x01,0x82,0x2a, 0x30,0xe4,0x04,0xd2, 0x34,0x16,0xc1,0x9e, 0x9a,0x42,0x12,0x63,
+ 0x53,0x87,0xfc,0x5d, 0xc9,0x14,0xe1,0x42, 0x43,0x08,0x2f,0x9a, 0xd4,
+ // [301 Bytes]
+};
+
+static BZ2Manager_t wiimmfi_code_kor_mgr
+ = { wiimmfi_code_kor_bz2, sizeof(wiimmfi_code_kor_bz2), 0, 0 };
+
+//-----------------------------------------------------------------------------
+
+static BZ2Manager_t * GetWiimmfiCode ( str_mode_t mode )
+{
+ switch (mode)
+ {
+ case STR_M_PAL: return &wiimmfi_code_pal_mgr;
+ case STR_M_USA: return &wiimmfi_code_usa_mgr;
+ case STR_M_JAP: return &wiimmfi_code_jap_mgr;
+ case STR_M_KOR: return &wiimmfi_code_kor_mgr;
+ default: return 0;
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// lecode data ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static const u8 lecode_loader_pal_bz2[] =
+{
+ // decompressed len: 0x200 = 512 bytes
+ 0x00,0x00,0x02,0x00,
+
+ // bzipped data
+ 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xa1,0xb1, 0x2c,0x5f,0x00,0x00,
+ 0x16,0x7f,0xff,0xfc, 0x76,0x45,0x30,0x60, 0xc7,0xc4,0x6f,0x2f, 0x6f,0xc4,0x84,0x7f,
+ 0xe7,0xfe,0x6e,0xdc, 0x04,0x44,0x0c,0x43, 0x60,0x00,0x15,0x54, 0x00,0x48,0x49,0x7a,
+ 0x40,0x00,0x01,0xb0, 0x01,0x59,0xb2,0x20, 0xd4,0xd4,0xd0,0xd1, 0x4d,0xa9,0xb5,0x34,
+ 0xf5,0x3d,0x40,0x7a, 0x80,0x68,0x1b,0x50, 0x64,0xf5,0x03,0x40, 0x3c,0xa1,0xa0,0x06,
+ 0x43,0xd4,0xc8,0xf5, 0x19,0x1e,0x88,0x7b, 0x54,0x22,0x9e,0x80, 0x98,0x4d,0x49,0xe9,
+ 0x3d,0x4f,0x24,0x01, 0xa0,0x00,0x0d,0x00, 0x1a,0x0d,0x00,0x00, 0xd3,0x4f,0x29,0x88,
+ 0x00,0x64,0x0d,0x4d, 0x48,0xd3,0x11,0x18, 0x09,0x9a,0x99,0x18, 0x9a,0x69,0x89,0x93,
+ 0x10,0xc9,0x84,0x32, 0x64,0x34,0x61,0x36, 0x8d,0x4c,0x68,0x8f, 0x51,0xa6,0x34,0x09,
+ 0x90,0x29,0xc9,0x9c, 0x3a,0x07,0xfa,0xe1, 0x92,0x40,0x83,0x36, 0x02,0x8a,0x62,0x3f,
+ 0xb9,0x73,0x96,0x54, 0x1d,0xea,0x43,0xbc, 0x8c,0xb1,0x75,0x00, 0xd4,0xe6,0x09,0x05,
+ 0xb5,0x08,0xc9,0x83, 0xc5,0x95,0xcb,0x97, 0xa4,0x10,0xfc,0x88, 0x6b,0x5c,0x06,0x08,
+ 0xa4,0x31,0x85,0xa7, 0x80,0x46,0xeb,0x37, 0x3a,0xa8,0x18,0x22, 0x01,0x17,0xf6,0x46,
+ 0x43,0x05,0xdc,0x21, 0x55,0x8e,0x6e,0xc5, 0x89,0xaa,0x1b,0x08, 0xa6,0x7c,0x55,0x37,
+ 0x85,0x02,0x55,0xaa, 0x2d,0x62,0xf7,0xf4, 0xde,0x0b,0x2e,0xbb, 0xf2,0xa3,0x29,0x42,
+ 0xe6,0x78,0x2d,0x2d, 0xeb,0x95,0xdb,0xb1, 0x49,0x30,0xb7,0x11, 0xd4,0xa1,0x0c,0x2b,
+ 0x2c,0x22,0x54,0xd8, 0x87,0xeb,0x40,0xe5, 0x18,0xca,0xfa,0x10, 0xe9,0x92,0x3a,0xee,
+ 0xc1,0x41,0x9c,0x0a, 0x45,0xe2,0x46,0xed, 0x58,0x1a,0x0a,0xf7, 0xe8,0x5c,0x42,0x4d,
+ 0xd6,0x77,0x31,0xf7, 0xac,0x48,0xc5,0x18, 0xe3,0x53,0x80,0x54, 0xd9,0x3d,0x52,0x40,
+ 0x8b,0x28,0xc4,0x73, 0x3f,0x3e,0x92,0xe1, 0x77,0xe7,0x0a,0x9a, 0xae,0x38,0xc3,0x64,
+ 0x75,0x14,0x14,0x14, 0x89,0x35,0x16,0x33, 0xc4,0x42,0x00,0x9d, 0xb6,0x0b,0x98,0x40,
+ 0xf6,0xcc,0xda,0xf4, 0x1f,0xd7,0x4c,0x76, 0x21,0x89,0x44,0x6a, 0x92,0x11,0x22,0x45,
+ 0x6b,0x4a,0x40,0x81, 0x27,0x8d,0xfa,0x48, 0x3e,0x21,0x02,0x47, 0x0a,0x8a,0x02,0xa2,
+ 0x95,0xcb,0xb2,0xe3, 0x01,0x17,0x3d,0xc5, 0x41,0xae,0x16,0xb7, 0x84,0xaf,0xa4,0x8b,
+ 0x62,0x06,0x18,0xd2, 0x12,0x28,0xe3,0x38, 0xa4,0x30,0x32,0x04, 0x4c,0x2a,0x4b,0x22,
+ 0xa4,0xe7,0x94,0x2d, 0x7c,0x63,0xf3,0x59, 0xcb,0xf7,0x6e,0xd0, 0x42,0x21,0x74,0x9b,
+ 0xeb,0x55,0x32,0xeb, 0xd8,0x97,0x3e,0xb7, 0xee,0x55,0xb1,0x58, 0x5d,0xb9,0x55,0xe9,
+ 0x47,0x11,0x18,0x7b, 0x34,0x02,0xb3,0xf9, 0xf9,0x06,0xfb,0x5b, 0x8e,0x30,0xd4,0xb2,
+ 0xd8,0x62,0xf8,0xae, 0x75,0x02,0x06,0x20, 0xf0,0x69,0x79,0x9e, 0x40,0x98,0x48,0xeb,
+ 0x63,0xa1,0x3b,0xd0, 0x1a,0xe8,0x64,0x67, 0xfe,0x51,0x09,0x4c, 0xe4,0xa7,0xbc,0xce,
+ 0xa1,0x94,0xad,0x87, 0xfe,0x2e,0xe4,0x8a, 0x70,0xa1,0x21,0x43, 0x62,0x58,0xbe,
+ // 0x1ef = 495 Bytes
+};
+
+static BZ2Manager_t lecode_loader_pal_mgr
+ = { lecode_loader_pal_bz2, sizeof(lecode_loader_pal_bz2), 0, 0 };
+
+//-----------------------------------------------------------------------------
+
+static const u8 lecode_loader_usa_bz2[] =
+{
+ // decompressed len: 0x200 = 512 bytes
+ 0x00,0x00,0x02,0x00,
+
+ // bzipped data
+ 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x9f,0x2b, 0x3c,0x9b,0x00,0x00,
+ 0x16,0x7f,0xff,0xfc, 0x76,0x45,0x34,0x64, 0xc7,0xc0,0x6f,0x2f, 0x6f,0x8e,0x84,0x7f,
+ 0xe7,0xfe,0x6e,0xdc, 0x04,0x44,0x0c,0x43, 0x60,0x00,0x15,0x50, 0x40,0x48,0x49,0x7c,
+ 0x80,0x00,0x01,0xb0, 0x01,0x59,0x18,0xd8, 0x6a,0x68,0x20,0x9e, 0xa9,0xa7,0xa4,0x7a,
+ 0x23,0x40,0x00,0x07, 0x94,0x0d,0xa8,0x32, 0x01,0x84,0x00,0x1a, 0x1e,0x93,0xd2,0x69,
+ 0xa0,0x00,0x7e,0xa8, 0x45,0x4f,0xd3,0x53, 0x14,0xfd,0x15,0x3d, 0xa8,0x4d,0x3c,0x90,
+ 0x00,0x00,0x64,0xd3, 0x4d,0x00,0x34,0x00, 0x00,0x00,0x6d,0x13, 0x20,0x1a,0x7a,0x80,
+ 0x4a,0x9a,0x94,0xf4, 0x64,0x35,0x34,0x34, 0x64,0x18,0x8d,0x00, 0x62,0x06,0x81,0x84,
+ 0x68,0xc8,0x1a,0x32, 0x06,0x99,0x33,0x51, 0xa7,0xa9,0xa6,0x8c, 0x98,0x83,0x09,0x2b,
+ 0xc9,0x63,0x5c,0xfe, 0x70,0xc4,0x78,0x20, 0xbd,0xe2,0xad,0x5b, 0x0e,0xa6,0x38,0xb3,
+ 0x75,0xc5,0xa9,0x21, 0x3d,0x14,0xda,0x6d, 0x83,0xd5,0xaf,0x23, 0x10,0x3f,0x0d,0x4c,
+ 0xb1,0xd1,0x49,0xc4, 0x89,0x20,0x43,0xe3, 0x41,0x54,0xda,0x50, 0xcb,0xc2,0xc8,0xdf,
+ 0x00,0x22,0xa5,0x2b, 0x4a,0xb3,0x0a,0x19, 0x04,0x4b,0x97,0xfd, 0x97,0x2b,0x78,0x58,
+ 0x12,0xbb,0xd4,0x9a, 0xeb,0x24,0xd6,0x4e, 0x32,0x12,0xe1,0x3b, 0xe1,0x62,0xf8,0xf1,
+ 0xc6,0x57,0x0b,0x95, 0x52,0x23,0x0f,0x2e, 0xa7,0xb0,0x2d,0x64, 0x59,0x40,0x9a,0xf8,
+ 0x5b,0x72,0xf9,0x6c, 0xbd,0xbd,0x3f,0xd2, 0xa9,0xce,0xf5,0x3a, 0x6a,0x50,0xa8,0x93,
+ 0x5e,0x23,0x99,0xb1, 0xbe,0x27,0xa6,0x32, 0x1c,0x96,0xec,0x57, 0x8f,0x5a,0xe9,0xd9,
+ 0x90,0x90,0x11,0x74, 0x28,0x1b,0xb2,0x46, 0x9a,0x96,0x55,0x49, 0xee,0x39,0xd8,0x90,
+ 0xc3,0xdd,0x6d,0xcc, 0x99,0x24,0x3e,0xe0, 0xa1,0x12,0x36,0x89, 0xb1,0x5b,0x0c,0xc4,
+ 0x83,0x41,0x73,0x9c, 0xd0,0x6a,0x05,0x8a, 0x10,0x3e,0xfb,0x8f, 0x12,0x0e,0x72,0x51,
+ 0x21,0x6f,0x61,0x35, 0xb5,0x44,0x1f,0x09, 0x55,0x01,0x91,0x08, 0x24,0x9b,0x5a,0x72,
+ 0xf4,0xd5,0x05,0x79, 0x73,0x4d,0x0d,0x35, 0x84,0xd5,0x42,0x7a, 0x35,0x50,0x0f,0xad,
+ 0x30,0x52,0x35,0x27, 0x11,0x01,0x0b,0x84, 0xc8,0x04,0xc8,0x9c, 0xcf,0x37,0x3c,0x08,
+ 0x65,0xbb,0x89,0x4f, 0x4b,0x63,0x24,0x09, 0x49,0x5a,0x18,0xd5, 0x0c,0xc8,0x16,0x10,
+ 0x9a,0x46,0x51,0x21, 0xe0,0x6b,0xea,0x88, 0x4d,0x6d,0xb8,0xa4, 0xe9,0x06,0x36,0xd2,
+ 0x46,0x1b,0x0c,0x49, 0x7d,0x47,0x18,0x4c, 0x10,0x81,0x77,0xb1, 0xee,0xe5,0x45,0x5b,
+ 0xca,0x85,0x8b,0x3f, 0x7c,0x41,0xd6,0x5d, 0xf9,0x9b,0x13,0xb4, 0x9e,0x8a,0x3d,0x5a,
+ 0x05,0xdf,0xf4,0xf6, 0x13,0x9a,0xe2,0xc9, 0x67,0x37,0x17,0xef, 0xf4,0xbb,0xe6,0xf5,
+ 0xa4,0xb9,0x4c,0x41, 0x48,0x67,0x8f,0xdd, 0xc5,0x76,0x3f,0x45, 0xaf,0xe9,0x83,0x89,
+ 0x94,0xb5,0x4d,0xf2, 0x1d,0x19,0x5f,0x59, 0x61,0x8c,0x5b,0x19, 0xd6,0x69,0xad,0xba,
+ 0xc5,0x4e,0xff,0x17, 0x72,0x45,0x38,0x50, 0x90,0x9f,0x2b,0x3c, 0x9b,
+ // 0x1ed = 493 Bytes
+};
+
+static BZ2Manager_t lecode_loader_usa_mgr
+ = { lecode_loader_usa_bz2, sizeof(lecode_loader_usa_bz2), 0, 0 };
+
+//-----------------------------------------------------------------------------
+
+static const u8 lecode_loader_jap_bz2[] =
+{
+ // decompressed len: 0x200 = 512 bytes
+ 0x00,0x00,0x02,0x00,
+
+ // bzipped data
+ 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x5f,0x33, 0xd7,0x7b,0x00,0x00,
+ 0x16,0x7f,0xff,0xfc, 0x76,0x45,0x30,0x60, 0xc7,0xc0,0x6f,0x2f, 0x7f,0xc4,0x84,0x7f,
+ 0xe7,0xfe,0x6e,0xdc, 0x44,0x44,0x0c,0x43, 0x60,0x00,0x15,0x50, 0x00,0x48,0x4d,0x6c,
+ 0x80,0x00,0x01,0xb0, 0x01,0x59,0x18,0xd8, 0x6a,0x04,0x34,0x4d, 0x4f,0x49,0xa7,0xa9,
+ 0xa7,0xa4,0x03,0xd4, 0x00,0x3f,0x54,0x0f, 0x28,0x00,0x6d,0x4c, 0x86,0x40,0xf4,0x8f,
+ 0x53,0x10,0xd0,0x00, 0x37,0xaa,0x11,0x53, 0xf4,0xd2,0x60,0x54, 0xf6,0xa1,0x3d,0x4f,
+ 0x6a,0x40,0x00,0x01, 0x90,0x01,0x90,0x00, 0x00,0x19,0x0c,0x98, 0x80,0x34,0x61,0x04,
+ 0xa9,0xa9,0x06,0x9a, 0x9b,0x48,0x1a,0x34, 0x68,0xc8,0x64,0x34, 0xc8,0xd1,0x91,0xa6,
+ 0x86,0x80,0x0d,0x34, 0xd0,0x64,0x7a,0x26, 0x9e,0xa1,0xb5,0x34, 0xd3,0x64,0x80,0xba,
+ 0x50,0x9e,0xb1,0xaa, 0x7f,0x38,0x3e,0x60, 0x41,0xfb,0xc5,0x9b, 0x37,0x20,0x53,0x71,
+ 0x66,0xda,0x16,0xaa, 0x87,0x0a,0x29,0xb4, 0x9e,0x18,0x8b,0x3c, 0x8b,0xb7,0x84,0xd4,
+ 0xcd,0x20,0x1d,0x6e, 0x24,0x99,0xc2,0x1f, 0x1a,0x4a,0xae,0xca, 0x88,0x4c,0x17,0x0f,
+ 0x4a,0x40,0x8a,0xa2, 0xad,0x36,0x97,0x51, 0x08,0x11,0x2f,0xb6, 0xf8,0xf2,0xef,0xb6,
+ 0x37,0x65,0x84,0x4a, 0xe1,0x51,0x51,0x10, 0x9d,0x09,0x2b,0x7d, 0xfd,0xd4,0x8d,0x34,
+ 0xc5,0x76,0x73,0x92, 0x46,0x41,0xe6,0xf6, 0x0f,0x86,0x45,0xb3, 0x87,0xef,0x83,0x8d,
+ 0x3b,0xb1,0x6d,0xee, 0x18,0x3d,0x64,0x31, 0xa7,0xd3,0xa6,0xc3, 0x02,0x44,0x12,0x89,
+ 0xeb,0x34,0x38,0x84, 0x08,0x94,0xa3,0x96, 0xe5,0x99,0x66,0x8e, 0x83,0x77,0xba,0x2d,
+ 0x98,0x0d,0x7e,0x76, 0x41,0x2c,0x52,0xb0, 0x96,0xae,0x82,0xd4, 0x2d,0x0a,0xd0,0x42,
+ 0xa5,0x76,0xf9,0x27, 0x32,0xd5,0xa3,0x70, 0x08,0xa3,0x3e,0xf1, 0x5a,0x15,0x61,0x21,
+ 0x4e,0x45,0x3d,0x03, 0x21,0x95,0x4e,0x11, 0x29,0xf7,0xd3,0x3c, 0xa7,0x31,0x85,0x04,
+ 0x64,0x9f,0x1d,0xad, 0xa6,0x21,0x08,0x49, 0xdd,0x06,0x7a,0x08, 0x40,0x4d,0xab,0x31,
+ 0x3b,0x48,0x8e,0xb4, 0xa8,0x96,0x66,0xad, 0x21,0x12,0x1f,0xa6, 0xcd,0x9a,0x66,0x07,
+ 0x29,0x0f,0x5c,0x07, 0xc8,0x30,0x3d,0xc2, 0xa2,0x01,0x51,0x13, 0x97,0x5d,0xc7,0x03,
+ 0x32,0xee,0x12,0x41, 0xe5,0x31,0x92,0x04, 0x94,0x93,0x31,0xa8, 0x19,0x71,0xa4,0x1e,
+ 0xb8,0x06,0x49,0x18, 0xe0,0xd8,0x51,0x50, 0x56,0x96,0xc5,0xaa, 0xe9,0x0b,0x1b,0xa5,
+ 0x0b,0xfd,0x59,0x01, 0x58,0xb5,0x64,0x25, 0x88,0x46,0x9c,0x1b, 0x3c,0x79,0xd5,0x6b,
+ 0x54,0x9d,0x6a,0xdd, 0x8c,0x32,0xb0,0xf7, 0xcf,0x04,0x87,0x4e, 0x44,0x51,0xec,0xc8,
+ 0x3f,0xc5,0xab,0xc4, 0x54,0x66,0x9c,0x5c, 0x86,0x22,0x3a,0xeb, 0x4c,0x43,0x4b,0xf5,
+ 0xc4,0x14,0x84,0x82, 0x24,0x1c,0xf8,0x25, 0xd1,0x72,0xf0,0x55, 0x5e,0xd4,0x44,0xaf,
+ 0xf4,0x1b,0x19,0x5f, 0x79,0x61,0x6b,0x75, 0x6b,0xaf,0xd6,0x4a, 0x29,0x71,0x6c,0xe0,
+ 0x7f,0x8b,0xb9,0x22, 0x9c,0x28,0x48,0x2f, 0x99,0xeb,0xbd,0x80,
+ // 0x1ec = 492 Bytes
+};
+
+static BZ2Manager_t lecode_loader_jap_mgr
+ = { lecode_loader_jap_bz2, sizeof(lecode_loader_jap_bz2), 0, 0 };
+
+//-----------------------------------------------------------------------------
+
+static const u8 lecode_loader_kor_bz2[] =
+{
+ // decompressed len: 0x200 = 512 bytes
+ 0x00,0x00,0x02,0x00,
+
+ // bzipped data
+ 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0xaa,0xbf, 0x22,0x0b,0x00,0x00,
+ 0x16,0x7f,0xff,0xfc, 0x76,0x45,0x30,0x60, 0xc7,0xc4,0x6f,0x2f, 0x6f,0x94,0x84,0x7f,
+ 0xe7,0xfe,0x6e,0xdc, 0x04,0x44,0x0c,0x43, 0x64,0x00,0x15,0x50, 0x00,0x48,0x49,0x6a,
+ 0x40,0x00,0x01,0xb0, 0x01,0x59,0xb2,0x36, 0x22,0x0a,0x7a,0x35, 0x43,0x1a,0x4d,0xa2,
+ 0x64,0x62,0x06,0x03, 0x28,0x61,0x1a,0x60, 0x06,0x90,0xd3,0x02, 0x18,0xd3,0x42,0x32,
+ 0x62,0x69,0xa3,0xf5, 0x08,0xa7,0xa0,0x98, 0x9a,0x68,0x29,0xe9, 0x92,0x00,0x68,0x00,
+ 0x00,0xc8,0x06,0x83, 0x40,0x01,0xa1,0xe8, 0x68,0x9e,0xa0,0x06, 0x80,0x4a,0x9a,0x90,
+ 0x9f,0xa9,0xa8,0xf4, 0x8c,0x80,0x60,0x8d, 0x0d,0x00,0x01,0x84, 0x69,0xa6,0x9a,0x06,
+ 0x9a,0x06,0x9a,0x34, 0xd3,0xd4,0x33,0x51, 0xbd,0x49,0xa6,0x86, 0x5a,0x6e,0xd6,0x68,
+ 0xd4,0x3f,0xde,0x1f, 0x36,0x20,0xfe,0xf1, 0x5e,0xbd,0x98,0x13, 0xde,0x69,0xdb,0x03,
+ 0x55,0x10,0xa2,0x8a, 0x75,0x36,0xc3,0x0d, 0xab,0xc8,0xb7,0x7f, 0x09,0x79,0xa4,0x03,
+ 0xae,0xda,0x49,0xd7, 0x10,0x7a,0x52,0xbb, 0x5c,0x51,0x05,0xb1, 0xd3,0xda,0x68,0x22,
+ 0xb7,0x95,0xa5,0x61, 0x6a,0x20,0x82,0x23, 0x3d,0xdf,0x1e,0x7e, 0x16,0x45,0x59,0x39,
+ 0xab,0xb8,0x26,0xe3, 0x36,0x29,0x4c,0x91, 0x55,0xbb,0x17,0xa4, 0x96,0x51,0x75,0x6e,
+ 0x57,0xad,0x4d,0xa0, 0x9d,0xfb,0xfb,0x98, 0xd3,0x14,0x28,0x44, 0x0b,0x43,0x1d,0x99,
+ 0xed,0xfa,0x3b,0xbe, 0xec,0x94,0x8e,0x98, 0x60,0xc6,0x0b,0x2d, 0x85,0xc8,0x5b,0xa9,
+ 0x85,0x80,0x7e,0xd8, 0x56,0xb4,0x35,0xf0, 0xcf,0x7b,0xa3,0xa8, 0xb7,0x01,0x00,0x2e,
+ 0x98,0x05,0x85,0x9f, 0x06,0xe7,0x4c,0x18, 0x1a,0x96,0x9e,0x74, 0x2e,0xbe,0x3d,0x07,
+ 0x88,0xd2,0x84,0xa4, 0x12,0xe6,0x16,0x80, 0xd3,0x06,0x21,0x08, 0x65,0x12,0xce,0x64,
+ 0x24,0x09,0x2d,0xc4, 0x12,0xd6,0x55,0x53, 0xa8,0x32,0x90,0x83, 0x48,0xa4,0x41,0xa9,
+ 0x2d,0x76,0x84,0x18, 0x0f,0xee,0x03,0x96, 0xc4,0x0e,0x55,0xdc, 0xa2,0x8a,0x8d,0xc7,
+ 0xe5,0x04,0x4a,0x22, 0xd4,0x80,0x89,0xb9, 0x15,0x4e,0xc1,0x01, 0x88,0x3c,0x69,0x39,
+ 0xa9,0x10,0x23,0x60, 0xa4,0xa0,0x29,0x29, 0x58,0xa9,0xd8,0x60, 0x23,0x90,0xb0,0xad,
+ 0x99,0xfd,0x45,0xad, 0xa1,0x32,0x92,0x45, 0xba,0xd8,0xc3,0x1a, 0x02,0x45,0x1c,0x60,
+ 0x29,0x0c,0x0e,0xbc, 0xdd,0x91,0x52,0x17, 0x43,0x4d,0xe6,0x94, 0x2e,0xa4,0x64,0xf3,
+ 0x70,0xe5,0xfa,0xa9, 0x74,0x21,0x10,0x91, 0x18,0x37,0x78,0x33, 0x6d,0x4d,0x2e,0x8c,
+ 0xf0,0x17,0xe4,0x2b, 0x1f,0x16,0xf8,0x63, 0x30,0xa2,0x8a,0x3d, 0x5c,0x07,0xf0,0xb9,
+ 0x98,0x71,0x39,0x37, 0x1f,0x45,0xb8,0x4c, 0x67,0x8b,0x78,0x89, 0xf3,0x50,0x41,0x48,
+ 0x48,0x3f,0x8c,0x55, 0xc8,0x59,0xfc,0x19, 0x9c,0x87,0x9a,0x27, 0xcc,0x16,0xc0,0x38,
+ 0x43,0x46,0xd8,0x60, 0x84,0x37,0x42,0x2f, 0x89,0xe2,0x7c,0x28, 0x33,0xdd,0xe2,0xee,
+ 0x48,0xa7,0x0a,0x12, 0x15,0x57,0xe4,0x41, 0x60,
+ // 0x1e9 = 489 Bytes
+};
+
+static BZ2Manager_t lecode_loader_kor_mgr
+ = { lecode_loader_kor_bz2, sizeof(lecode_loader_kor_bz2), 0, 0 };
+
+//-----------------------------------------------------------------------------
+
+static BZ2Manager_t * GetLecodeLoader ( str_mode_t mode )
+{
+ switch (mode)
+ {
+ case STR_M_PAL: return &lecode_loader_pal_mgr;
+ case STR_M_USA: return &lecode_loader_usa_mgr;
+ case STR_M_JAP: return &lecode_loader_jap_mgr;
+ case STR_M_KOR: return &lecode_loader_kor_mgr;
+ default: return 0;
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// more data ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static const u8 codehandleronly_bz2[] =
+{
+ // 4 bytes decompressed len
+ 0x00,0x00,0x0a,0xb8,
+
+ // bzipped data, includes a GCH header of sizeof(gch_header_t) == 16 bytes
+ // 0x0:
+ 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x88,0x5d, 0x08,0x9e,0x00,0x02,
+ 0x4f,0xff,0xff,0xff, 0xfd,0xe5,0xf5,0xff, 0xff,0xff,0xff,0xff, 0xfd,0xef,0xf6,0x7e,
+ 0xfd,0xfc,0xf7,0xff, 0xff,0xff,0xf5,0x7b, 0xff,0xfe,0x7d,0x4e, 0x75,0x75,0x45,0x46,
+ 0xe4,0xe4,0xff,0xd0, 0x05,0x3e,0xdd,0x80, 0x1d,0xd5,0xb5,0xd7, 0x1d,0x57,0x6e,0xa3,
+ 0x6c,0x11,0x4c,0x22, 0x64,0x9e,0xa9,0xfa, 0xa7,0xe9,0x95,0x3d, 0xa8,0x8f,0x53,0xda,
+ 0x93,0x41,0xb4,0x9e, 0x48,0xd1,0xfa,0xa0, 0x06,0x9e,0x46,0xa6, 0x9a,0x34,0x01,0xa3,
+ 0xd4,0x03,0x34,0x47, 0x84,0x86,0x8d,0x3d, 0x41,0xea,0x34,0xf4, 0x83,0xd4,0xd1,0xa3,
+ 0xf5,0x40,0x7a,0x80, 0x00,0x00,0x3d,0x4c, 0x86,0x80,0x00,0x68, 0x7a,0x8f,0x49,0xe5,
+ 0x04,0x53,0xd2,0x0d, 0x20,0x35,0x30,0x9a, 0x9e,0xa1,0xea,0x79, 0xa9,0x3d,0xaa,0x7a,
+ 0x9e,0x9e,0x82,0x65, 0x34,0x7a,0x81,0xe4, 0x46,0xd3,0x51,0xea, 0x6d,0x4d,0x0d,0xa9,
+ 0xea,0x1e,0x9a,0x4d, 0x34,0x01,0xea,0x68, 0xf5,0x1a,0x00,0x0c, 0x8d,0x03,0x41,0xa0,
+ 0xd1,0xea,0x06,0x80, 0x00,0x00,0x00,0x0d, 0x34,0x00,0x1a,0x15, 0x4f,0x4d,0x4c,0xa7,
+ 0xa9,0xa7,0x91,0xa6, 0xa1,0x1b,0x4c,0x9a, 0x00,0x00,0x00,0x08, 0xc0,0x02,0x60,0x98,
+ 0x00,0x03,0x40,0x46, 0x00,0x09,0x82,0x60, 0x00,0x26,0x00,0x00, 0x4c,0x46,0x00,0x02,
+ 0x60,0x26,0x00,0x1a, 0x08,0x00,0xd3,0x40, 0xc8,0x34,0x00,0x34, 0x0d,0x00,0xd0,0x06,
+ 0x43,0x43,0x4c,0x40, 0x0d,0x03,0x13,0x40, 0xd0,0x19,0x00,0x00, 0x00,0xc8,0xd1,0x88,
+ // 0x100:
+ 0x0d,0x00,0x00,0x00, 0x06,0x86,0x46,0x8c, 0x98,0x08,0xc9,0x93, 0x4d,0x03,0x4d,0x21,
+ 0x04,0xd3,0x49,0x95, 0x3d,0xa6,0x46,0x40, 0x53,0x4f,0x26,0xa7, 0x8d,0x29,0xb4,0xca,
+ 0x1a,0x79,0xa5,0x01, 0xe9,0x34,0x7a,0x83, 0x69,0xa9,0x82,0x68, 0x06,0x81,0x90,0x00,
+ 0x66,0x51,0x91,0xea, 0x7a,0x46,0x8c,0x8d, 0x36,0x8c,0xa7,0xea, 0x4c,0xd4,0xc9,0x91,
+ 0xa0,0x0d,0x01,0xfa, 0xa0,0x00,0x19,0x1a, 0x3c,0xa6,0x77,0x37, 0x61,0x49,0xb5,0xfd,
+ 0x12,0xb5,0x55,0x35, 0x28,0x01,0x28,0x12, 0xd4,0xf8,0x84,0x4a, 0xca,0xdc,0x30,0x0d,
+ 0x2a,0x2f,0x0e,0x06, 0xc4,0xbe,0x15,0xb5, 0x0c,0xca,0x0e,0x1b, 0x4f,0x31,0x16,0xd9,
+ 0xf1,0xbe,0x3b,0xdb, 0xd4,0xa2,0x22,0xda, 0x45,0x51,0xf9,0x21, 0xe0,0x31,0x35,0x0b,
+ 0x66,0x00,0xc0,0xb4, 0x89,0x10,0xc2,0x4d, 0x66,0x0f,0x5f,0x4d, 0x7e,0xeb,0xc4,0x29,
+ 0xdc,0xc1,0x45,0xbb, 0x74,0x39,0xaf,0xc5, 0xe1,0xa4,0x41,0x10, 0x7c,0x26,0x16,0x41,
+ 0xee,0x61,0xb3,0x88, 0x06,0x60,0x4e,0x20, 0x79,0x42,0x4a,0x51, 0xc5,0xe1,0x76,0x23,
+ 0xab,0xd1,0x6a,0x73, 0xd6,0x2b,0x05,0x12, 0x59,0xe4,0xa2,0x17, 0x6b,0x86,0x01,0x22,
+ 0x61,0x1a,0xe1,0x8b, 0x53,0x78,0x18,0xfa, 0xb6,0x82,0xa9,0x00, 0x82,0x27,0x2f,0x26,
+ 0xa2,0x2c,0x60,0xc9, 0x7a,0xba,0xa8,0x51, 0x20,0x66,0x87,0x8b, 0x06,0x7b,0xf7,0x8e,
+ 0x68,0x32,0xe2,0x5e, 0x26,0x73,0xbd,0x96, 0xc4,0x21,0x56,0xf1, 0x64,0x28,0x79,0xce,
+ 0x7a,0x47,0xc2,0xb9, 0xaf,0xa7,0xc6,0xf4, 0x25,0x99,0x40,0xb9, 0x8a,0x60,0x55,0x1c,
+ // 0x200:
+ 0x5b,0x75,0x74,0x40, 0xc3,0x0c,0xc7,0x35, 0x1c,0xa4,0xa9,0x07, 0x0f,0x3d,0x82,0xb0,
+ 0x89,0x26,0x89,0x09, 0x3e,0xeb,0xc7,0x51, 0x2f,0xec,0x75,0xee, 0x3b,0xa3,0xd3,0xbb,
+ 0xed,0x38,0x62,0xaa, 0xaf,0xf4,0xa3,0xac, 0x23,0xf3,0x28,0x91, 0xc2,0xa1,0x1e,0x22,
+ 0xd9,0x9a,0xf6,0x26, 0xac,0xce,0xae,0x73, 0x20,0x30,0x58,0x2a, 0x34,0xd2,0x20,0x50,
+ 0x7d,0xb6,0xf6,0xd4, 0xde,0x4f,0x28,0x43, 0x16,0x90,0x5c,0xc6, 0x3b,0x86,0xc2,0x36,
+ 0xd0,0xf4,0x98,0xf7, 0x4c,0x02,0x2f,0x73, 0x6f,0x32,0x60,0x24, 0x83,0xbd,0x2b,0xae,
+ 0x19,0x10,0x24,0xa2, 0x88,0x8e,0x20,0x5d, 0x52,0xc1,0x10,0xb5, 0x00,0x78,0x8d,0x8d,
+ 0x1b,0x99,0xba,0xa9, 0x59,0xd0,0xd6,0x08, 0x70,0x08,0xf9,0x32, 0xd5,0x04,0x5f,0xc4,
+ 0x89,0x3f,0xcc,0xa9, 0x4f,0x81,0xf3,0x5f, 0xe1,0x8c,0xc6,0xe5, 0x90,0x13,0xde,0x9b,
+ 0x65,0x67,0xb7,0xdb, 0x10,0x41,0x28,0x49, 0x42,0x2c,0x67,0x88, 0x00,0xff,0x22,0xe4,
+ 0x46,0xde,0x17,0x0d, 0xc1,0x82,0xe2,0xc2, 0xa3,0xd7,0x82,0x21, 0xbe,0x1c,0x88,0x05,
+ 0x88,0xd4,0x28,0x46, 0x68,0x28,0x40,0x9c, 0x1a,0xdb,0x70,0x44, 0x21,0xaa,0x01,0x07,
+ 0xdd,0x6b,0x6b,0x5c, 0x1f,0x23,0xae,0x20, 0x0d,0x02,0x81,0xa4, 0x38,0xee,0xb2,0x19,
+ 0x3a,0xfd,0xcf,0x94, 0x03,0x4c,0x42,0x12, 0x63,0x7d,0x9b,0x81, 0xae,0x3a,0xfc,0xce,
+ 0x39,0x73,0xca,0xae, 0xd3,0xee,0xd5,0xf7, 0x15,0x71,0xc6,0x5c, 0xf1,0xfb,0x15,0x4b,
+ 0xcd,0xd9,0x5f,0x36, 0xdb,0x65,0x5d,0xce, 0x4e,0xb2,0x7d,0x94, 0x39,0x8c,0x17,0x25,
+ // 0x300:
+ 0x10,0x3d,0x68,0x74, 0x9a,0xc0,0xe6,0x39, 0x06,0x49,0xd9,0xdf, 0x2e,0x47,0x81,0xad,
+ 0xcb,0x6b,0x8f,0x1d, 0xfd,0xff,0x7e,0x75, 0x61,0x6c,0xac,0x75, 0x41,0x0b,0x83,0x38,
+ 0xe2,0x90,0x43,0x01, 0xb8,0x82,0x68,0x96, 0xc1,0x4a,0x46,0x01, 0xa0,0x58,0x67,0x85,
+ 0xbc,0x82,0xc5,0x84, 0x90,0xc8,0x38,0x12, 0xc5,0x4e,0x05,0xb5, 0xd0,0x59,0x3e,0x08,
+ 0xdd,0x52,0x00,0x2d, 0x65,0x17,0x03,0x35, 0xa2,0x58,0xf8,0x0a, 0x5a,0x75,0x86,0x2e,
+ 0x34,0x15,0x10,0xf2, 0x3c,0x30,0xfa,0x80, 0x1e,0x51,0xb9,0x1d, 0x3c,0x93,0xc2,0x77,
+ 0x99,0x49,0x15,0xc4, 0xe1,0x86,0x9b,0x9d, 0x4a,0xc9,0xdf,0x15, 0x66,0xd9,0xcd,0x64,
+ 0xd2,0x1a,0x14,0x62, 0x6c,0x85,0x7f,0x99, 0x87,0x77,0x9b,0xe3, 0xca,0xc5,0xa3,0xfd,
+ 0x67,0xa4,0x83,0xb6, 0x0e,0xe8,0xc3,0x5c, 0x75,0x63,0x5b,0xf7, 0x35,0x82,0x6e,0x13,
+ 0xac,0xaa,0x29,0x91, 0xbb,0x10,0xbd,0xd4, 0x43,0xa8,0xa2,0xde, 0x20,0x26,0xd3,0x38,
+ 0xb4,0x77,0x24,0x67, 0x45,0xab,0xba,0xde, 0x7e,0x29,0xbc,0x68, 0x1f,0xe6,0xeb,0xc5,
+ 0xd1,0x08,0x26,0x42, 0xbb,0x56,0xba,0xc0, 0x0f,0x3a,0x81,0xfb, 0x19,0xb8,0x2f,0x2e,
+ 0x54,0x33,0x68,0xdd, 0xbc,0x26,0x94,0xff, 0xaf,0xf3,0xe5,0x51, 0xac,0x79,0xf0,0x40,
+ 0xc0,0xe5,0x2b,0x30, 0xec,0x79,0xe1,0xc6, 0x36,0xf5,0x28,0xd3, 0xe6,0x7c,0x1e,0x7c,
+ 0xf7,0xc1,0x07,0x96, 0x34,0x85,0x7a,0xd2, 0x3d,0xbd,0x8b,0xdb, 0xe1,0x1e,0x82,0xce,
+ 0xff,0xd2,0x14,0x39, 0x29,0x23,0x80,0x02, 0x12,0xe0,0x34,0x04, 0x15,0x12,0xe8,0xe9,
+ // 0x400:
+ 0x98,0x86,0x3f,0x68, 0xb4,0x51,0x0f,0x74, 0x09,0xbf,0xa0,0x0c, 0x49,0x32,0xcb,0x86,
+ 0xfa,0xfb,0x9f,0xd3, 0x1d,0xe4,0xac,0x27, 0xa6,0xde,0xe2,0xd7, 0x46,0xd9,0xb3,0x97,
+ 0x0b,0x1f,0x09,0xb5, 0x1f,0x4a,0xfb,0xe1, 0x31,0x82,0x16,0xac, 0x61,0xf6,0x15,0xa0,
+ 0xe0,0xe3,0xa0,0x1c, 0x50,0xe1,0x01,0x7f, 0x10,0x9d,0x1e,0x35, 0x13,0x67,0x81,0x48,
+ 0x8d,0x24,0xf0,0x02, 0x79,0x02,0xba,0xa2, 0x30,0x8e,0x7d,0xe8, 0x7a,0x34,0xe6,0x8a,
+ 0x0c,0xd1,0x8c,0x85, 0x03,0x13,0x4f,0x0c, 0x98,0x60,0x40,0x45, 0x48,0x9d,0x3a,0x47,
+ 0x2a,0x95,0x6c,0xa9, 0xf6,0x19,0x2d,0x99, 0x3e,0xc3,0x83,0xd6, 0xd6,0x51,0x63,0xe5,
+ 0x96,0x22,0x2b,0xa2, 0xa3,0x41,0x61,0x35, 0x4b,0xc9,0x2b,0x06, 0xbc,0x55,0xc4,0xc9,
+ 0x23,0x6e,0x50,0xa7, 0xba,0x3f,0xcf,0x5b, 0xf3,0x10,0x5a,0xfc, 0x38,0x81,0xdd,0x6a,
+ 0xca,0x2c,0x66,0x69, 0x60,0xbc,0xa4,0x04, 0xcd,0x7e,0x6a,0xab, 0x1d,0xae,0x01,0x08,
+ 0xd5,0x25,0x2c,0xa1, 0xaa,0x58,0x45,0x39, 0x34,0xc6,0x86,0x62, 0xa4,0x7e,0x49,0xab,
+ 0x65,0x25,0xf4,0x76, 0xb3,0xa7,0xaf,0x99, 0x1e,0x7c,0x93,0x42, 0x24,0xa4,0x20,0x08,
+ 0x39,0x5c,0x36,0x82, 0x76,0x3e,0x22,0x9e, 0xe3,0xe6,0xe1,0x7a, 0x8b,0xac,0x1d,0xca,
+ 0x98,0x73,0x6b,0x3b, 0xfa,0x0a,0xbf,0x80, 0xdb,0xd4,0x21,0x45, 0xed,0x7b,0xca,0xb6,
+ 0x28,0xaa,0x20,0xe4, 0xe7,0x0e,0xab,0x0a, 0x94,0x1c,0x23,0x84, 0x32,0x6a,0x26,0x76,
+ 0x5e,0xe1,0x8b,0xca, 0x4b,0x66,0x85,0x37, 0x2d,0x89,0x96,0x48, 0x50,0xa1,0xc2,0x16,
+ // 0x500:
+ 0x84,0x1f,0xf4,0xef, 0x70,0x94,0x3b,0x6e, 0xd5,0x49,0x6e,0x97, 0x04,0xa6,0xff,0x2c,
+ 0x2c,0x69,0x0e,0x92, 0x65,0xdb,0x5b,0xeb, 0xf5,0x5a,0x55,0xd2, 0x88,0xf0,0x78,0x49,
+ 0x57,0x4b,0x1d,0x5a, 0x68,0x4a,0x0b,0x48, 0x24,0x7a,0x26,0x19, 0x6b,0x22,0xfc,0x9a,
+ 0xe6,0xb2,0xd0,0x03, 0x36,0x77,0x04,0xb1, 0x1a,0x57,0x24,0xb9, 0xd9,0x6f,0x2c,0x65,
+ 0xb4,0x64,0x50,0xbc, 0xbc,0x5f,0x0d,0x6d, 0xd1,0xc1,0x30,0x0d, 0xd6,0xbc,0xa5,0xcf,
+ 0x47,0xac,0xce,0xa4, 0xb6,0x6d,0xa5,0x73, 0xdd,0xd5,0x49,0x7f, 0xb3,0xe8,0x76,0x88,
+ 0xa3,0x9c,0x2b,0x1a, 0xf7,0x13,0xdc,0xfd, 0x3d,0x89,0x55,0x04, 0xfe,0xc1,0xaf,0x1f,
+ 0x0c,0x2b,0xc4,0x1b, 0x72,0x0d,0x11,0x77, 0x0e,0x7b,0x40,0xea, 0x6c,0x57,0xc2,0x9f,
+ 0x15,0x35,0x11,0x70, 0x0a,0x20,0xa1,0x73, 0x31,0x4d,0x43,0x02, 0x72,0xeb,0x58,0xdf,
+ 0xd2,0x9a,0x16,0x82, 0xc0,0xb6,0x47,0xa5, 0x07,0xf7,0xa5,0xcf, 0xa2,0xcd,0x8a,0x24,
+ 0xc5,0x9c,0x1c,0x71, 0xab,0x10,0x3e,0x83, 0x68,0x84,0x58,0xcf, 0x6e,0x26,0x17,0x2f,
+ 0x54,0x44,0x52,0x88, 0x06,0x31,0x24,0xaa, 0x03,0x4c,0x4d,0x30, 0x85,0xd4,0x95,0xc1,
+ 0x5c,0x46,0x9d,0xba, 0x20,0xf8,0x24,0x42, 0x57,0xe5,0x08,0xff, 0x37,0xe2,0x3e,0x0a,
+ 0xa4,0x26,0xfb,0x9b, 0x5a,0xca,0x94,0x5a, 0x47,0x0a,0xac,0x3d, 0xc4,0x35,0x11,0xc5,
+ 0x9c,0x63,0x7e,0x4b, 0xac,0x74,0xc3,0x0d, 0x13,0xc5,0x36,0x30, 0x88,0x37,0x64,0x4e,
+ 0x0b,0xb1,0x01,0x03, 0xde,0xc9,0x5d,0x44, 0xae,0xa6,0x5f,0xe4, 0xbc,0x9b,0x3a,0x5a,
+ // 0x600:
+ 0x5b,0x4a,0xd9,0x64, 0x14,0x4d,0x44,0x01, 0xeb,0xfb,0x89,0xf6, 0x0f,0xc0,0x42,0x14,
+ 0x05,0x02,0x12,0x10, 0x91,0x4c,0x8d,0x85, 0xcd,0xd5,0xc7,0xa6, 0xb7,0x69,0x83,0xa7,
+ 0x30,0x2c,0xdd,0xa8, 0x39,0x71,0xa3,0xc6, 0x29,0x86,0x43,0xc6, 0x05,0x83,0x57,0xf3,
+ 0x86,0x77,0x7a,0x2c, 0x50,0xb9,0xc0,0xad, 0x26,0xec,0x10,0x40, 0x0c,0x0c,0x04,0xc4,
+ 0xcb,0xb2,0xeb,0xb6, 0x1b,0x9b,0x50,0x16, 0x62,0x42,0x57,0x56, 0x35,0xd1,0x49,0x0b,
+ 0x14,0x16,0xb6,0x48, 0x40,0xe6,0xd4,0xe8, 0x51,0x91,0x89,0x38, 0xa5,0x2e,0x71,0x7a,
+ 0x60,0xd8,0xc5,0x7f, 0x3b,0xcb,0xbd,0x88, 0x38,0x10,0x08,0xbb, 0xd6,0x31,0x83,0x05,
+ 0xfd,0x6e,0x8d,0x63, 0xb6,0x25,0x04,0xf0, 0x4b,0x09,0x0b,0x89, 0x67,0x5e,0x18,0x91,
+ 0x63,0x4e,0x14,0x20, 0x0d,0x09,0x7a,0xf2, 0x32,0xfe,0xd5,0x6b, 0x5e,0xc1,0x73,0xc8,
+ 0xa6,0xc5,0xed,0xd4, 0x32,0xc4,0x9d,0xce, 0xfc,0x17,0x94,0x15, 0x45,0x46,0x5d,0x8a,
+ 0x7b,0x20,0xf6,0xce, 0x67,0x03,0xc9,0x80, 0x45,0x3d,0x33,0x6d, 0x21,0x4b,0x37,0xf3,
+ 0xf3,0xce,0x43,0x3a, 0xa5,0x4e,0x59,0x66, 0xa4,0x52,0x29,0x17, 0x4a,0x31,0x9e,0x86,
+ 0x02,0x7a,0x65,0x36, 0x43,0x81,0x04,0x0b, 0xf7,0x92,0x32,0xff, 0x1e,0xa6,0x1c,0x01,
+ 0x41,0x00,0x36,0xbc, 0xfd,0x20,0xd5,0x0d, 0x64,0x69,0x67,0x6c, 0x4c,0x9f,0x3d,0xc1,
+ 0xec,0x1c,0xd2,0x22, 0xe1,0x49,0x86,0xaa, 0xca,0xa6,0xcd,0xe1, 0xb3,0x31,0x46,0xb9,
+ 0x47,0xab,0x91,0x20, 0x6a,0x21,0xc6,0x1e, 0xa9,0x53,0x6a,0xe2, 0x41,0xa2,0xc9,0x44,
+ // 0x700:
+ 0x9c,0x9f,0x2d,0x06, 0xc1,0x73,0xc3,0xb8, 0xc4,0xb5,0x26,0x41, 0x97,0x06,0xa6,0xcb,
+ 0x1c,0x0b,0x48,0x29, 0xfe,0xf7,0x90,0x70, 0xa2,0x8b,0x01,0xb1, 0xe3,0x34,0x55,0x6a,
+ 0x6d,0x01,0x02,0x5f, 0x6a,0xa2,0x1e,0x95, 0x52,0xa9,0xce,0x2d, 0xfb,0xd5,0x86,0xc0,
+ 0xd2,0x05,0x03,0x8a, 0x34,0xfc,0x45,0x48, 0x20,0x80,0x4d,0x3e, 0x27,0x03,0xa9,0xf5,
+ 0x5d,0xd4,0x15,0x2f, 0x90,0x8f,0x8e,0x30, 0x6b,0x9a,0x39,0x3d, 0x05,0xcc,0x0d,0x48,
+ 0xd5,0x65,0x09,0x7e, 0x1b,0x03,0xb7,0xbd, 0x1a,0x03,0xa2,0xaa, 0x88,0x34,0x56,0xee,
+ 0xaa,0xb5,0x00,0xda, 0x91,0x79,0xf3,0xea, 0x8e,0xd5,0xce,0x0a, 0x50,0x7f,0xe2,0xee,
+ 0x48,0xa7,0x0a,0x12, 0x11,0x0b,0xa1,0x13, 0xc0,
+ // 0x779 = 1913 Bytes
+};
+
+static BZ2Manager_t codehandleronly_mgr
+ = { codehandleronly_bz2, sizeof(codehandleronly_bz2), 0, 0 };
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const u8 codehandler_bz2[] =
+{
+ // 4 bytes decompressed len
+ 0x00,0x00,0x10,0xc8,
+
+ // bzipped data, includes a GCH header of sizeof(gch_header_t) == 16 bytes
+ // 0x0:
+ 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x1e,0xd9, 0x97,0x25,0x00,0x03,
+ 0xe9,0xff,0xff,0xff, 0xfd,0xff,0xff,0xff, 0xff,0xff,0xff,0xff, 0xfd,0xff,0xff,0x7e,
+ 0xfd,0xff,0xff,0xff, 0xff,0xff,0xf7,0x7f, 0xff,0xfe,0x7f,0x4e, 0x77,0x75,0xe5,0x66,
+ 0xe6,0xf4,0xff,0xe0, 0x08,0x9f,0x5c,0xbe, 0xf8,0x7b,0x4e,0xb4, 0xe9,0xdd,0xb1,0xdf,
+ 0x6b,0x6f,0xb6,0xed, 0xb7,0xcd,0xe9,0xec, 0x7b,0xef,0x5a,0x1d, 0x8f,0x70,0x35,0x32,
+ 0x21,0x1a,0x27,0xa4, 0xd4,0xf2,0x66,0xa6, 0x46,0x54,0xf4,0x9f, 0x88,0x93,0xfd,0x52,
+ 0x69,0xfa,0x9a,0x6a, 0x1e,0xa7,0xb5,0x35, 0x07,0xa9,0xe9,0x1a, 0x33,0xd5,0x34,0xf6,
+ 0xa6,0x89,0xb1,0x21, 0xa6,0xd4,0xf4,0x8f, 0x14,0x3d,0x43,0xf5, 0x1a,0x64,0xd3,0x51,
+ 0xfa,0x51,0xe9,0x1e, 0xa0,0xf5,0x1e,0x48, 0x68,0x32,0x68,0x7a, 0x11,0xb4,0x83,0x47,
+ 0xa8,0x3d,0x46,0x81, 0xe9,0x00,0x06,0x86, 0x8f,0x48,0x7e,0xa9, 0xa6,0x41,0x13,0x50,
+ 0x84,0xfc,0x91,0xa9, 0xb5,0x18,0x99,0x4f, 0x49,0xa0,0xf6,0xa1, 0xe8,0x8f,0x50,0x8f,
+ 0x4d,0x36,0xa8,0x34, 0xd3,0xd2,0x62,0x0d, 0x1b,0x51,0xea,0x68, 0x06,0x9b,0x53,0xd4,
+ 0x0d,0x00,0x0d,0x34, 0x0d,0x00,0x06,0x80, 0x1a,0x34,0x00,0x34, 0xd0,0x1a,0x00,0x00,
+ 0x00,0x00,0x00,0x34, 0x00,0x04,0x44,0xd4, 0xc4,0xc4,0xc8,0xf4, 0x8d,0x1a,0x53,0xf4,
+ 0x26,0x9a,0x9b,0x50, 0xf2,0x9e,0xa1,0xb4, 0xd4,0x06,0x8f,0x50, 0x07,0xa8,0x3d,0x40,
+ 0x06,0x80,0x1a,0x68, 0xf2,0x8c,0x80,0x01, 0xa0,0x1e,0xa0,0x0c, 0x80,0x00,0x34,0x1a,
+ // 0x100:
+ 0x07,0xa8,0x00,0x00, 0x00,0x06,0x80,0x00, 0x00,0x69,0xa1,0x14, 0xf2,0x35,0x32,0x20,
+ 0x26,0x86,0x9a,0x26, 0x13,0x4d,0x34,0x34, 0x03,0x4c,0x8d,0xa9, 0xa1,0x90,0x1a,0x7a,
+ 0x9a,0x03,0x43,0x40, 0x34,0x06,0x80,0xd0, 0x03,0x40,0x00,0x01, 0xa0,0x00,0x0d,0x00,
+ 0x64,0x34,0x69,0xa0, 0x69,0xa0,0x00,0x00, 0x00,0x0d,0x00,0x03, 0x53,0xd2,0x41,0x26,
+ 0x9e,0x52,0x3d,0x13, 0xd4,0x69,0xe5,0x1e, 0xa0,0x3d,0x11,0xa1, 0xea,0x3d,0x20,0x32,
+ 0x00,0x00,0xf4,0x86, 0x80,0x00,0x00,0x00, 0x0d,0x0c,0x83,0x20, 0x00,0x00,0x01,0xa0,
+ 0x1a,0x0d,0x00,0x06, 0x80,0x00,0x00,0x00, 0x00,0x00,0x25,0x35, 0x08,0x26,0xa3,0x4c,
+ 0x24,0x6d,0x4c,0x8f, 0x54,0xc9,0xb4,0x62, 0x9b,0x42,0x6f,0x2a, 0x60,0x4f,0x29,0x93,
+ 0x6a,0x7a,0x8d,0x1a, 0x7a,0x46,0x9a,0x07, 0xa8,0x34,0xd0,0x64, 0xd0,0x34,0x7a,0x8d,
+ 0x03,0x43,0x43,0x20, 0x06,0x99,0x00,0x34, 0x03,0x43,0x40,0x0d, 0x1a,0x69,0xa0,0x00,
+ 0x34,0x00,0x34,0x03, 0x46,0x87,0xa9,0xb4, 0xde,0xf4,0x75,0xe1, 0x8b,0xbc,0x72,0xbd,
+ 0x73,0x68,0x58,0x04, 0x99,0x8b,0x84,0x6b, 0x11,0xac,0xe9,0x36, 0x54,0xef,0xb6,0x1d,
+ 0xac,0x8c,0x79,0x0a, 0xbb,0x3f,0xcd,0xf1, 0xac,0xb1,0xd1,0xd3, 0xb6,0x54,0x4c,0x17,
+ 0x24,0xca,0xac,0x92, 0xf6,0xf8,0x49,0x67, 0x6d,0x9b,0x8c,0x8f, 0x66,0x2c,0x5a,0x6b,
+ 0xcd,0xf7,0x23,0x77, 0x97,0x67,0x7c,0xf2, 0x4c,0xad,0x5c,0x67, 0x42,0xe4,0x18,0xd6,
+ 0x12,0x38,0x2f,0x61, 0xef,0x30,0x61,0x6d, 0xdb,0x51,0x04,0x4c, 0x58,0xf9,0x8b,0x04,
+ // 0x200:
+ 0x4a,0x75,0x24,0xbd, 0x41,0x04,0x88,0x35, 0x87,0x5e,0x18,0x80, 0x83,0x13,0x81,0x03,
+ 0xe0,0xe3,0xdb,0x63, 0xdd,0xe9,0xac,0xa5, 0xee,0x0e,0x36,0xda, 0xe5,0xe2,0x04,0x60,
+ 0xcf,0x1e,0x9c,0x51, 0xd0,0xab,0xb4,0x5d, 0xc9,0x04,0x87,0x18, 0xca,0x34,0x22,0xa4,
+ 0xe0,0x6e,0x9b,0xa2, 0xd5,0x0f,0x59,0x22, 0x90,0x5c,0xf6,0x61, 0xcd,0x18,0x9c,0xb2,
+ 0xb0,0x0c,0x02,0x74, 0x6e,0xd2,0xb2,0x05, 0x8e,0xba,0x3b,0x32, 0x83,0x14,0x7d,0x14,
+ 0xa9,0x87,0x19,0x66, 0xf2,0x3e,0x27,0x97, 0xa2,0xab,0x72,0x00, 0x2e,0xbe,0x5f,0x56,
+ 0x04,0x20,0x05,0x2b, 0x70,0x12,0x21,0x28, 0xcd,0x5c,0xa5,0x4d, 0xad,0x98,0xf9,0x67,
+ 0x8f,0x16,0xbd,0xdb, 0xca,0x16,0x38,0xe9, 0x4b,0x82,0xa4,0x6d, 0xc8,0x0b,0x6e,0x1a,
+ 0x21,0x00,0x82,0x20, 0xa5,0xaa,0x8c,0x04, 0x72,0x40,0xd9,0x96, 0xf9,0xe3,0xe8,0x49,
+ 0xe2,0x80,0xf4,0xa2, 0x01,0xe0,0x3c,0xa6, 0x06,0xc5,0x21,0x52, 0xe1,0x2b,0x08,0xa2,
+ 0x39,0x77,0xad,0x23, 0x9c,0x28,0x38,0xb7, 0x64,0x74,0xec,0xd4, 0x24,0xde,0x03,0x4e,
+ 0x92,0xcd,0x53,0x6e, 0x9a,0x38,0xc6,0x67, 0x84,0x12,0xb3,0x6d, 0x79,0x2b,0xaf,0x5d,
+ 0x26,0xf9,0xa6,0x1b, 0xec,0xa4,0x9d,0x08, 0x02,0xd0,0x64,0x08, 0x20,0x66,0x4c,0xee,
+ 0xe0,0x29,0x17,0x22, 0x42,0xd8,0xdf,0xc6, 0xaa,0x54,0x96,0xd2, 0x90,0x4f,0x40,0x5d,
+ 0xbb,0x2c,0xa4,0xb4, 0x6b,0xd5,0x98,0x03, 0x42,0xc0,0x0b,0x5a, 0x60,0x4b,0xf3,0x4a,
+ 0x1c,0xa0,0x88,0x58, 0x02,0x82,0x35,0x28, 0x08,0xf3,0x02,0x28, 0xb2,0x06,0xc6,0x24,
+ // 0x300:
+ 0x53,0x28,0x49,0x10, 0x62,0x4a,0x67,0x54, 0x41,0x58,0x93,0x84, 0x53,0x73,0xec,0xa1,
+ 0xe4,0xe3,0x94,0x79, 0x46,0x16,0x18,0x7c, 0x48,0x31,0x45,0x69, 0x30,0x73,0x00,0x4c,
+ 0x1f,0x7a,0x68,0xf0, 0x1d,0x6a,0x80,0xd4, 0xde,0x60,0x62,0x0a, 0x2a,0x26,0x28,0x7d,
+ 0x4e,0xa0,0x88,0x58, 0x56,0xce,0xcb,0xa5, 0x8d,0xb4,0xeb,0x48, 0xb1,0xf8,0xe9,0xd2,
+ 0x06,0x3d,0x34,0xf6, 0x80,0x5b,0x88,0x49, 0x05,0xd4,0x43,0x2a, 0x8a,0x01,0x65,0xa2,
+ 0xa7,0x7f,0x29,0x28, 0x42,0xa5,0x1c,0x71, 0xeb,0xc9,0x96,0xa2, 0xbf,0x48,0xa0,0xa8,
+ 0x34,0x65,0x89,0x68, 0x66,0xb2,0xf1,0xc6, 0xa4,0x7b,0xd6,0x56, 0xf2,0xcd,0xb2,0xd3,
+ 0x53,0x9d,0x64,0xdd, 0xb4,0x26,0x17,0x68, 0x68,0xb7,0x64,0xb1, 0x5e,0xa6,0x29,0x2d,
+ 0xcf,0x70,0xd5,0x2a, 0x09,0x21,0x2f,0x25, 0xce,0x22,0xaa,0x4e, 0x75,0x3d,0x83,0x99,
+ 0xa9,0xd3,0xe1,0x9e, 0xc0,0x65,0x51,0x32, 0x66,0xc9,0x2e,0x0a, 0xfd,0xac,0x9e,0xcc,
+ 0xd3,0x90,0x56,0x3a, 0x2d,0x53,0xa5,0x23, 0x65,0x5e,0xf9,0xcd, 0x0f,0x03,0xb7,0xbd,
+ 0xe0,0x44,0x6b,0x17, 0x86,0x5a,0x01,0xc4, 0x50,0xa9,0x05,0x06, 0x6e,0xfc,0x1a,0x47,
+ 0x53,0xe7,0x98,0x42, 0x49,0xf0,0x87,0x6f, 0x0c,0x36,0x45,0x4e, 0x59,0xf5,0x2d,0x63,
+ 0x93,0xf7,0xb4,0xb6, 0xb1,0xc4,0xe3,0x6f, 0xcc,0xec,0x06,0x3e, 0x3d,0x0e,0x09,0x9a,
+ 0x83,0x78,0x48,0xba, 0x45,0xf2,0xcc,0xa9, 0xc9,0x86,0x3d,0xbb, 0x7a,0xcf,0xaf,0x55,
+ 0xcd,0xb2,0x74,0x8a, 0x02,0x09,0x51,0x25, 0x49,0x69,0x58,0x61, 0x4b,0x88,0x90,0xc2,
+ // 0x400:
+ 0xd1,0x44,0xc7,0x24, 0xab,0xf0,0xc2,0x0f, 0xd4,0x95,0x23,0xf6, 0x55,0x57,0xe6,0x32,
+ 0x16,0xa9,0x87,0xf7, 0x1a,0x59,0x32,0xc5, 0xf5,0x23,0x68,0x6e, 0x26,0x0b,0xa3,0x46,
+ 0xe6,0x0d,0x7c,0x30, 0xde,0x00,0x79,0xd6, 0xd9,0x81,0x13,0x16, 0xfa,0x08,0xe7,0xa8,
+ 0x82,0xd5,0xce,0xdc, 0xf2,0x95,0x66,0x53, 0x62,0xc9,0x18,0x9e, 0x86,0xb1,0x13,0x06,
+ 0x1a,0x02,0x08,0x89, 0x6c,0xef,0xb6,0x30, 0x9f,0xbe,0x3b,0x43, 0x20,0xd3,0x90,0xd7,
+ 0xe9,0x84,0x2c,0x30, 0xe0,0x4c,0x58,0x42, 0x80,0x91,0x69,0x72, 0x90,0x6c,0xf7,0x41,
+ 0x45,0x73,0x07,0xe9, 0x4c,0x1a,0x63,0x14, 0x82,0x88,0x8e,0x22, 0x94,0x94,0x62,0x50,
+ 0x59,0x2c,0x71,0x48, 0x6d,0x8d,0xda,0x7b, 0x19,0xcf,0x11,0x4b, 0x46,0x05,0x7d,0x96,
+ 0x4b,0x0a,0xad,0xa5, 0xad,0xc4,0xaf,0xd2, 0xd5,0xde,0x40,0xe0, 0x10,0xc5,0xaa,0x66,
+ 0x25,0x45,0xcb,0xb2, 0x9a,0x72,0x3d,0xe4, 0x2e,0xf5,0xd5,0xa7, 0x33,0x65,0x7b,0xb7,
+ 0x1d,0x52,0xa4,0x36, 0x51,0x0c,0xce,0xe2, 0xaf,0x75,0x0f,0xd8, 0xd8,0x5f,0xec,0xe1,
+ 0x1a,0xdc,0x2b,0x0e, 0xf3,0xad,0x16,0x0b, 0xab,0x1a,0xda,0xb0, 0xc4,0xda,0x62,0xc2,
+ 0x4b,0x22,0x62,0x08, 0x7a,0x47,0x92,0x94, 0x23,0x71,0x18,0xed, 0x09,0xc2,0x40,0x1c,
+ 0xb0,0x46,0x55,0x54, 0xf0,0xb8,0x5c,0x51, 0x02,0xed,0x97,0x4c, 0x60,0xbd,0x19,0x1b,
+ 0xb5,0x61,0x5a,0x40, 0xb2,0xb6,0x32,0xb5, 0xf6,0xcd,0x9c,0x30, 0xc8,0xd1,0xe0,0xb3,
+ 0x73,0x84,0x02,0xa6, 0xe1,0x5d,0x83,0x74, 0x88,0x04,0x93,0xa6, 0x98,0x63,0x19,0xca,
+ // 0x500:
+ 0x88,0x61,0xc7,0x0d, 0x8c,0x8c,0x01,0xe7, 0xcc,0x12,0xd8,0x4c, 0x56,0x9e,0xce,0xff,
+ 0x38,0x1d,0xe8,0x67, 0x0b,0x37,0xf8,0x10, 0x6d,0xeb,0xcd,0xff, 0x57,0x5a,0x6b,0x95,
+ 0x32,0x0c,0x8d,0xbe, 0xeb,0x2e,0xd9,0xe2, 0xf8,0xa7,0xc1,0xb8, 0x77,0xa7,0x15,0xa5,
+ 0x9d,0xc2,0xc2,0xfb, 0xec,0x69,0x15,0xdb, 0x0e,0xc4,0xf2,0x86, 0xc7,0x0a,0x0b,0xf5,
+ 0xf9,0x0e,0xe2,0xcf, 0xdc,0xf9,0x2f,0xd6, 0x5e,0x2e,0x53,0x0a, 0x83,0x95,0x86,0xd6,
+ 0x2d,0x95,0xb6,0x37, 0xcd,0xc7,0xf6,0xa2, 0x7c,0x7d,0x24,0x4d, 0x2b,0xfe,0x5d,0xf7,
+ 0x32,0x0e,0x9c,0x11, 0x02,0x90,0x71,0xc7, 0x74,0xfa,0x6d,0x94, 0x5d,0xe3,0xb5,0x6a,
+ 0xeb,0xad,0xda,0xfb, 0x52,0x73,0xc7,0x18, 0xc6,0x99,0x7b,0x86, 0x66,0x69,0x2d,0xed,
+ 0x6b,0x12,0xc2,0xf0, 0x81,0x0c,0x7b,0xf7, 0x49,0x15,0x31,0x8f, 0xd5,0x20,0x2a,0x33,
+ 0x37,0x36,0x76,0x37, 0x31,0x6d,0x03,0x1a, 0xc1,0xd3,0x1e,0x82, 0xf5,0xe9,0x6e,0x8e,
+ 0x9b,0xbd,0x21,0x48, 0xc4,0x0a,0xbb,0x8c, 0xc4,0x89,0x91,0x35, 0xdd,0x3f,0x36,0x92,
+ 0x30,0x46,0x48,0x5c, 0x04,0x9a,0xde,0xf0, 0x15,0x34,0xa7,0xcc, 0xeb,0xcc,0x13,0xfe,
+ 0x26,0x2e,0xd5,0x74, 0xf2,0xcd,0xd1,0x03, 0x32,0x93,0xac,0xeb, 0x15,0xb2,0x79,0x0c,
+ 0x2a,0x08,0x0d,0x34, 0x0d,0x59,0x59,0x9c, 0x68,0xe0,0xaa,0x36, 0x95,0xa3,0x9d,0x55,
+ 0x20,0x8f,0x12,0x3e, 0x10,0xe9,0x04,0xfc, 0x5a,0x0b,0x53,0xa9, 0x27,0x54,0x85,0x44,
+ 0x35,0x47,0x07,0xc1, 0xd9,0xe2,0x07,0x84, 0x24,0x7c,0x28,0x20, 0xc8,0x4e,0x98,0x7e,
+ // 0x600:
+ 0x33,0x4a,0x0e,0xc6, 0x54,0xef,0xeb,0xe7, 0xf3,0x6a,0x98,0x04, 0x0b,0x82,0x7d,0xcb,
+ 0x47,0x60,0x3e,0xb7, 0x78,0xed,0xc6,0x9c, 0xc6,0x04,0x5d,0x50, 0x79,0x60,0xd1,0xd4,
+ 0x58,0x57,0x42,0x53, 0x23,0x9e,0x3e,0xd8, 0x10,0xa8,0x07,0x28, 0x53,0xa6,0x84,0xf1,
+ 0x0f,0x74,0x10,0xef, 0x2a,0xfd,0x3c,0x71, 0xce,0x06,0x2e,0x82, 0x39,0x64,0x13,0x3a,
+ 0x46,0x0b,0x30,0xa6, 0x68,0xd8,0x58,0x05, 0x92,0xf3,0x4d,0xc7, 0x20,0xd6,0xd3,0xe7,
+ 0x7b,0x5c,0x00,0x73, 0x17,0x3f,0x01,0x0f, 0xcd,0xfc,0x5f,0xf9, 0x10,0xa8,0x21,0x1d,
+ 0x3b,0x4c,0x8b,0x0d, 0x4c,0x04,0x42,0x01, 0x29,0x27,0x60,0xa4, 0x09,0x47,0x98,0x4c,
+ 0xdf,0x62,0x90,0x0b, 0xd1,0xad,0x18,0x19, 0x67,0xba,0x51,0xea, 0x2c,0x63,0xbe,0x94,
+ 0xb5,0xdd,0xac,0x3e, 0x16,0xc3,0x0a,0x43, 0x79,0xce,0xf9,0x45, 0xef,0xff,0xd9,0xed,
+ 0xf1,0x3f,0xd9,0x3a, 0x42,0x82,0xdd,0xfc, 0xba,0x7a,0x2d,0x48, 0x96,0xdb,0xb5,0x3c,
+ 0x0a,0x4b,0xc8,0x4b, 0x3d,0x94,0x4a,0x01, 0x9c,0xd9,0xe9,0xdb, 0x2a,0xd4,0xc9,0x2d,
+ 0xb9,0x8c,0xc0,0xb5, 0x5c,0xba,0x60,0xb8, 0x90,0x92,0x3d,0xc9, 0x69,0x86,0x66,0x48,
+ 0x06,0x1d,0x48,0x47, 0x30,0xa7,0xa6,0x43, 0x62,0xb0,0xf2,0xaa, 0x30,0xe3,0x9f,0xa0,
+ 0xad,0x14,0x30,0xe1, 0x1e,0x85,0x34,0x57, 0x23,0x91,0x51,0x93, 0x9b,0x33,0x64,0x87,
+ 0x03,0xfd,0x42,0x45, 0x9f,0x9b,0x55,0xf5, 0x1d,0x08,0x57,0x0e, 0xdc,0x20,0x88,0x4d,
+ 0xc9,0xf5,0x49,0xac, 0x36,0x24,0x0c,0x39, 0xc5,0x77,0xb3,0x5a, 0x6f,0x30,0xc7,0x50,
+ // 0x700:
+ 0x59,0x25,0x0d,0x92, 0x91,0x6c,0x29,0x82, 0x3f,0x65,0xee,0xb7, 0xa4,0x37,0xc9,0x1a,
+ 0x7f,0x58,0xd6,0xfd, 0x2e,0xa8,0x90,0x64, 0x74,0xa1,0x66,0x0f, 0xbb,0xe8,0x28,0xa5,
+ 0x45,0x4c,0x8a,0x4f, 0xb5,0x87,0x05,0xc4, 0xfa,0x0c,0xf2,0x0e, 0xcd,0x01,0x98,0x08,
+ 0x45,0x87,0xb2,0x2a, 0xa5,0x0c,0xb4,0x74, 0xd9,0xe1,0x5f,0x59, 0x68,0xf6,0x6e,0xe6,
+ 0x9b,0x25,0xd6,0x4b, 0xd6,0x2a,0xc6,0x5e, 0x75,0x4c,0xb0,0xfc, 0x5b,0x04,0x10,0x07,
+ 0xde,0xc7,0x35,0x99, 0x3a,0x27,0x45,0x20, 0x54,0x11,0x5a,0x7d, 0xb3,0x33,0x51,0xa9,
+ 0xc8,0x52,0x5c,0x4c, 0x25,0xe6,0x17,0x96, 0x5c,0x43,0xe8,0xd3, 0x60,0x95,0x92,0x66,
+ 0xdd,0x29,0x62,0x92, 0xfa,0xaa,0xd8,0x58, 0xeb,0x4f,0x8f,0x74, 0xf7,0x50,0xe5,0xee,
+ 0xd9,0x55,0x18,0x94, 0x72,0x2b,0x7a,0x1e, 0x8a,0x17,0x24,0x85, 0x07,0x36,0x51,0xb5,
+ 0x2d,0x3a,0xa9,0x21, 0x54,0xaa,0xb7,0x94, 0xee,0xe1,0x95,0x35, 0x23,0x3c,0x94,0x16,
+ 0x45,0x88,0xf5,0xe2, 0x11,0xb7,0x5c,0x86, 0x97,0xf9,0x15,0x9b, 0xd8,0x39,0x31,0xf5,
+ 0x0f,0xd7,0x90,0xab, 0x66,0xc2,0x0c,0x15, 0xeb,0x86,0x30,0x43, 0xea,0x63,0x77,0xc6,
+ 0x93,0xb6,0x56,0x80, 0x17,0x12,0x2d,0xc6, 0xd0,0xd0,0xe2,0xc3, 0x42,0x68,0xcc,0x8b,
+ 0x60,0x20,0x35,0x95, 0x50,0x62,0x92,0x7e, 0x81,0xd9,0x68,0xf4, 0xad,0xbe,0xc3,0x38,
+ 0x63,0x1f,0x1a,0xfe, 0xff,0xc8,0x02,0x3e, 0x34,0x52,0x0c,0x99, 0x28,0xa8,0xe1,0x61,
+ 0x63,0x97,0xc6,0x90, 0x5c,0xcb,0x79,0x9b, 0x84,0x4e,0x9b,0xd7, 0x5f,0x40,0x4a,0xa1,
+ // 0x800:
+ 0x0c,0xe0,0x62,0x34, 0xa6,0x6a,0xd0,0x88, 0x04,0xd2,0x84,0x10, 0xca,0xfe,0x17,0xef,
+ 0x3d,0x2b,0xf7,0x13, 0x9d,0xe4,0x57,0xac, 0x84,0x78,0xb6,0xae, 0xbf,0xb4,0xf9,0x4f,
+ 0x83,0xea,0xb2,0xd1, 0x56,0x75,0x8e,0xd1, 0x72,0xc2,0x1e,0xa2, 0xc0,0x91,0xd9,0x6f,
+ 0xcf,0x68,0x51,0xa5, 0x9f,0x87,0x78,0x37, 0x32,0xe7,0x29,0x58, 0xa3,0x6b,0x7c,0x56,
+ 0x03,0xc4,0xc2,0x85, 0x12,0xdd,0x10,0x52, 0x39,0xa0,0x98,0x12, 0xa1,0xa7,0xd5,0xf1,
+ 0x90,0x92,0x1d,0x24, 0x3e,0x16,0x3d,0x56, 0x28,0x90,0x9d,0xbf, 0x5f,0x55,0x07,0x18,
+ 0x80,0xda,0x9c,0xd2, 0x43,0xab,0xfc,0x4a, 0x8c,0x3d,0xba,0xcf, 0xdf,0xdb,0x28,0x2b,
+ 0x3d,0x83,0xd4,0xcf, 0xd0,0xf3,0x64,0x71, 0x71,0x98,0xd6,0x8f, 0x13,0x35,0x6a,0xfc,
+ 0xed,0x7a,0x41,0xed, 0x0c,0x62,0xab,0x46, 0x9c,0xc7,0x29,0x53, 0xc6,0x67,0x5e,0xf8,
+ 0x69,0x79,0x3c,0xe0, 0xc2,0xec,0x0a,0x52, 0x51,0x80,0x8e,0x7c, 0x9d,0x11,0x02,0x1f,
+ 0x91,0x6b,0x9c,0x96, 0xb4,0xa1,0x2d,0x79, 0x6c,0x5d,0x6e,0x12, 0x97,0x97,0x0e,0xb2,
+ 0xfb,0xba,0x4a,0x14, 0x6d,0xe0,0xe6,0x30, 0xb2,0xa0,0x19,0xc8, 0x86,0xb5,0x18,0x19,
+ 0xb3,0x80,0xb0,0x50, 0x40,0x44,0x4b,0x93, 0xa3,0x70,0x4a,0x9e, 0xdb,0xa9,0xd0,0x38,
+ 0x17,0x10,0xf4,0x66, 0xbb,0xa5,0x26,0x8a, 0xf2,0xb8,0xe7,0x8b, 0x2b,0xc2,0x41,0xf0,
+ 0xcb,0x80,0x30,0x0e, 0xa5,0x90,0x42,0x28, 0x65,0x13,0xb2,0xa9, 0x01,0xd1,0xd9,0x34,
+ 0x00,0xc2,0xc1,0x92, 0xd5,0x14,0xdb,0x94, 0xaf,0xd9,0x4b,0x28, 0x54,0x11,0xf4,0xe6,
+ // 0x900:
+ 0xbe,0xb9,0xac,0x06, 0x67,0xb3,0xc5,0xcc, 0x54,0xf9,0xa9,0x2c, 0x13,0x46,0xde,0x12,
+ 0xe6,0xc9,0x8c,0xa1, 0xeb,0xb6,0x0d,0x4b, 0xe7,0xa8,0xb1,0x4f, 0x31,0x0c,0x34,0xd3,
+ 0x6a,0xd3,0x73,0x49, 0x3c,0xbb,0xc3,0x32, 0xb2,0x9d,0x29,0x3e, 0x70,0x96,0x56,0xd4,
+ 0xb9,0x2a,0xc8,0x83, 0x47,0x51,0x67,0xe0, 0x7e,0x84,0x02,0x68, 0xc9,0x7d,0xbc,0xc1,
+ 0x93,0x3a,0x27,0x0d, 0x8d,0x0a,0x03,0x9c, 0xb0,0xdc,0x75,0xf1, 0xbb,0x34,0x59,0x8e,
+ 0x2d,0x9a,0xa7,0xf2, 0xff,0xa3,0x9f,0x3d, 0xa2,0x41,0xc4,0x74, 0xc9,0x03,0x69,0x5a,
+ 0xad,0x3b,0x53,0x49, 0xf8,0x80,0xb2,0x3f, 0x21,0x2f,0xa5,0x69, 0xf3,0x56,0x49,0xa6,
+ 0xab,0xcb,0x58,0x80, 0x80,0x01,0x9c,0xcc, 0x8c,0xc2,0x40,0xce, 0x86,0x75,0x57,0xa4,
+ 0x31,0x11,0x43,0x21, 0x93,0x09,0x32,0x4c, 0x21,0x0c,0xc2,0x43, 0x55,0xb0,0x70,0x61,
+ 0xfe,0x3a,0x49,0x6c, 0x6a,0xcd,0x34,0xa0, 0x90,0x25,0x84,0xcb, 0x9a,0x3a,0x43,0x96,
+ 0x82,0xe7,0x66,0x20, 0x4e,0xd3,0x34,0x73, 0x78,0xfd,0xe6,0x27, 0x8e,0xb2,0x02,0xc4,
+ 0x28,0x4c,0x1b,0x37, 0x9a,0xdb,0x13,0x5f, 0xd4,0xce,0xa1,0x40, 0x3c,0xa5,0x21,0x0c,
+ 0xb7,0xbc,0x31,0x34, 0xcc,0x47,0x7a,0xe6, 0x30,0xa8,0xed,0x6c, 0xf0,0x25,0xd7,0xf7,
+ 0xbc,0xe9,0xae,0xa9, 0xd7,0x11,0x36,0x43, 0x30,0x19,0x12,0x83, 0x6e,0x2a,0xd9,0x90,
+ 0x22,0x8e,0x8d,0x21, 0xd4,0x22,0xb9,0xc8, 0x30,0x80,0x75,0x7a, 0x74,0x44,0x3c,0x6a,
+ 0xa7,0x9a,0x83,0xd8, 0xf8,0xb5,0x45,0x8e, 0x20,0x42,0x60,0x45, 0xc5,0x7a,0x29,0x48,
+ // 0xa00:
+ 0x97,0xe1,0x04,0x6e, 0xee,0x01,0x54,0x80, 0xc1,0x60,0xfd,0x13, 0xda,0x4e,0x6b,0xe1,
+ 0x5b,0x64,0x2d,0x89, 0xe2,0x04,0x4a,0x66, 0x96,0x73,0x4c,0xa9, 0x5e,0xf0,0x94,0x2f,
+ 0x66,0x99,0xb1,0xb4, 0xfa,0x0c,0x3a,0xf6, 0x51,0xe1,0x5b,0xb8, 0x69,0xc8,0xb9,0x16,
+ 0xd7,0x3f,0x26,0x6c, 0xb3,0x8d,0xc0,0xf7, 0x22,0x49,0x23,0x7d, 0xa5,0x42,0x01,0x16,
+ 0xa6,0xc0,0xbb,0x90, 0x32,0x62,0x88,0x6b, 0x8b,0xe2,0x5c,0x14, 0x52,0x8d,0xb4,0x05,
+ 0x77,0x0e,0xc6,0x3a, 0x75,0xdd,0xac,0x52, 0xb3,0xd5,0x2c,0x9c, 0x8e,0xc5,0x66,0x74,
+ 0xd4,0x28,0x54,0x2c, 0x71,0x14,0x6e,0x9b, 0x62,0x39,0x27,0x30, 0x58,0xbd,0x93,0xfc,
+ 0xc7,0xb2,0x70,0x40, 0x82,0x20,0xdf,0x82, 0x61,0xcc,0xe8,0xf3, 0xe6,0x8b,0x56,0x49,
+ 0x2d,0x16,0xa3,0x21, 0x02,0x7d,0x26,0xab, 0xf0,0xa4,0x72,0x57, 0x51,0xf6,0xfc,0x2e,
+ 0xa1,0x3e,0x86,0xaa, 0xa5,0xc8,0x40,0xc5, 0x0d,0xbb,0x57,0x31, 0x18,0x35,0xa5,0x46,
+ 0x56,0x0d,0x6b,0xa8, 0x91,0x1d,0xab,0x58, 0xee,0xc4,0x55,0x6a, 0x6d,0x9a,0x2f,0xa1,
+ 0xb0,0xcd,0x86,0xc0, 0xc6,0x8c,0xeb,0x86, 0x0a,0xcc,0x10,0x7f, 0x59,0xb1,0x32,0x38,
+ 0x86,0x22,0xc6,0x4a, 0x82,0xa9,0xe9,0x98, 0xcb,0xff,0x5c,0x90, 0x12,0x10,0x0a,0x60,
+ 0x9e,0x0b,0x1f,0xe7, 0x72,0x29,0x9c,0x5c, 0xa5,0xae,0xb0,0xa8, 0xe4,0x34,0x92,0xcb,
+ 0x01,0x0b,0x28,0xc9, 0x72,0xd6,0xbe,0x0b, 0x7c,0xcb,0x52,0x38, 0x0a,0x7a,0x58,0x15,
+ 0xd9,0x96,0x36,0x3e, 0x64,0xcc,0x80,0x6a, 0x27,0x0e,0x50,0x53, 0xa1,0xa1,0x40,0x32,
+ // 0xb00:
+ 0x79,0xfe,0x0e,0xc2, 0x0a,0x11,0x1d,0xcf, 0x60,0x8a,0x41,0x1a, 0x2c,0xec,0xc8,0x36,
+ 0xa4,0xad,0xab,0x10, 0x7c,0x04,0x6f,0x64, 0x67,0x01,0x8b,0xb3, 0xdd,0xb2,0xaf,0x16,
+ 0x04,0x6c,0x56,0xe5, 0x0b,0xc3,0x12,0x10, 0xec,0x0c,0xab,0x19, 0x70,0x09,0x73,0x52,
+ 0xba,0x81,0x2d,0x24, 0x88,0x8c,0x33,0x25, 0x3e,0xab,0x56,0x20, 0x12,0x0e,0xd9,0x08,
+ 0x5e,0x63,0x00,0xc4, 0x00,0xb0,0xf0,0x62, 0x72,0xa4,0xc3,0xa8, 0x52,0x90,0xc9,0x63,
+ 0xd0,0x35,0x1c,0x7a, 0x54,0x2d,0x0f,0x81, 0x06,0x32,0x23,0x6b, 0xe3,0x07,0xca,0xd5,
+ 0xec,0xac,0xad,0xad, 0x52,0xc2,0x8a,0x6b, 0x24,0xc5,0x8a,0xdd, 0x27,0x21,0x08,0x1e,
+ 0xe7,0x60,0x6d,0xfd, 0x2e,0x37,0x11,0x16, 0x8d,0x39,0x1b,0x6a, 0xf6,0x93,0x41,0x82,
+ 0x63,0x72,0x49,0x2f, 0xf5,0x13,0x6d,0xd1, 0x9e,0x59,0xb8,0x4e, 0x78,0xd4,0x50,0xe5,
+ 0x66,0x63,0x03,0xa1, 0x8a,0xac,0x43,0xa0, 0x91,0xd6,0x2b,0xb6, 0x1d,0x86,0xf7,0xbd,
+ 0x98,0xc3,0x73,0xaf, 0x21,0xf6,0xc2,0x0c, 0x1d,0x7a,0x0b,0xce, 0xeb,0x05,0x4b,0xae,
+ 0x51,0xc6,0x33,0xd0, 0xc7,0xfe,0x2e,0xe4, 0x8a,0x70,0xa1,0x20, 0x3d,0xb3,0x2e,0x4a,
+ // 0xbc0 = 3008 Bytes
+};
+
+static BZ2Manager_t codehandler_mgr
+ = { codehandler_bz2, sizeof(codehandler_bz2), 0, 0 };
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// struct staticr_t ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void InitializeSTR ( staticr_t * str )
+{
+ memset(str,0,sizeof(*str));
+ memset(str->dol_flags,'-',sizeof(str->dol_flags)-1);
+ str->fname = EmptyString;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void ResetSTR ( staticr_t * str )
+{
+ if (str)
+ {
+ if (str->data_alloced)
+ FREE(str->data);
+ FreeString(str->fname);
+
+ InitializeSTR(str);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool CheckSizeSTR ( staticr_t * str )
+{
+ DASSERT(str);
+
+ switch (str->data_size)
+ {
+ case 4903876:
+ str->mode = STR_M_PAL;
+ break;
+
+ case 4902804:
+ str->mode = STR_M_USA;
+ break;
+
+ case 4901564:
+ str->mode = STR_M_JAP;
+ break;
+
+ case 4905468:
+ str->mode = STR_M_KOR;
+ break;
+
+ default:
+ str->mode = STR_M_UNKNOWN;
+ str->str_status = STR_S_ANALYZED | STR_S_WRONG_SIZE;
+ return false;
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError LoadSTR
+(
+ staticr_t * str, // valid pointer
+ bool initialize, // true: initialize 'bmg'
+ ccp fname, // filename of source
+ bool ignore_no_file // ignore if file does not exists
+ // and return warning ERR_NOT_EXISTS
+)
+{
+ DASSERT(str);
+ DASSERT(fname);
+ TRACE("LoadSTR(%d,%d) fbname=%s\n",initialize,ignore_no_file,fname);
+
+ if (initialize)
+ InitializeSTR(str);
+ else
+ ResetSTR(str);
+
+ str->data_alloced = true;
+ enumError err = OpenReadFILE( fname, 0, ignore_no_file,
+ &str->data, &str->data_size, &str->fname, &str->fatt );
+ if (err)
+ return err;
+
+// [[analyse-magic]]
+ str->fform = GetByMagicFF(str->data,str->data_size,str->data_size);
+ switch(str->fform)
+ {
+ case FF_STATICR:
+ if (!CheckSizeSTR(str))
+ break;
+ str->is_staticr = true;
+ return ERR_OK;
+
+ case FF_DOL:
+ str->is_dol = true;
+ return ERR_OK;
+
+ default:
+ break;
+ }
+
+ if (!ignore_no_file)
+ ERROR0(ERR_WARNING,"Neither StaticR nor DOL file: %s\n",fname);
+ return ERR_WARNING;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool ReplaceRegionChar ( staticr_t *str, char *buf, uint buf_size, ccp fname )
+{
+ DASSERT(str);
+ DASSERT(buf);
+ DASSERT(buf_size>100);
+
+ bool stat = false;
+ const char reg_char = GetStrModeRegionChar(str->mode);
+
+ if (reg_char)
+ {
+ StringCopyS(buf,buf_size,fname);
+ char *p = strrchr(buf,'/');
+ if (!p)
+ p = buf;
+
+ for ( ; *p; p++ )
+ if ( *p == '@' )
+ {
+ *p = reg_char;
+ stat = true;
+ }
+ }
+ return stat;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// dol_sect_info_t ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static const dol_sect_info_t dol_info_pal[DOL_NN_SECTIONS] =
+{
+ { 0, "T0", 0x100, 0x80004000, 0x2460, 1,0,1, 0,
+ { 0x67,0xa0,0xfd,0x48,0x8f, 0x9b,0xe2,0xb8,0xa1,0x35,
+ 0xed,0x61,0x48,0xe7,0x41, 0x0b,0xb6,0x5b,0xdf,0x2f }},
+ { 1, "T1", 0x2560, 0x800072c0, 0x23db20, 1,0,1, 0,
+ { 0x1c,0xbe,0x24,0xaf,0x98, 0x6a,0x6e,0x3e,0xbe,0xa1,
+ 0x5c,0xc9,0x66,0x49,0xce, 0xc7,0x7d,0x73,0x75,0x7b }},
+ { 2, "T2", 0, 0, 0, 0,0,0, 0, {0} },
+ { 3, "T3", 0, 0, 0, 0,0,0, 0, {0} },
+ { 4, "T4", 0, 0, 0, 0,0,0, 0, {0} },
+ { 5, "T5", 0, 0, 0, 0,0,0, 0, {0} },
+ { 6, "T6", 0, 0, 0, 0,0,0, 0, {0} },
+ { 7, "D0", 0x240080, 0x80006460, 0x5c0, 1,0,1, 0,
+ { 0xc8,0xbf,0xf1,0x5d,0x1d, 0x5a,0x05,0x77,0xbc,0xec,
+ 0x42,0x05,0xe7,0x11,0x76, 0x64,0xa1,0x87,0xfd,0x3d }},
+ { 8, "D1", 0x240640, 0x80006a20, 0x8a0, 1,0,1, 0,
+ { 0x6c,0xe1,0x67,0x91,0x92, 0x92,0x44,0x05,0x12,0x12,
+ 0x64,0xff,0x57,0x7f,0xae, 0x81,0xe5,0xa8,0xf8,0x7a }},
+ { 9, "D2", 0x240ee0, 0x80244de0, 0xc0, 1,0,1, 0,
+ { 0xf6,0x7f,0x2b,0x81,0x56, 0x65,0x9d,0xac,0xd2,0x56,
+ 0xb0,0xcd,0x6c,0xca,0xfd, 0x72,0x39,0xe5,0x3d,0x2e }},
+ { 10, "D3", 0x240fa0, 0x80244ea0, 0x20, 1,0,1, 0,
+ { 0xeb,0x28,0xbc,0x20,0x23, 0x0a,0xce,0xa4,0xc9,0xe0,
+ 0x6d,0x67,0xee,0x25,0x20, 0x1c,0xa1,0x55,0x39,0x7d }},
+ { 11, "D4", 0x240fc0, 0x80244ec0, 0x136c0, 1,0,1, 0,
+ { 0x4f,0xd6,0xcf,0x0f,0x65, 0x76,0xc1,0x23,0x96,0x86,
+ 0xc8,0x37,0x1b,0x95,0x93, 0x9f,0xba,0x72,0xf6,0xc5 }},
+ { 12, "D5", 0x254680, 0x80258580, 0x4bac0, 1,0,1, 0,
+ { 0x4a,0x9a,0x76,0x06,0x69, 0x35,0x0b,0xd2,0x54,0xd2,
+ 0xbf,0x22,0xac,0xd9,0xf9, 0xc8,0x13,0xcf,0x95,0xd8 }},
+ { 13, "D6", 0x2a0140, 0x80384c00, 0x13c0, 1,0,1, 0,
+ { 0xaa,0x9a,0x25,0x46,0xb8, 0xd9,0x57,0x5e,0x22,0x6d,
+ 0x72,0x2f,0x71,0xff,0x86, 0x49,0xc6,0x12,0x2d,0xcf }},
+ { 14, "D7", 0x2a1500, 0x80386fa0, 0x21a0, 1,0,1, 0,
+ { 0x43,0x0d,0x6f,0xd3,0xe0, 0x12,0x72,0x2f,0x44,0x5b,
+ 0x9e,0xdc,0x92,0x1b,0xe0, 0x62,0x8e,0x72,0xe8,0x07 }},
+ { 15, "D8", 0, 0, 0, 0,0,0, 0, {0} },
+ { 16, "D9", 0, 0, 0, 0,0,0, 0, {0} },
+ { 17, "D10", 0, 0, 0, 0,0,0, 0, {0} },
+ { 18, "BSS", 0, 0x802a4080, 0xe50fc, 1,0,0, 0, {0} },
+ { 19, "ENT", 0, 0x800060a4, 0, 1,0,0, 0, {0} },
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const dol_sect_info_t dol_info_usa[DOL_NN_SECTIONS] =
+{
+ { 0, "T0", 0x100, 0x80004000, 0x2460, 1,0,1, 0,
+ { 0x5e,0x16,0x94,0x9f,0x50, 0x5f,0xc6,0xc3,0x6e,0xca,
+ 0x76,0x5b,0x8a,0x07,0x6e, 0x23,0x68,0x77,0x84,0x67 }},
+ { 1, "T1", 0x2560, 0x800072c0, 0x23da80, 1,0,1, 0,
+ { 0x3a,0x6d,0x28,0xc3,0xdd, 0xc8,0x10,0xcc,0xf0,0xb2,
+ 0x7f,0xe7,0xd8,0x7d,0x12, 0x8f,0x62,0x72,0xd0,0xed }},
+ { 2, "T2", 0, 0, 0, 0,0,0, 0, {0} },
+ { 3, "T3", 0, 0, 0, 0,0,0, 0, {0} },
+ { 4, "T4", 0, 0, 0, 0,0,0, 0, {0} },
+ { 5, "T5", 0, 0, 0, 0,0,0, 0, {0} },
+ { 6, "T6", 0, 0, 0, 0,0,0, 0, {0} },
+ { 7, "D0", 0x23ffe0, 0x80006460, 0x5c0, 1,0,1, 0,
+ { 0x47,0x6d,0x32,0x6c,0x21, 0xaa,0x65,0xf2,0x9e,0x69,
+ 0xf9,0xde,0xd2,0xaa,0xb6, 0xc2,0x76,0x94,0x19,0x09 }},
+ { 8, "D1", 0x2405a0, 0x80006a20, 0x8a0, 1,0,1, 0,
+ { 0x73,0x5b,0x90,0x85,0x58, 0x13,0x3f,0x1e,0x56,0x67,
+ 0xeb,0x61,0x3a,0xa9,0xe9, 0x13,0xc4,0xd3,0xd5,0x6f }},
+ { 9, "D2", 0x240e40, 0x80244d40, 0xc0, 1,0,1, 0,
+ { 0x4a,0x87,0x49,0xbd,0xa1, 0x78,0x9f,0x8c,0x9c,0x9e,
+ 0x25,0x6a,0x6b,0xda,0x19, 0x7f,0x7f,0xc7,0xf5,0x7f }},
+ { 10, "D3", 0x240f00, 0x80244e00, 0x20, 1,0,1, 0,
+ { 0xe1,0x05,0x64,0x2d,0x86, 0x59,0xc9,0xac,0x76,0xde,
+ 0x8c,0x84,0xd0,0x67,0xc6, 0x9a,0x37,0x2f,0xd5,0xab }},
+ { 11, "D4", 0x240f20, 0x80244e40, 0x13420, 1,0,1, 0,
+ { 0xbd,0x62,0x9a,0x5e,0x1b, 0xda,0xf5,0x46,0xe8,0x04,
+ 0xcb,0xa1,0xfa,0xca,0xac, 0x38,0xac,0x7a,0xe6,0xaf }},
+ { 12, "D5", 0x254340, 0x80258260, 0x47aa0, 1,0,1, 0,
+ { 0x9b,0x08,0x11,0xf2,0xb5, 0xed,0x17,0x69,0x7e,0x4b,
+ 0xb3,0xef,0xb4,0x65,0x23, 0x78,0x21,0x24,0x27,0xc1 }},
+ { 13, "D6", 0x29bde0, 0x80380880, 0x13c0, 1,0,1, 0,
+ { 0x98,0x55,0xd3,0xa2,0x15, 0x10,0xe6,0xd4,0x78,0xa8,
+ 0x40,0x51,0xe5,0x51,0x77, 0x04,0x10,0x18,0x29,0x63 }},
+ { 14, "D7", 0x29d1a0, 0x80382c20, 0x21a0, 1,0,1, 0,
+ { 0x9b,0x2c,0x27,0x2b,0x6b, 0x43,0xff,0xce,0x11,0x05,
+ 0x92,0x29,0x42,0x26,0x6f, 0x9c,0x59,0xd6,0x55,0xe8 }},
+ { 15, "D8", 0, 0, 0, 0,0,0, 0, {0} },
+ { 16, "D9", 0, 0, 0, 0,0,0, 0, {0} },
+ { 17, "D10", 0, 0, 0, 0,0,0, 0, {0} },
+ { 18, "BSS", 0, 0x8029fd00, 0xe50fc, 1,0,0, 0, {0} },
+ { 19, "ENT", 0, 0x800060a4, 0, 1,0,0, 0, {0} },
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const dol_sect_info_t dol_info_jap[DOL_NN_SECTIONS] =
+{
+ { 0, "T0", 0x100, 0x80004000, 0x2460, 1,0,1, 0,
+ { 0xa6,0x86,0x2e,0xbc,0x13, 0xdd,0x9e,0x7e,0xfa,0xa9,
+ 0x0d,0x49,0x19,0x5f,0x9b, 0xe6,0x30,0x19,0x01,0x4e }},
+ { 1, "T1", 0x2560, 0x800072c0, 0x23da40, 1,0,1, 0,
+ { 0x93,0xb4,0x6b,0xbe,0xab, 0xf5,0x7d,0x98,0x93,0xf2,
+ 0x16,0x07,0x4f,0xc0,0x44, 0x82,0x01,0xd1,0x42,0x40 }},
+ { 2, "T2", 0, 0, 0, 0,0,0, 0, {0} },
+ { 3, "T3", 0, 0, 0, 0,0,0, 0, {0} },
+ { 4, "T4", 0, 0, 0, 0,0,0, 0, {0} },
+ { 5, "T5", 0, 0, 0, 0,0,0, 0, {0} },
+ { 6, "T6", 0, 0, 0, 0,0,0, 0, {0} },
+ { 7, "D0", 0x23ffa0, 0x80006460, 0x5c0, 1,0,1, 0,
+ { 0x7f,0xcd,0xe8,0xc8,0x06, 0xfb,0x10,0xbc,0x81,0x9c,
+ 0xb8,0xbb,0xa0,0x72,0x6a, 0xd8,0x75,0xb2,0xb7,0x6a }},
+ { 8, "D1", 0x240560, 0x80006a20, 0x8a0, 1,0,1, 0,
+ { 0x5a,0x2c,0x70,0x39,0x11, 0x58,0xa0,0x71,0xc0,0xb9,
+ 0x09,0xac,0x3e,0xde,0x55, 0x64,0xb0,0x70,0xb7,0x52 }},
+ { 9, "D2", 0x240e00, 0x80244d00, 0xc0, 1,0,1, 0,
+ { 0xb4,0x63,0xae,0x32,0x03, 0x5d,0xcc,0xa7,0xb0,0x64,
+ 0xee,0x4a,0x1c,0x24,0xd3, 0x10,0x84,0xd9,0xf7,0xe1 }},
+ { 10, "D3", 0x240ec0, 0x80244dc0, 0x20, 1,0,1, 0,
+ { 0x40,0x65,0xb7,0x71,0x65, 0x6e,0xbc,0x7f,0x04,0x1c,
+ 0xe3,0x44,0xb5,0x32,0x42, 0x0d,0x43,0x05,0x76,0xa8 }},
+ { 11, "D4", 0x240ee0, 0x80244e00, 0x13120, 1,0,1, 0,
+ { 0xba,0x43,0xcc,0x17,0x8b, 0x01,0x44,0xc0,0x5b,0x38,
+ 0x59,0x9d,0xfc,0x2c,0x14, 0xcd,0xa6,0x9b,0xc1,0x8a }},
+ { 12, "D5", 0x254000, 0x80257f20, 0x4bac0, 1,0,1, 0,
+ { 0x16,0x24,0x5d,0xad,0xb9, 0x7f,0xe9,0x5f,0x00,0x7f,
+ 0xef,0x6f,0xec,0xf6,0x24, 0xe5,0x35,0xc1,0x7c,0x38 }},
+ { 13, "D6", 0x29fac0, 0x80384580, 0x13c0, 1,0,1, 0,
+ { 0xb7,0x6b,0x13,0x92,0x1c, 0xad,0x77,0x78,0x92,0xe8,
+ 0xff,0xe4,0x5d,0x45,0xe6, 0x1c,0x06,0xb8,0x4d,0xdb }},
+ { 14, "D7", 0x2a0e80, 0x80386920, 0x21a0, 1,0,1, 0,
+ { 0x4d,0xc4,0xf5,0x81,0x3b, 0x8b,0x9e,0x5d,0xc3,0x36,
+ 0xfb,0x91,0xa9,0xc0,0x5b, 0xd7,0x59,0x11,0xee,0x25 }},
+ { 15, "D8", 0, 0, 0, 0,0,0, 0, {0} },
+ { 16, "D9", 0, 0, 0, 0,0,0, 0, {0} },
+ { 17, "D10", 0, 0, 0, 0,0,0, 0, {0} },
+ { 18, "BSS", 0, 0x802a3a00, 0xe50fc, 1,0,0, 0, {0} },
+ { 19, "ENT", 0, 0x800060a4, 0, 1,0,0, 0, {0} },
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const dol_sect_info_t dol_info_kor[DOL_NN_SECTIONS] =
+{
+ { 0, "T0", 0x100, 0x80004000, 0x2460, 1,0,1, 0,
+ { 0x8e,0x71,0x21,0xdf,0x02, 0x9f,0xff,0x15,0x45,0x5b,
+ 0xc9,0x30,0xdd,0xda,0x1a, 0x84,0x35,0x41,0xe8,0xc3 }},
+ { 1, "T1", 0x2560, 0x800072c0, 0x23dea0, 1,0,1, 0,
+ { 0xfc,0xf6,0x28,0x91,0x85, 0x47,0x11,0xc3,0x4e,0xac,
+ 0x2e,0x61,0x47,0x53,0x5e, 0xe1,0x7c,0x3c,0xb8,0xe6 }},
+ { 2, "T2", 0, 0, 0, 0,0,0, 0, {0} },
+ { 3, "T3", 0, 0, 0, 0,0,0, 0, {0} },
+ { 4, "T4", 0, 0, 0, 0,0,0, 0, {0} },
+ { 5, "T5", 0, 0, 0, 0,0,0, 0, {0} },
+ { 6, "T6", 0, 0, 0, 0,0,0, 0, {0} },
+ { 7, "D0", 0x240400, 0x80006460, 0x5c0, 1,0,1, 0,
+ { 0x2d,0x62,0xc1,0xdb,0x2a, 0x82,0x64,0x0a,0xa7,0x61,
+ 0xdd,0x6d,0xfc,0x4a,0x4d, 0x59,0xb4,0x51,0x6c,0x27 }},
+ { 8, "D1", 0x2409c0, 0x80006a20, 0x8a0, 1,0,1, 0,
+ { 0x8b,0x99,0x7f,0x81,0xdf, 0x8b,0x0e,0x1d,0x96,0x3e,
+ 0xbf,0xe7,0xa3,0xb2,0x0e, 0x92,0x7d,0xcc,0x5f,0x5b }},
+ { 9, "D2", 0x241260, 0x80245160, 0xc0, 1,0,1, 0,
+ { 0x7f,0xcd,0xfb,0xdd,0x3e, 0x2b,0xf4,0xf2,0x35,0xd0,
+ 0x4a,0x57,0xcb,0x9a,0xb0, 0xa2,0x2a,0x86,0xf0,0xa5 }},
+ { 10, "D3", 0x241320, 0x80245220, 0x20, 1,0,1, 0,
+ { 0xcd,0x27,0xcf,0xe9,0xce, 0x3f,0x28,0xbc,0x64,0xb3,
+ 0xb2,0x05,0x70,0x10,0xaf, 0x9a,0x5c,0xe6,0x04,0xf7 }},
+ { 11, "D4", 0x241340, 0x80245240, 0x13100, 1,0,1, 0,
+ { 0x23,0x8c,0xc9,0x84,0xd1, 0x07,0x8a,0x1b,0xc4,0x56,
+ 0x1b,0x56,0x30,0xe0,0x1a, 0x4d,0xbc,0xa1,0x48,0x0c }},
+ { 12, "D5", 0x254440, 0x80258340, 0x39d00, 1,0,1, 0,
+ { 0x4a,0x10,0xfb,0x98,0x57, 0xb0,0xa5,0x96,0xb7,0xf6,
+ 0x92,0x06,0xed,0xf2,0xdf, 0x04,0x5e,0x6d,0x63,0x55 }},
+ { 13, "D6", 0x28e140, 0x80372c00, 0x13e0, 1,0,1, 0,
+ { 0xad,0x8f,0x5f,0x3d,0xf0, 0x9d,0xa5,0x26,0x9f,0x71,
+ 0xfb,0xeb,0x7b,0x68,0x99, 0x72,0x71,0xdc,0x65,0xcc }},
+ { 14, "D7", 0x28f520, 0x80374fc0, 0x21a0, 1,0,1, 0,
+ { 0xc3,0x04,0xfc,0x7f,0xf8, 0x6f,0xf0,0x39,0xa9,0xe7,
+ 0xda,0x78,0xf3,0x1b,0xfa, 0x41,0x91,0x7d,0xf1,0x3c }},
+ { 15, "D8", 0, 0, 0, 0,0,0, 0, {0} },
+ { 16, "D9", 0, 0, 0, 0,0,0, 0, {0} },
+ { 17, "D10", 0, 0, 0, 0,0,0, 0, {0} },
+ { 18, "BSS", 0, 0x80292080, 0xe511c, 1,0,0, 0, {0} },
+ { 19, "ENT", 0, 0x800060a4, 0, 1,0,0, 0, {0} },
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+const dol_sect_info_t * GetInfoDOL ( str_mode_t mode )
+{
+ switch (mode)
+ {
+ case STR_M_PAL: return dol_info_pal;
+ case STR_M_USA: return dol_info_usa;
+ case STR_M_JAP: return dol_info_jap;
+ case STR_M_KOR: return dol_info_kor;
+ default: return 0;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool GetSectionDOL
+(
+ staticr_t * str, // valid pointer
+ dol_sect_info_t * ret_info, // valid pointer: returned data
+ uint section, // 0 .. DOL_NN_SECTIONS
+ // (7*TEXT, 11*DATA, BSS, ENTRY )
+ bool calc_hash // true: calculate SHA1 hash if data available
+
+)
+{
+ DASSERT(str);
+ DASSERT(ret_info);
+
+ bool stat = GetDolSectionInfo( ret_info,( dol_header_t*)str->data,
+ str->data_size, section );
+ if ( calc_hash && stat && ret_info->data_valid )
+ {
+ ret_info->hash_valid = true;
+ SHA1( ret_info->data, ret_info->size, ret_info->hash );
+ }
+ return stat;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const DolSectionMap_t * FindSectionDOL ( uint size, u8 *hash )
+{
+ if ( !size || !hash )
+ return 0;
+
+ uint beg = 0;
+ uint end = N_DOL_SECTION_MAP - 1;
+
+ while ( beg <= end )
+ {
+ const uint idx = (beg+end)/2;
+ const DolSectionMap_t *dsm = DolSectionMap + idx;
+ if ( size < dsm->size )
+ end = idx - 1 ;
+ else if ( size > dsm->size )
+ beg = idx + 1;
+ else
+ {
+ const int stat = memcmp(hash,dsm->hash,sizeof(dsm->hash));
+ if ( stat < 0 )
+ end = idx - 1 ;
+ else if ( stat > 0 )
+ beg = idx + 1;
+ else
+ return dsm;
+ }
+ }
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ct-code dol extensions ///////////////
+///////////////////////////////////////////////////////////////////////////////
+// [[ct_dol_ext_t]]
+
+typedef struct ct_dol_ext_t
+{
+ u32 t2_addr;
+ BZ2Source_t t2_src;
+
+ u32 d8_addr;
+ BZ2Source_t d8_src;
+}
+ct_dol_ext_t;
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+ct_dol_ext_t ct_dol_ext_pal =
+{
+ 0x802a5000,
+ { "rmcp/boot_code.bin;boot_code.bin",
+ ctcode_boot_code_pal_bz2, sizeof(ctcode_boot_code_pal_bz2),
+ FF_CT0_CODE },
+
+ 0x80002800,
+ { "rmcp/boot_data.bin;boot_data.bin",
+ ctcode_boot_data_pal_bz2, sizeof(ctcode_boot_data_pal_bz2),
+ FF_CT0_DATA },
+};
+
+//-----------------------------------------------------------------------------
+
+ct_dol_ext_t ct_dol_ext_usa =
+{
+ 0x802a5000,
+ { "rmce/boot_code.bin;boot_code.bin",
+ ctcode_boot_code_usa_bz2, sizeof(ctcode_boot_code_usa_bz2),
+ FF_CT0_CODE },
+
+ 0x80002800,
+ { "rmce/boot_data.bin;boot_data.bin",
+ ctcode_boot_data_usa_bz2, sizeof(ctcode_boot_data_usa_bz2),
+ FF_CT0_DATA },
+};
+
+//-----------------------------------------------------------------------------
+
+ct_dol_ext_t ct_dol_ext_jap =
+{
+ 0x802a5000,
+ { "rmcj/boot_code.bin;boot_code.bin",
+ ctcode_boot_code_jap_bz2, sizeof(ctcode_boot_code_jap_bz2),
+ FF_CT0_CODE },
+
+ 0x80002800,
+ { "rmcj/boot_data.bin;boot_data.bin",
+ ctcode_boot_data_jap_bz2, sizeof(ctcode_boot_data_jap_bz2),
+ FF_CT0_DATA },
+};
+
+//-----------------------------------------------------------------------------
+
+ct_dol_ext_t ct_dol_ext_kor =
+{
+ 0x802a5000,
+ { "rmck/boot_code.bin;boot_code.bin",
+ 0, 0, // [[2do]] [[ct-korea]]
+ FF_CT0_CODE },
+
+ 0x80002800,
+ { "rmck/boot_data.bin;boot_data.bin",
+ 0, 0, // [[2do]] [[ct-korea]]
+ FF_CT0_DATA },
+};
+
+//-----------------------------------------------------------------------------
+
+ct_dol_ext_t * GetCtExtDOL ( str_mode_t mode )
+{
+ switch (mode)
+ {
+ case STR_M_PAL: return &ct_dol_ext_pal;
+ case STR_M_USA: return &ct_dol_ext_usa;
+ case STR_M_JAP: return &ct_dol_ext_jap;
+ case STR_M_KOR: return &ct_dol_ext_kor;
+ default: return 0;
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// PatchHTTPS() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static uint PatchHTTPS_HTTP
+(
+ staticr_t * str, // valid pointer
+ https_mode_t mode
+)
+{
+ DASSERT(str);
+ char buf[200], domain_buf[100];
+
+ static const char nwn[] = "nintendowifi.net";
+ static uint nwn_len = sizeof(nwn) - 1;
+
+ const bool mode_sake = mode == HTTPS_SAKE0 || mode == HTTPS_SAKE1;
+ const bool repl_domain = mode == HTTPS_DOMAIN || mode_sake;
+
+ if ( repl_domain && strlen(wifi_domain) > nwn_len )
+ {
+ ERROR0(ERR_SEMANTIC,
+ "Domain must be smaller than %u characters if using --https=DOMAIN: %s",
+ nwn_len+1, wifi_domain );
+ opt_https = mode = HTTPS_HTTP; // fall back
+ }
+
+ uint count = 0;
+ const str_server_patch_t *pat;
+ const str_flags_t dol_flag = str->is_dol ? STR_F_IS_DOL : 0;
+
+ for ( pat = ServerPatch; pat->mode; pat++ )
+ {
+ // dirty code: pat->flags is either 0(srel), 1(mdol), 2(mdol+special)
+ if ( pat->mode != str->mode || (pat->flags & STR_F_IS_DOL) != dol_flag )
+ continue;
+ if ( pat->flags & STR_F_IS_OPTIONAL && opt_wc24 )
+ continue;
+
+ const str_server_list_t *sl = ServerList + pat->serv_type;
+ memset(buf,0,sizeof(buf));
+
+ if ( pat->flags & STR_F_RESTORE_HTTPS )
+ {
+ strncpy(buf,"https://",sizeof(buf));
+ }
+ else if ( pat->flags & STR_F_IS_SPECIAL && wifi_domain_is_wiimmfi )
+ {
+ switch (pat->serv_type)
+ {
+ case SERV_NASW:
+ snprintf(buf,sizeof(buf),"http://naswii.%s/ac",wifi_domain);
+ break;
+
+ case SERV_NASW_DEV:
+ snprintf(buf,sizeof(buf),"https://main.nas.%s/p%c",
+ wifi_domain,
+ tolower((int)VersusPointsInfo[pat->mode].region_code) );
+ break;
+
+ case SERV_NASW_TEST:
+ snprintf(buf,sizeof(buf),"http://ca.nas.%s/ca",wifi_domain);
+ break;
+
+ default:
+ ASSERT(0);
+ break;
+ }
+ PRINT("STR_F_IS_SPECIAL: %u %s\n",pat->serv_type,buf);
+ }
+ else if ( mode_sake && pat->serv_type == SERV_SAKE )
+ {
+ // special case for SAKE servers
+
+ if (!pat->is_http)
+ snprintf(buf,sizeof(buf),"gs.%s%s", nwn, pat->param );
+ else
+ {
+ snprintf(buf,sizeof(buf),"%s://%s%s",
+ mode == HTTPS_SAKE0 && pat->is_http == 2 ? "https" : "http",
+ sl->domain, pat->param );
+ }
+ }
+ else if ( pat->serv_type == SERV_NONE )
+ {
+ DASSERT(pat->is_http==2);
+ char * dest = StringCopyS(buf,sizeof(buf),
+ mode == HTTPS_RESTORE ? "https" : "http" );
+ if ( pat->url_len == 8 )
+ StringCopyE(dest,buf+sizeof(buf),"://");
+ }
+ else if (!pat->is_http)
+ {
+ snprintf(buf,sizeof(buf),"gs.%s%s",
+ repl_domain ? wifi_domain : nwn, pat->param);
+ }
+ else if ( repl_domain )
+ {
+ snprintf(buf,sizeof(buf),"http://%.*s%s%s",
+ sl->domain_len - nwn_len, sl->domain,
+ wifi_domain, pat->param );
+ }
+ else
+ {
+ ccp domain = sl->domain;
+ if ( mode == HTTPS_HTTP && !memcmp(domain,"naswii.",7) )
+ {
+ snprintf(domain_buf,sizeof(domain_buf),"nas.%s",domain+7);
+ domain = domain_buf;
+ }
+
+ snprintf(buf,sizeof(buf),"%s://%s%s",
+ mode == HTTPS_RESTORE && pat->is_http == 2 ? "https" : "http",
+ domain, pat->param );
+ }
+
+ char *data = (char*)str->data + pat->offset;
+ if (memcmp(data,buf,pat->url_len+1))
+ {
+ //HexDump16(stderr,0,0,data,pat->url_len+1);
+ //HexDump16(stderr,0,0,buf,pat->url_len+1);
+ memcpy(data,buf,pat->url_len+1);
+ count++;
+ }
+ }
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint PatchHTTPS
+(
+ staticr_t * str // valid pointer
+)
+{
+ switch (opt_https)
+ {
+ case HTTPS_RESTORE:
+ case HTTPS_HTTP:
+ case HTTPS_DOMAIN:
+ case HTTPS_SAKE0:
+ case HTTPS_SAKE1:
+ return PatchHTTPS_HTTP(str,opt_https);
+
+ case HTTPS_NONE:
+ case HTTPS__N:
+ break;
+ }
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// main.dol support ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void AnalyzeDOL
+(
+ staticr_t * str, // valid pointer
+ bool restore // true: restore patched data
+)
+{
+ DASSERT(str);
+ str->dol_status = DOL_S_ANALYZED;
+
+ if (PatchHTTPS_HTTP(str,HTTPS_RESTORE))
+ str->dol_status |= DOL_S_HTTPS;
+
+ static str_mode_t tab_mode[]
+ = { STR_M_PAL, STR_M_USA, STR_M_JAP, STR_M_KOR, STR_M_UNKNOWN };
+
+ static sha1_hash_t tab_sha1_mode[] =
+ {
+ // STR_M_PAL
+ { 0x12, 0xb0, 0x2a, 0xf1, 0x9e, 0x07, 0xd3, 0x6d, 0x8a, 0x38,
+ 0xc9, 0xa7, 0xfe, 0x89, 0x15, 0x55, 0x2f, 0x2e, 0x58, 0x5f },
+
+ // STR_M_USA
+ { 0x0a, 0xed, 0x61, 0x96, 0x28, 0x11, 0xa1, 0x64, 0xf5, 0x8c,
+ 0xd7, 0xed, 0xfc, 0x71, 0x6e, 0xeb, 0x05, 0xb8, 0x4d, 0xab },
+
+ // STR_M_JAP
+ { 0x69, 0xed, 0xa1, 0xa4, 0x05, 0xde, 0x0e, 0xe1, 0x29, 0x09,
+ 0x15, 0x4f, 0xd5, 0xdc, 0x2c, 0x33, 0x62, 0x9f, 0x6f, 0x37 },
+
+ // STR_M_KOR
+ { 0x86, 0x2a, 0x38, 0xf2, 0xd7, 0x56, 0xe5, 0x96, 0x2c, 0xa1,
+ 0x9f, 0xd0, 0x62, 0xd0, 0xbd, 0x55, 0x67, 0x10, 0xed, 0x7e },
+ };
+
+ if ( str->data_size < 0x1100 )
+ return;
+
+ sha1_hash_t hash;
+ SHA1(str->data+0x100,0x1000,hash);
+
+ uint i;
+ for ( i = 0; tab_mode[i] != STR_M_UNKNOWN; i++ )
+ {
+ if (!memcmp(tab_sha1_mode+i,hash,sizeof(hash)))
+ {
+ str->is_main = true;
+ str->mode = tab_mode[i];
+ str->dol_status |= DOL_S_KNOWN;
+
+ SHA1(str->data,str->data_size,hash);
+ const DolSectionMap_t *dsm = FindSectionDOL(str->data_size,hash);
+ if ( dsm && dsm->mode == str->mode && dsm->dol_stat )
+ {
+ str->dol_status |= dsm->dol_stat;
+ str->dol_info = dsm->info;
+ if ( str->dol_status & DOL_S_WIIMMFI )
+ str->dol_status &= ~DOL_S_HTTPS;
+ else if ( str->dol_status & DOL_S_HTTPS )
+ str->dol_status &= ~DOL_S_ORIG;
+ }
+ break;
+ }
+ }
+ str->dol_info_flags = ( str->dol_info_flags & ~DIF_MODE )
+ | ( str->mode - STR_M_PAL & DIF_MODE );
+
+
+ //-- check VBI
+
+ const u32 vbi_addr = GetVbiAddress(str);
+ if (vbi_addr)
+ {
+ dol_header_t *dh = (dol_header_t*)str->data;
+ const u32 offset = GetDolOffsetByAddr(dh,vbi_addr,4,0);
+ if (offset)
+ {
+ const u32 cur_value = be32(str->data+offset);
+ if ( cur_value != ORIG_VBI_VALUE )
+ {
+ if (restore)
+ write_be32(str->data+offset,ORIG_VBI_VALUE);
+ str->dol_status = str->dol_status & ~DOL_S_ORIG | DOL_S_VBI;
+
+ if ( ( cur_value & 0xff000000 ) == 0x4b000000 )
+ {
+ str->vbi_address = cur_value & 0x00ffffff;
+ if ( str->vbi_address & 0x00800000 )
+ str->vbi_address |= 0xff000000;
+ str->vbi_address += vbi_addr;
+ }
+ }
+ }
+ }
+
+
+ //-- check patched_by VERSION
+
+ char *id_ptr = GetIdPointer(str);
+ if (id_ptr)
+ {
+ char *id_end = id_ptr + PATCHED_BY_SIZE;
+ char *end = id_ptr;
+ while ( end < id_end && (uchar)*end >= ' ' )
+ end++;
+ while ( end > id_ptr && ( end[-1] == '*' || end[-1] == ' ' ) )
+ end--;
+
+ memset(str->patched_by,0,sizeof(str->patched_by));
+ if ( id_ptr < end )
+ memcpy(str->patched_by,id_ptr,end-id_ptr);
+ if (restore)
+ memset(id_ptr,'*',PATCHED_BY_SIZE);
+ }
+
+
+ //-- check sdkver VERSION
+
+ const u32 vers_addr = GetSdkverAddress(str->mode);
+ if (vbi_addr)
+ {
+ dol_header_t *dh = (dol_header_t*)str->data;
+ const u32 offset = GetDolOffsetByAddr(dh,vers_addr,8,0);
+ if ( offset && memcmp(str->data+offset,ORIG_SDK_VERS,8) )
+ {
+ memcpy(str->sdk_version,str->data+offset,8);
+ str->dol_status = str->dol_status & ~DOL_S_ORIG | DOL_S_SDKVER;
+ if (restore)
+ memcpy(str->data+offset,ORIG_SDK_VERS,8);
+ }
+ }
+
+
+ //-- check second host
+
+ u32 h2_orig, *h2 = GetSecondHostPointer(str,&h2_orig);
+ if (h2)
+ {
+ if ( ntohl(*h2) == ASM_NOP )
+ {
+ str->dol_status = str->dol_status & ~DOL_S_ORIG | DOL_S_2HOST;
+ if (restore)
+ *h2 = htonl(h2_orig);
+ }
+ }
+
+
+ //-- finish
+
+ if (!str->dol_info_flags_set)
+ {
+ str->dol_info_flags_set = true;
+ if ( str->dol_status & DOL_S_ORIG )
+ str->dol_info_flags |= DIF_ORIG;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void PrintStatusDOL
+(
+ FILE * f, // destination file
+ int indent, // indention
+ staticr_t * str, // valid pointer
+ int long_mode // >0: print more track details if changed
+)
+{
+ indent = NormalizeIndent(indent);
+ if (!(str->dol_status&DOL_S_ANALYZED))
+ AnalyzeDOL(str,true);
+
+ #ifdef TEST
+ if ( opt_new > 0 )
+ {
+ u32 off1 = GetVbiAddress(str);
+ u32 off2 = FindDolAddressOfVBI(str->data,str->data_size);
+ printf("VBI: %08x %c= %08x : %s\n",
+ off1, off1 == off2 ? '=' : '!',
+ off2, str->fname );
+ }
+ #endif
+
+ switch (str->mode)
+ {
+ case STR_M_PAL:
+ fprintf(f,"%*sPAL version of main.dol found.\n", indent,"" );
+ break;
+
+ case STR_M_USA:
+ fprintf(f,"%*sNTSC/USA version of main.dol found.\n", indent,"" );
+ break;
+
+ case STR_M_JAP:
+ fprintf(f,"%*sNTSC/JAP version of main.dol found.\n", indent,"" );
+ break;
+
+ case STR_M_KOR:
+ fprintf(f,"%*sNTSC/KOREA version of main.dol found.\n", indent,"" );
+ break;
+
+ default:
+ fprintf(f,"%*sUnknown main.dol found.\n", indent,"" );
+ return;
+ }
+
+ if (str->dol_cleaned)
+ fprintf(f,"%*s- DOL cleaned before analysis.\n", indent,"");
+
+ if ( str->patched_by[0] )
+ fprintf(f,"%*s- Patched by: %s\n", indent,"", str->patched_by );
+
+
+ if ( str->dol_status & DOL_S_HTTPS )
+ {
+ fprintf(f,"%*s- Known 'https://' URLs modified.\n", indent,"");
+ }
+ else if (str->dol_info)
+ {
+ fprintf(f,"%*s- %s\n", indent,"",str->dol_info);
+ return;
+ }
+
+ const dol_header_t *dh = (dol_header_t*)str->data;
+ if ( str->dol_status & DOL_S_VBI )
+ {
+ if (str->vbi_address)
+ fprintf(f,"%*s- VBI hook to address 0x%08x%s found.\n",
+ indent,"", str->vbi_address,
+ GetDolSectionAddrInfo(dh,str->vbi_address,4) );
+ else
+ fprintf(f,"%*s- VBI hook found.\n", indent,"");
+ }
+
+ #ifdef HAVE_WIIMM_EXT
+ if ( str->dol_status & DOL_S_SDKVER )
+ fprintf(f,"%*s- Value of 'sdkver' changed from '001000' to '%s'.\n",
+ indent,"", str->sdk_version );
+ #endif
+
+ if ( str->dol_status & DOL_S_2HOST )
+ fprintf(f,"%*s- Suppress second 'Host:' header at login.\n",
+ indent,"" );
+
+ const dol_sect_info_t *orig = GetInfoDOL(str->mode);
+ if (orig)
+ {
+ uint sect;
+ for ( sect = 0; sect < DOL_NN_SECTIONS; sect++, orig++ )
+ {
+ dol_sect_info_t info;
+ GetSectionDOL(str,&info,sect,true);
+ noPRINT("%-4s: %d,%d : %8x %8x %8x\n",
+ info.name, info.sect_valid, info.hash_valid,
+ info.off, info.addr, info.size );
+
+ if (info.sect_valid)
+ {
+ bool find_section = info.hash_valid;
+
+ if (!orig->sect_valid)
+ fprintf(f,"%*s- Additional section %s.\n", indent,"", orig->name );
+ else if ( orig->size != info.size )
+ fprintf(f,"%*s- Size of section %s changed.\n", indent,"", orig->name );
+ else if ( orig->hash_valid && memcmp(orig->hash,info.hash,sizeof(info.hash)))
+ fprintf(f,"%*s- Section %s modified.\n", indent,"", orig->name );
+ else if ( orig->addr != info.addr )
+ {
+ if ( sect == DOL_IDX_ENTRY )
+ fprintf(f,"%*s- Entry point moved from %08x%s to %08x%s.\n",
+ indent,"",
+ orig->addr, GetDolSectionAddrInfo(dh,orig->addr,4),
+ info.addr, GetDolSectionAddrInfo(dh,info.addr,4) );
+ else
+ fprintf(f,"%*s- Section %s moved.\n", indent,"", orig->name );
+ }
+ else
+ find_section = false;
+
+ if (find_section)
+ {
+ const DolSectionMap_t *dsm = FindSectionDOL(info.size,info.hash);
+ if (dsm)
+ printf("%*s- %s\n",indent+3,"",dsm->info);
+ else if (info.data)
+ {
+ bool found = false;
+ if ( info.addr == 0x80001800 && info.size > 0x800 )
+ {
+ if ( be64(info.data+0x11c) == 0x3c6000d06063c0deull )
+ {
+ found = true;
+ printf("%*s- Gecko Code Handler (Only)\n",indent+3,"");
+ }
+ else if ( be64(info.data+0x770) == 0x3c6000d06063c0deull )
+ {
+ ccp attrib = "full";
+ const u64 val = be64(info.data+0x56C);
+ if ( val == 0x92f8680090786810ull )
+ attrib = "Slot A";
+ else if ( val == 0x92f8681490786824ull )
+ attrib = "Slot B";
+ printf("%*s- Gecko Code Handler (%s).\n",indent+3,"",attrib);
+ found = true;
+ }
+ }
+
+ if ( !found && info.addr == 0x802c0000 )
+ printf("%*s- Maybe code loader by wstrt.\n",indent+3,"");
+ }
+ }
+ }
+ else if (orig->sect_valid)
+ fprintf(f,"%*s- Section %s removed.\n", indent,"", orig->name );
+ }
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// versus points ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+uint GetVersusPointsOffset ( str_mode_t mode )
+{
+ switch (mode)
+ {
+ case STR_M_PAL: return 0x37fd50;
+ case STR_M_USA: return 0x37fc60;
+ case STR_M_JAP: return 0x37fa20;
+ case STR_M_KOR: return 0x380130;
+ default: return 0;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// [[VersusPointsInfo]]
+
+const VersusPointsInfo_t VersusPointsInfo[STR_M__N] =
+{
+ { STR_M_UNKNOWN, '0', 0, 0 },
+ { STR_M_PAL, 'P', 0x37fd50, 0x06890030 },
+ { STR_M_USA, 'E', 0x37fc60, 0x0688bbc0 },
+ { STR_M_JAP, 'J', 0x37fa20, 0x0688f680 },
+ { STR_M_KOR, 'K', 0x380130, 0x0687e430 },
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+str_mode_t opt_cheat_region = STR_M_UNKNOWN;
+
+int ScanOptCheatRegion ( ccp arg )
+{
+ if ( !arg || !*arg )
+ {
+ opt_cheat_region = STR_M_UNKNOWN;
+ return 0;
+ }
+
+ static const KeywordTab_t tab[] =
+ {
+ { STR_M_PAL, "EUROPE", "PAL", 0 },
+ { STR_M_PAL, "AUSTRALIA", "OCEANIA", 0 },
+ { STR_M_USA, "AMERICA", "NTSC", 0 },
+ { STR_M_JAP, "JAPAN", 0, 0 },
+ { STR_M_KOR, "KOREA", 0, 0 },
+ { 0,0,0,0 }
+ };
+
+ const KeywordTab_t * cmd = ScanKeyword(0,arg,tab);
+ if (cmd)
+ {
+ opt_cheat_region = cmd->id;
+ return 0;
+ }
+
+ ERROR0(ERR_SYNTAX,"Invalid cheat region (option --cheat): '%s'\n",arg);
+ return 1;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// analyze helpers ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void AnalyzeTracks
+(
+ staticr_t * str, // valid pointer
+ u8 * data, // data to analyze and modify
+ const u32 * offset_tab, // null terminated table with offsets
+ const u32 * ref_tab, // reference table
+ uint base_index, // base index of first track
+ uint n, // number of values in 'ref_tab'
+ int base_status // base status bit
+)
+{
+ DASSERT(str);
+ DASSERT(data);
+ DASSERT(offset_tab);
+ DASSERT(ref_tab);
+ DASSERT(n>0);
+ DASSERT(n<=32);
+
+ const uint cmp_size = n * sizeof(u32);
+
+ uint i;
+ u32 be_ref[32];
+ for ( i = 0; i < n; i++ )
+ write_be32( be_ref + i, base_index + ref_tab[i] );
+ TRACE_HEXDUMP16(0,0,be_ref,cmp_size); //putchar('\n');
+
+ uint ok_count = 0; // number of unmodified records
+ uint mod_count = 0; // number of modified records
+
+ u8 *first = 0;
+ const u32 *off_ptr;
+ for ( off_ptr = offset_tab ; *off_ptr; off_ptr++ )
+ {
+ DASSERT( *off_ptr < str->data_size );
+ u8 * d = data + *off_ptr;
+ if (!memcmp(d,be_ref,cmp_size))
+ {
+ ok_count++;
+ continue;
+ }
+ mod_count++;
+
+ if (!first)
+ first = d;
+ else if (memcmp(first,d,cmp_size))
+ str->str_status |= base_status << STR_S_SHIFT_ORDER_DIFF;
+
+ uint fail_buf[32];
+ memset(fail_buf,0,sizeof(fail_buf));
+ for ( i = 0; i < n; i++ )
+ {
+ const u32 idx = be32(d+4*i) - base_index;
+ if ( idx >= n )
+ {
+ str->str_status |= base_status << STR_S_SHIFT_ORDER_DIFF;
+ break;
+ }
+ if ( fail_buf[idx] )
+ {
+ str->str_status |= base_status << STR_S_SHIFT_ORDER_MULTI;
+ break;
+ }
+ fail_buf[idx]++;
+ }
+ if ( i == n )
+ str->str_status |= base_status << STR_S_SHIFT_ORDER_ALT;
+
+ TRACE_HEXDUMP16(0,*off_ptr,d,cmp_size); //putchar('\n');
+ }
+
+ if ( mod_count && ok_count )
+ str->str_status |= base_status << STR_S_SHIFT_ORDER_DIFF;
+
+
+ //--- normalize
+
+ for ( off_ptr = offset_tab ; *off_ptr; off_ptr++ )
+ {
+ DASSERT( *off_ptr < str->data_size );
+ u8 * d = data + *off_ptr;
+ memcpy(d,be_ref,cmp_size);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void AnalyzeRegion
+(
+ staticr_t * str, // valid pointer
+ u8 * data // data to analyze and modify
+)
+{
+ DASSERT(str);
+ DASSERT(data);
+
+ bool is_battle;
+ for ( is_battle = false; is_battle <= true; is_battle++ )
+ {
+ region_info_t ri;
+ SetupRegionInfo(&ri,str,is_battle);
+
+ uint ok_stat = ri.flag_known;
+
+ const uint offset_idx = str->mode - STR_ZBI_FIRST;
+ memset(ri.region,0,MAX_REGION_PATCH*sizeof(*ri.region));
+ uint idx, patch_count = 0, unknown_count = 0;
+
+ for ( idx = 0; idx < ri.max_patch; idx++ )
+ {
+ ri.region[idx] = -1;
+ const region_patch_t *rp = ri.patch_tab + idx;
+ if (!rp->orig)
+ break;
+
+ u8 *focus = data + rp->offset[offset_idx];
+ u32 val = be32(focus);
+ if ( val != rp->orig )
+ {
+ patch_count++;
+ if ( ( val & 0xffff0000 ) == rp->patch )
+ {
+ ri.region[idx] = (u16)val;
+ if ( ri.region[idx] != ri.region[0] )
+ ok_stat = ri.flag_differ;
+ }
+ else
+ unknown_count++;
+ write_be32(focus,rp->orig);
+ }
+ }
+
+ if (unknown_count)
+ str->str_status |= ri.flag_unknown;
+ else if (patch_count)
+ str->str_status |= ok_stat;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void AnalyzeAllRanks
+(
+ staticr_t * str, // valid pointer
+ u8 * data, // data to analyze and modify
+ const u32 * offset_tab // null terminated table with offsets
+)
+{
+ DASSERT(str);
+ DASSERT(data);
+ DASSERT(offset_tab);
+
+ uint i, patch_count = 0, known_count = 0;
+ for ( i = 0; offset_tab[i]; i++ )
+ {
+ u8 *d = data + offset_tab[i];
+ if (memcmp(d,all_ranks_orig,all_ranks_size))
+ {
+ patch_count++;
+ if ( memcmp(d,all_ranks_patch1,all_ranks_size)
+ || memcmp(d,all_ranks_patch2,all_ranks_size))
+ {
+ known_count++;
+ }
+ memcpy(d,all_ranks_orig,all_ranks_size);
+ }
+ }
+
+ if ( i == known_count )
+ {
+ str->str_status |= STR_S_ALL_RANKS_KNOWN;
+ }
+ else if ( patch_count )
+ str->str_status |= STR_S_ALL_RANKS_UNKNOWN;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void AnalyzeVsBt
+(
+ staticr_t * str, // valid pointer
+ u8 * data, // data to analyze and modify
+ const u32 * offset_tab, // null terminated table with offsets
+ ccp name, // 'vs' or 'bt'
+ uint flag // flag to set
+)
+{
+ DASSERT(str);
+ DASSERT(data);
+ DASSERT(offset_tab);
+
+ uint i;
+ for ( i = 0; offset_tab[i]; i++ )
+ {
+ u8 *d = data + offset_tab[i];
+ if (strncmp((ccp)d,name,3))
+ {
+ str->str_status |= flag;
+ memcpy(d,name,2);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void AnalyzeVersusPoints
+(
+ staticr_t * str, // valid pointer
+ u8 * data // data to analyze and modify
+)
+{
+ DASSERT(str);
+ DASSERT(data);
+
+ const u32 offset = GetVersusPointsOffset(str->mode);
+ if (offset)
+ {
+ data += offset;
+ if (memcmp(data,MkwPointsTab_NINTENDO,12*12))
+ {
+ str->str_status |= STR_S_VERSUS_POINTS;
+ memcpy(data,MkwPointsTab_NINTENDO,12*12);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void AnalyzeCannon
+(
+ staticr_t * str, // valid pointer
+ u8 * data // data to analyze and modify
+)
+{
+ DASSERT(str);
+ DASSERT(data);
+
+ const u32 off = GetCannonOffset(str);
+ if ( off && !(str->str_status&STR_S_CANNON) )
+ {
+ memcpy(str->cannon_data,data+off,sizeof(str->cannon_data));
+ if (memcmp(data+off,CannonDataLEX+4,sizeof(CannonDataLEX)-4))
+ {
+ str->str_status |= STR_S_CANNON;
+ memcpy(data+off,CannonDataLEX+4,sizeof(CannonDataLEX)-4);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void AnalyzeMenO
+(
+ staticr_t * str, // valid pointer
+ u8 * data // data to analyze and modify
+)
+{
+ DASSERT(str);
+ DASSERT(data);
+
+ const u32 *optr = GetMenuOffsetTab(str->mode);
+ if (optr)
+ {
+ uint count = 0, total = 0;
+ const int *idx;
+ for ( idx = meno_patch_idx; *idx >= 0; idx++ )
+ {
+ total++;
+ u8 *d = data + optr[*idx] + 13; // 13 = 'O' offset
+ if ( *d == 'O' )
+ {
+ *d = 'u';
+ count++;
+ }
+ }
+
+ if ( total == count )
+ str->str_status |= STR_S_MENO;
+ }
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// analyze staticr ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static void AnalyzePAL
+(
+ staticr_t * str // valid pointer
+)
+{
+ DASSERT(str);
+
+
+ //--- first hash test
+
+ static const u8 good_hash[20] =
+ {
+ 0x88,0x7b,0xcc,0x07,0x67, 0x81,0xf5,0xb0,0x05,0xcc,
+ 0x31,0x7a,0x6e,0x3c,0xc8, 0xfd,0x5f,0x91,0x13,0x00
+ };
+
+ sha1_hash_t hash;
+ SHA1(str->data,str->data_size,hash);
+ if (!memcmp(hash,good_hash,sizeof(hash)))
+ {
+ if (!( str->str_status & STR_S_HTTPS ))
+ str->str_status |= STR_S_ORIG;
+ #ifndef TEST
+ return;
+ #endif
+ }
+
+
+ //--- create a copy
+
+ u8 * data = MALLOC(str->data_size);
+ memcpy(data,str->data,str->data_size);
+
+
+ //--- ctgp 4.4 tests
+
+ static const u32 ctgp44[] =
+ {
+ // addr orig value ctgp value
+ //-------------------------------
+ 0x3d22b8, 0x00000db4, 0xff750030,
+ 0x3d22c8, 0x00000db4, 0xff750030,
+ 0x40df78, 0x00012660, 0xff7742e0,
+ 0x40df88, 0x00012660, 0xff7742e0,
+ 0,0,0
+ };
+
+ const u32 * ptr;
+ uint orig_failed = 0, ctgp_failed = 0;
+ for ( ptr = ctgp44; *ptr; ptr += 3 )
+ {
+ DASSERT( *ptr + 4 <= str->data_size );
+ const u32 val = be32( str->data + *ptr );
+ if ( val != ptr[1] ) orig_failed++;
+ if ( val != ptr[2] ) ctgp_failed++;
+ write_be32( data + *ptr, ptr[1] );
+ }
+
+ if (orig_failed)
+ {
+ str->str_status |= STR_S_CTGP44_LIKE;
+ if (!ctgp_failed)
+ str->str_status |= STR_S_CTGP44;
+ }
+
+
+ //--- analyse tracks and arenas
+
+ AnalyzeTracks( str, data, pal_track_offset, track_pos_default,
+ MKW_TRACK_BEG, MKW_N_TRACKS, STR_S_TRACK_ORDER );
+ AnalyzeTracks( str, data, pal_arena_offset, arena_pos_default,
+ MKW_ARENA_BEG, MKW_N_ARENAS, STR_S_ARENA_ORDER );
+
+
+ //--- analyse misc
+
+ AnalyzeRegion(str,data);
+ AnalyzeAllRanks(str,data,pal_all_ranks_offset);
+ AnalyzeVsBt(str,data,pal_vs_offset,"vs",STR_S_VS);
+ AnalyzeVsBt(str,data,pal_bt_offset,"bt",STR_S_BT);
+ AnalyzeVersusPoints(str,data);
+ AnalyzeCannon(str,data);
+ AnalyzeMenO(str,data);
+
+
+ //--- last hash test
+
+ SHA1(data,str->data_size,hash);
+ if (memcmp(hash,good_hash,sizeof(hash)))
+ str->str_status |= STR_S_UNKNOWN_PATCH;
+
+
+ //--- clean
+
+ FREE(data);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void AnalyzeUSA
+(
+ staticr_t * str // valid pointer
+)
+{
+ DASSERT(str);
+
+ //--- first hash test
+
+ static const u8 good_hash[20] =
+ {
+ 0x07,0x2b,0xa4,0x43,0x82, 0xfd,0xca,0x9b,0xf5,0xc0,
+ 0xec,0x9d,0x1e,0x9b,0x92, 0x2c,0xaf,0xd9,0x92,0x60
+ };
+
+ sha1_hash_t hash;
+ SHA1(str->data,str->data_size,hash);
+ if (!memcmp(hash,good_hash,sizeof(hash)))
+ {
+ str->str_status |= STR_S_ORIG;
+ #ifndef TEST
+ return;
+ #endif
+ }
+
+
+ //--- create a copy
+
+ u8 * data = MALLOC(str->data_size);
+ memcpy(data,str->data,str->data_size);
+
+
+ //--- ctgp 4.4 tests
+
+ static const u32 ctgp44[] =
+ {
+ // addr orig value ctgp value
+ //-------------------------------
+ 0x3d1c38, 0x00000c14, 0xff7546e0,
+ 0x3d1c48, 0x00000c14, 0xff7546e0,
+ 0x40cda0, 0x00012098, 0xff778720,
+ 0x40cdb0, 0x00012098, 0xff778720,
+ 0,0,0
+ };
+
+ const u32 * ptr;
+ uint orig_failed = 0, ctgp_failed = 0;
+ for ( ptr = ctgp44; *ptr; ptr += 3 )
+ {
+ DASSERT( *ptr + 4 <= str->data_size );
+ const u32 val = be32( str->data + *ptr );
+ if ( val != ptr[1] ) orig_failed++;
+ if ( val != ptr[2] ) ctgp_failed++;
+ write_be32( data + *ptr, ptr[1] );
+ }
+
+ if (orig_failed)
+ {
+ str->str_status |= STR_S_CTGP44_LIKE;
+ if (!ctgp_failed)
+ str->str_status |= STR_S_CTGP44;
+ }
+
+
+ //--- anlyze tracks and arenas
+
+ AnalyzeTracks( str, data, usa_track_offset, track_pos_default,
+ MKW_TRACK_BEG, MKW_N_TRACKS, STR_S_TRACK_ORDER );
+ AnalyzeTracks( str, data, usa_arena_offset, arena_pos_default,
+ MKW_ARENA_BEG, MKW_N_ARENAS, STR_S_ARENA_ORDER );
+
+
+ //--- analyse misc
+
+ AnalyzeRegion(str,data);
+ AnalyzeAllRanks(str,data,usa_all_ranks_offset);
+ AnalyzeVsBt(str,data,usa_vs_offset,"vs",STR_S_VS);
+ AnalyzeVsBt(str,data,usa_bt_offset,"bt",STR_S_BT);
+ AnalyzeVersusPoints(str,data);
+ AnalyzeMenO(str,data);
+
+
+ //--- last hash test
+
+ SHA1(data,str->data_size,hash);
+ if (memcmp(hash,good_hash,sizeof(hash)))
+ str->str_status |= STR_S_UNKNOWN_PATCH;
+
+
+ //--- clean
+
+ FREE(data);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void AnalyzeJAP
+(
+ staticr_t * str // valid pointer
+)
+{
+ DASSERT(str);
+
+ //--- first hash test
+
+ static const u8 good_hash[20] =
+ {
+ 0x20,0xb4,0x16,0x1d,0x41, 0x5e,0x40,0x92,0xa6,0x29,
+ 0x9b,0xb2,0x85,0xfa,0xa6, 0x22,0x83,0x5b,0x2b,0x16
+ };
+
+ sha1_hash_t hash;
+ SHA1(str->data,str->data_size,hash);
+ if (!memcmp(hash,good_hash,sizeof(hash)))
+ {
+ str->str_status |= STR_S_ORIG;
+ #ifndef TEST
+ return;
+ #endif
+ }
+
+
+ //--- create a copy
+
+ u8 * data = MALLOC(str->data_size);
+ memcpy(data,str->data,str->data_size);
+
+
+ //--- anlyze tracks and arenas
+
+ AnalyzeTracks( str, data, jap_track_offset, track_pos_default,
+ MKW_TRACK_BEG, MKW_N_TRACKS, STR_S_TRACK_ORDER );
+ AnalyzeTracks( str, data, jap_arena_offset, arena_pos_default,
+ MKW_ARENA_BEG, MKW_N_ARENAS, STR_S_ARENA_ORDER );
+
+
+ //--- analyse misc
+
+ AnalyzeRegion(str,data);
+ AnalyzeAllRanks(str,data,jap_all_ranks_offset);
+ AnalyzeVsBt(str,data,jap_vs_offset,"vs",STR_S_VS);
+ AnalyzeVsBt(str,data,jap_bt_offset,"bt",STR_S_BT);
+ AnalyzeVersusPoints(str,data);
+ AnalyzeMenO(str,data);
+
+
+ //--- last hash test
+
+ SHA1(data,str->data_size,hash);
+ if (memcmp(hash,good_hash,sizeof(hash)))
+ str->str_status |= STR_S_UNKNOWN_PATCH;
+
+
+ //--- clean
+
+ FREE(data);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void AnalyzeKOR
+(
+ staticr_t * str // valid pointer
+)
+{
+ DASSERT(str);
+
+ //--- first hash test
+
+ static const u8 good_hash[20] =
+ {
+ 0xe0,0x00,0x62,0x50,0xb1, 0xe5,0xc1,0xbd,0xee,0x8b,
+ 0xdb,0x0e,0x78,0x9e,0xa2, 0x00,0xb3,0xa8,0x46,0xe4
+ };
+
+ sha1_hash_t hash;
+ SHA1(str->data,str->data_size,hash);
+ if (!memcmp(hash,good_hash,sizeof(hash)))
+ {
+ str->str_status |= STR_S_ORIG;
+ #ifndef TEST
+ return;
+ #endif
+ }
+
+
+ //--- create a copy
+
+ u8 * data = MALLOC(str->data_size);
+ memcpy(data,str->data,str->data_size);
+
+
+ //--- anlyze tracks and arenas
+
+ AnalyzeTracks( str, data, kor_track_offset, track_pos_default,
+ MKW_TRACK_BEG, MKW_N_TRACKS, STR_S_TRACK_ORDER );
+ AnalyzeTracks( str, data, kor_arena_offset, arena_pos_default,
+ MKW_ARENA_BEG, MKW_N_ARENAS, STR_S_ARENA_ORDER );
+
+
+ //--- analyse misc
+
+ AnalyzeRegion(str,data);
+ AnalyzeAllRanks(str,data,kor_all_ranks_offset);
+ AnalyzeVsBt(str,data,kor_vs_offset,"vs",STR_S_VS);
+ AnalyzeVsBt(str,data,kor_bt_offset,"bt",STR_S_BT);
+ AnalyzeVersusPoints(str,data);
+ AnalyzeMenO(str,data);
+
+
+ //--- last hash test
+
+ SHA1(data,str->data_size,hash);
+ if (memcmp(hash,good_hash,sizeof(hash)))
+ str->str_status |= STR_S_UNKNOWN_PATCH;
+
+
+ //--- clean
+
+ FREE(data);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void AnalyzeSTR
+(
+ staticr_t * str // valid pointer
+)
+{
+ DASSERT(str);
+
+ if (str->is_dol)
+ {
+ if (!(str->dol_status&DOL_S_ANALYZED))
+ AnalyzeDOL(str,true);
+ return;
+ }
+
+ if (!CheckSizeSTR(str))
+ return;
+
+ str->str_status = STR_S_ANALYZED;
+ if (PatchHTTPS_HTTP(str,HTTPS_RESTORE))
+ str->str_status |= STR_S_HTTPS;
+
+ switch (str->mode)
+ {
+ case STR_M_PAL:
+ AnalyzePAL(str);
+ break;
+
+ case STR_M_USA:
+ AnalyzeUSA(str);
+ break;
+
+ case STR_M_JAP:
+ AnalyzeJAP(str);
+ break;
+
+ case STR_M_KOR:
+ AnalyzeKOR(str);
+ break;
+
+ default:
+ break;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void PrintStatusRegion
+(
+ FILE * f, // destination file
+ int indent, // indention
+ staticr_t * str, // valid pointer
+ bool is_battle, // true: is battle
+ int long_mode // >0: print more track details if changed
+)
+{
+ DASSERT(f);
+ DASSERT(str);
+
+ region_info_t ri;
+ SetupRegionInfo(&ri,str,is_battle);
+
+ if ( long_mode > 1
+ || long_mode > 0 && str->str_status & (ri.flag_unknown|ri.flag_differ) )
+ {
+ fprintf(f,"%*s- %c%s region settings:\n",
+ indent,"", toupper((int)ri.name[0]), ri.name+1 );
+
+ const region_patch_t *rp;
+ for ( rp = ri.patch_tab; rp->orig; rp++ )
+ {
+ const uint offset = rp->offset[str->mode - STR_ZBI_FIRST];
+ const u8 *d = str->data + offset;
+ fprintf(f,"%*s%9x: %02x %02x %02x %02x : ",
+ indent,"", offset, d[0], d[1], d[2], d[3] );
+ const u32 val = be32(d);
+ if ( val == rp->orig )
+ fprintf(f,"%-23s : %s\n","not patched",rp->info);
+ else if ( (val & 0xffff0000) == rp->patch )
+ fprintf(f,"region %4x/hex = %5u : %s\n",
+ val&0xffff, val&0xffff, rp->info );
+ else
+ fprintf(f,"%-23s : %s\n","unknown patch",rp->info);
+ }
+ }
+ else if ( str->str_status & ri.flag_differ )
+ {
+ fprintf(f,"%*s- Known %s region patch found, regions = ", indent,"", ri.name );
+ ccp sep = "";
+ uint i;
+ for ( i = 0; i < ri.max_patch; i++ )
+ {
+ if ( ri.region[i] < 0 )
+ fprintf(f,"%sunpatched",sep);
+ else
+ fprintf(f,"%s%#x=%u",sep,ri.region[i],ri.region[i]);
+ sep = ", ";
+ }
+ fputc('\n',f);
+ }
+ else if ( str->str_status & ri.flag_known )
+ fprintf(f,"%*s- Known %s region patch found, region = 0x%02x = %u\n",
+ indent,"", ri.name, ri.region[0], ri.region[0] );
+ else if ( str->str_status & ri.flag_unknown )
+ fprintf(f,"%*s- Unknown %s region patch found.\n", indent,"", ri.name );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void PrintStatusSTR
+(
+ FILE * f, // destination file
+ int indent, // indention
+ staticr_t * str, // valid pointer
+ int long_mode // >0: print more track details if changed
+)
+{
+ DASSERT(f);
+ DASSERT(str);
+
+ if (str->is_dol)
+ {
+ PrintStatusDOL(f,indent,str,long_mode);
+ return;
+ }
+
+ indent = NormalizeIndent(indent);
+ if (!(str->str_status&STR_S_ANALYZED))
+ AnalyzeSTR(str);
+
+ switch (str->mode)
+ {
+ case STR_M_PAL:
+ fprintf(f,"%*sPAL version of StaticR found.\n", indent,"" );
+ break;
+
+ case STR_M_USA:
+ fprintf(f,"%*sNTSC/USA version of StaticR found.\n", indent,"" );
+ break;
+
+ case STR_M_JAP:
+ fprintf(f,"%*sNTSC/JAP version of StaticR found.\n", indent,"" );
+ break;
+
+ case STR_M_KOR:
+ fprintf(f,"%*sNTSC/KOREA version of StaticR found.\n", indent,"" );
+ break;
+
+ default:
+ fprintf(f,"%*sNo StaticR found.\n", indent,"" );
+ break;
+ }
+
+ const str_status_t st = str->str_status;
+
+ if ( st & STR_S_WRONG_SIZE )
+ fprintf(f,"%*s- Wrong file size.\n", indent,"" );
+
+ if ( st & STR_S_ORIG )
+ fprintf(f,"%*s- Original file.\n", indent,"" );
+
+ if ( st & STR_S_HTTPS )
+ fprintf(f,"%*s- Known 'https://' URLs modified.\n", indent,"" );
+
+ if ( st & STR_S_TRACK_ORDER_ALT )
+ fprintf(f,"%*s- Valid alternative track order detected.\n", indent,"" );
+ if ( st & STR_S_TRACK_ORDER_MULTI )
+ fprintf(f,"%*s- Multiple usage of singe track detected.\n", indent,"" );
+ if ( st & STR_S_TRACK_ORDER_BAD )
+ fprintf(f,"%*s- Bad track index detected.\n", indent,"" );
+ if ( st & STR_S_TRACK_ORDER_DIFF )
+ fprintf(f,"%*s- Different track orders detected.\n", indent,"" );
+
+ if ( long_mode > 1 || long_mode > 0 && st & STR_S_TRACK_ORDER__FAIL )
+ {
+ fprintf(f,"%*s- Track assigning tables:\n", indent,"" );
+ const u32 *tab = GetTrackOffsetTab(str);
+ DASSERT(tab);
+ for(;;)
+ {
+ u32 offset = *tab++;
+ if (!offset)
+ break;
+ fprintf(f,"%*s%9x:", indent,"", offset );
+ const u32 * d = (u32*)( str->data + offset );
+ uint i;
+ for ( i = 0; i < MKW_N_TRACKS; i++ )
+ {
+ if (!(i%4))
+ fputc(' ',f);
+ fprintf(f,"%u%c", be32(d++), i == MKW_N_TRACKS-1 ? '\n' : ',' );
+ }
+ }
+ }
+
+ if ( st & STR_S_ARENA_ORDER_ALT )
+ fprintf(f,"%*s- Valid alternative arena order detected.\n", indent,"" );
+ if ( st & STR_S_ARENA_ORDER_MULTI )
+ fprintf(f,"%*s- Multiple usage of singe arena detected.\n", indent,"" );
+ if ( st & STR_S_ARENA_ORDER_BAD )
+ fprintf(f,"%*s- Bad arena index detected.\n", indent,"" );
+ if ( st & STR_S_ARENA_ORDER_DIFF )
+ fprintf(f,"%*s- Different arena orders detected.\n", indent,"" );
+
+ if ( long_mode > 1 || long_mode > 0 && st & STR_S_ARENA_ORDER__FAIL )
+ {
+ fprintf(f,"%*s- Arena assigning tables:\n", indent,"" );
+ const u32 *tab = GetArenaOffsetTab(str);
+ DASSERT(tab);
+ for(;;)
+ {
+ u32 offset = *tab++;
+ if (!offset)
+ break;
+ fprintf(f,"%*s%9x:", indent,"", offset );
+ const u32 * d = (u32*)( str->data + offset );
+ uint i;
+ for ( i = 0; i < MKW_N_ARENAS; i++ )
+ {
+ if (!(i%5))
+ fputc(' ',f);
+ fprintf(f,"%u%c", be32(d++), i == MKW_N_ARENAS-1 ? '\n' : ',' );
+ }
+ }
+ }
+
+ if ( st & STR_S_CTGP44 )
+ fprintf(f,"%*s- CTGP 4.4 modification found.\n", indent,"" );
+ else if ( st & STR_S_CTGP44_LIKE )
+ fprintf(f,"%*s- CTGP 4.4 like modification found.\n", indent,"" );
+
+ PrintStatusRegion( f, indent, str, false, long_mode );
+ PrintStatusRegion( f, indent, str, true, long_mode );
+
+ if ( st & STR_S_ALL_RANKS_KNOWN )
+ fprintf(f,"%*s- Known 'all ranks' patch found.\n",
+ indent,"" );
+ else if ( st & STR_S_ALL_RANKS_UNKNOWN )
+ fprintf(f,"%*s- Unknown 'all ranks' patch found.\n", indent,"" );
+
+ if ( st & STR_S_VS )
+ {
+ fprintf(f,"%*s- Versus identification ('vs') patched:",indent,"");
+ const u32 *ptr;
+ for ( ptr = GetVsBtOffsetTab(str,false); *ptr; ptr++ )
+ fprintf(f," %.2s",str->data+*ptr);
+ fputc('\n',f);
+ }
+
+ if ( st & STR_S_BT )
+ {
+ fprintf(f,"%*s- Battle identification ('bt') patched:",indent,"");
+ const u32 *ptr;
+ for ( ptr = GetVsBtOffsetTab(str,true); *ptr; ptr++ )
+ fprintf(f," %.2s",str->data+*ptr);
+ fputc('\n',f);
+ }
+
+ if ( st & STR_S_VERSUS_POINTS )
+ {
+ const u32 offset = GetVersusPointsOffset(str->mode);
+ fprintf(f,"%*s- Versus points modified to '%s'.\n",
+ indent,"", GetMkwPointInfo(str->data+offset)->info );
+ }
+
+ if ( st & STR_S_CANNON )
+ {
+ fprintf(f,"%*s- Cannon parameters modified.\n", indent,"" );
+ const u8 *orig = CannonDataLEX+4;
+ const u8 *data = str->cannon_data;
+ int idx;
+ for ( idx = 0; orig < CannonDataLEX + sizeof(CannonDataLEX);
+ idx++, orig += CANNON_SIZE, data += CANNON_SIZE )
+ {
+ if (memcmp(orig,data,CANNON_SIZE))
+ {
+ printf("%*s > Cannon %u orig: %12.4f %12.4f %12.4f %12.4f\n",
+ indent,"",
+ idx, bef4(orig), bef4(orig+4), bef4(orig+8), bef4(orig+12) );
+ printf("%*s Cannon %u data: %12.4f %12.4f %12.4f %12.4f\n",
+ indent,"",
+ idx, bef4(data), bef4(data+4), bef4(data+8), bef4(data+12) );
+ }
+ }
+ }
+
+ if ( st & STR_S_MENO )
+ fprintf(f,"%*s- Known 'MenO' patches found.\n", indent,"" );
+
+ if ( st & STR_S_UNKNOWN_PATCH )
+ fprintf(f,"%*s- Unknown patches found.\n", indent,"" );
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// PatchSTR() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static uint PatchTracks
+(
+ staticr_t * str, // valid pointer
+ const u32 * offset_tab, // null terminated table with offsets
+ const u32 * track_tab, // table with track indexes
+ uint base_index, // base index of first track
+ uint n // number of values in 'ref_tab'
+)
+{
+ DASSERT(str);
+ DASSERT(offset_tab);
+ DASSERT(track_tab);
+ DASSERT(n>0);
+ DASSERT(n<=32);
+
+ uint count = 0;
+ const uint copy_size = n * sizeof(u32);
+
+ uint i;
+ u32 be_data[32];
+ for ( i = 0; i < n; i++ )
+ write_be32( be_data + i, base_index + track_tab[i] );
+ TRACE_HEXDUMP16(0,0,be_data,copy_size);
+
+ for ( ; *offset_tab; offset_tab++ )
+ {
+ DASSERT( *offset_tab < str->data_size );
+ u8 * d = str->data + *offset_tab;
+ if (memcmp(d,be_data,copy_size))
+ {
+ count++;
+ memcpy(d,be_data,copy_size);
+ }
+ }
+
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint PatchVsBt
+(
+ staticr_t *str, // valid pointer
+ bool is_bt, // false: VS, true: BT
+ enum VsBtMode mode, // mode
+ ccp patch // string to set
+)
+{
+ DASSERT(str);
+ DASSERT(str);
+
+ uint count = 0;
+ const u32 *tab = GetVsBtOffsetTab(str,is_bt);
+ switch (mode)
+ {
+ case VSBT_OFF:
+ break;
+
+ case VSBT_PATCH:
+ while (*tab)
+ {
+ u8 *d = str->data + *tab++;
+ if ( d[0] != patch[0] || d[1] != patch[1] )
+ {
+ *d++ = patch[0];
+ *d = patch[1];
+ count++;
+ }
+ }
+ break;
+
+ case VSBT_TEST:
+ {
+ char c0 = is_bt ? 'b' : 'v';
+ char c1 = '1';
+ while (*tab)
+ {
+ u8 *d = str->data + *tab++;
+ if ( d[0] != c0 || d[1] != c1 )
+ {
+ *d++ = c0;
+ *d = c1;
+ count++;
+ }
+ c1++;
+ }
+ }
+ break;
+ }
+
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#if 0 // region specific patch functions not needed yet
+///////////////////////////////////////////////////////////////////////////////
+
+static uint PatchPAL
+(
+ // returns: number of patches
+
+ staticr_t * str // valid pointer
+)
+{
+ DASSERT(str);
+ DASSERT(str->data);
+ DASSERT(str->data_size);
+ DASSERT( str->mode == STR_M_PAL );
+
+ uint count = 0;
+
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint PatchUSA
+(
+ // returns: number of patches
+
+ staticr_t * str // valid pointer
+)
+{
+ DASSERT(str);
+ DASSERT(str->data);
+ DASSERT(str->data_size);
+ DASSERT( str->mode == STR_M_USA );
+
+ uint count = 0;
+
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint PatchJAP
+(
+ // returns: number of patches
+
+ staticr_t * str // valid pointer
+)
+{
+ DASSERT(str);
+ DASSERT(str->data);
+ DASSERT(str->data_size);
+ DASSERT( str->mode == STR_M_JAP );
+
+ uint count = 0;
+
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint PatchKOR
+(
+ // returns: number of patches
+
+ staticr_t * str // valid pointer
+)
+{
+ DASSERT(str);
+ DASSERT(str->data);
+ DASSERT(str->data_size);
+ DASSERT( str->mode == STR_M_KOR );
+
+ uint count = 0;
+
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#endif // region specific patch functions not needed yet
+///////////////////////////////////////////////////////////////////////////////
+
+static uint PatchRegion
+(
+ staticr_t * str, // valid pointer
+ bool is_battle // true: is battle
+)
+{
+ DASSERT(str);
+ region_info_t ri;
+ SetupRegionInfo(&ri,str,is_battle);
+
+ uint count = 0;
+ u8 temp[20];
+
+ DASSERT(ri.opt_region);
+ if ( *ri.opt_region == SREG_RESTORE )
+ {
+ const region_patch_t *rp;
+ for ( rp = ri.patch_tab; rp->orig; rp++ )
+ {
+ if ( rp->patch_mode & RPAT_TEST && !*ri.opt_x )
+ continue;
+
+ write_be32(temp,rp->orig);
+ u8 *d = str->data + rp->offset[str->mode - STR_ZBI_FIRST];
+ if (memcmp(d,temp,4))
+ {
+ memcpy(d,temp,4);
+ count++;
+ }
+ }
+ }
+ else if ( *ri.opt_region >= 0 )
+ {
+ uint val = *ri.opt_region & 0xffff;
+ uint add = *ri.opt_t;
+
+ const region_patch_t *rp;
+ for ( rp = ri.patch_tab; rp->orig; rp++ )
+ {
+ if ( rp->patch_mode & RPAT_TEST && !*ri.opt_x )
+ continue;
+
+ write_be32( temp, rp->patch | ( rp->patch_mode & RPAT_FF ? val & 0xff : val ) );
+ val += add;
+ u8 *d = str->data + rp->offset[str->mode - STR_ZBI_FIRST];
+ if (memcmp(d,temp,4))
+ {
+ memcpy(d,temp,4);
+ count++;
+ }
+ }
+ }
+
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint PatchSTR
+(
+ // returns: number of patches
+
+ staticr_t * str // valid pointer
+)
+{
+ DASSERT(str);
+ if (!str->data_size)
+ return 0;
+ DASSERT(str->data);
+
+ if (str->is_dol)
+ return PatchDOL(str);
+
+ if (!CheckSizeSTR(str))
+ return 0;
+
+ uint count = 0;
+
+
+ //--- patch tracks and arenas
+
+ if (opt_tracks)
+ {
+ const u32 *tab = GetTrackOffsetTab(str);
+ if (tab)
+ count += PatchTracks( str, tab, track_pos, MKW_TRACK_BEG, MKW_N_TRACKS );
+ }
+
+ if (opt_arenas)
+ {
+ const u32 *tab = GetArenaOffsetTab(str);
+ if (tab)
+ count += PatchTracks( str, tab, arena_pos, MKW_ARENA_BEG, MKW_N_ARENAS );
+ }
+
+
+ //--- patch region
+
+ count += PatchRegion(str,false);
+ count += PatchRegion(str,true);
+
+
+ //--- patch all-ranks
+
+ if ( opt_all_ranks )
+ {
+ ccp p1, p2;
+ if ( opt_all_ranks < 0 )
+ p1 = p2 = all_ranks_orig;
+ else
+ {
+ p1 = all_ranks_patch1;
+ p2 = all_ranks_patch2;
+ }
+
+ const u32 *tab = GetAllRanksOffsetTab(str);
+ if (tab)
+ {
+ while (*tab)
+ {
+ u8 *d = str->data + *tab++;
+ if (memcmp(d,p1,all_ranks_size))
+ {
+ memcpy(d,p1,all_ranks_size);
+ count++;
+ ccp temp = p1;
+ p1 = p2;
+ p2 = temp;
+ }
+ }
+ }
+ }
+
+
+ //--- patch vs & bt
+
+ count += PatchVsBt(str,false,patch_vs_mode,patch_vs_str);
+ count += PatchVsBt(str,true, patch_bt_mode,patch_bt_str);
+
+
+ //--- patch versus points
+
+ if (opt_points_used)
+ {
+ const u32 offset = GetVersusPointsOffset(str->mode);
+ if (offset)
+ {
+ u8 *data = str->data + offset;
+ if (memcmp(data,MkwPointsTab,12*12))
+ {
+ count++;
+ memcpy(data,MkwPointsTab,12*12);
+ }
+ }
+ }
+
+
+ //--- patch cannon params
+
+ u32 offset = GetCannonOffset(str);
+ if (offset)
+ {
+ u8 *data = str->data + offset;
+ uint i;
+ for ( i = 0; i < CANNON_N; i++ )
+ {
+ const cannon_param_t *cp = cannon_param+i;
+ if (cp->valid)
+ {
+ uint j;
+ for ( j = 0; j < CANNON_N_PARAM; j++, data += sizeof(float) )
+ write_bef4(data,cp->param[j]);
+ }
+ else
+ data += CANNON_SIZE;
+ }
+ }
+
+
+ //-- patch 'Menu*' -> 'MenO*'
+
+ if ( opt_meno )
+ {
+ const u32 *optr = GetMenuOffsetTab(str->mode);
+ if (optr)
+ {
+ const int *idx;
+ for ( idx = meno_patch_idx; *idx >= 0; idx++ )
+ {
+ u8 *d = str->data + optr[*idx] + 13; // 13 = 'O' offset
+ if ( *d != 'O' )
+ {
+ *d = 'O';
+ count++;
+ }
+ }
+ }
+ }
+
+
+ //--- patch https
+
+ count += PatchHTTPS(str);
+
+
+ #if 0 // region specific patch functions not needed yet
+
+ //--- patch version specific
+
+ str->str_status = STR_S_ANALYZED;
+ switch (str->mode)
+ {
+ case STR_M_PAL:
+ count += PatchPAL(str);
+ break;
+
+ case STR_M_USA:
+ count += PatchUSA(str);
+ break;
+
+ case STR_M_JAP:
+ count += PatchJAP(str);
+ break;
+
+ case STR_M_KOR:
+ count += PatchKOR(str);
+ break;
+
+ default:
+ break;
+ }
+ #endif
+
+ return count + PatchByListWPF(str);
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// CleanDOL() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static bool CleanDol ( staticr_t * str ) // return TRUE if dol data modified.
+{
+ AnalyzeDOL(str,true);
+ if ( str->dol_status & DOL_S_ORIG )
+ {
+ str->dol_status = 0;
+ return 0;
+ }
+
+ uint sect_list;
+ dol_header_t *dol_head = (dol_header_t*)str->data;
+
+ uint new_size = RemoveDolSections(dol_head,0x7f83,&sect_list,logging>2?stdlog:0);
+ bool stat = new_size > 0;
+ noPRINT("SIZE: %x -> %x / sect=%x\n",str->data_size,new_size,sect_list);
+ if (stat)
+ str->data_size = new_size;
+
+
+ //--- fix entry point
+
+ const dol_sect_info_t *orig = GetInfoDOL(str->mode);
+ DASSERT(orig);
+ if ( orig[DOL_IDX_ENTRY].addr != ntohl(dol_head->entry_addr) )
+ {
+ stat = true;
+ dol_head->entry_addr = htonl(orig[DOL_IDX_ENTRY].addr);
+ }
+
+
+ //--- restore HTTPS links
+
+ PatchHTTPS_HTTP(str,HTTPS_RESTORE);
+
+
+ //--- terminate
+
+ str->dol_status = 0;
+ str->dol_cleaned = true;
+ return stat;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// PatchDOL() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+// GCH support
+
+#define GCT_ADDRESS 0x80001800u
+#define GCT_MAX 0x80003000u
+#define GCT_MM_BASE (GCT_ADDRESS-sizeof(gch_header_t))
+
+static MemMap_t gct_data_mem = {0}; // memory map of GCT section
+static GrowBuffer_t gecko_data = {0};
+static GrowBuffer_t wcode_job = {0};
+static GrowBuffer_t wcode_data = {0};
+
+
+static bool use_gct_loader = false;
+
+///////////////////////////////////////////////////////////////////////////////
+// patched by
+
+#define STD_PATCHED_BY WSTRT_SHORT " v" VERSION " r" REVISION
+static char opt_patched_by[PATCHED_BY_SIZE+1] = STD_PATCHED_BY;
+
+patched_by_t patched_by_mode = PBY_AUTO;
+
+//-----------------------------------------------------------------------------
+
+int ScanOptPBMode ( ccp arg )
+{
+ if ( !arg || !*arg )
+ {
+ patched_by_mode = PBY_AUTO;
+ return 0;
+ }
+
+ static const KeywordTab_t tab[] =
+ {
+ { PBY_OFF, "OFF", 0, 0 },
+ { PBY_RESET, "RESET", 0, 0 },
+ { PBY_WIIMMFI, "WIIMMFI", 0, 0 },
+ { PBY_AUTO, "AUTO", "DEFAULT", 0 },
+ { PBY_ALWAYS, "ALWAYS", 0, 0 },
+ { 0,0,0,0 }
+ };
+
+ const KeywordTab_t * cmd = ScanKeyword(0,arg,tab);
+ if (cmd)
+ {
+ patched_by_mode = cmd->id;
+ return 0;
+ }
+
+ ERROR0(ERR_SYNTAX,"Invalid mode for option --pb-mode: '%s'\n",arg);
+ return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined(TEST) || defined(DEBUG) //|| HAVE_WIIMM_EXT
+ static void TraceGCT ( ccp info )
+ {
+ fprintf(stdlog,"%s#GCT: gecko=%u/%u, wc-job=%u/%u, wc-data=%u/%u%s%s%s\n",
+ collog->b_cyan,
+ gecko_data.used, gecko_data.size,
+ wcode_job.used, wcode_job.size,
+ wcode_data.used, wcode_data.size,
+ info ? " : " : "",
+ info ? info : "",
+ collog->reset );
+ }
+#else
+ static inline void TraceGCT ( ccp info ) {}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void InititializeGCT()
+{
+ static bool done = false;
+ if (!done)
+ {
+ done = true;
+
+ InitializeMemMap(&gct_data_mem);
+ InitializeGrowBuffer(&gecko_data,MiB);
+ InitializeGrowBuffer(&wcode_job,MiB);
+ InitializeGrowBuffer(&wcode_data,MiB);
+ gecko_data.grow_size = 0x1000;
+ wcode_job.grow_size = 0x0100;
+ wcode_data.grow_size = 0x0400;
+
+ if (logging>2) TraceGCT("Init");
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void ResetGCT()
+{
+ InititializeGCT();
+
+ ResetMemMap(&gct_data_mem);
+ ClearGrowBuffer(&gecko_data);
+ ClearGrowBuffer(&wcode_job);
+ ClearGrowBuffer(&wcode_data);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError AddSectionHelper2
+(
+ // return ERR_OK | ERR_WARNING of created with warnings | ERR_ERROR on abort
+ staticr_t *str, // valid pointer
+ const wch_segment_t *seg, // segement info, local endian, data+size ignored
+ const u8 *seg_data, // segement data
+ uint seg_size, // segement data
+ ccp fname // filename for messages
+)
+{
+ //--- patching possible?
+
+ const u32 patch_addr = GetVbiAddress(str);
+ if (!patch_addr)
+ return 0;
+
+
+ //--- check overlaps
+
+ dol_header_t *dh = (dol_header_t*)str->data;
+ const uint overlap = CountOverlappedDolSections(dh,seg->addr,seg_size);
+ if (overlap)
+ {
+ if (!force_count)
+ {
+ ERROR0(ERR_WARNING,
+ "New section would overlap %u existing section%s -> ignore: %s\n",
+ overlap, overlap == 1 ? "" : "s", fname );
+ return ERR_ERROR;
+ }
+
+ ERROR0(ERR_WARNING,
+ "New section overlaps %u existing section%s: %s\n",
+ overlap, overlap == 1 ? "" : "s", fname );
+ }
+
+
+ //--- find first unused section
+
+ int dol_seg_type;
+ switch (seg->type)
+ {
+ case 't': dol_seg_type = 0; break; // Any segment, TEXT first
+ case 'T': dol_seg_type = 1; break; // Only TEXT segment
+ case 'D': dol_seg_type = 2; break; // Only DATA segment
+ default: dol_seg_type = 3; break; // Any segment, DATA first (alias 'd')
+ }
+
+ dol_sect_info_t di;
+ if (!FindFirstFreeDolSection(&di,dh,dol_seg_type))
+ {
+ ERROR0(ERR_WARNING,
+ "DOL: No free section available: %s\n", str->fname );
+ return ERR_ERROR;
+ }
+ PRINT("NEW SECTION: %s\n",di.name);
+
+
+ //--- append seg_data to end of dol
+
+ uint offset = str->data_size;
+ str->data_size += seg_size;
+ str->data = REALLOC(str->data,str->data_size);
+ dh = (dol_header_t*)str->data;
+ memcpy( str->data + offset, seg_data, seg_size );
+
+
+ //--- create section
+
+ PRINT(" - create section %s [%08x..%08x], f-off 0x%06x, size 0x%x\n",
+ di.name, seg->addr, seg->addr+seg_size, offset, seg_size );
+
+ if ( verbose > 0 )
+ fprintf(stdlog,
+ "- Create section %s [%08x..%08x], file offset 0x%06x, size 0x%x\n",
+ di.name, seg->addr, seg->addr+seg_size, offset, seg_size );
+
+ dh->sect_off [di.section] = htonl(offset);
+ dh->sect_addr[di.section] = htonl(seg->addr);
+ dh->sect_size[di.section] = htonl(seg_size);
+
+
+ //--- patch entry point
+
+ u32 addr = seg->main_entry_old;
+ if (addr)
+ {
+ const u32 store = GetDolOffsetByAddr(dh,addr,sizeof(u32),0);
+ if (store)
+ memcpy(str->data+store,&dh->entry_addr,sizeof(u32));
+ }
+
+ if (seg->main_entry)
+ {
+ if ( verbose >= 1 )
+ fprintf(stdlog,
+ "- Change entry point from %08x to %08x\n",
+ ntohl(dh->entry_addr), seg->main_entry );
+ write_be32(&dh->entry_addr,seg->main_entry);
+ }
+
+
+ //--- patch VBI entry point
+
+ if (seg->vbi_entry)
+ {
+ const u32 offset = GetDolOffsetByAddr(dh,patch_addr,sizeof(u32),0);
+ if (!offset)
+ {
+ ERROR0(ERR_WARNING,
+ "Can't patch DOL at address 0x%08x: %s\n",
+ patch_addr, str->fname );
+ }
+ else
+ {
+ u32 addr = ntohl(seg->vbi_entry_old);
+ if (addr)
+ {
+ const u32 store = GetDolOffsetByAddr(dh,addr,sizeof(u32),0);
+ if (!store)
+ {
+ ERROR0(ERR_WARNING,
+ "Can't store old VBI at DOL address 0x%08x: %s\n",
+ seg->vbi_entry_old, str->fname );
+ }
+ else
+ memcpy(str->data+store,str->data+offset,sizeof(u32));
+ }
+
+ const u32 code = ( seg->vbi_entry - patch_addr ) & 0xffffff | 0x4b000000;
+ PRINT("PATCH addr %08x off %08x from %08x to %08x\n",
+ patch_addr, offset, be32(str->data+offset), code );
+ if ( verbose > 0 )
+ fprintf(stdlog,
+ "- Patch address %08x (off %08x, VBI) from %08x to %08x\n",
+ patch_addr, offset, be32(str->data+offset), code );
+ write_be32(str->data+offset,code);
+ }
+ }
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef struct gct_sect_t
+{
+ u16 off; // source offset
+ u16 size; // size of section
+ u16 idx; // initial section index
+ u16 dest; // dest address of section (add 0x8000000 for real address)
+}
+gct_sect_t;
+
+//-----------------------------------------------------------------------------
+
+static int sort_sect_list_by_size
+ ( const gct_sect_t * a, const gct_sect_t * b )
+{
+ return (int)b->size - (int)a->size;
+}
+
+//-----------------------------------------------------------------------------
+
+static int sort_sect_list_by_dest
+ ( const gct_sect_t * a, const gct_sect_t * b )
+{
+ return (int)a->dest - (int)b->dest;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+typedef struct cheat_code_loader_t
+{
+ u32 entry; // entry point => ASM: bl start
+ u32 dummy_04; // ...
+
+ //--- data, see cheat-code-loader.sh
+
+ u32 return_addr; // absolute return address
+ u32 var_version; // 'vars' version of current code loader
+ u32 space; // needed space, must not < source_len
+
+ u32 patch_addr; // address of patch list, relative to 'vars'
+ u32 data_addr; // data address, relative to 'vars'
+ u32 data_end; // data-end address, relative to 'vars'
+ u32 gecko_off; // offset of first gecko code relative to data
+
+ u32 verify_addr; // address to verify own codehandler
+ u32 verify_val; // use this to verify & to decode
+ u32 ch_patch_1; // abs address for first codehandler patch
+ u32 ch_patch_2; // abs address for second codehandler patch
+ u32 deny_user_gch; // 1: skip patching on !rSZS
+ u32 info_flags; // info flags for NAS (8 bits)
+}
+__attribute__ ((packed)) cheat_code_loader_t;
+
+#define CCL_VAR_OFFSET offsetof(cheat_code_loader_t,return_addr)
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef enum { CHT_NONE, CHT_ONLY, CHT_FULL } code_handler_t;
+
+//-----------------------------------------------------------------------------
+
+static const u8 loader_asm_bz2[] =
+{
+ // decompressed len: 0x338 = 824 bytes
+ 0x00,0x00,0x03,0x38,
+
+ // bzipped data
+ 0x42,0x5a,0x68,0x39, 0x31,0x41,0x59,0x26, 0x53,0x59,0x9d,0xf6, 0x23,0xf1,0x00,0x01,
+ 0x20,0xff,0xff,0xfd, 0xde,0xc7,0xf6,0xc5, 0xef,0xf5,0xff,0x64, 0x5b,0xef,0xd0,0x6a,
+ 0xdc,0xc5,0xd7,0xfc, 0xde,0x79,0xe0,0x53, 0xd6,0x51,0x10,0x55, 0xc3,0x41,0x65,0x45,
+ 0x84,0x65,0x44,0xc0, 0x01,0xf5,0xc2,0xca, 0xd2,0x1a,0xa6,0x8c, 0xa6,0x90,0xf5,0x3d,
+ 0x27,0xa9,0x90,0x00, 0xd0,0x06,0x26,0x83, 0xd2,0x00,0x69,0x88, 0x1a,0x00,0x19,0x00,
+ 0x1a,0x1a,0x32,0x01, 0xa0,0xf4,0x41,0x90, 0x32,0x00,0xc8,0x11, 0x34,0x41,0x1a,0x4f,
+ 0xd5,0x3d,0x27,0x94, 0xf5,0x32,0x1e,0xa0, 0x19,0x34,0x19,0x00, 0xd0,0xf4,0x80,0x00,
+ 0x00,0x00,0x00,0x00, 0x00,0x00,0x0d,0x00, 0xd0,0x06,0xd4,0x41, 0x80,0x4c,0x46,0x09,
+ 0x93,0x08,0x30,0x98, 0x98,0x13,0x00,0x43, 0x08,0xc0,0x43,0x02, 0x60,0x4c,0x26,0x08,
+ 0xc0,0x00,0x04,0xd0, 0xc2,0x62,0x69,0x81, 0x03,0x4d,0x42,0x98, 0x50,0x09,0xb4,0x3d,
+ 0x40,0x69,0xa8,0xc9, 0x80,0x68,0x00,0x01, 0x32,0x34,0xd1,0xa6, 0x00,0x11,0x81,0xa0,
+ 0x32,0x23,0x00,0x00, 0x08,0x69,0x91,0xe9, 0x0c,0xa2,0x54,0xaf, 0x84,0x8f,0x0a,0x52,
+ 0x54,0xba,0xa7,0xd1, 0x59,0x0d,0x00,0x4b, 0x69,0xdf,0xb0,0xb0, 0xb3,0xd3,0x4a,0x1c,
+ 0xa2,0x85,0xc9,0x8f, 0xc7,0xb6,0x9e,0x7e, 0x12,0x2b,0x1d,0x13, 0x3f,0x9e,0x9e,0x4b,
+ 0xb0,0x31,0x30,0x83, 0x55,0x67,0x43,0xde, 0x80,0x53,0x40,0x4f, 0xcd,0x7b,0x06,0x1c,
+ 0x04,0xd7,0x3e,0x0c, 0xc5,0xd2,0x66,0x27, 0xf5,0x19,0x65,0xeb, 0x81,0x01,0x48,0xa9,
+ 0x51,0x42,0x9b,0xba, 0x56,0x19,0x92,0x45, 0x52,0x6e,0x22,0xe0, 0xb5,0x97,0x2c,0x23,
+ 0xda,0x75,0x1c,0x16, 0x2e,0xb1,0x88,0x8e, 0x91,0xa0,0xcf,0x5a, 0xc9,0x5e,0x99,0x27,
+ 0xe1,0x52,0x18,0x16, 0x42,0x60,0x17,0x49, 0x28,0xc2,0x8e,0x18, 0x12,0x10,0xda,0x46,
+ 0x01,0x76,0x36,0xd4, 0x07,0xe3,0x9d,0x42, 0x5c,0xc2,0xba,0x55, 0x7f,0xbd,0x14,0x02,
+ 0x70,0x29,0x37,0x17, 0xf6,0xda,0x50,0x5b, 0x05,0xa1,0xb9,0x06, 0xcd,0xb0,0xde,0x93,
+ 0x88,0x8b,0x0f,0x9d, 0x2a,0x4d,0xc8,0x6b, 0x7c,0xf1,0x01,0xf2, 0x14,0x6b,0x11,0x48,
+ 0x27,0x7d,0xb8,0xab, 0x43,0x15,0x5d,0x46, 0x21,0x1f,0x70,0x56, 0x77,0x4a,0x82,0xcc,
+ 0x8b,0xf1,0xc9,0xc0, 0x73,0xde,0x98,0x74, 0x4b,0x96,0x39,0xb2, 0x6b,0x99,0xee,0x23,
+ 0x0d,0xf0,0x69,0x16, 0x12,0x26,0xf7,0x2c, 0xe8,0x60,0x78,0xfa, 0x13,0x3b,0x56,0xc8,
+ 0xaa,0x69,0x82,0x3e, 0xc8,0x2b,0xee,0xad, 0xb9,0x65,0x8e,0x44, 0x98,0x27,0x77,0xcd,
+ 0xc5,0xcf,0x48,0x17, 0x93,0x04,0x2a,0x82, 0x96,0xa2,0x6d,0x64, 0x1c,0x03,0x7d,0x91,
+ 0xd1,0x4f,0x38,0x78, 0x7f,0x2a,0xfb,0x03, 0xe6,0x2f,0xe3,0x9f, 0xe5,0x0b,0x36,0x58,
+ 0x8c,0x70,0xba,0x9d, 0x3b,0xf3,0x4b,0xfe, 0xb9,0x53,0x6a,0x4c, 0xe8,0x6c,0xb9,0xfc,
+ 0x32,0xa3,0x13,0xc9, 0xed,0x29,0x10,0x4c, 0x48,0xe1,0x32,0x81, 0x0a,0x24,0x80,0x20,
+ 0x15,0x82,0x0d,0x89, 0x26,0xa1,0x29,0x66, 0x08,0x0a,0xd0,0x9e, 0xe0,0xce,0x50,0x7b,
+ 0xc5,0xfb,0x95,0x66, 0x42,0x65,0x84,0xda, 0xdf,0x37,0x52,0x53, 0x23,0x06,0x09,0x39,
+ 0xf6,0x1f,0x63,0xf1, 0x0b,0x13,0xd6,0x9c, 0xa2,0x63,0x2d,0xef, 0xa2,0xb2,0x8d,0xd2,
+ 0xa0,0xdd,0xe8,0x39, 0x9e,0xf5,0x71,0xa4, 0xd1,0xc5,0x74,0x11, 0x11,0xbb,0x42,0x20,
+ 0x53,0x92,0xff,0xc5, 0x36,0x67,0x4e,0xa4, 0x1b,0x55,0x95,0xef, 0x3a,0x30,0x12,0x85,
+ 0xea,0xe2,0x75,0xbf, 0x1a,0xb8,0xc8,0xa8, 0x12,0x2d,0x19,0x17, 0x91,0xdd,0xa7,0x23,
+ 0xbb,0xe7,0x18,0xb4, 0x61,0x78,0xa8,0xc4, 0x3b,0x84,0x5a,0x2d, 0x35,0x5f,0xac,0xd8,
+ 0x82,0xae,0x21,0x8e, 0x23,0x7d,0xf3,0x93, 0x2d,0xff,0x7b,0xbc, 0xbd,0x42,0x65,0xfc,
+ 0x1e,0xf9,0x2d,0xf9, 0x69,0x5a,0x12,0x08, 0x20,0x90,0x41,0x00, 0xc9,0x62,0xd2,0x88,
+ 0xc2,0x2a,0xa2,0x95, 0xc6,0x52,0xab,0x53, 0x31,0xb5,0xae,0x62, 0x36,0x07,0x94,0x8f,
+ 0x05,0x87,0x98,0x4a, 0x26,0x1a,0x0f,0x1f, 0xdb,0x2e,0x20,0xbb, 0x00,0x3c,0xb8,0x74,
+ 0x4c,0xb2,0x04,0x8f, 0x98,0xec,0x1d,0x43, 0x10,0x97,0x08,0x33, 0x05,0x0a,0xa0,0x48,
+ 0x23,0x04,0x50,0xc0, 0xfd,0x68,0x2e,0x24, 0x97,0xb1,0x4c,0xac, 0xb6,0x12,0xd4,0x00,
+ 0x65,0xdb,0x5d,0x74, 0x19,0x20,0x9e,0x58, 0xc2,0x10,0x12,0x00, 0x2b,0x52,0x00,0xea,
+ 0xa8,0xe0,0x54,0x84, 0xd0,0x7e,0x52,0x38, 0x08,0x2a,0x82,0x40, 0x59,0x3c,0xde,0xc5,
+ 0x08,0x1c,0x8f,0x4a, 0x79,0x27,0x98,0xbb, 0x92,0x29,0xc2,0x84, 0x84,0xef,0xb1,0x1f,
+ 0x88,
+ // 0x2e1 = 737 Bytes
+};
+
+static BZ2Manager_t loader_asm_mgr
+ = { loader_asm_bz2, sizeof(loader_asm_bz2), 0, 0 };
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError SetupCodeLoader
+(
+ // return ERR_OK | ERR_WARNING of created with warnings,
+ // ERR_ERROR on abort, ERR_NOTHING_TO_DO if nothing to do
+
+ staticr_t *str, // valid pointer
+ const wch_segment_t *seg, // NULL or segement info, seg->data is ignored
+ u8 *code_data, // NULL or code data
+ uint code_size, // NULL or size of code data
+ uint cheat_offset, // offset first cheat code
+ code_handler_t ch_type, // code handler type
+ ccp fname // filename for messages
+)
+{
+ ASSERT( code_size >= cheat_offset );
+ const uint total_data_size = wcode_job.used + wcode_data.used
+ + code_size - cheat_offset;
+
+ #if defined(TEST) || defined(DEBUG) // || HAVE_WIIMM_EXT
+ if (logging>2)
+ fprintf(stderr,
+ "SetupCodeLoader() %d+%d+%d = %d, c-off=0x%x, ch-type=%d, seg=%d => %s\n",
+ code_size, wcode_job.used, wcode_data.used, total_data_size,
+ cheat_offset, ch_type, seg!=0,
+ total_data_size ? "\e[41;37;1m CREATE WIIMMFI CODE \e[0m" : "quit" );
+ #endif
+
+ if (!total_data_size)
+ return ERR_NOTHING_TO_DO;
+
+
+ //--- data
+
+ GrowBuffer_t data;
+ InitializeGrowBuffer(&data,MiB);
+ data.grow_size = 0x1000;
+
+
+ //--- code handler
+
+ const u32 security_off = GCT_REG_OFFSET + 16; // gecko register 4
+ const u32 security_val = 0xdc020463;
+
+ enumError err = ERR_OK;
+ if ( code_size )
+ {
+ ASSERT(code_data);
+ write_be32(code_data+security_off,security_val);
+ write_be32(code_data+security_off+8,security_val);
+ write_be64(code_data+cheat_offset,GCT_TERM_NUM);
+ err = AddSectionHelper2(str,seg,code_data,cheat_offset+8,fname);
+ if (err)
+ goto abort;
+ code_data += cheat_offset;
+ code_size -= cheat_offset;
+ write_be64(code_data,GCT_MAGIC8_NUM);
+ str->dol_flags[DPF_CHEAT] = 'l';
+ }
+
+
+ //--- setup data buffer
+
+ DecodeBZIP2Manager(&loader_asm_mgr);
+ InsertGrowBuffer(&data,loader_asm_mgr.data,loader_asm_mgr.size);
+ cheat_code_loader_t ccl;
+ memcpy(&ccl,data.ptr,sizeof(ccl));
+ ccl.info_flags = htonl(str->dol_info_flags);
+
+ uint code_offset = loader_asm_mgr.size - CCL_VAR_OFFSET;
+ uint deny_user_gch = opt_allow_user_gch < OFFON_AUTO;
+ if (wcode_job.used)
+ {
+ ccl.patch_addr = htonl(code_offset);
+ InsertGrowBuffer(&data,wcode_job.ptr,wcode_job.used);
+ InsertGrowBuffer(&data,EmptyString,4);
+ code_offset += wcode_job.used + 4;
+ deny_user_gch = opt_allow_user_gch <= OFFON_AUTO;
+ }
+ str->dol_flags[DPF_ALLOW_USER] = deny_user_gch ? 'd' : 'a';
+ if ( deny_user_gch && code_size )
+ {
+ str->dol_flags[DPF_ALLOW_USER] = 's';
+ deny_user_gch = 2;
+ }
+ ccl.deny_user_gch = htonl(deny_user_gch);
+ ccl.data_addr = htonl(code_offset);
+ ccl.gecko_off = htonl( wcode_data.used );
+
+ if (wcode_data.used)
+ InsertGrowBuffer(&data,wcode_data.ptr,wcode_data.used);
+
+ if (code_size)
+ {
+ InsertGrowBuffer(&data,code_data,code_size);
+ ccl.verify_addr = htonl(seg->addr+security_off);
+ }
+ ccl.data_end = htonl(data.used-CCL_VAR_OFFSET);
+ const u32 space = data.used - CCL_VAR_OFFSET - code_offset;
+ ccl.space = htonl( space > opt_gct_space ? space : opt_gct_space );
+ ccl.verify_val = htonl(security_val);
+
+ if ( ch_type == CHT_FULL )
+ {
+ ccl.ch_patch_1 = htonl(seg->addr+0x04DC);
+ ccl.ch_patch_2 = htonl(seg->addr+0x0758);
+ }
+ else if ( ch_type == CHT_ONLY )
+ ccl.ch_patch_1 = htonl(seg->addr+0x0104);
+
+ memcpy(data.ptr,&ccl,sizeof(ccl));
+
+
+ //--- create segment
+
+ wch_segment_t dseg;
+ memset(&dseg,0,sizeof(dseg));
+ dseg.type = 't';
+ dseg.size = data.used;
+ dseg.addr = opt_gct_addr < 0x80000000
+ ? FindFreeBssSpaceDOL((dol_header_t*)str->data,dseg.size,32,0)
+ : opt_gct_addr;
+ dseg.main_entry = dseg.addr;
+ dseg.main_entry_old = dseg.addr + offsetof(cheat_code_loader_t,return_addr);
+
+ u32 *ptr = (u32*)( data.ptr + loader_asm_mgr.size );
+ u32 *end = (u32*)( data.ptr + data.used );
+ u32 key = security_val;
+ while ( ptr < end )
+ {
+ const u32 orig = be32(ptr);
+ const u32 val = orig ^ key;
+ write_be32(ptr, val << 19 | val >> 13 );
+ key += orig >> 5;
+ ptr++;
+ }
+
+ err = AddSectionHelper2(str,&dseg,data.ptr,dseg.size,fname);
+
+ //--- clean
+abort:
+ ResetGrowBuffer(&data);
+ ClearGrowBuffer(&wcode_job);
+ ClearGrowBuffer(&wcode_data);
+
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError AddSectionHelper
+(
+ // return ERR_OK | ERR_WARNING of created with warnings | ERR_ERROR on abort
+ staticr_t *str, // valid pointer
+ const wch_segment_t *seg, // segement info, seg->data is ignored
+ const u8 *seg_data, // segement data
+ ccp fname // filename for messages
+)
+{
+ //--- patching possible?
+
+ const u32 patch_addr = GetVbiAddress(str);
+ if (!patch_addr)
+ return 0;
+
+
+ //--- check presence of 'codehandleronly' or 'codehandler'
+ //--- and optional replace 'codehandleronly' by 'codehandler'
+
+ u8 *new_data = 0;
+ uint seg_size = seg->size;
+
+ code_handler_t ch_type = CHT_NONE;
+ uint cheat_offset = 0;
+
+ BZ2Manager_t *cho = &codehandleronly_mgr;
+ DecodeBZIP2Manager(cho);
+ if ( cho->size > sizeof(gch_header_t) )
+ {
+ const u8 *cho_data = cho->data + sizeof(gch_header_t);
+ const uint cho_size = cho->size - sizeof(gch_header_t);
+ if ( seg_size >= cho_size && !memcmp(cho_data,seg_data,cho_size) )
+ {
+ ch_type = CHT_ONLY;
+ cheat_offset = cho_size;
+
+ if (opt_full_gch)
+ {
+ PRINT("\e[35;1m Only: %u >= ( %u -> %u -> %u )\e[0m\n",
+ seg_size, codehandleronly_mgr.src_size,
+ codehandleronly_mgr.size, cho_size );
+
+ BZ2Manager_t *chf = &codehandler_mgr;
+ DecodeBZIP2Manager(chf);
+ if ( chf->size > sizeof(gch_header_t) )
+ {
+ if ( verbose > 0 )
+ fprintf(stdlog,
+ "- Replace 'codehandleronly' by 'codehandler'\n");
+
+ const u8 *chf_data = chf->data + sizeof(gch_header_t);
+ const uint chf_size = chf->size - sizeof(gch_header_t);
+ const uint new_size = seg_size - cho_size + chf_size;
+ new_data = MALLOC(new_size);
+ memcpy( new_data, chf_data, chf_size );
+ memcpy( new_data+chf_size, seg_data+cho_size, seg_size-cho_size );
+ seg_data = new_data;
+ seg_size = new_size;
+
+ ch_type = CHT_FULL;
+ cheat_offset = chf_size;
+ }
+ }
+ }
+ }
+
+ if ( ch_type == CHT_NONE )
+ {
+ BZ2Manager_t *chf = &codehandler_mgr;
+ DecodeBZIP2Manager(chf);
+ if ( chf->size > sizeof(gch_header_t) )
+ {
+ const u8 *chf_data = chf->data + sizeof(gch_header_t);
+ const uint chf_size = chf->size - sizeof(gch_header_t);
+ if ( seg_size >= chf_size && !memcmp(chf_data,seg_data,chf_size) )
+ {
+ ch_type = CHT_FULL;
+ cheat_offset = chf_size;
+ }
+ }
+ }
+
+
+ //--- find and remove separators
+
+ enum { MAX_SECT = 200 };
+ gct_sect_t sect_list[MAX_SECT+1],
+ *sect = sect_list,
+ *sect_end = sect_list + MAX_SECT;
+ memset(sect_list,0,sizeof(sect_list));
+
+ if ( cheat_offset > sizeof(gch_header_t) && opt_gct_scan_sep )
+ {
+ if (!new_data)
+ {
+ // we need modifiable data!
+ new_data = MEMDUP(seg_data,seg_size);
+ seg_data = new_data;
+ }
+
+ u8 *src = new_data + cheat_offset;
+ u8 *dest = src;
+ u8 *end = new_data + seg_size;
+
+ while ( src + 8 <= end )
+ {
+ PRINT(">> LINE %3zd => %3zd : %08x %08x\n",
+ ( src - new_data - cheat_offset ) / 8,
+ ( dest - new_data - cheat_offset ) / 8,
+ be32(src), be32(src+4) );
+
+ uint copy_lines = 0;
+ if ( be32(src) == 0xf0000000 && !be32(src+4) )
+ {
+ const u32 off = dest - new_data;
+ sect->size = off - sect->off;
+ sect++;
+ sect->idx = sect - sect_list;
+ sect->off = off;
+ sect->size = 8;
+ *(u64*)dest = *(u64*)src;
+ src += 8;
+ dest += 8;
+ break;
+ }
+ else if ( be32(src) == GCT_SEP_NUM1 && !be16(src+4) )
+ {
+ if ( opt_gct_sep )
+ {
+ const u32 off = dest - new_data;
+ if ( sect->off != off && sect < sect_end )
+ {
+ sect->size = off - sect->off;
+ sect++;
+ sect->idx = sect - sect_list;
+ sect->off = off;
+ sect->size = 0;
+ }
+ }
+
+ PRINT("\t>> SEP#%zu found: %08x %08x\n",
+ sect-sect_list, be32(src),be32(src+4) );
+ copy_lines = be16(src+6);
+ src += 8;
+ }
+ else if ( *src == 0xc0 || *src == 0xc2 || *src == 0xd0 || *src == 0xd2 )
+ {
+ if ( opt_gct_asm_sep )
+ {
+ const u32 off = dest - new_data;
+ if ( sect->off != off && sect < sect_end )
+ {
+ sect->size = off - sect->off;
+ sect++;
+ sect->idx = sect - sect_list;
+ sect->off = off;
+ sect->size = 0;
+ }
+ }
+
+ PRINT(">> ASM#%zu found: %08x %08x\n",
+ sect-sect_list, be32(src), be32(src+4) );
+ copy_lines = be32(src+4) + 1;
+ }
+ else if ( ( *src == 0x66 || *src == 0x68 )
+ && ( src[1] == 0x00 || src[1] == 0x10 || src[1] == 0x20 ) )
+ {
+ PRINT(">> GOTO/GOSUB#%zu found: %08x %08x\n",
+ sect-sect_list, be32(src), be32(src+4) );
+ s16 go = be16(src+2);
+ if ( go >= 0 )
+ copy_lines = go+1;
+ }
+ else
+ {
+ *(u64*)dest = *(u64*)src;
+ src += 8;
+ dest += 8;
+ continue;
+ }
+
+ if ( copy_lines > 0 )
+ {
+ // use it this way to detect bad large values
+ const uint max = ( end - src ) / 8;
+ if ( copy_lines > max )
+ copy_lines = max;
+ copy_lines *= 8; // now we have copy_bytes!
+
+ memmove(dest,src,copy_lines);
+ src += copy_lines;
+ dest += copy_lines;
+ }
+ }
+
+ //PRINT(">>> %zd %zd /%zd\n",dest-new_data,src-new_data,end-new_data);
+ if ( src >= end )
+ seg_size = dest - new_data;
+ else if ( src > dest )
+ memset(dest,0,src-dest);
+ }
+ const int n_sect = sect - sect_list;
+
+
+ //--- move GCT part
+
+ str->dol_flags[DPF_CHEAT] = 'c';
+
+ enumError err = ERR_OK;
+ if ( opt_gct_move >= OFFON_ON && cheat_offset >= 0x100 )
+ {
+ use_loader:;
+ if (!new_data)
+ {
+ // we need modifiable data!
+ new_data = MEMDUP(seg_data,seg_size);
+ seg_data = new_data;
+ }
+
+ err = SetupCodeLoader(str,seg,new_data,seg_size,cheat_offset,ch_type,fname);
+ goto abort;
+ }
+
+
+ //--- split GCT part
+
+ const u32 off_end = seg->addr + seg_size;
+ PRINT("N=%d, of=%08x\n",n_sect,off_end);
+ if ( n_sect > 2
+ && seg->addr + cheat_offset < 0x80002ff0
+ && off_end >= 0x80003000 && off_end < 0x80004000 )
+ {
+ #ifdef TEST0
+ {
+ uint i;
+ for ( i = 0, sect = sect_list; i <= n_sect; i++, sect++ )
+ PRINT(" -> SECT %2u: %4x .. %4x, size %4x\n",
+ i, sect->off, sect->off + sect->size, sect->size );
+ }
+ #endif
+
+ bool sorted = false;
+ restart:;
+ u8 data[0x4000];
+ memset(data,0,sizeof(data));
+
+ const u32 base_addr = seg->addr - 0x80000000;
+ memcpy(data+base_addr,seg_data,cheat_offset+8);
+
+ struct data_t { u32 used; u32 size; u8 *data; } data_tab[] =
+ {
+ { cheat_offset+8, 0x2ff8 - base_addr, data + base_addr },
+ { 0, 0x37f8 - 0x3200, data + 0x3200 },
+ { 0, 0x3ff8 - 0x3880, data + 0x3880 },
+ {0,0,0}
+ };
+
+ uint i;
+ struct data_t *dp;
+ bool fail = false;
+
+ if ( opt_gct_list > 1 )
+ {
+ fprintf(stdlog,"\nSummary of detected GCT sections:\n");
+ for ( i = 1, sect = sect_list+1; i < n_sect; i++, sect++ )
+ fprintf(stdlog," S%02u: %04x .. %04x, size %04x = %4u/dec\n",
+ sect->idx, sect->off, sect->off+sect->size,
+ sect->size, sect->size );
+ }
+
+ for ( i = 1, sect = sect_list+1; i < n_sect; i++, sect++ )
+ {
+ bool done = false;
+ for ( dp = data_tab; dp->data; dp++ )
+ if ( sect->size <= dp->size - dp->used )
+ {
+ memcpy(dp->data+dp->used,seg_data+sect->off,sect->size);
+ sect->dest = dp->data + dp->used - data;
+ dp->used += sect->size;
+ done = true;
+ break;
+ }
+ if (!done)
+ {
+ sect->dest = M1(sect->dest);
+ ERROR0(ERR_WARNING,
+ "No space for cheat code section %u (size %x): %s\n",
+ i, sect->size, fname );
+ fail = true;
+ }
+ }
+
+ #ifdef TEST0
+ {
+ uint i;
+ const struct data_t *dp;
+ for ( i = 0, dp = data_tab; dp->data; i++, dp++ )
+ printf(" -> %2u: %04zx .. %04zx : %4x / %4x : %4x\n",
+ i, dp->data - data, dp->data - data + dp->used,
+ dp->used, dp->size, dp->size - dp->used );
+ }
+ #endif
+
+ if (fail)
+ {
+ if (!sorted)
+ {
+ qsort( sect_list+1, n_sect-1, sizeof(*sect_list),
+ (qsort_func)sort_sect_list_by_size );
+ sorted = true;
+ goto restart;
+ }
+
+ FREE(new_data);
+ ERROR0(ERR_ERROR,
+ "Not enough space for cheat codes: %s\n",fname);
+ return ERR_WARNING;
+ }
+
+ if ( opt_gct_list > 0 )
+ {
+ qsort( sect_list+1, n_sect-1, sizeof(*sect_list),
+ (qsort_func)sort_sect_list_by_dest );
+ fprintf(stdlog,"\nSummary of GCT section distribution:\n");
+ for ( i = 1, sect = sect_list+1; i < n_sect; i++, sect++ )
+ {
+ fprintf(stdlog,
+ " S%02u: %04x .. %04x, size %04x = %4u/dec => ",
+ sect->idx, sect->off, sect->off+sect->size,
+ sect->size, sect->size );
+ if (IS_M1(sect->dest))
+ fprintf(stdlog,"FAILED!\n");
+ else
+ fprintf(stdlog,"%04x .. %04x\n",sect->dest,sect->dest+sect->size);
+ }
+ fputc('\n',stdlog);
+ }
+
+ struct data_t *prev = 0;
+ for ( dp = data_tab; dp->data; dp++ )
+ if ( dp->used)
+ {
+ if (prev)
+ {
+ u8 *dest = prev->data + prev->used;
+ write_be16(dest,0x6620);
+ write_be16( dest+2, ( dp->data - dest - 8 ) / 8 );
+ write_be32(dest+4,0);
+ prev->used += 8;
+ }
+ prev = dp;
+ }
+ if (prev)
+ {
+ u8 *dest = prev->data + prev->used;
+ write_be32(dest,0xf0000000ul);
+ write_be32(dest+4,0);
+ prev->used += 8;
+ }
+
+ #ifdef TEST0
+ {
+ uint i;
+ const struct data_t *dp;
+ for ( i = 0, dp = data_tab; dp->data; i++, dp++ )
+ printf(" -> %2u: %04zx .. %04zx : %4x / %4x : %4x\n",
+ i, dp->data - data, dp->data - data + dp->used,
+ dp->used, dp->size, dp->size - dp->used );
+ }
+ #endif
+
+ err = AddSectionHelper2(str,seg,data_tab->data,data_tab->used,fname);
+ if ( err == ERR_OK )
+ {
+ wch_segment_t dseg;
+ memset(&dseg,0,sizeof(dseg));
+ dseg.type = 'd';
+ if ( data_tab[1].used )
+ {
+ dseg.addr = data_tab[1].data - data + 0x80000000;
+ const uint size = data_tab[2].used
+ ? data_tab[2].data + data_tab[2].used - data_tab[1].data
+ : data_tab[1].used;
+ err = AddSectionHelper2(str,&dseg,data_tab[1].data,size,fname);
+ }
+ else if ( data_tab[2].used )
+ {
+ dseg.addr = data_tab[2].data - data + 0x80000000;
+ err = AddSectionHelper2(str,&dseg,data_tab[2].data,data_tab[2].used,fname);
+ }
+ }
+ return err;
+ }
+
+
+ //--- warn if overlapping 0x80003xxx
+
+ if ( ch_type != CHT_NONE
+ && ( seg->addr < 0x80004000 && off_end > 0x80003000 ))
+ {
+ if ( opt_gct_move >= OFFON_AUTO && cheat_offset >= 0x100 )
+ goto use_loader;
+
+ FREE(new_data);
+ ERROR0(ERR_ERROR,
+ "Cheat codes must not overlay 0x80003000..0x80004000: %s\n",
+ fname);
+ return ERR_WARNING;
+ }
+
+
+ //--- standard: use 1 section
+
+ err = AddSectionHelper2(str,seg,seg_data,seg_size,fname);
+
+ abort:
+ FREE(new_data);
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError ReadCodeFile ( staticr_t *str, ccp fname, u8 **data, uint *size )
+{
+ DASSERT(str);
+ DASSERT(fname);
+ DASSERT(data);
+ DASSERT(size);
+
+ *data = 0;
+ *size = 0;
+
+ if ( fname && fname[0] == '@' )
+ {
+ BZ2Manager_t *wcm = 0;
+ if (!strcmp(fname,"@LECODE")) wcm = GetLecodeLoader(str->mode);
+// else if (!strcmp(fname,"@XXX")) wcm = GetXXXX(str->mode);
+
+ if (wcm)
+ {
+ DecodeBZIP2Manager(wcm);
+ *data = wcm->data;
+ *size = wcm->size;
+ return ERR_OK;
+ }
+ }
+
+ enumError err = OpenReadFILE(fname,0,true,data,size,0,0);
+ if (err)
+ {
+ char path_buf[PATH_MAX];
+ if (ReplaceRegionChar(str,path_buf,sizeof(path_buf),fname))
+ err = OpenReadFILE(path_buf,0,true,data,size,0,0);
+
+ if (err)
+ {
+ ERROR0(ERR_WARNING,"Can't load file: %s\n",fname);
+ return err;
+ }
+ }
+
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError AddWCode ( cvp data, uint size )
+{
+ DASSERT(data);
+ DASSERT(!(size&7));
+ InititializeGCT();
+
+ u8 *ptr = (u8*)data;
+ u8 *end = ptr + size;
+ u32 buf[5];
+
+ if ( be64(ptr) == GCT_MAGIC8_NUM )
+ ptr += 8;
+
+ while ( ptr < end )
+ {
+ switch (*ptr)
+ {
+ case 0x04:
+ buf[0] = htonl(0x04);
+ buf[1] = htonl( be32(ptr) & 0xffffff | 0x80000000 );
+ buf[2] = ((u32*)ptr)[1];
+ InsertGrowBuffer(&wcode_job,buf,12);
+ use_gct_loader = true;
+ ptr += 8;
+ break;
+
+ case 0xc2:
+ {
+ const uint len = 8 * be32(ptr+4);
+ buf[0] = htonl(0xc2);
+ buf[1] = htonl( be32(ptr) & 0xffffff | 0x80000000 );
+ buf[2] = htonl( wcode_data.used );
+ buf[3] = htonl( wcode_data.used + len - 4 );
+ InsertGrowBuffer(&wcode_job,buf,16);
+ InsertGrowBuffer(&wcode_data,ptr+8,len);
+ use_gct_loader = true;
+ ptr += 8 + len;
+ }
+ break;
+
+ case 0xf0:
+ if ( be32(ptr) != GCT_SEP_NUM1 )
+ goto term;
+ ptr += 8; // ignore separators
+ break;
+
+ default:
+ return ERROR0(ERR_WARNING,"WCODE %02x not supported => ABORT\n",*ptr);
+ }
+ }
+ term:
+ if (logging>2) TraceGCT("WCode");
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError AddCheatCode
+(
+ // returns ERR_OK, ERR_NOTHING_TO_DO, ERR_WARNING or ERR_INTERNAL
+
+ const u8 *code_data, // cheat code data
+ uint code_size, // cheat code size
+ bool check_dup, // TRUE: check for duplicates
+ ccp fname // file name for error messages
+)
+{
+ DASSERT(code_data);
+
+ //--- verify code_size and skip magic and terminator
+
+ code_size &= ~7; // multiple of 8, cut additional bytes
+ if ( code_size >= 8 && be64(code_data) == GCT_MAGIC8_NUM )
+ {
+ // skip magic
+ code_data += 8;
+ code_size -= 8;
+ }
+
+ if ( code_size >= 8 && be64(code_data+code_size-8) == GCT_TERM_NUM )
+ {
+ // ignore terminator
+ code_size -= 8;
+ }
+
+ if (!code_size)
+ return ERR_NOTHING_TO_DO;
+
+
+ //--- first code -> add code handler
+
+ InititializeGCT();
+
+ if (!gct_data_mem.used)
+ {
+ BZ2Manager_t *mgr = opt_full_gch
+ ? &codehandler_mgr : &codehandleronly_mgr;
+ enumError err = DecodeBZIP2Manager(mgr);
+ if (err)
+ return ERROR0(ERR_INTERNAL,0);
+
+ gct_data_mem.begin = GCT_ADDRESS;
+ MemMapItem_t *mi;
+ mi = InsertMemMap(&gct_data_mem,GCT_ADDRESS,mgr->size-sizeof(gch_header_t));
+ StringCopyS(mi->info,sizeof(mi->info),"Code Handler");
+ mi = InsertMemMap(&gct_data_mem,GCT_MM_BASE+mgr->size,8);
+ StringCopyS(mi->info,sizeof(mi->info),"GCT magic");
+ if (!use_gct_loader)
+ {
+ mi = InsertMemMap(&gct_data_mem,GCT_MAX,0);
+ StringCopyS(mi->info,sizeof(mi->info),"-- max possible --");
+ }
+
+ InsertGrowBuffer(&gecko_data,mgr->data,mgr->size);
+ u64 magic = hton64(GCT_MAGIC8_NUM);
+ InsertGrowBuffer(&gecko_data,&magic,sizeof(magic));
+ if (logging>2)
+ TraceGCT("Setup");
+ }
+
+
+ //--- check, if same code already inserted
+
+ if ( check_dup && code_size <= gecko_data.used )
+ {
+ const u8 *ptr = gecko_data.ptr;
+ const u8 *end = ptr + gecko_data.used - code_size;
+
+ while ( ptr <= end )
+ {
+ if (!memcmp(ptr,code_data,code_size))
+ return ERR_NOTHING_TO_DO;
+ ptr += 8;
+ }
+ }
+
+
+ //--- add cheat code
+
+ MemMapItem_t *mi
+ = InsertMemMap(&gct_data_mem,GCT_MM_BASE+gecko_data.used,code_size);
+ snprintf(mi->info,sizeof(mi->info),
+ "%4u line%s of GCT file", code_size/8, code_size == 8 ? "" : "s" );
+
+ if ( opt_gct_scan_sep && opt_gct_sep && be32(code_data) != GCT_SEP_NUM1 )
+ {
+ u32 buf[2];
+ buf[0] = htonl(GCT_SEP_NUM1);
+ buf[1] = htonl(code_size/8);
+ InsertGrowBuffer(&gecko_data,buf,8);
+ }
+
+ InsertGrowBuffer(&gecko_data,code_data,code_size);
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError AddWCodeFile ( staticr_t *str, ccp fname )
+{
+ DASSERT(str);
+ if ( !fname || !*fname )
+ return ERR_OK;
+
+ u8 *data;
+ uint size;
+ enumError err = ReadCodeFile(str,fname,&data,&size);
+ if (err)
+ return err;
+
+// [[analyse-magic]]
+ const file_format_t fform = GetByMagicFF(data,size,size);
+ if ( fform == FF_GCT )
+ {
+ err = opt_wcode == WCODE_GECKO
+ ? AddCheatCode(data,size,true,fname)
+ : AddWCode(data,size);
+ }
+ else
+ err = ERROR0(ERR_WARNING,"Not a GCT file: %s\n",fname);
+
+ FREE(data);
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if HAVE_XSRC
+ #include "xsrc/x-staticr.c"
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint AddSectionGCH
+(
+ staticr_t *str, // valid pointer
+ u8 *gh_data, // GCH data
+ uint gh_size, // GHC size
+ ccp fname, // filename for messages
+ bool get_min_addr // true: do nothing and get minimal section addr
+)
+{
+ DASSERT(str);
+ DASSERT(str->data_size);
+ DASSERT(str->data);
+ DASSERT(str->is_dol);
+ DASSERT(gh_data||!gh_size);
+
+
+ //--- get param
+
+ const u32 patch_addr = GetVbiAddress(str);
+ if (!patch_addr)
+ return 0;
+
+
+ //--- check for GCT
+
+// [[analyse-magic]]
+ const file_format_t fform = GetByMagicFF(gh_data,gh_size,gh_size);
+ if ( fform == FF_GCT )
+ {
+ AddCheatCode(gh_data,gh_size,false,fname);
+ return 0;
+ }
+
+ const gch_header_t *gh = (gch_header_t*)gh_data;
+ if (!IsValidGCH(gh,gh_size))
+ {
+ #if HAVE_XSRC
+ return IsNotGCH(str,fname,gh_data,gh_size,get_min_addr);
+ #else
+ ERROR0(ERR_WARNING,"Not a valid GCH file: %s\n",fname);
+ return 0;
+ #endif
+ }
+
+ gh_size -= sizeof(gch_header_t); // from now we only need the raw data size
+
+ if (get_min_addr)
+ return ntohl(gh->addr);
+
+ wch_segment_t seg;
+ memset(&seg,0,sizeof(seg));
+ seg.type = 't';
+ seg.addr = ntohl(gh->addr);
+ seg.size = gh_size;
+ seg.vbi_entry = ntohl(gh->vbi_entry);
+
+ const enumError err = AddSectionHelper(str,&seg,gh->data,fname);
+ return err < ERR_ERROR;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint AddSectionDOL
+(
+ staticr_t * str, // valid pointer
+ ccp fname, // filename to open
+ bool get_min_addr // true: do nothing and get minimal section addr
+)
+{
+ DASSERT(str);
+ DASSERT(str->data_size);
+ DASSERT(str->data);
+ DASSERT(str->is_dol);
+
+ if ( !fname || !*fname )
+ return 0;
+
+
+ //--- get param
+
+ const u32 patch_addr = GetVbiAddress(str);
+ if (!patch_addr)
+ return 0;
+
+
+ //--- load file
+
+ u8 *gh_data = 0;
+ uint gh_size = 0;
+ enumError err = ReadCodeFile(str,fname,&gh_data,&gh_size);
+ if (err)
+ return err;
+
+ const uint stat = AddSectionGCH(str,gh_data,gh_size,fname,get_min_addr);
+ FREE(gh_data);
+ return stat;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint AddSectionsDOL
+(
+ staticr_t * str, // valid pointer
+ bool get_min_addr // true: do nothing and get minimal section addr
+)
+{
+ DASSERT(str);
+ DASSERT(str->data_size);
+ DASSERT(str->data);
+ DASSERT(str->is_dol);
+
+ uint i, count = get_min_addr ? 0xffffffff : 0;
+ for ( i = 0; i < opt_sect_list.used; i++ )
+ {
+ ccp fname = opt_sect_list.field[i];
+ const uint stat = AddSectionDOL(str,fname,get_min_addr);
+ if (!get_min_addr)
+ count += stat;
+ else if ( stat && count > stat )
+ count = stat;
+ }
+
+ if ( gecko_data.used )
+ {
+ if (logging>0)
+ fprintf(stdlog,">> GCT DATA/GROW, %u = 0x%x bytes\n",
+ gecko_data.used, gecko_data.used );
+
+ if (!get_min_addr)
+ {
+ // terminate section and add it to dol
+ MemMapItem_t *mi = InsertMemMap(&gct_data_mem,GCT_MM_BASE+gecko_data.used,8);
+ StringCopyS(mi->info,sizeof(mi->info),"GCT terminator");
+
+ u64 term = hton64(GCT_TERM_NUM);
+ InsertGrowBuffer(&gecko_data,&term,sizeof(term));
+ count += AddSectionGCH(str,gecko_data.ptr,gecko_data.used,"GCT",false);
+
+ if ( verbose > 1 || logging > 1 )
+ {
+ fputs("\n Memory map of code handler and GCT files:\n",stdlog);
+ PrintMemMap(&gct_data_mem,stdlog,5,"part");
+ fputc('\n',stdlog);
+ }
+ }
+ else if ( count > 0x80001800 )
+ count = 0x80001800;
+ ResetGCT();
+ }
+
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint CreateSectionDOL
+(
+ staticr_t * str // valid pointer
+)
+{
+ DASSERT(str);
+ DASSERT(str->data_size);
+ DASSERT(str->data);
+ DASSERT(str->is_dol);
+
+ enum
+ {
+ OFF_SECT_ADDR = 0x10,
+ OFF_REL_ENTRY = 0x14,
+ OFF_ORIG_ENTRY = 0x18,
+ OFF_RDOL_ADDR = 0x1c,
+ OFF_END_PARAM = 0x20,
+
+ RED_DOL_SIZE = 0xe4 - 0x48, // reduced DOL size
+ };
+
+ u32 dol_sect_offset = 0; // file offset of DOL section
+ u32 dol_sect_addr = 0; // address of DOL section
+ dol_header_t *dh = (dol_header_t*)str->data;
+
+
+ //--- find max dol address
+
+ u32 max_addr = dh->bss_addr + dh->bss_size;
+ uint sect;
+ for ( sect = 0; sect < DOL_N_SECTIONS; sect++ )
+ {
+ const u32 size = ntohl(dh->sect_size[sect]);
+ if (size)
+ {
+ const u32 off = ntohl(dh->sect_addr[sect]) + size;
+ if ( max_addr < off )
+ max_addr = off;
+ }
+ }
+ max_addr = ALIGN32(max_addr,4);
+
+
+ //--- iterate new section list
+
+ uint count;
+ for ( count = sect = 0; sect < n_section_list; sect++ )
+ {
+ dol_sect_select_t *sl = section_list + sect;
+ sl->offset = 0;
+ if ( sl->find_mode < 0 )
+ continue;
+
+ dol_sect_info_t si;
+ if (!FindDolSectionBySelector(&si,dh,sl))
+ {
+ ERROR0(ERR_WARNING,"DOL section '%s' not available: %s\n",
+ sl->name, str->fname );
+ return 0;
+ }
+
+ bool is_dol_info = !strcasecmp(sl->fname,".dol");
+ if (is_dol_info)
+ {
+ sl->use_param = 0;
+ sl->size = RED_DOL_SIZE;
+ }
+ else if (!sl->data)
+ {
+ if (OpenReadFILE( sl->fname, 0, true, &sl->data, &sl->size, 0, 0 ))
+ {
+ ERROR0(ERR_WARNING,"Can't load file for '%s': %s\n",
+ sl->name, sl->fname );
+ sl->find_mode = -1;
+ continue;
+ }
+ }
+ count++;
+
+ uint offset = str->data_size;
+ sl->offset = offset;
+ str->data_size += sl->size;
+ str->data = REALLOC(str->data,str->data_size);
+ dh = (dol_header_t*)str->data;
+ u8 *dest = str->data + offset;
+
+ u32 sect_addr = !sl->addr && sl->use_param
+ ? be32(sl->data+OFF_SECT_ADDR) : sl->addr;
+
+ if (is_dol_info)
+ {
+ sl->data = (u8*)&dh->sect_addr;
+ if (!sect_addr)
+ sect_addr = 0x80004000 - RED_DOL_SIZE;
+ dol_sect_addr = sect_addr;
+ dol_sect_offset = offset;
+ }
+
+ if (!sect_addr)
+ sect_addr = max_addr;
+ const u32 end_addr = ALIGN32( sect_addr + sl->size, 4 );
+ if ( max_addr < end_addr )
+ max_addr = end_addr;
+
+ u32 new_entry = 0;
+ bool param_valid = false;
+ if ( sl->use_param && sl->size >= OFF_END_PARAM )
+ {
+ new_entry = be32(sl->data+OFF_REL_ENTRY);
+ if (!new_entry)
+ {
+ param_valid = true;
+ }
+ else if ( !(new_entry&3) && new_entry < sl->size )
+ {
+ param_valid = true;
+ new_entry += sect_addr;
+ }
+ else
+ new_entry = 0;
+ }
+ if (!param_valid)
+ sl->use_param = false;
+
+
+ //--- create section
+
+ PRINT(" - create section %s[%d] [%08x..%08x], f-off 0x%06x, size 0x%x, par=0x%x\n",
+ si.name, si.section, sect_addr, sect_addr + sl->size,
+ offset, sl->size, new_entry );
+
+ if (verbose>0)
+ {
+ fprintf(stdlog,
+ "- Create section %s [%08x..%08x], file offset 0x%06x, size 0x%x",
+ si.name, sect_addr, sect_addr + sl->size, offset, sl->size );
+ if (new_entry)
+ fprintf(stdlog,", entry 0x%x\n",new_entry);
+ else if (param_valid)
+ fprintf(stdlog,", use param\n");
+ else
+ fputc('\n',stdlog);
+ }
+
+ dh->sect_off [si.section] = htonl(offset);
+ dh->sect_addr[si.section] = htonl(sect_addr);
+ dh->sect_size[si.section] = htonl(sl->size);
+ memcpy( dest, sl->data, sl->size );
+
+ //--- store original entry point
+
+ memcpy(dest+OFF_ORIG_ENTRY,&dh->entry_addr,sizeof(u32));
+
+ //--- use relative entry point of new section
+
+ if (new_entry)
+ dh->entry_addr = htonl(new_entry);
+ }
+
+
+ //--- final '.dol' section support
+
+ if (dol_sect_offset)
+ {
+ //- refresh dol section
+
+ PRINT("UPDATE RED-DOL\n");
+ dol_header_t *dh = (dol_header_t*)str->data;
+ memcpy( str->data + dol_sect_offset, &dh->sect_addr, RED_DOL_SIZE );
+
+ //- update sections with 'use_param'
+
+ for ( sect = 0; sect < n_section_list; sect++ )
+ {
+ dol_sect_select_t *sl = section_list + sect;
+ PRINT("UPDATE sect %s [off=%x,param=%d] := %x\n",
+ sl->name, sl->offset+OFF_RDOL_ADDR, sl->use_param, dol_sect_addr );
+ if ( sl->offset && sl->use_param )
+ write_be32( str->data + sl->offset + OFF_RDOL_ADDR, dol_sect_addr );
+ }
+ }
+
+ return count;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static uint PatchCTCODEDOL
+(
+ staticr_t * str // valid pointer
+)
+{
+ DASSERT(str);
+ DASSERT(str->data_size);
+ DASSERT(str->data);
+ DASSERT(str->is_dol);
+
+ if (!opt_add_ctcode)
+ return 0;
+
+ if (!(str->dol_status&DOL_S_ORIG))
+ {
+ ERROR0(ERR_WARNING,"Can't add CT-CODE to modified DOL: %s\n",str->fname);
+ return 0;
+ }
+
+ ct_dol_ext_t *ce = GetCtExtDOL(str->mode);
+ if (!ce)
+ {
+ ERROR0(ERR_WARNING,"Can't add CT-CODE: %s\n",str->fname);
+ return 0;
+ }
+
+ BZ2Source_t *bs = &ce->t2_src;
+ SearchBZ2S(bs,ct_dir_list.field,ct_dir_list.used,SEA_LOG_OFF);
+ if ( !bs->data || !bs->size )
+ {
+ missing_ctcode:;
+ ccp sem = strchr(bs->search,';');
+ ERROR0(ERR_WARNING,
+ "Missing %.*s => Can't add CT-CODE: %s\n\n",
+ (int)( sem ? sem - bs->search : strlen(bs->search) ),
+ bs->search, str->fname );
+ return 0;
+ }
+
+ bs = &ce->d8_src;
+ SearchBZ2S(&ce->d8_src,ct_dir_list.field,ct_dir_list.used,SEA_LOG_OFF);
+ if ( !bs->data || !bs->size )
+ goto missing_ctcode;
+
+ const BZ2Source_t *t2 = &ce->t2_src;
+ const BZ2Source_t *d8 = &ce->d8_src;
+
+
+ //--- check data address
+
+ bool move_d8 = opt_move_d8 || opt_sect_list.used;
+ u32 d8_addr = move_d8 ? 0x802a8000 : ce->d8_addr;
+
+ dol_header_t *dh = (dol_header_t*)str->data;
+ if (CountOverlappedDolSections(dh,d8_addr,d8->size))
+ {
+ move_d8 = true;
+ d8_addr = 0x802a8000;
+ if (CountOverlappedDolSections(dh,d8_addr,d8->size))
+ {
+ ERROR0(ERR_WARNING,"No space for CTCODE/D8 section: %s\n",str->fname);
+ return 0;
+ }
+ }
+
+ if (CountOverlappedDolSections(dh,ce->t2_addr,t2->size))
+ {
+ ERROR0(ERR_WARNING,"No space for CTCODE/T2 section: %s\n",str->fname);
+ return 0;
+ }
+
+ dol_sect_info_t t2info;
+ if (!FindFirstFreeDolSection(&t2info,dh,0))
+ {
+ err2sect:
+ ERROR0(ERR_WARNING,"Need 2 unused sections for CTCODE: %s\n",str->fname);
+ return 0;
+ }
+ const int T_SECT = t2info.section;
+ dh->sect_size[T_SECT] = htonl(t2->size); // mark used for next search
+
+ dol_sect_info_t d8info;
+ if (!FindFirstFreeDolSection(&d8info,dh,3))
+ {
+ dh->sect_size[T_SECT] = htonl(0);
+ goto err2sect;
+ }
+ const int D_SECT = d8info.section;
+
+
+ //--- logging
+
+ uint offset = str->data_size;
+ if ( verbose > 0 )
+ {
+ fprintf(stdlog,
+ "- Create section %s [%08x..%08x], file offset 0x%06x, size 0x%x\n",
+ t2info.name, ce->t2_addr,
+ ce->t2_addr + t2->size, offset, t2->size );
+ fprintf(stdlog,
+ "- Create section %s [%08x..%08x], file offset 0x%06x, size 0x%x\n",
+ d8info.name, ce->d8_addr,
+ ce->d8_addr + d8->size, offset + t2->size, d8->size );
+ }
+
+
+ //--- assign data and add section
+
+ str->data_size += t2->size + d8->size;
+ str->data = REALLOC(str->data,str->data_size);
+ dh = (dol_header_t*)str->data;
+
+ PRINT("%s: off=%6x, addr=%8x, size=%6x\n",t2info.name,offset,ce->t2_addr,t2->size);
+ memcpy( str->data + offset, t2->data, t2->size );
+ write_be32( str->data + offset + 0x14, d8_addr );
+ dh->sect_off [T_SECT] = htonl(offset);
+ dh->sect_addr[T_SECT] = htonl(ce->t2_addr);
+ dh->sect_size[T_SECT] = htonl(t2->size);
+
+ offset += t2->size;
+ PRINT("%s: off=%6x, addr=%8x, size=%6x\n",d8info.name,offset,ce->d8_addr,d8->size);
+ memcpy( str->data + offset, d8->data, d8->size );
+ dh->sect_off [D_SECT] = htonl(offset);
+ dh->sect_addr[D_SECT] = htonl(d8_addr);
+ dh->sect_size[D_SECT] = htonl(d8->size);
+
+ memcpy(&dh->entry_addr,t2->data+0x0c,4);
+
+ ClearBZ2S(&ce->t2_src,1);
+ ClearBZ2S(&ce->d8_src,1);
+ str->dol_flags[DPF_CTCODE] = 'C';
+ str->dol_info_flags |= DIF_CTCODE;
+ return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint PatchDOL
+(
+ // returns: number of patches
+
+ staticr_t * str // valid pointer
+)
+{
+ DASSERT(str);
+ if (!str->data_size)
+ return 0;
+ DASSERT(str->data);
+ DASSERT(str->is_dol);
+ if (!str->is_dol)
+ return 0;
+
+ uint clean_count = 0;
+ if ( opt_clean_dol && CleanDol(str) )
+ clean_count++;
+
+ if (!(str->dol_status&DOL_S_ANALYZED))
+ AnalyzeDOL(str,false);
+
+ InititializeGCT();
+ if ( opt_wcode >= WCODE_ENABLED )
+ {
+ if (wifi_domain_is_wiimmfi)
+ {
+ BZ2Manager_t *wcm = GetWiimmfiCode(str->mode);
+ if (wcm)
+ {
+ DecodeBZIP2Manager(wcm);
+ if ( opt_wcode == WCODE_GECKO )
+ {
+ str->dol_flags[DPF_WCODE] = 'w';
+ AddCheatCode(wcm->data,wcm->size,true,"WCODE");
+ }
+ else
+ {
+ str->dol_flags[DPF_WCODE] = 'W';
+ AddWCode(wcm->data,wcm->size);
+ }
+ }
+ }
+
+ uint i;
+ for ( i = 0; i < opt_wcode_list.used; i++ )
+ if (AddWCodeFile(str,opt_wcode_list.field[i]))
+ opt_wcode_list.field[i] = 0;
+ }
+
+ uint patch_count = 0;
+ patch_count += PatchHTTPS(str);
+ patch_count += PatchCTCODEDOL(str);
+ patch_count += AddSectionsDOL(str,false);
+ patch_count += CreateSectionDOL(str);
+ patch_count += PatchByListWPF(str);
+
+ if (use_gct_loader)
+ {
+ enumError err = SetupCodeLoader(str,0,0,0,0,CHT_NONE,0);
+ if ( err == ERR_OK || err == ERR_WARNING )
+ patch_count++;
+ }
+
+
+ //-- sdkver by region
+
+ if ( opt_str_vs_region >= 0 )
+ {
+ const u32 vers_addr = GetSdkverAddress(str->mode);
+ if (vers_addr)
+ {
+ dol_header_t *dh = (dol_header_t*)str->data;
+ const u32 offset = GetDolOffsetByAddr(dh,vers_addr,8,0);
+ if (offset)
+ {
+ u8 old[8];
+ memcpy(old,str->data+offset,sizeof(old));
+
+ char buf[20];
+ snprintf(buf,sizeof(buf), "%05u", opt_str_vs_region & 0xffff);
+ memcpy(str->data+offset+1,buf,6);
+ if ( str->data[offset] == '0' )
+ str->data[offset]++;
+
+ if (memcmp(old,str->data+offset,sizeof(old)))
+ patch_count++;
+ }
+ }
+ }
+
+
+ //--- patch second host
+
+ bool wiimmfi_patched = false;
+ if ( wifi_domain_is_wiimmfi || opt_wcode >= WCODE_ENABLED )
+ {
+ u32 *h2 = GetSecondHostPointer(str,0);
+ noPRINT("h2=%p %x\n",h2,h2?ntohl(*h2):0);
+ if ( h2 && ntohl(*h2) != ASM_NOP )
+ {
+ write_be32(h2,ASM_NOP);
+ patch_count++;
+ wiimmfi_patched = true;
+ }
+ }
+
+
+ //-- patched by
+
+ patched_by_t pb_mode = patched_by_mode;
+ switch((int)pb_mode)
+ {
+ case PBY_WIIMMFI:
+ if (wiimmfi_patched)
+ pb_mode = PBY_ALWAYS;
+ break;
+
+ case PBY_AUTO:
+ if (patch_count)
+ pb_mode = PBY_ALWAYS;
+ break;
+ }
+
+ if ( pb_mode == PBY_RESET )
+ {
+ char *dest = GetIdPointer(str);
+ if (dest)
+ memset(dest,'*',PATCHED_BY_SIZE);
+ }
+ else if ( pb_mode == PBY_ALWAYS )
+ {
+ char *dest = GetIdPointer(str);
+ if (dest)
+ {
+ char *end = dest + PATCHED_BY_SIZE;
+ if (*str->dol_flags)
+ dest += snprintf( dest, PATCHED_BY_SIZE, "%.*s [%s]",
+ PATCHED_BY_SIZE - 3 - DPF__N,
+ opt_patched_by, str->dol_flags );
+ else
+ dest = StringCopyE(dest,end,opt_patched_by);
+ while ( dest < end )
+ *dest++ = ' ';
+ }
+ }
+
+ return clean_count + patch_count;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// patch via WPF ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+uint PatchByListWPF
+(
+ staticr_t *str // valid pointer
+)
+{
+ DASSERT(str);
+ DASSERT(str->data_size);
+ DASSERT(str->data);
+
+ uint i, count = 0;
+ for ( i = 0; i < opt_wpf_list.used; i++ )
+ {
+ ccp fname = opt_wpf_list.field[i];
+ if ( fname && *fname )
+ {
+ const int stat = PatchByFileWPF(str,fname);
+ if ( stat < 0 )
+ {
+ FreeString(opt_wpf_list.field[i]);
+ opt_wpf_list.field[i] = 0;
+ }
+ else if ( stat > 0 )
+ count++;
+ }
+ }
+
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int PatchByFileWPF
+(
+ staticr_t *str, // valid pointer
+ ccp fname
+)
+{
+ DASSERT(str);
+ DASSERT(fname);
+
+
+ //--- load file
+
+ u8 *data = 0;
+ uint size = 0;
+ if (OpenReadFILE(fname,0,true,&data,&size,0,0))
+ {
+ ERROR0(ERR_WARNING,"Can't load WPF file: %s\n",fname);
+ return -1;
+ }
+
+
+ //--- analyse file
+
+// [[analyse-magic]]
+ const file_format_t fform = GetByMagicFF(data,size,size);
+ PRINT(">>> [%s,%u]: %s\n",GetNameFF(fform,0),size,fname);
+ bool done = false;
+ int stat = -1;
+
+ if ( size > sizeof(wpf_head_t) && be32(data+4) <= size )
+ {
+ wpf_t *wpf = (wpf_t*)(data+sizeof(wpf_head_t));
+ if ( fform == FF_WPF )
+ {
+ done = true;
+ stat = PatchByWPF( str, wpf, size-sizeof(wpf_head_t), fname );
+ }
+
+ #if HAVE_XSRC
+ if ( fform == FF_XPF )
+ {
+ done = true;
+ stat = PatchByXPF( str, wpf, size-sizeof(wpf_head_t), fname );
+ }
+ #endif
+ }
+
+ if (!done)
+ ERROR0(ERR_WARNING,"Not a WPF file:: %s\n",fname);
+ FREE(data);
+ return stat;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int PatchByWPF
+(
+ staticr_t *str, // valid pointer
+ const wpf_t *wpf, // wpf patch list
+ uint size, // size of data beginning at 'wpf'
+ ccp fname // filename for error messages
+)
+{
+ DASSERT(str);
+ DASSERT(fname);
+ printf("PatchByWPF(): %s\n",fname);
+
+ HexDump16(stdout,0,0,wpf, size < 32 ? size : 32);
+ return -1;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// options ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+bool opt_clean_dol = false; // true: remove additional DOL sections
+bool opt_add_ctcode = false; // true: ad CT-CODE to main.dol
+int opt_all_ranks = 0; // -1:off, 0:don't patch, 1:on
+bool opt_move_d8 = false; // true: move CT-CODE D8 section to another place
+bool opt_full_gch = false; // true: use full- of only- 'codehandler'
+
+bool opt_gct_scan_sep = true; // true: scan 'F0000001 0000xxxx' separators
+bool opt_gct_sep = false; // true: search cheat code separators
+bool opt_gct_asm_sep = false; // true: use beginning of ASM blocks as separator
+uint opt_gct_list = false; // >0: print a summary / >1: be verbose
+int opt_gct_move = OFFON_AUTO; // move GCT to heap
+u32 opt_gct_addr = 0x802c0000; // address for opt_gct_move
+u32 opt_gct_space = 0; // minimal code space for opt_gct_move
+int opt_allow_user_gch = OFFON_AUTO; // allow user define Gecko Code Handler
+
+StringField_t opt_sect_list = {0}; // files with section data of type GCT|GCH|WCH
+StringField_t opt_wpf_list = {0}; // files with patches of type WPF|XPF
+StringField_t opt_wcode_list = {0}; // GCT files for wcode jobs
+
+int opt_str_vs_region = SREG_NONE; // versus region patch mode
+bool opt_str_xvs_region = false; // true: patch also test offsets
+bool opt_str_tvs_region = false; // true: enable test mode with incremented regions
+
+int opt_str_bt_region = SREG_NONE; // battle region patch mode
+bool opt_str_xbt_region = false; // true: patch also test offsets
+bool opt_str_tbt_region = false; // true: enable test mode with incremented regions
+
+bool opt_meno = false; // true: patch 'Menu*' to 'MenO*'
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptRegionHelper ( ccp arg, bool is_battle, ccp prefix )
+{
+ region_info_t ri;
+ SetupRegionInfo(&ri,0,is_battle);
+
+ DASSERT(ri.opt_region);
+ if ( !arg || !*arg )
+ {
+ *ri.opt_region = SREG_NONE;
+ return 0;
+ }
+
+ DASSERT(ri.opt_x);
+ *ri.opt_x = *arg == 'x' || *arg == 'X';
+ if (*ri.opt_x)
+ arg++;
+
+ DASSERT(ri.opt_t);
+ *ri.opt_t = ( *arg == 't' || *arg == 'T' ) && arg[1] >= '0' && arg[1] <= '9';
+ if (*ri.opt_t)
+ arg++;
+
+ if ( *arg >= '0' && *arg <= '9' )
+ {
+ u32 num;
+ const enumError err =
+ ScanSizeOptU32(
+ &num, // u32 * num
+ arg, // ccp source
+ 1, // default_factor1
+ 0, // int force_base
+ "region", // ccp opt_name
+ 0, // u64 min
+ 0xffff, // u64 max
+ 1, // u32 multiple
+ 0, // u32 pow2
+ false // bool print_err
+ );
+
+ if ( err == ERR_OK )
+ {
+ *ri.opt_region = num;
+ return 0;
+ }
+ }
+
+ static const KeywordTab_t tab[] =
+ {
+ { SREG_JAPAN, "JAPAN", 0, 0 },
+ { SREG_AMERICA, "AMERICA", "USA", 0 },
+ { SREG_EUROPE, "EUROPE", 0, 0 },
+
+ { SREG_AUSTRALIA, "AUSTRALIA", "OCEANIA", 0 },
+ { SREG_KOREA, "KOREA", 0, 0 },
+ { SREG_TAIWAN, "TAIWAN", 0, 0 },
+ { SREG_CHINA, "CHINA", 0, 0 },
+
+ { SREG_NONE, "NONE", 0, 0 },
+ { SREG_RESTORE, "RESTORE", "NINTENDO", 0 },
+ { SREG_TEST, "TEST", 0, 9001 },
+ { 0,0,0,0 }
+ };
+
+ const KeywordTab_t * cmd = ScanKeyword(0,arg,tab);
+ if (cmd)
+ {
+ *ri.opt_region = cmd->id;
+ if ( *ri.opt_region == SREG_TEST )
+ {
+ *ri.opt_region = cmd->opt;
+ *ri.opt_t = true;
+ }
+ return 0;
+ }
+
+ ERROR0(ERR_SYNTAX,"Invalid `region' mode (option --%sregion): '%s'\n",prefix,arg);
+ return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptVersusRegion ( ccp arg )
+{
+ return ScanOptRegionHelper(arg,false,"vs-");
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptBattleRegion ( ccp arg )
+{
+ return ScanOptRegionHelper(arg,true,"bt-");
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptRegion ( ccp arg )
+{
+ const int stat = ScanOptRegionHelper(arg,false,"");
+
+ opt_str_bt_region = opt_str_vs_region;
+ opt_str_xbt_region = opt_str_xvs_region;
+ opt_str_tbt_region = opt_str_tvs_region;
+ if (opt_str_tbt_region)
+ opt_str_bt_region += N_TAB_VS_REGION_PATCH;
+
+ return stat;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptAllRanks ( ccp arg )
+{
+ if ( !arg || !*arg )
+ {
+ opt_all_ranks = 1;
+ return 0;
+ }
+
+ static const KeywordTab_t tab[] =
+ {
+ { -1, "RETORE", "NINTENDO", 0 },
+ { -1, "RESET", 0, 0 }, // [obsolete]] in 2015
+ { 0, "NONE", 0, 0 },
+ { 1, "ON", 0, 0 },
+ { 0,0,0,0 }
+ };
+
+ const KeywordTab_t * cmd = ScanKeyword(0,arg,tab);
+ if (cmd)
+ {
+ opt_all_ranks = cmd->id;
+ return 0;
+ }
+
+ ERROR0(ERR_SYNTAX,"Invalid `all-ranks' mode (option --all-ranks): '%s'\n",arg);
+ return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+const KeywordTab_t HttpsOptions[] =
+{
+ { HTTPS_NONE, "NONE", 0, 0 },
+ { HTTPS_RESTORE, "RESTORE", "NINTENDO", 0 },
+ { HTTPS_HTTP, "HTTP", 0, 0 },
+ { HTTPS_DOMAIN, "DOMAIN", 0, 0 },
+ { HTTPS_SAKE0, "SAKE0", "S0", 0 },
+ { HTTPS_SAKE1, "SAKE1", "S1", 0 },
+ { 0,0,0,0 }
+};
+
+https_mode_t opt_https;
+bool opt_wc24 = false;
+char mkw_domain[100] = "mariokartwii.gs.wiimmfi.de";
+ccp wifi_domain = mkw_domain + 16;
+bool wifi_domain_is_wiimmfi = false;
+wcode_t opt_wcode = WCODE_AUTO;
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptHttps ( ccp arg )
+{
+ const KeywordTab_t *cmd = ScanKeyword(0,arg,HttpsOptions);
+ if (cmd)
+ {
+ opt_https = cmd->id;
+ return 0;
+ }
+
+ ERROR0(ERR_SYNTAX,"Invalid mode for option --https: '%s'\n",arg);
+ return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ccp GetHttpsName ( https_mode_t mode, ccp return_if_failed )
+{
+ const KeywordTab_t *cmd;
+ for ( cmd = HttpsOptions; cmd->name1; cmd++ )
+ if ( cmd->id == mode )
+ return cmd->name1;
+ return return_if_failed;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptDomain ( ccp https, ccp arg )
+{
+ if (arg)
+ snprintf(mkw_domain,sizeof(mkw_domain),"mariokartwii.gs.%s",arg);
+
+ int stat = https ? ScanOptHttps(https) : 0;
+ wifi_domain_is_wiimmfi = opt_https >= HTTPS_DOMAIN
+ && ( !strcmp(wifi_domain,"wiimmfi.de")
+ || !strcmp(wifi_domain,"test.wiimmfi.de") );
+ return stat;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const KeywordTab_t wcode_keytab[] =
+{
+ { WCODE_OFF, "OFF", "0", 0 },
+ { WCODE_AUTO, "AUTO", "DEFAULT", 0 },
+ { WCODE_GECKO, "GECKO", "CHEAT", 0 },
+ { WCODE_ON, "ON", "1", 0 },
+ { 0,0,0,0 }
+};
+
+int ScanOptWCode ( ccp arg )
+{
+ if (!arg)
+ {
+ opt_wcode = WCODE_ON;
+ return 0;
+ }
+
+ int status;
+ const KeywordTab_t *key = ScanKeyword(&status,arg,wcode_keytab);
+ if (key)
+ {
+ opt_wcode = key->id;
+ return 0;
+ }
+
+ PrintKeywordError(wcode_keytab,arg,status,0,"option --wcode");
+ return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptPatchedBy ( ccp arg )
+{
+ if (!*arg)
+ arg = "";
+
+ while ( *arg > 0 && *arg <= ' ' )
+ arg++;
+
+ if (!*arg)
+ StringCopyS(opt_patched_by,sizeof(opt_patched_by),STD_PATCHED_BY);
+ else if ( *arg == '+' )
+ snprintf(opt_patched_by,sizeof(opt_patched_by),
+ "%s [%s]",STD_PATCHED_BY,arg+1);
+ else
+ StringCopyS(opt_patched_by,sizeof(opt_patched_by),arg);
+
+ uchar *ptr;
+ for ( ptr = (uchar*)opt_patched_by; *ptr; ptr++ )
+ if ( *ptr < ' ' || *ptr == 0xa0 )
+ *ptr = ' ';
+
+ noPRINT("< %s\n> %s\n",arg,opt_patched_by);
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum VsBtMode patch_vs_mode = VSBT_OFF;
+enum VsBtMode patch_bt_mode = VSBT_OFF;
+char patch_vs_str[2] = {0};
+char patch_bt_str[2] = {0};
+
+int ScanOptVS ( bool is_bt, bool allow_2, ccp arg )
+{
+ if (!arg)
+ return 0;
+
+ ccp std_str;
+ char *str;
+ enum VsBtMode *mode;
+ if (is_bt)
+ {
+ std_str = "bt";
+ str = patch_bt_str;
+ mode = &patch_bt_mode;
+ }
+ else
+ {
+ std_str = "vs";
+ str = patch_vs_str;
+ mode = &patch_vs_mode;
+ }
+
+ switch(strlen(arg))
+ {
+ case 0:
+ *mode = VSBT_OFF;
+ return 0;
+
+ case 1:
+ *mode = VSBT_PATCH;
+ str[0] = std_str[0];
+ str[1] = arg[0];
+ return 0;
+
+ case 2:
+ if (allow_2)
+ {
+ *mode = VSBT_PATCH;
+ str[0] = arg[0];
+ str[1] = arg[1];
+ return 0;
+ }
+ break;
+
+ case 4:
+ if (!strcasecmp(arg,"test"))
+ {
+ *mode = VSBT_TEST;
+ return 0;
+ }
+ break;
+ }
+
+ if (allow_2)
+ ERROR0(ERR_SYNTAX,"Only 1 or 2 characters allowed for option --%s2: %s\n",
+ std_str, arg );
+ else
+ ERROR0(ERR_SYNTAX,"Only 1 character allowed for option --%s: %s\n",
+ std_str, arg );
+ return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptCannon ( ccp arg )
+{
+ if ( !arg || !*arg )
+ {
+ memset(cannon_param,0,sizeof(cannon_param));
+ return 0;
+ }
+
+ char *end;
+ uint index = str2ul(arg,&end,10);
+ if ( end == arg )
+ return ERROR0(ERR_SYNTAX,"Option --cannon: Invalid argument: %s",arg);
+ if ( index >= CANNON_N )
+ return ERROR0(ERR_SYNTAX,"Option --cannon: Invalid index: %d",index);
+
+ cannon_param_t *cp = cannon_param + index;
+ memset(cp,0,sizeof(*cp));
+ const u8 * orig = CannonDataLEX+4 + index * CANNON_SIZE;
+ uint i;
+ for ( i = 0; i < CANNON_N_PARAM; i++, orig += sizeof(float) )
+ cp->param[i] = bef4(orig);
+
+ for ( i = 0; i < CANNON_N_PARAM; i++ )
+ {
+ arg = end;
+ while ( *arg == ' ' || *arg == '\t' || *arg == ',' )
+ arg++;
+ double d = strtod(arg,&end);
+ if ( end == arg )
+ break;
+ cp->param[i] = d;
+ }
+ if ( i > 0 )
+ cp->valid = true;
+
+ #if HAVE_PRINT
+ {
+ uint i;
+ cannon_param_t *cp = cannon_param;
+ for ( i = 0; i < CANNON_N; i++, cp++ )
+ PRINT(">CANNON[%u]: valid=%d, %10f %10f %10f %10f\n",
+ i, cp->valid, cp->param[0], cp->param[1], cp->param[2], cp->param[3] );
+ }
+ #endif
+
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// Option --create-sect ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+dol_sect_select_t section_list[DOL_N_SECTIONS];
+uint n_section_list = 0;
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptGctMove ( ccp arg )
+{
+ const int stat = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_FORCE,"Option --gct-move");
+ if ( stat == OFFON_ERROR )
+ return 1;
+
+ opt_gct_move = stat;
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptAllowUserGch ( ccp arg )
+{
+ const int stat
+ = ScanKeywordOffAutoOn(arg,OFFON_ON,OFFON_FORCE,"Option --allow-user-gch");
+ if ( stat == OFFON_ERROR )
+ return 1;
+
+ opt_allow_user_gch = stat;
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptGctAddr ( ccp arg )
+{
+ return ScanSizeOptU32(
+ &opt_gct_addr, // u32 * num
+ arg, // ccp source
+ 1, // default_factor1
+ 0, // int force_base
+ "gct-addr", // ccp opt_name
+ 0x00000000, // u64 min
+ 0x817fffff, // u64 max
+ 4, // u32 multiple
+ 0, // u32 pow2
+ true // bool print_err
+ ) != ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptGctSpace ( ccp arg )
+{
+ return ScanSizeOptU32(
+ &opt_gct_space, // u32 * num
+ arg, // ccp source
+ 1, // default_factor1
+ 0, // int force_base
+ "gct-space", // ccp opt_name
+ 0, // u64 min
+ 0x1000000, // u64 max
+ 4, // u32 multiple
+ 0, // u32 pow2
+ true // bool print_err
+ ) != ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int ScanOptCreateSect ( ccp arg )
+{
+ if ( !arg || !*arg )
+ return 0;
+
+ if ( n_section_list >= DOL_N_SECTIONS )
+ {
+ ERROR0(ERR_SEMANTIC,"Section list for --create-sect is full.\n");
+ return 1;
+ }
+
+
+ //--- check for 'P'
+
+ ccp arg0 = arg;
+ bool use_param = *arg == 'P' || *arg == 'p';
+ if (use_param)
+ {
+ arg++;
+ while ( *arg == ' ' || *arg == '\t' || *arg == ',' )
+ arg++;
+ }
+
+
+ //--- check for 'T' 'D' 'T#' 'D#'
+
+ int find_mode = 0, idx = -1;
+ if ( *arg == 'T' || *arg == 't' )
+ {
+ find_mode = 1;
+ arg++;
+ if ( *arg >= '0' && *arg < '0' + DOL_N_TEXT_SECTIONS )
+ idx = *arg++ - '0';
+ }
+ else if ( *arg == 'D' || *arg == 'd' )
+ {
+ find_mode = 2;
+ arg++;
+ if ( *arg >= '0' && *arg < '0' + DOL_N_DATA_SECTIONS )
+ idx = *arg++ - '0' + DOL_N_TEXT_SECTIONS;
+ }
+
+ if (find_mode)
+ {
+ ccp start = arg;
+ while ( *arg == ' ' || *arg == '\t' || *arg == ',' )
+ arg++;
+ if ( arg == start && *arg != '=' )
+ {
+ arg = arg0;
+ find_mode = 0;
+ idx = -1;
+ }
+ }
+
+ //--- check for an addr
+
+ char *end;
+ uint addr = str2ul(arg,&end,16);
+ if ( end > arg )
+ {
+ arg = end;
+ if (addr)
+ addr |= 0x80000000;
+ }
+ else
+ addr = 0;
+
+ if ( *arg != '=' )
+ {
+ arg = arg0;
+ use_param = false;
+ find_mode = 0;
+ idx = -1;
+ addr = 0;
+ }
+ else
+ arg++;
+
+ dol_sect_select_t *sl = section_list + n_section_list++;
+ memset(sl,0,sizeof(*sl));
+ sl->find_mode = find_mode;
+ sl->sect_idx = idx;
+ sl->use_param = use_param;
+ sl->addr = addr;
+ sl->fname = arg;
+
+ switch (sl->find_mode)
+ {
+ case 1:
+ sl->name[0] = 'T';
+ sl->name[1] = sl->sect_idx < 0 ? '*'
+ : '0' + sl->sect_idx;
+ break;
+
+ case 2:
+ sl->name[0] = 'D';
+ sl->name[1] = sl->sect_idx < 0 ? '*'
+ : '0' + sl->sect_idx - DOL_N_TEXT_SECTIONS;
+ break;
+
+ default:
+ sl->name[0] = '*';
+ sl->name[1] = '*';
+ break;
+ }
+
+ PRINT("CREATE-SECTION[%s,%c]: %u,%2d %#10x : %s\n",
+ sl->name, sl->use_param ? 'W' : '-',
+ sl->find_mode, sl->sect_idx, sl->addr, sl->fname );
+
+ return 0;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// DumpSTR() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError DumpSTR
+(
+ FILE * f, // dump to this file
+ int indent, // indention
+ staticr_t * str, // valid pointer
+ bool use_c // true: print in C language fragments
+)
+{
+ DASSERT(f);
+ DASSERT(str);
+
+ if (str->is_dol)
+ return DumpDOL(f,indent,str,use_c);
+
+ DASSERT(str->data);
+ DASSERT( str->data_size >= sizeof(rel_header_t) );
+
+ indent = NormalizeIndent(indent);
+
+
+ //----- dump header
+
+ rel_header_t hd;
+ be32n( (u32*)&hd, (u32*)str->data, sizeof(rel_header_t)/sizeof(u32) );
+
+ ccp mod_name = "-";
+ if ( hd.mod_name && hd.mod_name + hd.mod_name_len < str->data_size )
+ mod_name = (ccp)str->data + hd.mod_name;
+
+
+ fprintf(f,"%*sId: %6u = 0x%x\n", indent,"", hd.id, hd.id );
+ fprintf(f,"%*sN(sections): %6u\n", indent,"", hd.n_section );
+ fprintf(f,"%*sModule Name: %6s\n", indent,"", mod_name );
+ fprintf(f,"%*sReloc Offset: %6x\n", indent,"", hd.reloc_off );
+
+ fprintf(f,"\n%*sSection (0..%u):\n", indent,"", hd.n_section-1 );
+ rel_sect_info_t *si = (rel_sect_info_t*)( str->data + hd.section_off );
+ uint s;
+ for ( s = 0; s < hd.n_section; s++, si++ )
+ {
+ u32 size = be32(&si->size);
+ if (size)
+ {
+ u32 offset = be32(&si->offset);
+ if (!offset)
+ fprintf(f,"%*s%4u. BSS %7x\n", indent,"", s, size );
+ else
+ {
+ ccp type = offset & 1 ? "TEXT" : "DATA";
+ offset &= ~1;
+ fprintf(f,"%*s%4u. %s %7x : %6x .. %6x\n",
+ indent,"", s, type, size, offset, offset + size - 1 );
+ }
+ }
+ }
+
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// DumpDOL() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static enumError DumpDOL_c
+(
+ FILE * f, // dump to this file
+ staticr_t * str // valid pointer
+)
+{
+ DASSERT(f);
+ DASSERT(str);
+ DASSERT(str->data);
+ DASSERT( str->data_size >= sizeof(dol_header_t) );
+
+ dol_header_t dol;
+ if ( str->data_size < sizeof(dol) )
+ return ERR_WARNING;
+ ntoh_dol_header(&dol,(dol_header_t*)str->data);
+
+ fputs("\nstatic const dol_sect_info_t dol_info_xxx[DOL_NN_SECTIONS] =\n{\n",f);
+ char name[8];
+
+ uint sect;
+ for ( sect = 0; sect < DOL_NN_SECTIONS; sect++ )
+ {
+ dol_sect_info_t info;
+ GetSectionDOL(str,&info,sect,true);
+ snprintf(name,sizeof(name),"\"%s\",",info.name);
+ fprintf(f," { %2u, %-7s%#8x, %#10x, %#8x, %u,0,%u, 0,",
+ sect, name, info.off, info.addr, info.size,
+ info.sect_valid, info.hash_valid );
+
+ if (info.hash_valid)
+ {
+ u8 *ptr = info.hash;
+ uint line;
+ for ( line = 0; line < 2; line++ )
+ {
+ ccp sep = line ? ",\n\t\t " : "\n\t\t{ ";
+ uint idx;
+ for ( idx = 0; idx < sizeof(info.hash)/2; idx++ )
+ {
+ fprintf(f,"%s%s0x%02x",sep, idx==sizeof(info.hash)/4 ? " " : "", *ptr++ );
+ sep = ",";
+ }
+ }
+ fputs(" }},\n",f);
+ }
+ else
+ fputs(" {0} },\n",f);
+ }
+
+ fputs("};\n\n",f);
+ return ERR_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+enumError DumpDOL
+(
+ FILE * f, // dump to this file
+ int indent, // indention
+ staticr_t * str, // valid pointer
+ bool use_c // true: print in C language fragments
+)
+{
+ DASSERT(f);
+ DASSERT(str);
+
+ if (use_c)
+ return DumpDOL_c(f,str);
+
+ DASSERT(str->data);
+ DASSERT( str->data_size >= sizeof(dol_header_t) );
+
+ DumpDolHeader( f, indent, (dol_header_t*)str->data, str->data_size,
+ long_count > 1 ? 7 : long_count > 0 ? 3 : 1 );
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ExtractSTR() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ExtractSTR
+(
+ staticr_t * str, // valid pointer
+ ccp dest_dir // valid destination directory
+)
+{
+ DASSERT(str);
+ DASSERT(str->data);
+ DASSERT(dest_dir);
+
+ if (str->is_dol)
+ return ExtractDOL(str,dest_dir);
+
+ return ERROR0(ERR_WARNING,
+ "Extraction of StaticR.rel files is not implemented yet!\n");
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ExtractDOL() ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+enumError ExtractDOL
+(
+ staticr_t * str, // valid pointer
+ ccp dest_dir // valid destination directory
+)
+{
+ DASSERT(str);
+ DASSERT(str->data);
+ DASSERT(str->is_dol);
+ DASSERT(dest_dir);
+
+ CreatePath(dest_dir,true);
+
+ //--- open log file
+
+ char path_buf[PATH_MAX];
+ ccp path = PathCatPP(path_buf,sizeof(path_buf),dest_dir,"dol-setup.txt");
+ FILE *log = fopen(path,"wb");
+ if (!log)
+ return ERROR0(ERR_CANT_CREATE,
+ "Can't create setup file: %s\n",path);
+
+ static ccp sep = "#---------------------------------------------------------------------\n";
+ fprintf(log,"\n%s#sect offset address size sha1 checksum\n%s",sep,sep);
+
+
+ //--- extract 'dol-header.bin'
+
+ path = PathCatPP(path_buf,sizeof(path_buf),dest_dir,"dol-header.bin");
+ FILE *f = fopen(path,"wb");
+ if (!f)
+ {
+ fclose(log);
+ return ERROR0(ERR_CANT_CREATE,
+ "Can't create data file: %s\n",path);
+ }
+ fwrite(str->data,1,0x100,f);
+ fclose(f);
+
+
+ //--- extract sections
+
+ uint sect;
+ for ( sect = 0; sect < DOL_NN_SECTIONS; sect++ )
+ {
+ if ( sect == DOL_N_TEXT_SECTIONS || sect == DOL_N_SECTIONS )
+ fputs(sep,log);
+ dol_sect_info_t info;
+ GetSectionDOL(str,&info,sect,true);
+ if (!info.sect_valid)
+ {
+ //fprintf(log,"%-4s= -\n",info.name);
+ continue;
+ }
+
+ fprintf(log,"%-4s= %06x %08x %06x",info.name,info.off,info.addr,info.size);
+ if (info.data_valid)
+ {
+ if (info.hash_valid)
+ {
+ fputc(' ',log);
+ uint i;
+ for ( i = 0; i < sizeof(info.hash); i++ )
+ fprintf(log,"%02x",info.hash[i]);
+ }
+
+ path = PathCatPPE(path_buf,sizeof(path_buf),dest_dir,info.name,".bin");
+ FILE *f = fopen(path,"wb");
+ if (!f)
+ {
+ fclose(log);
+ return ERROR0(ERR_CANT_CREATE,
+ "Can't create data file: %s\n",path);
+ }
+ fwrite(info.data,1,info.size,f);
+ fclose(f);
+ }
+ fputc('\n',log);
+ }
+ fputs(sep,log);
+
+
+ //--- close log and return
+
+ fprintf(log,"\n");
+ fclose(log);
+ return ERR_OK;
+}
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// ctcode_data_tab[] ///////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#undef DEF_DATA
+#undef DEF_BZIP
+//#undef DEF_PATCH
+
+#define DEF_DATA(a) { #a, 0, a, sizeof(a) },
+#define DEF_BZIP(a) { #a, 1, a##_bz2, sizeof(a##_bz2) },
+//#define DEF_PATCH(m,a,b) { #a, m, b, sizeof(b), a },
+
+const data_tab_t maindol_data_tab[] =
+{
+ DEF_BZIP(ctcode_boot_code_pal)
+ DEF_BZIP(ctcode_boot_code_usa)
+ DEF_BZIP(ctcode_boot_code_jap)
+
+ DEF_BZIP(ctcode_boot_data_pal)
+ DEF_BZIP(ctcode_boot_data_usa)
+ DEF_BZIP(ctcode_boot_data_jap)
+
+ DEF_BZIP(codehandler)
+ DEF_BZIP(codehandleronly)
+
+ {0,0,0}
+};
+
+#undef DEF_DATA
+#undef DEF_BZIP
+//#undef DEF_PATCH
+
+//
+///////////////////////////////////////////////////////////////////////////////
+/////////////// END ///////////////
+///////////////////////////////////////////////////////////////////////////////
+