From 070b77548a99b8fdb6fe12c13440f984541fe5ee Mon Sep 17 00:00:00 2001 From: dimok789 <15055714+dimok789@users.noreply.github.com> Date: Sun, 6 May 2012 10:59:58 +0000 Subject: [PATCH] OK since the Wii System Menu Player is released now as I already mentioned the source for the loader will also be put on SVN again. Now that i am done with merging here is the MOD17 source. Changelog since last revision: - add banner/icon animation support for wii games / channels / gamecube - add custom banner for gamecube which loads the gamecube internal opening.bnr information (it is much better than the generic one of the system menu) (big thanks to diddy from the wiithemeteam for creating the brlyt/brlan for the banner) - add gamecube banner of system menu (not used right now since it sucks) - add banner grid of the system menu (resources loaded from nand) (with button highliter) - add big banner frame window of the system menu (resources loaded from nand). the banner window has buttons "Settings" and "Start" by default. if parental control is enabled the "Settings" button is replaced by "Back" - add play count text to the banner frame window (can be disabled as up until now in the gui settings) - add zoom in/out/scroll animations to banner grid and change into big frame window - add loading gc banners from ISO and DVD - add loading wii banners from DVD discs even if the game is not installed - add favorite level setting to the game settings since it is no longer on the banner frame window - add new settings section for banner settings - add option to choose between disc window and banner window - added back ehci module to support 2 usb drives and re-enabled the option - add option for 2 usb drives for d2x cIOS as well (will be supported in the future, not yet) - exchanged "GC Games" and "EmuNand Channels" on the game selection screen (more space for translators) - add and fixed gui numpad for entering numbers of any kind - add setting for tooltip delay time - fix reload of cover image on game list when only 1 item is left after a change (e.g. sort) - add use of old build in font for credits window because it doesnt look good with the system menu one - fixed effect bug when scrolling buttons in the settings and the wiimote is over button 1 - fixed bug where entering -1 (Use global) for cIOS was not possible in game settings - many source cleanups - add bnr chace setting - add DML configuration from the loader - removed creation of temporary "boot.bin" file - DML custom game paths. Additionally i added support for following forms of paths: 1. configpath/gamepath/game.iso 2. configpath/gamepath/game.gcm 3. configpath/gamepath/GAMEID6.iso 4. configpath/gamepath/GAMEID6.gcm Where gamepath can be anything you want inside the configure path in the custom path settings. I don't know if GCM files are allowed by DML but i just added support for it just in case. Changing path to USB is not blocked currently but games won't run from USB just keep that in mind. The "copy to SD before start" feature is not added yet. Listing the games should work though - DML cheats now work the same way as for wii, gct goes to "Cheatcode Path" as GAMEID.gct (same as on wii) - several DML internal configs are added as per game/global settings - added load of last cheat configuration from GCT files - added init of network into background thread so it is cancelable and has a timeout - added gc games copy from USB to SD on demand with choice menu to delete SD games to make space, SD games path can be chosen seperately from main gc path. all dupicate sd games are prefered then the main path to allow direct boot of games that are already on sd. main path can be on USB or anywhere else. - added gc disc dump functionality with multidisc, compress and align support (thanks to FIX94 and Overjoy for their disc dump source on which this is based) - added selection menu for gc multidisc which game to install - fixed loading game ID when it can't be read from path for GC games and with that loading the titles from GameTDB for them - lots of string handling improvements (thanks to gerbilsoft for his patch) - added gamecube banner cache loading (only loading) if available with GAMEID6.bnr or ID3.bnr in the banner cache path. wii games/channels also accept now ID3.bnr. - added several progress bar cancel buttons on some missing progress windows - improved calculation of progress speed. now it is always the last 15 secs average instead of overall progress speed. - added pulsing new icon on banner grid layout for new games - added resize of widescreen screenshots to 768 width - added mii extract on save game extract if the file does not yet exists - added mii extractor and SYSCONF extractor that always extracts and overwrites old files - changed wifi gecko to UDP socket - support for DML v1.2+ NOTE: Banner animation support requires AHBPROT flag to be enabled. If you use old HBC you should use our forwarder or update the HBC otherwise you wont have that support. I am hoping that now the themers and the translators can catch up with their stuff for the next upcomming official release. --- HBC/META.XML | 4 +- Languages/czech.lang | 319 ++- Languages/danish.lang | 329 ++- Languages/dutch.lang | 324 ++- Languages/english.lang | 299 ++- Languages/finnish.lang | 309 ++- Languages/french.lang | 329 ++- Languages/german.lang | 329 ++- Languages/hungarian.lang | 314 ++- Languages/italian.lang | 329 ++- Languages/japanese.lang | 329 ++- Languages/korean.lang | 309 ++- Languages/norwegian.lang | 319 ++- Languages/polish.lang | 314 ++- Languages/portuguese_br.lang | 314 ++- Languages/portuguese_pt.lang | 329 ++- Languages/russian.lang | 314 ++- Languages/schinese.lang | 319 ++- Languages/spanish.lang | 326 ++- Languages/swedish.lang | 314 ++- Languages/tchinese.lang | 329 ++- Languages/thai.lang | 314 ++- Languages/turkish.lang | 314 ++- Makefile | 17 +- Themes/Default.them | 335 ++- data/{ => binary}/app_booter.bin | Bin data/binary/custom_banner.bnr | Bin 0 -> 42064 bytes data/{ => binary}/stub.bin | Bin data/images/arrangeBannerGrid.png | Bin 0 -> 3481 bytes data/images/arrangeBannerGrid_gray.png | Bin 0 -> 900 bytes data/images/gc_banner_bg.png | Bin 0 -> 9875 bytes data/images/gc_icon_bg.png | Bin 0 -> 6985 bytes data/sounds/gc_banner.ogg | Bin 0 -> 40971 bytes filelist.sh | 2 +- gui.pnproj | 2 +- source/BoxCover/BoxCover.cpp | 12 +- source/Channels/channels.cpp | 21 +- source/Channels/channels.h | 2 +- source/Controls/DeviceHandler.hpp | 26 +- source/Controls/WiiPointer.cpp | 59 +- source/Controls/WiiPointer.h | 1 + source/FileOperations/DirList.cpp | 6 +- source/FileOperations/DirList.h | 14 +- source/FileOperations/fileops.cpp | 215 +- source/FileOperations/fileops.h | 2 + source/FreeTypeGX.cpp | 8 +- source/GUI/GuiBannerGrid.cpp | 560 +++++ source/GUI/GuiBannerGrid.h | 95 + source/GUI/gui.h | 47 +- source/GUI/gui_button.cpp | 53 +- source/GUI/gui_checkboxbrowser.cpp | 12 +- source/GUI/gui_checkboxbrowser.hpp | 6 +- source/GUI/gui_element.cpp | 42 +- source/GUI/gui_gamebrowser.h | 10 +- source/GUI/gui_gamecarousel.cpp | 4 +- source/GUI/gui_gamegrid.cpp | 6 +- source/GUI/gui_image_async.cpp | 2 +- source/GUI/gui_keyboard.cpp | 10 +- source/GUI/gui_numpad.cpp | 148 +- source/GUI/gui_numpad.h | 66 + source/GUI/gui_searchbar.cpp | 2 +- source/GUI/gui_text.cpp | 13 +- source/GameCube/DML_Config.h | 67 + source/GameCube/GCDumper.cpp | 394 ++++ source/GameCube/GCDumper.hpp | 86 + source/GameCube/GCGames.cpp | 372 +++- source/GameCube/GCGames.h | 18 +- source/SoundOperations/gui_bgm.cpp | 8 +- source/SoundOperations/gui_sound.cpp | 76 +- source/SoundOperations/gui_sound.h | 7 +- source/StartUpProcess.cpp | 30 +- source/SystemMenu/BannerFrame.cpp | 115 + source/SystemMenu/BannerFrame.h | 53 + source/SystemMenu/GCBanner.cpp | 43 + source/SystemMenu/GCBanner.h | 29 + source/SystemMenu/StaticFrame.cpp | 42 + source/SystemMenu/StaticFrame.h | 29 + source/SystemMenu/SystemMenuResources.cpp | 314 +++ source/SystemMenu/SystemMenuResources.h | 87 + source/ZipFile.cpp | 9 +- source/banner/Animator.cpp | 260 +++ source/banner/Animator.h | 169 ++ source/banner/Banner.cpp | 175 ++ source/banner/Banner.h | 55 + source/banner/BannerAsync.cpp | 234 ++ source/banner/BannerAsync.h | 64 + source/banner/BannerTools.h | 62 + source/banner/CustomBanner.cpp | 138 ++ source/banner/CustomBanner.h | 43 + source/banner/Layout.cpp | 391 ++++ source/banner/Layout.h | 144 ++ source/banner/Material.cpp | 591 +++++ source/banner/Material.h | 253 +++ source/banner/OpeningBNR.cpp | 468 +++- source/banner/OpeningBNR.hpp | 68 +- source/banner/Pane.cpp | 165 ++ source/banner/Pane.h | 143 ++ source/banner/Picture.cpp | 34 + source/banner/Picture.h | 40 + source/banner/QuadPane.cpp | 126 ++ source/banner/QuadPane.h | 73 + source/banner/Textbox.cpp | 280 +++ source/banner/Textbox.h | 85 + source/banner/Texture.cpp | 149 ++ source/banner/Texture.h | 102 + source/banner/WiiFont.cpp | 387 ++++ source/banner/WiiFont.h | 226 ++ source/banner/Window.cpp | 51 + source/banner/Window.h | 65 + source/cheats/cheatmenu.cpp | 87 +- source/cheats/gct.cpp | 320 ++- source/cheats/gct.h | 56 +- source/gecko.c | 11 +- source/homebrewboot/HomebrewBrowser.cpp | 2 +- source/input.cpp | 2 +- source/libs/libwbfs/gcdisc.c | 144 ++ source/libs/libwbfs/gcdisc.h | 32 + source/lstub.cpp | 12 +- source/memory/mem2.h | 2 + source/memory/memory.h | 2 +- source/menu.cpp | 9 +- source/menu/GameBrowseMenu.cpp | 231 +- source/menu/GameBrowseMenu.hpp | 7 + source/menu/WDMMenu.cpp | 2 +- source/menu/menu_install.cpp | 187 +- source/menu/menu_partition_selection.cpp | 2 +- source/mload/modules/ehcmodule_5.c | 1917 +++++++++-------- source/mload/modules/ehcmodule_5.h | 4 +- source/network/FileDownloader.cpp | 50 +- source/network/networkops.cpp | 6 +- source/patches/bca.c | 25 +- source/patches/gamepatches.c | 2 +- source/prompts/BannerWindow.cpp | 592 +++++ source/prompts/BannerWindow.hpp | 99 + source/prompts/CategoryPrompt.cpp | 3 +- source/prompts/CheckboxBrowserMenu.cpp | 116 + source/prompts/CheckboxBrowserMenu.h | 57 + source/prompts/DiscBrowser.cpp | 4 +- source/prompts/GCDeleteMenu.cpp | 124 ++ source/prompts/GCDeleteMenu.h | 34 + source/prompts/GCMultiDiscMenu.cpp | 103 + source/prompts/GCMultiDiscMenu.h | 33 + source/prompts/GameWindow.cpp | 79 +- source/prompts/GameWindow.hpp | 7 +- source/prompts/HomebrewPrompt.cpp | 6 +- source/prompts/ProgressWindow.cpp | 154 +- source/prompts/PromptWindow.cpp | 14 +- source/prompts/PromptWindows.cpp | 124 +- source/prompts/TitleBrowser.cpp | 6 +- source/prompts/filebrowser.cpp | 11 +- source/prompts/gameinfo.cpp | 82 +- source/prompts/gameinfo.h | 2 +- source/settings/CGameSettings.cpp | 136 +- source/settings/CGameSettings.h | 20 +- source/settings/CSettings.cpp | 446 ++-- source/settings/CSettings.h | 28 +- source/settings/SettingsEnums.h | 44 +- source/settings/SettingsPrompts.cpp | 40 +- source/settings/menus/BannerSettingsMenu.cpp | 179 ++ source/settings/menus/BannerSettingsMenu.hpp | 34 + source/settings/menus/CustomPathsSM.cpp | 51 + source/settings/menus/FeatureSettingsMenu.cpp | 85 +- source/settings/menus/FlyingButtonsMenu.cpp | 17 +- source/settings/menus/GCGameLoadSM.cpp | 305 +++ source/settings/menus/GCGameLoadSM.hpp | 44 + source/settings/menus/GUISettingsMenu.cpp | 79 +- source/settings/menus/GameLoadSM.cpp | 291 ++- source/settings/menus/GameSettingsMenu.cpp | 20 +- source/settings/menus/GlobalSettings.cpp | 54 +- source/settings/menus/HardDriveSM.cpp | 24 +- source/settings/menus/LoaderSettings.cpp | 96 +- source/settings/menus/UninstallSM.cpp | 2 +- source/sys.cpp | 24 +- source/system/IosLoader.cpp | 15 +- source/system/IosLoader.h | 3 + source/themes/CTheme.cpp | 93 +- source/themes/ThemeDownloader.cpp | 4 +- source/themes/ThemeMenu.cpp | 4 +- source/themes/filelist.h | 34 +- source/themes/gettheme.c | 4 +- source/usbloader/GameBooter.cpp | 132 +- source/usbloader/disc.c | 52 +- source/usbloader/usbstorage2.c | 6 +- source/usbloader/wbfs/wbfs_fat.cpp | 12 +- source/usbloader/wbfs/wbfs_fat.h | 2 +- source/usbloader/wbfs/wbfs_rw.c | 44 + source/usbloader/wbfs/wbfs_rw.h | 1 + source/utils/LanguageCode.c | 26 + source/utils/LanguageCode.h | 14 + source/utils/StringTools.c | 4 +- source/utils/ThreadedTask.cpp | 2 +- source/utils/U8Archive.cpp | 526 +++++ source/utils/U8Archive.h | 128 ++ source/utils/ash.cpp | 455 ++++ source/utils/ash.h | 30 + source/utils/gx_addons.c | 76 + source/utils/gx_addons.h | 35 + source/utils/lz77.c | 21 +- source/utils/lz77.h | 67 +- source/utils/timer.h | 10 +- source/utils/wifi_gecko.c | 8 +- source/video.cpp | 154 +- source/video.h | 4 + source/wad/nandtitle.h | 3 +- source/xml/GameTDB.cpp | 4 +- 205 files changed, 21266 insertions(+), 3135 deletions(-) rename data/{ => binary}/app_booter.bin (100%) create mode 100644 data/binary/custom_banner.bnr rename data/{ => binary}/stub.bin (100%) create mode 100644 data/images/arrangeBannerGrid.png create mode 100644 data/images/arrangeBannerGrid_gray.png create mode 100644 data/images/gc_banner_bg.png create mode 100644 data/images/gc_icon_bg.png create mode 100644 data/sounds/gc_banner.ogg create mode 100644 source/GUI/GuiBannerGrid.cpp create mode 100644 source/GUI/GuiBannerGrid.h create mode 100644 source/GUI/gui_numpad.h create mode 100644 source/GameCube/DML_Config.h create mode 100644 source/GameCube/GCDumper.cpp create mode 100644 source/GameCube/GCDumper.hpp create mode 100644 source/SystemMenu/BannerFrame.cpp create mode 100644 source/SystemMenu/BannerFrame.h create mode 100644 source/SystemMenu/GCBanner.cpp create mode 100644 source/SystemMenu/GCBanner.h create mode 100644 source/SystemMenu/StaticFrame.cpp create mode 100644 source/SystemMenu/StaticFrame.h create mode 100644 source/SystemMenu/SystemMenuResources.cpp create mode 100644 source/SystemMenu/SystemMenuResources.h create mode 100644 source/banner/Animator.cpp create mode 100644 source/banner/Animator.h create mode 100644 source/banner/Banner.cpp create mode 100644 source/banner/Banner.h create mode 100644 source/banner/BannerAsync.cpp create mode 100644 source/banner/BannerAsync.h create mode 100644 source/banner/BannerTools.h create mode 100644 source/banner/CustomBanner.cpp create mode 100644 source/banner/CustomBanner.h create mode 100644 source/banner/Layout.cpp create mode 100644 source/banner/Layout.h create mode 100644 source/banner/Material.cpp create mode 100644 source/banner/Material.h create mode 100644 source/banner/Pane.cpp create mode 100644 source/banner/Pane.h create mode 100644 source/banner/Picture.cpp create mode 100644 source/banner/Picture.h create mode 100644 source/banner/QuadPane.cpp create mode 100644 source/banner/QuadPane.h create mode 100644 source/banner/Textbox.cpp create mode 100644 source/banner/Textbox.h create mode 100644 source/banner/Texture.cpp create mode 100644 source/banner/Texture.h create mode 100644 source/banner/WiiFont.cpp create mode 100644 source/banner/WiiFont.h create mode 100644 source/banner/Window.cpp create mode 100644 source/banner/Window.h create mode 100644 source/libs/libwbfs/gcdisc.c create mode 100644 source/libs/libwbfs/gcdisc.h create mode 100644 source/prompts/BannerWindow.cpp create mode 100644 source/prompts/BannerWindow.hpp create mode 100644 source/prompts/CheckboxBrowserMenu.cpp create mode 100644 source/prompts/CheckboxBrowserMenu.h create mode 100644 source/prompts/GCDeleteMenu.cpp create mode 100644 source/prompts/GCDeleteMenu.h create mode 100644 source/prompts/GCMultiDiscMenu.cpp create mode 100644 source/prompts/GCMultiDiscMenu.h create mode 100644 source/settings/menus/BannerSettingsMenu.cpp create mode 100644 source/settings/menus/BannerSettingsMenu.hpp create mode 100644 source/settings/menus/GCGameLoadSM.cpp create mode 100644 source/settings/menus/GCGameLoadSM.hpp create mode 100644 source/utils/LanguageCode.c create mode 100644 source/utils/LanguageCode.h create mode 100644 source/utils/U8Archive.cpp create mode 100644 source/utils/U8Archive.h create mode 100644 source/utils/ash.cpp create mode 100644 source/utils/ash.h create mode 100644 source/utils/gx_addons.c create mode 100644 source/utils/gx_addons.h diff --git a/HBC/META.XML b/HBC/META.XML index 94994a36..f2d433ac 100644 --- a/HBC/META.XML +++ b/HBC/META.XML @@ -2,8 +2,8 @@ USB Loader GX USB Loader GX Team - 2.3 r1155 - 201202291948 + 2.3 r1166 + 201205061159 kbtextmaxlen means with term. '\0' { - txt[0] = keys[i]; - if (strlen(kbtextstr) < kbtextmaxlen - 1) // -1 --> kbtextmaxlen means with term. '\0' - { - kbtextstr[strlen(kbtextstr)] = txt[0]; - kbText->SetText(kbtextstr); - } - keyBtn[i]->SetState(STATE_SELECTED, t->chan); + int len = strlen(kbtextstr); + kbtextstr[len] = keys[i]; + kbtextstr[len+1] = 0; + kbText->SetText(kbtextstr); } + keyBtn[i]->SetState(STATE_SELECTED, t->chan); } } - - kbText->SetPosition(0, 53); } diff --git a/source/GUI/gui_numpad.h b/source/GUI/gui_numpad.h new file mode 100644 index 00000000..e8d03bbb --- /dev/null +++ b/source/GUI/gui_numpad.h @@ -0,0 +1,66 @@ +/**************************************************************************** + * Copyright (C) 2009 r-win + * Copyright (C) 2012 Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + ***************************************************************************/ +#ifndef GUI_NUMPAD_H_ +#define GUI_NUMPAD_H_ + +#include "gui.h" + +#define NUMPAD_BUTTONS 12 + +//!On-screen keyboard +class GuiNumpad: public GuiWindow +{ + public: + GuiNumpad(char * t, u32 max); + virtual ~GuiNumpad(); + const char *GetText() const { return kbtextstr; } + void Update(GuiTrigger * t); + protected: + u32 kbtextmaxlen; + char keys[NUMPAD_BUTTONS]; + char kbtextstr[256]; + GuiText * kbText; + + GuiText * keyBackText; + GuiImage * keyBackImg; + GuiButton * keyBack; + + GuiText * keyClearText; + GuiImage * keyClearImg; + GuiButton * keyClear; + + GuiButton * keyBtn[NUMPAD_BUTTONS]; + GuiImage * keyImg[NUMPAD_BUTTONS]; + GuiText * keyTxt[NUMPAD_BUTTONS]; + + GuiImage * keyTextboxImg; + + GuiImageData * keyTextbox; + GuiImageData * keyMedium; + + GuiTrigger * trigA; + GuiTrigger * trigB; +}; + +#endif diff --git a/source/GUI/gui_searchbar.cpp b/source/GUI/gui_searchbar.cpp index db89a4f9..26cae5c4 100644 --- a/source/GUI/gui_searchbar.cpp +++ b/source/GUI/gui_searchbar.cpp @@ -41,7 +41,7 @@ GuiSearchBar::GuiSearchBar() : { trig.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); trigB.SetButtonOnlyTrigger(-1, WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B, PAD_BUTTON_B); - SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); cnt = SearchChars.size(); buttons = new cSearchButton*[cnt]; diff --git a/source/GUI/gui_text.cpp b/source/GUI/gui_text.cpp index e65b4250..bd5e406e 100644 --- a/source/GUI/gui_text.cpp +++ b/source/GUI/gui_text.cpp @@ -46,7 +46,7 @@ GuiText::GuiText(const char * t, int s, GXColor c) textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; textScrollDelay = TEXT_SCROLL_DELAY; - alignmentHor = ALIGN_CENTRE; + alignmentHor = ALIGN_CENTER; alignmentVert = ALIGN_MIDDLE; if (t) @@ -75,7 +75,7 @@ GuiText::GuiText(const wchar_t * t, int s, GXColor c) textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; textScrollDelay = TEXT_SCROLL_DELAY; - alignmentHor = ALIGN_CENTRE; + alignmentHor = ALIGN_CENTER; alignmentVert = ALIGN_MIDDLE; if (t) @@ -240,6 +240,10 @@ void GuiText::SetFontSize(int s) void GuiText::SetMaxWidth(int width, int w) { + //! no need to reset timer on false set + if(wrapMode == w && maxWidth == width) + return; + LOCK( this ); maxWidth = width; @@ -524,10 +528,13 @@ void GuiText::WrapText() */ void GuiText::Draw() { - if (!text) return; + if (!text || (*text == 0)) return; if (!IsVisible()) return; + GX_LoadProjectionMtx(FSProjection2D, GX_ORTHOGRAPHIC); + GX_LoadPosMtxImm(FSModelView2D, GX_PNMTX0); + GXColor c = color; c.a = GetAlpha(); diff --git a/source/GameCube/DML_Config.h b/source/GameCube/DML_Config.h new file mode 100644 index 00000000..4cfa2cda --- /dev/null +++ b/source/GameCube/DML_Config.h @@ -0,0 +1,67 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef DML_CONFIG_H_ +#define DML_CONFIG_H_ + +#include + +#define DML_CONFIG_ADDRESS 0x80001700 +#define DML_CONFIG_ADDRESS_V1_2 0x81200000 +#define DML_MAGIC 0xD1050CF6 +#define DML_VERSION 0x00000001 + +enum DMLConfig +{ + DML_CFG_CHEATS = (1<<0), + DML_CFG_DEBUGGER = (1<<1), + DML_CFG_DEBUGWAIT = (1<<2), + DML_CFG_NMM = (1<<3), + DML_CFG_NMM_DEBUG = (1<<4), + DML_CFG_GAME_PATH = (1<<5), + DML_CFG_CHEAT_PATH = (1<<6), + DML_CFG_ACTIVITY_LED = (1<<7), + DML_CFG_PADHOOK = (1<<8), + DML_CFG_NODISC = (1<<9), + DML_CFG_BOOT_DISC = (1<<10), + DML_CFG_BOOT_DOL = (1<<11) +}; + +enum DMLVideoModes +{ + DML_VID_DML_AUTO = (0<<16), + DML_VID_FORCE = (1<<16), + DML_VID_NONE = (2<<16), + + DML_VID_FORCE_PAL50 = (1<<0), + DML_VID_FORCE_PAL60 = (1<<1), + DML_VID_FORCE_NTSC = (1<<2), + DML_VID_FORCE_PROG = (1<<3), + DML_VID_PROG_PATCH = (1<<4) + +}; + +typedef struct _DML_CFG +{ + u32 Magicbytes; // 0xD1050CF6 + u32 Version; // 0x00000001 + u32 VideoMode; + u32 Config; + char GamePath[255]; + char CheatPath[255]; +} DML_CFG; + +#endif diff --git a/source/GameCube/GCDumper.cpp b/source/GameCube/GCDumper.cpp new file mode 100644 index 00000000..a2ca45c9 --- /dev/null +++ b/source/GameCube/GCDumper.cpp @@ -0,0 +1,394 @@ +/*************************************************************************** + * Copyright (C) 2012 + * by OverjoY and FIX94 for Wiiflow + * + * Adjustments for USB Loader GX by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + ***************************************************************************/ +#include +#include +#include +#include +#include + +#include "GCDumper.hpp" +#include "FileOperations/fileops.h" +#include "language/gettext.h" +#include "prompts/ProgressWindow.h" +#include "usbloader/disc.h" +#include "usbloader/wdvd.h" +#include "usbloader/wbfs/wbfs_fat.h" +#include "usbloader/wbfs/wbfs_rw.h" +#include "utils/ShowError.h" +#include "utils/tools.h" +#include "gecko.h" + +static const u32 BUF_SIZE = (64*1024); + +GCDumper::GCDumper() + : force_align32(false) + , compressed(false) + , ReadBuffer((u8 *) memalign(32, ALIGN32(BUF_SIZE))) +{ + +} +GCDumper::~GCDumper() +{ + if(ReadBuffer) + free(ReadBuffer); +} + +s32 GCDumper::CopyDiscData(FILE *f, u64 offset, u32 length, u8 *buffer) +{ + u32 toread = 0; + u32 wrote = 0; + + while(length) + { + if(ProgressCanceled()) + return PROGRESS_CANCELED; + + ShowProgress(discWrote, discTotal); + + toread = std::min(length, BUF_SIZE); + s32 ret = __ReadDVDPlain(buffer, toread, offset); + if (ret < 0) + return ret; + + fwrite(buffer, 1, toread, f); + wrote += toread; + offset += toread; + length -= toread; + discWrote += toread; + } + return wrote; +} + +s32 GCDumper::ReadDiscHeader(void) +{ + if(!ReadBuffer) + return -1; + + s32 result = 0; + + struct discHdr *gcheader = (struct discHdr *) memalign(32, ALIGN32(sizeof(struct discHdr))); + if(!gcheader) + return -1; + + s32 ret = Disc_ReadHeader(gcheader); + if(ret < 0) { + free(gcheader); + return ret; + } + + if(memcmp(gcheader->id, "GCOPDV", 6) == 0) + { + while(result == 0) + { + __ReadDVDPlain(ReadBuffer, 0x10, 0x40+(gameOffsets.size()*4)); + u64 MultiGameOffset = ((u64)(*(u32*)ReadBuffer)) << 2ULL; + if(!MultiGameOffset) + break; + + ret = __ReadDVDPlain(gcheader, sizeof(struct discHdr), MultiGameOffset); + if(ret < 0) + result = -3; + + if(ReadDiscInfo(MultiGameOffset) < 0) + result = -4; + discHeaders.push_back(*gcheader); + gameOffsets.push_back(MultiGameOffset); + } + } + else + { + discHeaders.push_back(*gcheader); + gameOffsets.push_back(0); + + if(ReadDiscInfo(0) < 0) + result = -5; + } + + free(gcheader); + return result; +} + +int GCDumper::ReadDiscInfo(const u64 &game_offset) +{ + if(!ReadBuffer) + return -1; + + s32 ret = __ReadDVDPlain(ReadBuffer, 0x440, game_offset); + if(ret < 0) + return -2; + + u32 FSTOffset = *(u32*)(ReadBuffer+0x424); + u32 FSTSize = *(u32*)(ReadBuffer+0x428); + u32 GamePartOffset = *(u32*)(ReadBuffer+0x434); + u32 DataSize = *(u32*)(ReadBuffer+0x438); + u32 DiscSize = DataSize + GamePartOffset; + + u32 installSize = 0; + + if(!compressed) + { + installSize += DiscSize; + } + else + { + u8 *FSTBuffer = (u8 *)memalign(32, ALIGN32(FSTSize)); + + ret = __ReadDVDPlain(FSTBuffer, ALIGN32(FSTSize), game_offset+FSTOffset); + if(ret < 0) + { + free(FSTBuffer); + return -3; + } + + u8 *FSTable = (u8*)FSTBuffer; + u32 FSTEnt = *(u32*)(FSTable+0x08); + FST *fst = (FST *)(FSTable); + + installSize += (FSTOffset + FSTSize); + + u32 i; + u32 correction; + u32 align; + + for( i=1; i < FSTEnt; ++i ) + { + if( fst[i].Type ) { + continue; + } + else + { + for(align = 0x8000; align > 2; align/=2) + { + if((fst[i].FileOffset & (align-1)) == 0 || force_align32) + { + correction = 0; + while(((installSize+correction) & (align-1)) != 0) + correction++; + installSize += correction; + break; + } + } + installSize += fst[i].FileLength; + } + } + free(FSTBuffer); + } + + gameSizes.push_back(installSize); + return 0; +} + +s32 GCDumper::InstallGame(const char *installpath, u32 game) +{ + if(!ReadBuffer || game >= discHeaders.size() || game >= gameOffsets.size() || game >= gameSizes.size()) + return -1; + + const u64 &game_offset = gameOffsets[game]; + const struct discHdr &gcheader = discHeaders[game]; + + discWrote = 0; + discTotal = gameSizes[game]; + + //! check for enough free space + { + struct statvfs sd_vfs; + if(statvfs(installpath, &sd_vfs) != 0) + { + ShowError(tr("Could not get free device space for game.")); + return -102; + } + + if(((u64)sd_vfs.f_frsize * (u64)sd_vfs.f_bfree) < discTotal) + { + ShowError(tr("Not enough free space on device.")); + return -103; + } + } + + s32 ret = __ReadDVDPlain(ReadBuffer, 0x440, game_offset); + if(ret < 0) { + ShowError(tr("Disc read error.")); + return -2; + } + + u32 Disc = *(u8*)(ReadBuffer+0x06); + u32 ApploaderSize = *(u32*)(ReadBuffer+0x400); + u32 DOLOffset = *(u32*)(ReadBuffer+0x420); + u32 FSTOffset = *(u32*)(ReadBuffer+0x424); + u32 FSTSize = *(u32*)(ReadBuffer+0x428); + u32 GamePartOffset = *(u32*)(ReadBuffer+0x434); + u32 DataSize = *(u32*)(ReadBuffer+0x438); + u32 DOLSize = FSTOffset - DOLOffset; + u32 DiscSize = DataSize + GamePartOffset; + + u8 *FSTBuffer = (u8 *)memalign(32, ALIGN32(FSTSize)); + if(!FSTBuffer) { + ShowError(tr("Not enough memory for FST.")); + return -3; + } + + ret = __ReadDVDPlain(FSTBuffer, ALIGN32(FSTSize), game_offset+FSTOffset); + if(ret < 0) + { + free(FSTBuffer); + ShowError(tr("Disc read error.")); + return -3; + } + + char gametitle[65]; + snprintf(gametitle, sizeof(gametitle), "%s", gcheader.title); + Wbfs_Fat::CleanTitleCharacters(gametitle); + + char gamepath[512]; + snprintf(gamepath, sizeof(gamepath), "%s%s [%.6s]%s/", installpath, gametitle, gcheader.id, Disc ? "2" : ""); + + CreateSubfolder(gamepath); + + snprintf(gamepath, sizeof(gamepath), "%s%s [%.6s]%s/game.iso", installpath, gametitle, gcheader.id, Disc ? "2" : ""); + + FILE *f = fopen(gamepath, "wb"); + if(!f) + { + free(FSTBuffer); + ShowError(tr("Can't open file for write: %s"), gamepath); + return -4; + } + + u8 *FSTable = (u8*)FSTBuffer; + u32 FSTEnt = *(u32*)(FSTable+0x08); + + FST *fst = (FST *)(FSTable); + + gprintf("Dumping: %s %s\n", gcheader.title, compressed ? "compressed" : "full"); + + gprintf("Apploader size : %d\n", ApploaderSize); + gprintf("DOL offset : 0x%08x\n", DOLOffset); + gprintf("DOL size : %d\n", DOLSize); + gprintf("FST offset : 0x%08x\n", FSTOffset); + gprintf("FST size : %d\n", FSTSize); + gprintf("Num FST entries: %d\n", FSTEnt); + gprintf("Data Offset : 0x%08x\n", FSTOffset+FSTSize); + gprintf("Disc size : %d\n", DiscSize); + if(compressed) + gprintf("Compressed size: %d\n", discTotal); + + + gprintf("Writing %s\n", gamepath); + + s32 result = 0; + + ProgressCancelEnable(true); + StartProgress(tr("Installing Game Cube Game..."), gcheader.title, 0, true, true); + + if(compressed) + { + u32 align; + u32 correction; + u32 toread; + u32 wrote = 0; + + ret = CopyDiscData(f, game_offset, (FSTOffset + FSTSize), ReadBuffer); + if(ret < 0) + result = -3; + + wrote += (FSTOffset + FSTSize); + + for(u32 i = 1; (result == 0) && (i < FSTEnt); ++i) + { + if(ProgressCanceled()) { + result = PROGRESS_CANCELED; + break; + } + + if( fst[i].Type ) { + continue; + } + else + { + for(align = 0x8000; align > 2; align/=2) + { + if((fst[i].FileOffset & (align-1)) == 0 || force_align32) + { + correction = 0; + while(((wrote+correction) & (align-1)) != 0) + correction++; + + wrote += correction; + while(correction) + { + toread = std::min(correction, BUF_SIZE); + memset(ReadBuffer, 0, toread); + fwrite(ReadBuffer, 1, toread, f); + correction -= toread; + } + break; + } + } + ret = CopyDiscData(f, game_offset+fst[i].FileOffset, fst[i].FileLength, ReadBuffer); + if(ret < 0) { + result = -2; + break; + } + + fst[i].FileOffset = wrote; + wrote += ret; + } + } + + fseek(f, FSTOffset, SEEK_SET); + fwrite(fst, 1, FSTSize, f); + + gprintf("Done!! Disc old size: %d, disc new size: %d, saved: %d\n", DiscSize, wrote, DiscSize - wrote); + } + else + { + ret = CopyDiscData(f, game_offset, discTotal, ReadBuffer); + if( ret < 0 ) + result = -2; + else + gprintf("Done!! Disc size: %d\n", DiscSize); + } + + // Stop progress + ProgressStop(); + ProgressCancelEnable(false); + + free(FSTBuffer); + fclose(f); + + if(result < 0) + { + RemoveFile(gamepath); + char *pathPtr = strrchr(gamepath, '/'); + if(pathPtr) *pathPtr = 0; + RemoveFile(gamepath); + + if(result != PROGRESS_CANCELED) + ShowError(tr("Disc read error.")); + } + + return result; +} diff --git a/source/GameCube/GCDumper.hpp b/source/GameCube/GCDumper.hpp new file mode 100644 index 00000000..8c43d042 --- /dev/null +++ b/source/GameCube/GCDumper.hpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2012 + * by OverjoY and FIX94 for Wiiflow + * + * Adjustments for USB Loader GX by Dimok + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any + * damages arising from the use of this software. + * + * Permission is granted to anyone to use this software for any + * purpose, including commercial applications, and to alter it and + * redistribute it freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you + * must not claim that you wrote the original software. If you use + * this software in a product, an acknowledgment in the product + * documentation would be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and + * must not be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + ***************************************************************************/ + +#ifndef GCDUMPER_H_ +#define GCDUMPER_H_ + +#include +#include + +using namespace std; + +class GCDumper +{ +public: + GCDumper(); + ~GCDumper(); + s32 InstallGame(const char *installpath, u32 game); + s32 ReadDiscHeader(void); + int ReadDiscInfo(const u64 &game_offset); + void SetForceAlign(bool b) { force_align32 = b; } + void SetCompressed(bool b) { compressed = b; } + vector & GetDiscHeaders() { return discHeaders; } + vector & GetDiscSizes() { return gameSizes; } +private: + s32 CopyDiscData(FILE *f, u64 offset, u32 length, u8 *buffer); + + vector discHeaders; + vector gameSizes; + vector gameOffsets; + bool force_align32; + bool compressed; + u32 discWrote; + u32 discTotal; + u8 *ReadBuffer; + + typedef struct + { + union + { + struct + { + u32 Type :8; + u32 NameOffset :24; + }; + u32 TypeName; + }; + union + { + struct + { + u32 FileOffset; + u32 FileLength; + }; + struct + { + u32 ParentOffset; + u32 NextOffset; + }; + u32 entry[2]; + }; + } FST; +}; +#endif diff --git a/source/GameCube/GCGames.cpp b/source/GameCube/GCGames.cpp index ae058ca1..415e84d3 100644 --- a/source/GameCube/GCGames.cpp +++ b/source/GameCube/GCGames.cpp @@ -14,14 +14,21 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . ****************************************************************************/ +#include #include #include #include "GCGames.h" #include "FileOperations/fileops.h" #include "settings/GameTitles.h" #include "settings/CSettings.h" +#include "prompts/GCDeleteMenu.h" +#include "prompts/PromptWindows.h" +#include "prompts/ProgressWindow.h" +#include "language/gettext.h" #include "usbloader/wbfs/wbfs_fat.h" #include "utils/tools.h" +#include "menu.h" +#include "gecko.h" GCGames *GCGames::instance = NULL; @@ -48,11 +55,8 @@ const char *GCGames::GetPath(const char *gameID) const return ""; } -u32 GCGames::LoadGameList(const string &path) +void GCGames::LoadGameList(const string &path, vector &headerList, vector &pathList) { - PathList.clear(); - HeaderList.clear(); - struct discHdr tmpHdr; struct stat st; u8 id[8]; @@ -62,7 +66,7 @@ u32 GCGames::LoadGameList(const string &path) struct dirent *dirent; dir_iter = opendir(path.c_str()); - if (!dir_iter) return 0; + if (!dir_iter) return; while ((dirent = readdir(dir_iter)) != 0) { @@ -72,11 +76,6 @@ u32 GCGames::LoadGameList(const string &path) if (dirname[0] == '.') continue; - snprintf(fpath, sizeof(fpath), "%s/%s/game.iso", path.c_str(), dirname); - - if(stat(fpath, &st) != 0) - continue; - // reset id and title memset(id, 0, sizeof(id)); *fname_title = 0; @@ -99,29 +98,75 @@ u32 GCGames::LoadGameList(const string &path) if(isGameID(id)) { lay_a = true; - snprintf(fname_title, sizeof(fname_title), &dirname[7]); + snprintf(fname_title, sizeof(fname_title), "%s", &dirname[7]); } } } + else if(len == 6 && isGameID((u8*)dirname)) { + memcpy(id, dirname, 6); + lay_a = true; + } if(!lay_a && !lay_b) memset(id, 0, sizeof(id)); + bool found = false; + bool extracted = false; + + for(int i = 0; i < 4; i++) + { + char name[50]; + snprintf(name, sizeof(name), "%.6s.%s", (i % 2) == 0 ? "game" : (char *) id, i >= 2 ? "gcm" : "iso"); + snprintf(fpath, sizeof(fpath), "%s%s/%s", path.c_str(), dirname, name); + if((found = (stat(fpath, &st) == 0)) == true) + break; + } + + if(!found) + { + snprintf(fpath, sizeof(fpath), "%s%s/sys/boot.bin", path.c_str(), dirname); + if(stat(fpath, &st) != 0) + continue; + // this game is extracted + extracted = true; + } + + //! GAMEID was not found + if(!lay_a && !lay_b) { + // read game ID and title from disc header + // iso file + FILE *fp = fopen(fpath, "rb"); + if (fp != NULL) + { + memset(&tmpHdr, 0, sizeof(tmpHdr)); + fread(&tmpHdr, sizeof(struct discHdr), 1, fp); + fclose(fp); + + if (tmpHdr.gc_magic == GCGames::MAGIC) + { + memcpy(id, tmpHdr.id, 6); + snprintf(fname_title, sizeof(fname_title), "%s", tmpHdr.title); + } + } + } + // if we have titles.txt entry use that const char *title = GameTitles.GetTitle(id); + // if no titles.txt get title from dir or file name if (strlen(title) == 0 && !Settings.ForceDiscTitles && strlen(fname_title) > 0) title = fname_title; if (*id != 0 && strlen(title) > 0) { + string gamePath = string(path) + dirname + (extracted ? "/" : strrchr(fpath, '/')); memset(&tmpHdr, 0, sizeof(tmpHdr)); memcpy(tmpHdr.id, id, 6); strncpy(tmpHdr.title, title, sizeof(tmpHdr.title)-1); tmpHdr.magic = GCGames::MAGIC; - tmpHdr.type = TYPE_GAME_GC_IMG; - HeaderList.push_back(tmpHdr); - PathList.push_back(dirname); + tmpHdr.type = extracted ? TYPE_GAME_GC_EXTRACTED : TYPE_GAME_GC_IMG; + headerList.push_back(tmpHdr); + pathList.push_back(gamePath); continue; } @@ -130,15 +175,17 @@ u32 GCGames::LoadGameList(const string &path) FILE *fp = fopen(fpath, "rb"); if (fp != NULL) { + memset(&tmpHdr, 0, sizeof(tmpHdr)); fread(&tmpHdr, sizeof(struct discHdr), 1, fp); fclose(fp); if (tmpHdr.gc_magic == GCGames::MAGIC) { + string gamePath = string(path) + dirname + (extracted ? "/" : strrchr(fpath, '/')); tmpHdr.magic = tmpHdr.gc_magic; - tmpHdr.type = TYPE_GAME_GC_IMG; - HeaderList.push_back(tmpHdr); - PathList.push_back(dirname); + tmpHdr.type = extracted ? TYPE_GAME_GC_EXTRACTED : TYPE_GAME_GC_IMG; + headerList.push_back(tmpHdr); + pathList.push_back(gamePath); // Save title for next start GameTitles.SetGameTitle(tmpHdr.id, tmpHdr.title); @@ -147,6 +194,42 @@ u32 GCGames::LoadGameList(const string &path) } closedir(dir_iter); +} + +u32 GCGames::LoadAllGames(void) +{ + PathList.clear(); + HeaderList.clear(); + sdGCList.clear(); + sdGCPathList.clear(); + + LoadGameList(Settings.GameCubePath, HeaderList, PathList); + + if(strcmp(Settings.GameCubePath, Settings.GameCubeSDPath) != 0) + { + LoadGameList(Settings.GameCubeSDPath, sdGCList, sdGCPathList); + + for(u32 i = 0; i < sdGCList.size(); ++i) + { + u32 n; + for(n = 0; n < HeaderList.size(); ++n) + { + //! replace the one loaded from USB with the same games on SD since we can load them directly + if(memcmp(HeaderList[n].id, sdGCList[i].id, 6) == 0) + { + memcpy(&HeaderList[n], &sdGCList[i], sizeof(struct discHdr)); + PathList[n] = sdGCPathList[i]; + break; + } + } + + // Not available in the main GC path + if(n == HeaderList.size()) { + HeaderList.push_back(sdGCList[i]); + PathList.push_back(sdGCPathList[i]); + } + } + } return HeaderList.size(); } @@ -157,21 +240,102 @@ bool GCGames::RemoveGame(const char *gameID) if(*path == 0) return false; + RemoveSDGame(gameID); + + if(strcmp(Settings.GameCubePath, Settings.GameCubeSDPath) == 0) + return true; + + struct discHdr *header = NULL; + for(u32 i = 0; i < HeaderList.size(); ++i) + { + if(strncmp(gameID, (char*)HeaderList[i].id, 6) == 0) + { + header = &HeaderList[i]; + break; + } + } + if(!header) + return false; + char filepath[512]; int result = 0; int ret; - // Remove game iso - snprintf(filepath, sizeof(filepath), "%s%s/game.iso", Settings.GameCubePath, path); - ret = RemoveFile(filepath); - if(ret != 0) - result = -1; + // the main path is the SD path as it is prefered, now delete USB + char cIsoPath[256]; + snprintf(cIsoPath, sizeof(cIsoPath), "%s", path + strlen(Settings.GameCubeSDPath)); - // Remove path - snprintf(filepath, sizeof(filepath), "%s%s", Settings.GameCubePath, path); - ret = RemoveFile(filepath); - if(ret != 0) - result = -1; + if(header->type == TYPE_GAME_GC_IMG) + { + // Remove game iso + snprintf(filepath, sizeof(filepath), "%s%s", Settings.GameCubePath, cIsoPath); + ret = RemoveFile(filepath); + if(ret != 0) + result = -1; + + // Remove path + char *pathPtr = strrchr(filepath, '/'); + if(pathPtr) *pathPtr = 0; + ret = RemoveFile(filepath); + if(ret != 0) + result = -1; + } + else if(header->type == TYPE_GAME_GC_EXTRACTED) + { + //! remove extracted gamecube game + snprintf(filepath, sizeof(filepath), "%s%s", Settings.GameCubePath, cIsoPath); + ret = RemoveDirectory(path); + if(ret < 0) + result = -1; + } + + return (result == 0); +} + +bool GCGames::RemoveSDGame(const char *gameID) +{ + const char *path = GetPath(gameID); + if(*path == 0) + return false; + + struct discHdr *header = NULL; + for(u32 i = 0; i < HeaderList.size(); ++i) + { + if(strncmp(gameID, (char*)HeaderList[i].id, 6) == 0) + { + header = &HeaderList[i]; + break; + } + } + if(!header) + return false; + + char filepath[512]; + int result = 0; + int ret; + + if(header->type == TYPE_GAME_GC_IMG) + { + // Remove game iso + snprintf(filepath, sizeof(filepath), "%s", path); + ret = RemoveFile(filepath); + if(ret != 0) + result = -1; + + // Remove path + char *pathPtr = strrchr(filepath, '/'); + if(pathPtr) *pathPtr = 0; + ret = RemoveFile(filepath); + if(ret != 0) + result = -1; + } + else if(header->type == TYPE_GAME_GC_EXTRACTED) + { + //! remove extracted gamecube game + ret = RemoveDirectory(path); + if(ret < 0) + result = -1; + } return (result == 0); } @@ -183,11 +347,159 @@ float GCGames::GetGameSize(const char *gameID) return 0.0f; struct stat st; - char filepath[512]; - snprintf(filepath, sizeof(filepath), "%s%s/game.iso", Settings.GameCubePath, path); - if(stat(filepath, &st) != 0) + if(stat(path, &st) != 0) return 0.0f; return ((float) st.st_size / GB_SIZE); } + +bool GCGames::IsInstalled(const char *gameID) const +{ + for(u32 n = 0; n < HeaderList.size(); n++) + { + if(memcmp(HeaderList[n].id, gameID, 6) == 0) + return true; + } + return false; +} + +bool GCGames::CopyUSB2SD(const struct discHdr *header) +{ + const char *path = GetPath((char*)header->id); + if(*path == 0) + return false; + + int choice = WindowPrompt(tr("The game is on USB."), tr("Do you want to copy the game to SD or delete a game on SD?"), tr("Copy"), tr("Show SD"), tr("Cancel")); + if(choice == 0) + return false; + + const char *cpTitle = GameTitles.GetTitle(header); + + if(choice == 2) + { + GCDeleteMenu gcDeleteMenu; + gcDeleteMenu.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); + gcDeleteMenu.SetEffect(EFFECT_FADE, 20); + mainWindow->SetState(STATE_DISABLED); + mainWindow->Append(&gcDeleteMenu); + + gcDeleteMenu.Show(); + + gcDeleteMenu.SetEffect(EFFECT_FADE, -20); + while(gcDeleteMenu.GetEffect() > 0) usleep(1000); + + mainWindow->Remove(&gcDeleteMenu); + mainWindow->SetState(STATE_DEFAULT); + + if(!WindowPrompt(tr("Do you want to copy now?"), cpTitle, tr("Yes"), tr("Cancel"))) + return false; + } + + struct statvfs sd_vfs; + if(statvfs(Settings.GameCubeSDPath, &sd_vfs) != 0) + { + WindowPrompt(tr("Error:"), tr("SD Card could not be accessed."), tr("OK")); + return false; + } + + u64 filesize = 0; + + if(header->type == TYPE_GAME_GC_IMG) { + filesize = FileSize(path); + } + else if(header->type == TYPE_GAME_GC_EXTRACTED) { + StartProgress(tr("Getting game folder size..."), tr("Please wait"), 0, true, true); + ShowProgress(0, 1); + filesize = FileSize(path); + ProgressStop(); + } + + while(((u64)sd_vfs.f_frsize * (u64)sd_vfs.f_bfree) < filesize) + { + choice = WindowPrompt(tr("Error: Not enough space on SD."), tr("Do you want to delete a game on SD?"), tr("Yes"), tr("Cancel")); + if(choice == 0) + return false; + + GCDeleteMenu gcDeleteMenu; + gcDeleteMenu.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); + gcDeleteMenu.SetEffect(EFFECT_FADE, 20); + mainWindow->SetState(STATE_DISABLED); + mainWindow->Append(&gcDeleteMenu); + + gcDeleteMenu.Show(); + + gcDeleteMenu.SetEffect(EFFECT_FADE, -20); + while(gcDeleteMenu.GetEffect() > 0) usleep(1000); + + mainWindow->Remove(&gcDeleteMenu); + mainWindow->SetState(STATE_DEFAULT); + + statvfs(Settings.GameCubeSDPath, &sd_vfs); + } + + const char *cIsoPath = path + strlen(Settings.GameCubePath); + char destPath[512]; + snprintf(destPath, sizeof(destPath), "%s%s", Settings.GameCubeSDPath, cIsoPath); + + int res = -1; + + if(header->type == TYPE_GAME_GC_IMG) + { + ProgressCancelEnable(true); + StartProgress(tr("Copying GC game..."), cpTitle, 0, true, true); + + char *ptr = strrchr(destPath, '/'); + if(ptr) *ptr = 0; + + CreateSubfolder(destPath); + + snprintf(destPath, sizeof(destPath), "%s%s", Settings.GameCubeSDPath, cIsoPath); + + res = CopyFile(path, destPath); + } + else if(header->type == TYPE_GAME_GC_EXTRACTED) + { + res = CopyDirectory(path, destPath); + } + + // Refresh list + GCGames::Instance()->LoadAllGames(); + + ProgressStop(); + ProgressCancelEnable(false); + + if(res == PROGRESS_CANCELED) + { + if(header->type == TYPE_GAME_GC_IMG) + { + // remove file and path + RemoveFile(destPath); + char *ptr = strrchr(destPath, '/'); + if(ptr) *ptr = 0; + RemoveFile(destPath); + } + + WindowPrompt(tr("Copying Canceled"), 0, tr("OK")); + return false; + } + else if(res < 0) + { + if(header->type == TYPE_GAME_GC_IMG) + { + // remove file and path + RemoveFile(destPath); + char *ptr = strrchr(destPath, '/'); + if(ptr) *ptr = 0; + RemoveFile(destPath); + } + + WindowPrompt(tr("Error:"), tr("Failed copying file"), tr("OK")); + return false; + } + else + { + return WindowPrompt(tr("Successfully copied"), tr("Do you want to start the game now?"), tr("Yes"), tr("Cancel")); + } +} + diff --git a/source/GameCube/GCGames.h b/source/GameCube/GCGames.h index 02cffd47..d4a2efca 100644 --- a/source/GameCube/GCGames.h +++ b/source/GameCube/GCGames.h @@ -35,8 +35,12 @@ public: static u8 *GetOpeningBnr(const char *gameID); - u32 LoadGameList(const string &path); + u32 LoadAllGames(void); + + void LoadGameList(const string &path, vector &headerList, vector &pathList); + bool RemoveGame(const char *gameID); + bool RemoveSDGame(const char *gameID); float GetGameSize(const char *gameID); const char *GetPath(const char *gameID) const; @@ -44,15 +48,25 @@ public: vector & GetHeaders(void) { if(HeaderList.empty()) - LoadGameList(Settings.GameCubePath); + LoadAllGames(); return HeaderList; } + + vector & GetSDHeaders(void) { + return sdGCList; + } + + bool CopyUSB2SD(const struct discHdr *header); + bool IsInstalled(const char *gameID) const; private: + static GCGames *instance; vector PathList; vector HeaderList; + vector sdGCList; + vector sdGCPathList; }; #endif diff --git a/source/SoundOperations/gui_bgm.cpp b/source/SoundOperations/gui_bgm.cpp index 0796f276..b315dc9c 100644 --- a/source/SoundOperations/gui_bgm.cpp +++ b/source/SoundOperations/gui_bgm.cpp @@ -10,7 +10,7 @@ #include "menu.h" GuiBGM::GuiBGM(const u8 *s, int l, int v) : - GuiSound(s, l, v, false, 0) + GuiSound(s, l, v, 0) { currentPath = NULL; currentPlaying = 0; @@ -57,7 +57,7 @@ bool GuiBGM::LoadStandard() strcpy(Settings.ogg_path, ""); - bool ret = GuiSound::Load(Resources::GetFile("bg_music.ogg"), Resources::GetFileSize("bg_music.ogg"), false); + bool ret = GuiSound::Load(Resources::GetFile("bg_music.ogg"), Resources::GetFileSize("bg_music.ogg")); if (ret) Play(); @@ -71,7 +71,7 @@ bool GuiBGM::ParsePath(const char * folderpath) if (currentPath) delete[] currentPath; currentPath = new char[strlen(folderpath) + 1]; - sprintf(currentPath, "%s", folderpath); + strcpy(currentPath, folderpath); char * isdirpath = strrchr(folderpath, '.'); if (isdirpath) @@ -129,7 +129,7 @@ void GuiBGM::AddEntrie(const char * filename) if (!filename) return; char * NewEntrie = new char[strlen(filename) + 1]; - sprintf(NewEntrie, "%s", filename); + strcpy(NewEntrie, filename); PlayList.push_back(NewEntrie); } diff --git a/source/SoundOperations/gui_sound.cpp b/source/SoundOperations/gui_sound.cpp index d073dc5e..3e452ae9 100644 --- a/source/SoundOperations/gui_sound.cpp +++ b/source/SoundOperations/gui_sound.cpp @@ -87,11 +87,10 @@ GuiSound::GuiSound(const char * filepath) volume = 255; SoundEffectLength = 0; loop = 0; - allocated = false; Load(filepath); } -GuiSound::GuiSound(const u8 * snd, s32 len, int vol, bool isallocated, int v) +GuiSound::GuiSound(const u8 * snd, s32 len, int vol, int v) { sound = NULL; length = 0; @@ -106,8 +105,7 @@ GuiSound::GuiSound(const u8 * snd, s32 len, int vol, bool isallocated, int v) volume = vol; SoundEffectLength = 0; loop = 0; - allocated = false; - Load(snd, len, isallocated); + Load(snd, len); } GuiSound::~GuiSound() @@ -123,13 +121,10 @@ void GuiSound::FreeMemory() SoundHandler::Instance()->RemoveDecoder(voice); - if(allocated && sound != NULL) - { + if(sound != NULL && SoundEffectLength != 0) free(sound); - sound = NULL; - allocated = false; - } + sound = NULL; SoundEffectLength = 0; } @@ -140,22 +135,6 @@ bool GuiSound::Load(const char * filepath) if(!filepath) return false; - u32 magic; - FILE * f = fopen(filepath, "rb"); - if(!f) - return false; - - fread(&magic, 1, 4, f); - fclose(f); - - if(magic == 'IMD5') - { - u8 * snd = NULL; - u32 filesize = 0; - LoadFileToMem(filepath, &snd, &filesize); - return Load(snd, filesize, true); - } - SoundHandler::Instance()->AddDecoder(voice, filepath); SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice); @@ -173,28 +152,15 @@ bool GuiSound::Load(const char * filepath) return true; } -bool GuiSound::Load(const u8 * snd, s32 len, bool isallocated) +bool GuiSound::Load(const u8 * snd, s32 len) { FreeMemory(); if(!snd) return false; - if(!isallocated && *((u32 *) snd) == 'RIFF') - { - return LoadSoundEffect(snd, len); - } - - if(*((u32 *) snd) == 'IMD5') - { - UncompressSoundbin(snd, len, isallocated); - } - else - { - sound = (u8 *) snd; - length = len; - allocated = isallocated; - } + sound = (u8 *) snd; + length = len; SoundHandler::Instance()->AddDecoder(voice, sound, length); @@ -243,7 +209,6 @@ bool GuiSound::LoadSoundEffect(const u8 * snd, s32 len) sound = (u8 *) realloc(sound, done); SoundEffectLength = done; - allocated = true; return true; } @@ -333,7 +298,10 @@ void GuiSound::SetVolume(int vol) if(vol < 0) return; - volume = 255*(vol/100.0); + volume = (255 * vol)/100; + if(volume > 255) + volume = 255; + ASND_ChangeVolumeVoice(voice, volume, volume); } @@ -356,25 +324,3 @@ void GuiSound::Rewind() decoder->Rewind(); } - -void GuiSound::UncompressSoundbin(const u8 * snd, int len, bool isallocated) -{ - const u8 * file = snd+32; - if(*((u32 *) file) == 'LZ77') - { - u32 size = 0; - sound = uncompressLZ77(file, len-32, &size); - length = size; - } - else - { - length = len-32; - sound = (u8 *) malloc(length); - memcpy(sound, file, length); - } - - if(isallocated) - free((u8 *) snd); - - allocated = true; -} diff --git a/source/SoundOperations/gui_sound.h b/source/SoundOperations/gui_sound.h index fb3e7163..c72e0847 100644 --- a/source/SoundOperations/gui_sound.h +++ b/source/SoundOperations/gui_sound.h @@ -36,13 +36,13 @@ class GuiSound //!\param sound Pointer to the sound data //!\param filesize Length of sound data GuiSound(const char * filepath); - GuiSound(const u8 * sound, int filesize, int volume, bool allocated = false, int voice = -1); + GuiSound(const u8 * sound, int filesize, int volume, int voice = -1); //!Destructor virtual ~GuiSound(); //!Load a file and replace the old one virtual bool Load(const char * filepath); //!Load a file and replace the old one - bool Load(const u8 * sound, int filesize, bool allocated = true); + bool Load(const u8 * sound, int filesize); //!For quick playback of the internal soundeffects bool LoadSoundEffect(const u8 * snd, s32 len); //!Start sound playback @@ -63,8 +63,6 @@ class GuiSound void SetVolume(int v); //!\param l Loop (true to loop) virtual void SetLoop(u8 l); - //!Special sound case for sound.bin - void UncompressSoundbin(const u8 * snd, int len, bool isallocated); protected: //!Stops sound and frees all memory/closes files void FreeMemory(); @@ -74,7 +72,6 @@ class GuiSound int volume; //!< Sound volume (0-100) u8 loop; //!< Loop sound playback u32 SoundEffectLength; //!< Check if it is an app soundeffect for faster playback - bool allocated; //!< Is the file allocated or not }; #endif diff --git a/source/StartUpProcess.cpp b/source/StartUpProcess.cpp index f0af8d3a..01eee4d9 100644 --- a/source/StartUpProcess.cpp +++ b/source/StartUpProcess.cpp @@ -8,6 +8,7 @@ #include "gecko.h" #include "Controls/DeviceHandler.hpp" #include "wad/nandtitle.h" +#include "SystemMenu/SystemMenuResources.h" #include "system/IosLoader.h" #include "utils/timer.h" #include "settings/CSettings.h" @@ -186,7 +187,18 @@ int StartUpProcess::Run(int argc, char *argv[]) int StartUpProcess::Execute() { Settings.EntryIOS = IOS_GetVersion(); - SetTextf("Start up\n"); + + // Initialize to read the system menu resources + ISFS_Initialize(); + + SetTextf("Loading system menu resources\n"); + SystemMenuResources::Instance()->Init(); + + // Deinitialize ISFS + ISFS_Deinitialize(); + + // Reload app cios + SetTextf("Loading application cIOS\n"); if(IosLoader::LoadAppCios() < 0) { @@ -236,7 +248,7 @@ int StartUpProcess::Execute() SetupPads(); } - if(!IosLoader::IsHermesIOS()) + if(!IosLoader::IsHermesIOS() && !IosLoader::IsD2X()) { Settings.USBPort = 0; } @@ -248,17 +260,23 @@ int StartUpProcess::Execute() } else if(Settings.USBPort == 2) { - // Right now we support only one port at once - Settings.USBPort = 0; - /* SetTextf("Mounting USB Port to 1\n"); DeviceHandler::Instance()->MountUSBPort1(); - */ } // We only initialize once for the whole session ISFS_Initialize(); + SetTextf("Loading resources\n"); + // Do not allow banner grid mode without AHBPROT + // this function does nothing if it was already initiated before + if( !SystemMenuResources::Instance()->IsLoaded() && !SystemMenuResources::Instance()->Init() + && Settings.gameDisplay == BANNERGRID_MODE) + { + Settings.gameDisplay = LIST_MODE; + Settings.GameWindowMode = GAMEWINDOW_DISC; + } + gprintf("\tLoading game categories...%s\n", GameCategories.Load(Settings.ConfigPath) ? "done" : "failed"); gprintf("\tLoading font...%s\n", Theme::LoadFont(Settings.ConfigPath) ? "done" : "failed (using default)"); gprintf("\tLoading theme...%s\n", Theme::Load(Settings.theme) ? "done" : "failed (using default)"); diff --git a/source/SystemMenu/BannerFrame.cpp b/source/SystemMenu/BannerFrame.cpp new file mode 100644 index 00000000..9c602d27 --- /dev/null +++ b/source/SystemMenu/BannerFrame.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include "BannerFrame.h" +#include "SystemMenu/SystemMenuResources.h" +#include "utils/U8Archive.h" +#include "utils/StringTools.h" + +BannerFrame::~BannerFrame() +{ + delete [] UTF16ButtonA; + delete [] UTF16ButtonB; +} + +bool BannerFrame::Load( const U8Archive &chanTtlArc ) +{ + // read layout data + u8 *brlytFile = chanTtlArc.GetFile( "/arc/blyt/my_ChTop_a.brlyt" ); + if( !brlytFile ) + return false; + + if(!Layout::Load(brlytFile)) + return false; + + // load textures + LoadTextures(chanTtlArc); + + N_BtnA = FindPane("N_BtnA"); + N_BtnB = FindPane("N_BtnB"); + + Loaded = true; + + return true; +} + +void BannerFrame::SetButtonAText(const char *text) +{ + //!< Set button text for left button + const wchar_t *buttonText = wfmt("%s", text); + int len = wcslen(buttonText); + + delete [] UTF16ButtonA; + UTF16ButtonA = new u16[len+1]; + + for(int i = 0; i < len; i++) + UTF16ButtonA[i] = (u16) buttonText[i]; + UTF16ButtonA[len] = 0; + + Textbox *T_BtnA = (Textbox *)FindPane("T_BtnA"); + if(T_BtnA) + T_BtnA->SetText(UTF16ButtonA); +} + +void BannerFrame::SetButtonBText(const char *text) +{ + //!< Set button text for right button + const wchar_t *buttonText = wfmt("%s", text); + int len = wcslen(buttonText); + + delete [] UTF16ButtonB; + UTF16ButtonB = new u16[len+1]; + + for(int i = 0; i < len; i++) + UTF16ButtonB[i] = (u16) buttonText[i]; + UTF16ButtonB[len] = 0; + + Textbox *T_BtnB = (Textbox *)FindPane("T_BtnB"); + if(T_BtnB) + T_BtnB->SetText(UTF16ButtonB); +} + + +void BannerFrame::AdvanceFrame() +{ + if(!N_BtnA || !N_BtnB) + return; + + if(GrowEffectBtnA && ScaleBtnA < 1.05f) + { + ScaleBtnA += 0.01f; + N_BtnA->SetScale(ScaleBtnA); + } + else if(!GrowEffectBtnA && ScaleBtnA > 1.0f) + { + ScaleBtnA -= 0.01f; + N_BtnA->SetScale(ScaleBtnA); + } + + if(GrowEffectBtnB && ScaleBtnB < 1.05f) + { + ScaleBtnB += 0.01f; + N_BtnB->SetScale(ScaleBtnB); + } + else if(!GrowEffectBtnB && ScaleBtnB > 1.0f) + { + ScaleBtnB -= 0.01f; + N_BtnB->SetScale(ScaleBtnB); + } + + // we don't load any brlan for it + // Layout::AdvanceFrame(); +} diff --git a/source/SystemMenu/BannerFrame.h b/source/SystemMenu/BannerFrame.h new file mode 100644 index 00000000..9871c15d --- /dev/null +++ b/source/SystemMenu/BannerFrame.h @@ -0,0 +1,53 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef BANNERFRAME_H +#define BANNERFRAME_H + +#include "banner/Layout.h" + +// this is a class to deal with the frame the goes over the large banners +class BannerFrame : public Layout +{ +public: + BannerFrame() + : Loaded(false), + GrowEffectBtnA(false), GrowEffectBtnB(false), + ScaleBtnA(1.f), ScaleBtnB(1.f), + N_BtnA(0), N_BtnB(0), + UTF16ButtonA(0), UTF16ButtonB(0) + { } + virtual ~BannerFrame(); + bool Load( const U8Archive &archive ); + bool IsLoaded() const { return Loaded; } + void AdvanceFrame(); + void SetButtonAGrow(bool b) { GrowEffectBtnA = b; } + void SetButtonBGrow(bool b) { GrowEffectBtnB = b; } + void SetButtonAText(const char *text); + void SetButtonBText(const char *text); +private: + bool Loaded; + bool GrowEffectBtnA; + bool GrowEffectBtnB; + float ScaleBtnA; + float ScaleBtnB; + Pane *N_BtnA; + Pane *N_BtnB; + u16 *UTF16ButtonA; + u16 *UTF16ButtonB; +}; + +#endif // BANNERFRAME_H diff --git a/source/SystemMenu/GCBanner.cpp b/source/SystemMenu/GCBanner.cpp new file mode 100644 index 00000000..c90e1aff --- /dev/null +++ b/source/SystemMenu/GCBanner.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include "SystemMenu/SystemMenuResources.h" +#include "GCBanner.h" + +bool GCBanner::Load( const U8Archive &GCBannArc ) +{ + // read layout data + const u8 *brlytFile = GCBannArc.GetFile("/arc/blyt/my_GCTop_a.brlyt"); + if( !brlytFile ) + return false; + + if(!Layout::Load(brlytFile)) + return false; + + u32 length_start = 0, length_loop = 0; + + const u8 *brlan_loop = GCBannArc.GetFile("/arc/anim/my_GCTop_a_BackLoop.brlan"); + if (brlan_loop) + length_loop = Animator::LoadAnimators((const RLAN_Header *)brlan_loop, *this, 1); + + LoadTextures(GCBannArc); + SetLanguage("ENG"); + SetLoopStart(length_start); + SetLoopEnd(length_start + length_loop); + SetFrame(0); + + return true; +} diff --git a/source/SystemMenu/GCBanner.h b/source/SystemMenu/GCBanner.h new file mode 100644 index 00000000..50966933 --- /dev/null +++ b/source/SystemMenu/GCBanner.h @@ -0,0 +1,29 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef GCBANNER_H +#define GCBANNER_H + +#include "banner/Layout.h" + +// this is a class to deal with the gc disc channel +class GCBanner : public Layout +{ +public: + bool Load( const U8Archive &archive ); +}; + +#endif // STATICFRAME_H diff --git a/source/SystemMenu/StaticFrame.cpp b/source/SystemMenu/StaticFrame.cpp new file mode 100644 index 00000000..e1caa827 --- /dev/null +++ b/source/SystemMenu/StaticFrame.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include "StaticFrame.h" + +bool StaticFrame::Load( const U8Archive &chanSelArc ) +{ + // read layout data + const u8 *brlytFile = chanSelArc.GetFile("/arc/blyt/my_IplTop_b.brlyt"); + if( !brlytFile ) + return false; + + if(!Layout::Load(brlytFile)) + return false; + + u32 length_start = 0, length_loop = 0; + + const u8 *brlan_loop = chanSelArc.GetFile("/arc/anim/my_IplTop_b.brlan"); + if (brlan_loop) + length_loop = Animator::LoadAnimators((const RLAN_Header *)brlan_loop, *this, 1); + + LoadTextures(chanSelArc); + SetLanguage("ENG"); + SetLoopStart(length_start); + SetLoopEnd(length_start + length_loop); + SetFrame(0); + + return true; +} diff --git a/source/SystemMenu/StaticFrame.h b/source/SystemMenu/StaticFrame.h new file mode 100644 index 00000000..5a07187c --- /dev/null +++ b/source/SystemMenu/StaticFrame.h @@ -0,0 +1,29 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef STATICFRAME_H +#define STATICFRAME_H + +#include "banner/Layout.h" + +// this is a class to deal with the frame the goes as default channel +class StaticFrame : public Layout +{ +public: + bool Load( const U8Archive &archive ); +}; + +#endif // STATICFRAME_H diff --git a/source/SystemMenu/SystemMenuResources.cpp b/source/SystemMenu/SystemMenuResources.cpp new file mode 100644 index 00000000..153b0b43 --- /dev/null +++ b/source/SystemMenu/SystemMenuResources.cpp @@ -0,0 +1,314 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok, giantpune + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include "gecko.h" +#include "SystemMenu/SystemMenuResources.h" +#include "memory/mem2.h" +#include "utils/U8Archive.h" +#include "wad/nandtitle.h" + +SystemMenuResources *SystemMenuResources::instance = NULL; + +SystemMenuResources::SystemMenuResources(): + isInited( false ), + wbf1( NULL ), + wbf2( NULL ), + wbf1Buffer( NULL ), + wbf2Buffer( NULL ), + chanTtlAsh( NULL ), + chanSelAsh( NULL ), + GCBannAsh( NULL ), + systemFont( NULL ) +{ +} + +SystemMenuResources::~SystemMenuResources() +{ + FreeEverything(); +} + +bool SystemMenuResources::Init() +{ + if( isInited ) + return true; + + // get tmd + tmd *p_tmd = NandTitles.GetTMD( 0x100000002ull ); + if( !p_tmd ) + { + gprintf( "can\'t get system menu TMD\n" ); + return false; + } + + // determine resource cid + u16 idx = 0xffff; + tmd_content *contents = TMD_CONTENTS( p_tmd ); + for( u16 i = 0; i < p_tmd->num_contents; i++ ) + { + if( contents[ i ].index == 1 ) + { + idx = i; + break; + } + } + if( idx == 0xffff ) + { + gprintf( "SM main resource not found\n" ); + return false; + } + // build file path + char path[ ISFS_MAXPATH ]__attribute__((aligned( 32 ))); + sprintf( path, "/title/00000001/00000002/content/%08x.app", contents[ idx ].cid ); + + // get resource archive + u8* resourceArc = NULL; + u32 resourceLen = 0; + int ret; + + if( ( ret = NandTitle::LoadFileFromNand( path, &resourceArc, &resourceLen ) ) < 0 ) + { + gprintf( "Error reading resource from nand: %i\n", ret ); + return false; + } + + // create U8 archive for reading files + U8Archive mainArc( resourceArc, resourceLen ); + + // Load the font archive + InitFontArchive(); + + // read ash files + chanTtlAsh = mainArc.GetFileAllocated( "/layout/common/chanTtl.ash", &chanTtlAshSize); + if(!chanTtlAsh) + { + gprintf( "Error while loading chanTtl.ash\n" ); + } + // move this to mem2 + else if(!isMEM2Buffer(chanTtlAsh)) + { + u8 *tmp = (u8 *) MEM2_alloc(chanTtlAshSize); + if(tmp) + { + memcpy(tmp, chanTtlAsh, chanTtlAshSize); + free(chanTtlAsh); + chanTtlAsh = tmp; + } + } + + // read ash files + chanSelAsh = mainArc.GetFileAllocated( "/layout/common/chanSel.ash", &chanSelAshSize); + if(!chanSelAsh) + { + gprintf( "Error while loading chanSel.ash\n" ); + } + // move this to mem2 + else if(!isMEM2Buffer(chanSelAsh)) + { + u8 *tmp = (u8 *) MEM2_alloc(chanSelAshSize); + if(tmp) + { + memcpy(tmp, chanSelAsh, chanSelAshSize); + free(chanSelAsh); + chanSelAsh = tmp; + } + } +/* Currently not used since we use a custom banner + // load GC banner for GC games + GCBannAsh = mainArc.GetFileAllocated( "/layout/common/GCBann.ash", &GCBannAshSize); + if(!GCBannAsh) + { + gprintf( "Error while loading GCBannAsh.ash\n" ); + } + // move this to mem2 + else if(!isMEM2Buffer(GCBannAsh)) + { + u8 *tmp = (u8 *) MEM2_alloc(GCBannAshSize); + if(tmp) + { + memcpy(tmp, GCBannAsh, GCBannAshSize); + free(GCBannAsh); + GCBannAsh = tmp; + } + } +*/ + // done with the huge U8 archie now that we already got everything out of it + free( resourceArc ); + + isInited = true; + return true; +} + +typedef struct map_entry +{ + char name[8]; + u8 hash[20]; +} __attribute__((packed)) map_entry_t; + +static const char contentMapPath[] ATTRIBUTE_ALIGN(32) = "/shared1/content.map"; +static const u8 WFB_HASH[] = { 0x4f, 0xad, 0x97, 0xfd, 0x4a, 0x28, 0x8c, 0x47, 0xe0, 0x58, 0x7f, 0x3b, 0xbd, 0x29, 0x23, 0x79, 0xf8, 0x70, 0x9e, 0xb9 }; +static const u8 WIIFONT_HASH[] = {0x32, 0xb3, 0x39, 0xcb, 0xbb, 0x50, 0x7d, 0x50, 0x27, 0x79, 0x25, 0x9a, 0x78, 0x66, 0x99, 0x5d, 0x03, 0x0b, 0x1d, 0x88}; +static const u8 WIIFONT_HASH_KOR[] = {0xb7, 0x15, 0x6d, 0xf0, 0xf4, 0xae, 0x07, 0x8f, 0xd1, 0x53, 0x58, 0x3e, 0x93, 0x6e, 0x07, 0xc0, 0x98, 0x77, 0x49, 0x0e}; + +bool SystemMenuResources::InitFontArchive(void) +{ + // get content.map + u8 *contentMap = NULL; + u32 mapsize = 0; + + NandTitle::LoadFileFromNand(contentMapPath, &contentMap, &mapsize); + if(!contentMap) + { + gprintf( "!contentMap\n" ); + return false; + } + + int fileCount = mapsize / sizeof(map_entry_t); + map_entry_t *mapEntryList = (map_entry_t *) contentMap; + + // search content.map for brfna archive + for( int i = 0; i < fileCount; i++ ) + { + if( memcmp(mapEntryList[i].hash, WFB_HASH, 20 ) ) + continue; + + // Name found + char font_filename[32] ATTRIBUTE_ALIGN(32); + snprintf( font_filename, sizeof( font_filename ), "/shared1/%.8s.app", mapEntryList[ i ].name ); + + u8 *fontArchiveBuffer = NULL; + u32 fontArchiveSize; + NandTitle::LoadFileFromNand( font_filename, &fontArchiveBuffer, &fontArchiveSize ); + if( !fontArchiveBuffer ) + { + free(contentMap); + return false; + } + + U8Archive fontArc(fontArchiveBuffer, fontArchiveSize); + wbf1Buffer = fontArc.GetFileAllocated("wbf1.brfna"); + wbf2Buffer = fontArc.GetFileAllocated("wbf2.brfna"); + + if(wbf1Buffer) + { + wbf1 = new WiiFont; + wbf1->SetName("wbf1.brfna"); + wbf1->Load(wbf1Buffer); + } + if(wbf2Buffer) + { + wbf2 = new WiiFont; + wbf2->SetName("wbf2.brfna"); + wbf2->Load(wbf2Buffer); + } + + free(fontArchiveBuffer); + break; + } + + if(!systemFont) + InitSystemFontArchive(CONF_GetLanguage() == CONF_LANG_KOREAN, contentMap, mapsize); + if(!systemFont) + InitSystemFontArchive(CONF_GetLanguage() != CONF_LANG_KOREAN, contentMap, mapsize); + + free( contentMap ); + + // not found + if( !systemFont || !wbf1Buffer || !wbf2Buffer ) + { + // shared fonts not found + return false; + } + + return true; +} + +bool SystemMenuResources::InitSystemFontArchive(bool korean, u8 *contentMap, u32 mapsize) +{ + int fileCount = mapsize / sizeof(map_entry_t); + + map_entry_t *mapEntryList = (map_entry_t *) contentMap; + + for (int i = 0; i < fileCount; i++) + { + if (memcmp(mapEntryList[i].hash, korean ? WIIFONT_HASH_KOR : WIIFONT_HASH, 20) != 0) + continue; + + // Name found, load it and unpack it + char font_filename[32] ATTRIBUTE_ALIGN(32); + snprintf(font_filename, sizeof(font_filename), "/shared1/%.8s.app", mapEntryList[i].name); + + u8 *fontArchive = NULL; + u32 filesize = 0; + + NandTitle::LoadFileFromNand(font_filename, &fontArchive, &filesize); + if(!fontArchive) + continue; + + U8Archive systemFontArc(fontArchive, filesize); + + systemFont = systemFontArc.GetFileAllocated(1, &systemFontSize); + if(!systemFont) + { + free(fontArchive); + continue; + } + // move this to mem2 + else if(!isMEM2Buffer(systemFont)) + { + u8 *tmp = (u8 *) MEM2_alloc(systemFontSize); + if(tmp) + { + memcpy(tmp, systemFont, systemFontSize); + free(systemFont); + systemFont = tmp; + } + } + + free(fontArchive); + gprintf("Loaded Wii System Font\n"); + return true; + } + + return false; +} + +void SystemMenuResources::FreeEverything() +{ + if(chanTtlAsh) + free(chanTtlAsh); + if(chanSelAsh) + free(chanSelAsh); + if(GCBannAsh) + free(GCBannAsh); + if(wbf1Buffer) + free(wbf1Buffer); + if(wbf2Buffer) + free(wbf2Buffer); + if(systemFont) + free(systemFont); + + delete wbf1; + delete wbf2; + + chanTtlAsh = NULL; + chanSelAsh = NULL; + GCBannAsh = NULL; + systemFont = NULL; + wbf1Buffer = NULL; + wbf2Buffer = NULL; + wbf1 = NULL; + wbf2 = NULL; +} diff --git a/source/SystemMenu/SystemMenuResources.h b/source/SystemMenu/SystemMenuResources.h new file mode 100644 index 00000000..c7f0fed2 --- /dev/null +++ b/source/SystemMenu/SystemMenuResources.h @@ -0,0 +1,87 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef SYSTEMMENURESOURCES_H +#define SYSTEMMENURESOURCES_H + +#include +#include "BannerFrame.h" +#include "StaticFrame.h" +#include "utils/U8Archive.h" + +class SystemMenuResources +{ +public: + static SystemMenuResources *Instance() { if(!instance) instance = new SystemMenuResources; return instance; } + + static void DestroyInstance() { delete instance; instance = NULL; } + + bool Init(); + bool IsLoaded() const { return isInited; } + + WiiFont *GetWbf1() const { return wbf1; } + WiiFont *GetWbf2() const { return wbf2; } + + const u8 *GetChanSelAsh() const { return chanSelAsh; } + u32 GetChanSelAshSize() const { return chanSelAshSize; } + + const u8 *GetChanTtlAsh() const { return chanTtlAsh; } + u32 GetChanTtlAshSize() const { return chanTtlAshSize; } + + const u8 *GetGCBannAsh() const { return GCBannAsh; } + u32 GetGCBannAshSize() const { return GCBannAshSize; } + + const u8 *GetSystemFont() const { return systemFont; } + u32 GetSystemFontSize() const { return systemFontSize; } + +protected: + SystemMenuResources(); + ~SystemMenuResources(); + + static SystemMenuResources *instance; + + bool InitFontArchive(void); + bool InitSystemFontArchive(bool korean, u8 *contentMap, u32 mapsize); + + bool isInited; + + //! banner font + WiiFont *wbf1; + WiiFont *wbf2; + u8 *wbf1Buffer; + u8 *wbf2Buffer; + + //! chanTtl.ash contains the frame and buttons that goes around the large banners + u8 *chanTtlAsh; + u32 chanTtlAshSize; + + //! chanSel.ash contains the channel grid layouts + u8 *chanSelAsh; + u32 chanSelAshSize; + + //! GCBann.ash contains the gamecube channel banner + u8 *GCBannAsh; + u32 GCBannAshSize; + + //! system font + u8 *systemFont; + u32 systemFontSize; + + // free data + void FreeEverything(); +}; + +#endif // SYSTEMMENURESOURCES_H diff --git a/source/ZipFile.cpp b/source/ZipFile.cpp index b298fb69..3e356360 100644 --- a/source/ZipFile.cpp +++ b/source/ZipFile.cpp @@ -124,6 +124,8 @@ bool ZipFile::ExtractAll(const char *dest) int ret = unzGoToFirstFile(File); if (ret != UNZ_OK) Stop = true; + ProgressCancelEnable(true); + while (!Stop) { if (unzGetCurrentFileInfo(File, &cur_file_info, filename, sizeof(filename), NULL, 0, NULL, 0) != UNZ_OK) Stop @@ -154,7 +156,11 @@ bool ZipFile::ExtractAll(const char *dest) do { - ShowProgress(tr( "Extracting files..." ), 0, pointer + 1, done, uncompressed_size, true); + if(ProgressCanceled()) { + Stop = true; + break; + } + ShowProgress(tr( "Extracting files..." ), 0, pointer + 1, done, uncompressed_size, true, false); if (uncompressed_size - done < blocksize) blocksize = uncompressed_size - done; @@ -179,6 +185,7 @@ bool ZipFile::ExtractAll(const char *dest) buffer = NULL; ProgressStop(); + ProgressCancelEnable(false); return true; } diff --git a/source/banner/Animator.cpp b/source/banner/Animator.cpp new file mode 100644 index 00000000..573f5f3b --- /dev/null +++ b/source/banner/Animator.cpp @@ -0,0 +1,260 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include +#include "Animator.h" +#include "Layout.h" + +// load keyframes from a brlan file +u32 Animator::LoadAnimators(const RLAN_Header *header, Layout& layout, u8 key_set) +{ + u32 frame_count = 0; + + if (!header || header->magic != MAGIC_ANIMATION || header->endian != 0xFEFF || header->version != 0x0008) + return 0; // bad header + + // first section + const u8 *position = ((const u8 *) header) + header->offset; + + for(u32 i = 0; i < header->section_count; ++i) + { + section_t *section = (section_t *) position; + position += section->size; + + if (section->magic == MAGIC_PANE_ANIMATION_INFO) + { + const PAI1_Header *pai = (const PAI1_Header *) section; + u16 animator_count = pai->animator_count; + frame_count += pai->frame_count; + + // read animation file names + const u32 *nameOffsets = (const u32 *)(pai + 1); + for(u32 i = 0; i < pai->file_count; i++) + { + const char* name = (((const char *) nameOffsets) + nameOffsets[i]); + layout.AddPalette(name, key_set); + } + + const u32 *offsets = (const u32 *) (((const u8 *)section) + pai->entry_offset); + // read each animator + for(u32 n = 0; n < animator_count; n++) + { + const AnimatorHeader *animHdr = (const AnimatorHeader *) (((const u8 *)section) + offsets[n]); + std::string anim_name(animHdr->name, 0, 20); + + Animator* animator = animHdr->is_material ? + (Animator*) layout.FindMaterial(anim_name) : + (Animator*) layout.FindPane(anim_name); + + if (animator) + animator->LoadKeyFrames((const u8 *) animHdr, animHdr->tag_count, sizeof(AnimatorHeader), key_set); + } + } + else + { + gprintf("Unknown: %c%c%c%c\n", position[0], position[1], position[2], position[3]); + } + } + + return frame_count; +} + +void Animator::LoadKeyFrames(const u8 *file, u8 tag_count, u32 offset, u8 key_set) +{ + const u32 *tag_offsets = (const u32 *) (file + offset); + + for(u32 tag = 0; tag < tag_count; tag++) + { + const Anim_Header *animHdr = (const Anim_Header *) (file + tag_offsets[tag]); + + u32 animation_type = animHdr->animation_type; + u8 frame_count = animHdr->frame_count; + + const u32 *frame_offsets = (const u32 *) (animHdr + 1); + + for(u32 frame = 0; frame < frame_count; frame++) + { + const KeyFrame_Header *keyFrame = (const KeyFrame_Header *)(((const u8 *) animHdr) + frame_offsets[frame]); + const KeyType frame_type(static_cast(animation_type), keyFrame->index, keyFrame->target); + + switch (keyFrame->data_type) + { + // step key frame + case 0x01: + keys[key_set].step_keys[frame_type].Load((const u8 *) (keyFrame+1), keyFrame->key_count); + break; + + // hermite key frame + case 0x02: + keys[key_set].hermite_keys[frame_type].Load((const u8 *) (keyFrame+1), keyFrame->key_count); + break; + + default: + break; + } + } + } +} + +void Animator::SetFrame(FrameNumber frame_number, u8 key_set) +{ + std::map::iterator itr; + for(itr = keys[key_set].hermite_keys.begin(); itr != keys[key_set].hermite_keys.end(); itr++) + { + const KeyType& frame_type = itr->first; + const float frame_value = itr->second.GetFrame(frame_number); + + ProcessHermiteKey(frame_type, frame_value); + } + + std::map::iterator itr2; + for(itr2 = keys[key_set].step_keys.begin(); itr2 != keys[key_set].step_keys.end(); itr2++) + { + const KeyType& frame_type = itr2->first; + StepKeyHandler::KeyData const frame_data = itr2->second.GetFrame(frame_number); + + ProcessStepKey(frame_type, frame_data); + } +} + +void StepKeyHandler::Load(const u8 *file, u16 count) +{ + while (count--) + { + FrameNumber frame; + frame = *((FrameNumber *) file); + file += 4; + + KeyData& data = keys[frame]; + data.data1 = *file; + file++; + data.data2 = *file; + file++; + + file += 2; + } +} + +void HermiteKeyHandler::Load(const u8 *file, u16 count) +{ + while (count--) + { + std::pair pair; + + // read the frame number, value and slope + pair.first = *((FrameNumber *) file); + file += 4; + pair.second.value = *((float *) file); + file += 4; + pair.second.slope = *((float *) file); + file += 4; + + keys.insert(pair); + + //std::cout << "\t\t\t" "frame: " << frame << ' ' << keys[frame] << '\n'; + } +} + +StepKeyHandler::KeyData StepKeyHandler::GetFrame(FrameNumber frame_number) const +{ + // assuming not empty, a safe assumption currently + + // find the current frame, or the one after it + std::map::const_iterator frame_it = keys.lower_bound(frame_number); + + // current frame is higher than any keyframe, use the last keyframe + if (keys.end() == frame_it) + --frame_it; + + // if this is after the current frame and not the first keyframe, use the previous one + if (frame_number < frame_it->first && keys.begin() != frame_it) + --frame_it; + + return frame_it->second; +} + +float HermiteKeyHandler::GetFrame(FrameNumber frame_number) const +{ + // assuming not empty, a safe assumption currently + + // find the current keyframe, or the one after it + std::multimap::const_iterator next = keys.lower_bound(frame_number); + + // current frame is higher than any keyframe, use the last keyframe + if (keys.end() == next) + --next; + + std::multimap::const_iterator prev = next; + + // if this is after the current frame and not the first keyframe, use the previous one + if (frame_number < prev->first && keys.begin() != prev) + --prev; + + const float nf = next->first - prev->first; + if (fabs(nf) < 0.01) + { + // same frame numbers, just return the first's value + return prev->second.value; + } + else + { + // different frames, blend them together + // this is a "Cubic Hermite spline" apparently + + frame_number = (frame_number < prev->first) ? prev->first : + ((frame_number > next->first) ? next->first : frame_number); + + const float t = (frame_number - prev->first) / nf; + + // old curve-less code + //return prev->second.value + (next->second.value - prev->second.value) * t; + + // curvy code from marcan, :p + return + prev->second.slope * nf * (t + powf(t, 3) - 2 * powf(t, 2)) + + next->second.slope * nf * (powf(t, 3) - powf(t, 2)) + + prev->second.value * (1 + (2 * powf(t, 3) - 3 * powf(t, 2))) + + next->second.value * (-2 * powf(t, 3) + 3 * powf(t, 2)); + } +} + +void Animator::ProcessHermiteKey(const KeyType& type, float value) +{ +// std::cout << "unhandled key (" << GetName() << "): type: " << FourCC(type.type) +// << " index: " << (int)type.index +// << " target: " << (int)type.target +// << " value: " << value +// << '\n'; + gprintf("Animator::ProcessHermiteKey\n"); +} + +void Animator::ProcessStepKey(const KeyType& type, StepKeyHandler::KeyData data) +{ +// std::cout << "unhandled key (" << GetName() << "): type: " << FourCC(type.type) +// << " index: " << (int)type.index +// << " target: " << (int)type.target +// << " data:" << (int)data.data1 << " " << (int)data.data2 +// << '\n'; + gprintf("Animator::ProcessStepKey\n"); +} diff --git a/source/banner/Animator.h b/source/banner/Animator.h new file mode 100644 index 00000000..962d8186 --- /dev/null +++ b/source/banner/Animator.h @@ -0,0 +1,169 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef WII_BNR_ANIMATOR_H_ +#define WII_BNR_ANIMATOR_H_ + +#include +#include +#include +#include "BannerTools.h" + +typedef float FrameNumber; + +enum AnimationType +{ + ANIMATION_TYPE_PANE = MAKE_FOURCC('R', 'L', 'P', 'A'), + ANIMATION_TYPE_TEXTURE_SRT = MAKE_FOURCC('R', 'L', 'T', 'S'), + ANIMATION_TYPE_VISIBILITY = MAKE_FOURCC('R', 'L', 'V', 'I'), + ANIMATION_TYPE_VERTEX_COLOR = MAKE_FOURCC('R', 'L', 'V', 'C'), + ANIMATION_TYPE_MATERIAL_COLOR = MAKE_FOURCC('R', 'L', 'M', 'C'), + ANIMATION_TYPE_TEXTURE_PALETTE = MAKE_FOURCC('R', 'L', 'T', 'P'), + ANIMATION_TYPE_IND_MATERIAL = MAKE_FOURCC('R', 'L', 'I', 'M'), +}; + +struct RLAN_Header +{ + u32 magic; + u16 endian; + u16 version; + u32 file_size; + u16 offset; + u16 section_count; +} __attribute__((packed)); + +struct PAI1_Header +{ + u32 magic; + u32 section_size; + u16 frame_count; + u8 loop; + u8 pad; + u16 file_count; + u16 animator_count; + u32 entry_offset; +} __attribute__((packed)); + +struct AnimatorHeader +{ + char name[20]; + u8 tag_count; + u8 is_material; + u16 apad; +} __attribute__((packed)); + +struct Anim_Header +{ + u32 animation_type; + u8 frame_count; + u8 pad[3]; +} __attribute__((packed)); + +struct KeyFrame_Header +{ + u8 index; + u8 target; + u8 data_type; + u8 pad; + u16 key_count; + u16 pad1; + u32 offset; +} __attribute__((packed)); + +struct KeyType +{ + KeyType(AnimationType _type, u8 _index, u8 _target) + : type(_type) + , index(_index) + , target(_target) + {} + + bool operator<(const KeyType& rhs) const + { + return memcmp(this, &rhs, sizeof(*this)) < 0; + } + + const AnimationType type; + const u8 index, target; +}; + +class StepKeyHandler +{ +public: + void Load(const u8* file, u16 count); + + struct KeyData + { + u8 data1, data2; + }; + + KeyData GetFrame(FrameNumber frame_number) const; + +private: + std::map keys; +}; + +class HermiteKeyHandler +{ +public: + void Load(const u8* file, u16 count); + + struct KeyData + { + float value, slope; + }; + + float GetFrame(FrameNumber frame_number) const; + +private: + std::multimap keys; +}; + +class Layout; +class Animator +{ +public: + static const u32 MAGIC_ANIMATION = MAKE_FOURCC('R', 'L', 'A', 'N'); + static const u32 MAGIC_PANE_ANIMATION_INFO = MAKE_FOURCC('p', 'a', 'i', '1'); + + static u32 LoadAnimators(const RLAN_Header *header, Layout& layout, u8 key_set); + + void LoadKeyFrames(const u8 *file, u8 tag_count, u32 offset, u8 key_set); + virtual void SetFrame(FrameNumber frame, u8 key_set); + +protected: + Animator() {} + + virtual void ProcessHermiteKey(const KeyType& type, float value); + virtual void ProcessStepKey(const KeyType& type, StepKeyHandler::KeyData data); +private: + + struct + { + std::map step_keys; + std::map hermite_keys; + } keys[2]; +}; + +#endif diff --git a/source/banner/Banner.cpp b/source/banner/Banner.cpp new file mode 100644 index 00000000..3aeabcc9 --- /dev/null +++ b/source/banner/Banner.cpp @@ -0,0 +1,175 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#include +#include "SystemMenu/SystemMenuResources.h" +#include "utils/U8Archive.h" +#include "utils/LanguageCode.h" +#include "Banner.h" + +Banner::Banner() + : banner_bin(NULL) + , icon_bin(NULL) + , sound_bin(NULL) + , banner_bin_size(0) + , icon_bin_size(0) + , sound_bin_size(0) + , layout_banner(NULL) + , layout_icon(NULL) +{ +} + +Banner::~Banner() +{ + delete layout_banner; + delete layout_icon; + + if(banner_bin) + free(banner_bin); + if(icon_bin) + free(icon_bin); + if(sound_bin) + free(sound_bin); +} + +bool Banner::LoadBanner(const u8 *opening_bnr, u32 len) +{ + delete layout_banner; + if(banner_bin) + free(banner_bin); + + U8Archive openingArchive(opening_bnr, len); + + banner_bin = openingArchive.GetFileAllocated("/meta/banner.bin", &banner_bin_size); + if(!banner_bin) { + return false; + } + + layout_banner = LoadLayout(U8Archive(banner_bin, banner_bin_size), "banner", CONF_GetLanguageString()); + + return (layout_banner != NULL); +} + +bool Banner::LoadIcon(const u8 *opening_bnr, u32 len) +{ + delete layout_icon; + if(icon_bin) + free(icon_bin); + + U8Archive openingArchive(opening_bnr, len); + + icon_bin = openingArchive.GetFileAllocated("/meta/icon.bin", &icon_bin_size); + if(!icon_bin) { + return false; + } + + layout_icon = LoadLayout(U8Archive(icon_bin, icon_bin_size), "icon", CONF_GetLanguageString()); + + return (layout_icon != NULL); +} + +bool Banner::LoadSound(const u8 *opening_bnr, u32 len) +{ + if(sound_bin) + free(sound_bin); + + U8Archive openingArchive(opening_bnr, len); + + sound_bin = openingArchive.GetFileAllocated("/meta/sound.bin", &sound_bin_size); + if(!sound_bin) { + return false; + } + return true; +} + +Layout* Banner::LoadLayout(const U8Archive &archive, const std::string& lyt_name, const std::string &language) +{ + const u8 *brlyt = archive.GetFile("/arc/blyt/" + lyt_name + ".brlyt"); + if(!brlyt) { + return NULL; + } + + Layout *layout = new Layout; + layout->Load(brlyt); + + u32 length_start = 0, length_loop = 0; + + const u8 *brlan_start = archive.GetFile("/arc/anim/" + lyt_name + "_Start.brlan"); + const u8 *brlan_loop = 0; + + // try the alternative file + if(!brlan_start) + brlan_start = archive.GetFile("/arc/anim/" + lyt_name + "_In.brlan"); + + if (brlan_start) + length_start = Animator::LoadAnimators((const RLAN_Header *)brlan_start, *layout, 0); + + brlan_loop = archive.GetFile("/arc/anim/" + lyt_name + ".brlan"); + if(!brlan_loop) + brlan_loop = archive.GetFile("/arc/anim/" + lyt_name + "_Loop.brlan"); + if(!brlan_loop) + brlan_loop = archive.GetFile("/arc/anim/" + lyt_name + "_Rso0.brlan");// added for "artstyle" wiiware + + if (brlan_loop) + length_loop = Animator::LoadAnimators((const RLAN_Header *)brlan_loop, *layout, 1); + + // load textures after loading the animations so we get the list of tpl filenames from the brlans + layout->LoadTextures(archive); + layout->LoadFonts(archive); + layout->SetLanguage(language); + layout->SetLoopStart(length_start); + layout->SetLoopEnd(length_start + length_loop); + layout->SetFrame(0); + + return layout; +} + +void Banner::swap(Banner &ban) +{ + u8 *tmp_banner_bin = this->banner_bin; + u8 *tmp_icon_bin = this->icon_bin; + u8 *tmp_sound_bin = this->sound_bin; + u32 tmp_banner_bin_size = this->banner_bin_size; + u32 tmp_icon_bin_size = this->icon_bin_size; + u32 tmp_sound_bin_size = this->sound_bin_size; + Layout *tmp_layout_banner = this->layout_banner; + Layout *tmp_layout_icon = this->layout_icon; + + this->banner_bin = ban.banner_bin; + this->icon_bin = ban.icon_bin; + this->sound_bin = ban.sound_bin; + this->banner_bin_size = ban.banner_bin_size; + this->icon_bin_size = ban.icon_bin_size; + this->sound_bin_size = ban.sound_bin_size; + this->layout_banner = ban.layout_banner; + this->layout_icon = ban.layout_icon; + + ban.banner_bin = tmp_banner_bin; + ban.icon_bin = tmp_icon_bin; + ban.sound_bin = tmp_sound_bin; + ban.banner_bin_size = tmp_banner_bin_size; + ban.icon_bin_size = tmp_icon_bin_size; + ban.sound_bin_size = tmp_sound_bin_size; + ban.layout_banner = tmp_layout_banner; + ban.layout_icon = tmp_layout_icon; +} diff --git a/source/banner/Banner.h b/source/banner/Banner.h new file mode 100644 index 00000000..3b781c81 --- /dev/null +++ b/source/banner/Banner.h @@ -0,0 +1,55 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef _BANNER_H_ +#define _BANNER_H_ + +#include "Layout.h" + +class Banner +{ +public: + Banner(); + virtual ~Banner(); + + bool LoadBanner(const u8 *opening_bnr, u32 len); + bool LoadIcon(const u8 *opening_bnr, u32 len); + bool LoadSound(const u8 *opening_bnr, u32 len); + + Layout *getBanner() const { return layout_banner; } + Layout *getIcon() const { return layout_icon; } + const u8 *getSound() const { return sound_bin; } + u32 getSoundSize() const { return sound_bin_size; } + + void swap(Banner &ban); + +protected: + Layout* LoadLayout(const U8Archive &banner_file, const std::string& lyt_name, const std::string &language); + + u8 *banner_bin, *icon_bin, *sound_bin; + u32 banner_bin_size, icon_bin_size, sound_bin_size; + Layout *layout_banner, *layout_icon; +}; + +#endif diff --git a/source/banner/BannerAsync.cpp b/source/banner/BannerAsync.cpp new file mode 100644 index 00000000..163d1f71 --- /dev/null +++ b/source/banner/BannerAsync.cpp @@ -0,0 +1,234 @@ +/* +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#include +#include +#include +#include +#include "settings/CSettings.h" +#include "OpeningBNR.hpp" +#include "BannerAsync.h" + +vector BannerAsync::List; +queue BannerAsync::DeleteList; +lwp_t BannerAsync::Thread = LWP_THREAD_NULL; +mutex_t BannerAsync::ListLock = LWP_THREAD_NULL; +BannerAsync * BannerAsync::InUse = NULL; +bool BannerAsync::SleepThread = false; +bool BannerAsync::CloseThread = false; + + +BannerAsync::BannerAsync(const discHdr *hdr) + : header(hdr) +{ + ThreadInit(); + ThreadAdd(this); +} + +BannerAsync::~BannerAsync() +{ + ThreadRemove(this); + while(InUse == this) + usleep(100); +} + +void BannerAsync::ThreadAdd(BannerAsync *banner) +{ + LWP_MutexLock(ListLock); + List.push_back(banner); + LWP_MutexUnlock(ListLock); + LWP_ResumeThread(Thread); +} + +void BannerAsync::ThreadRemove(BannerAsync *banner) +{ + LWP_MutexLock(ListLock); + for(u32 i = 0; i < List.size(); ++i) + { + if(List[i] == banner) + { + List.erase(List.begin()+i); + break; + } + } + LWP_MutexUnlock(ListLock); +} + +void BannerAsync::RemoveBanner(BannerAsync *banner) +{ + LWP_MutexLock(ListLock); + DeleteList.push(banner); + LWP_MutexUnlock(ListLock); + LWP_ResumeThread(Thread); +} + +void BannerAsync::ClearQueue() +{ + LWP_MutexLock(ListLock); + List.clear(); + LWP_MutexUnlock(ListLock); +} + +void BannerAsync::PushFront(BannerAsync *banner) +{ + if(banner == InUse) + return; + + LWP_MutexLock(ListLock); + for(u32 i = 0; i < List.size(); ++i) + { + if(List[i] == banner) + { + List.erase(List.begin()+i); + break; + } + } + List.insert(List.begin(), banner); + LWP_MutexUnlock(ListLock); + LWP_ResumeThread(Thread); +} + +void * BannerAsync::BannerAsyncThread(void *arg) +{ + while(!CloseThread) + { + while(!List.empty() && !CloseThread && !SleepThread) + { + LWP_MutexLock(ListLock); + //! Delete the delete queue + while(!DeleteList.empty()) + { + delete DeleteList.front(); + DeleteList.pop(); + } + //! Check if there is still something to do + if(List.empty()) + { + LWP_MutexUnlock(ListLock); + continue; + } + + //! Get first entry and pop + InUse = List.front(); + List.erase(List.begin()); + LWP_MutexUnlock(ListLock); + + if (!InUse || !InUse->header) + continue; + + const u8 * banner = NULL; + u32 bannerSize = 0; + + if((InUse->header->type == TYPE_GAME_GC_IMG) || (InUse->header->type == TYPE_GAME_GC_DISC) || (InUse->header->type == TYPE_GAME_GC_EXTRACTED)) + { + //! first see if a cache file is present and load that if needed + if(BNRInstance::Instance()->Load(InUse->header)) + { + banner = BNRInstance::Instance()->Get(); + bannerSize = BNRInstance::Instance()->GetSize(); + } + else + { + //! load our default one + CustomBanner *gcBanner = BNRInstance::Instance()->CreateGCIcon(InUse->header); + if(gcBanner) { + InUse->swap(*gcBanner); + delete gcBanner; + } + } + } + else + { + BNRInstance::Instance()->Load(InUse->header); + banner = BNRInstance::Instance()->Get(); + bannerSize = BNRInstance::Instance()->GetSize(); + } + + if(banner != NULL && bannerSize > 0) + InUse->LoadIcon(banner, bannerSize); + + InUse = NULL; + } + + //! Delete the delete queue here as well in case list was empty + if(!DeleteList.empty()) + { + LWP_MutexLock(ListLock); + while(!DeleteList.empty()) + { + delete DeleteList.front(); + DeleteList.pop(); + } + LWP_MutexUnlock(ListLock); + } + + if(!CloseThread) + LWP_SuspendThread(Thread); + } + + return NULL; +} + +void BannerAsync::ResumeThread(void) +{ + SleepThread = false; + + if(Thread != LWP_THREAD_NULL) + LWP_ResumeThread(Thread); +} + +void BannerAsync::HaltThread(void) +{ + SleepThread = true; + + if(Thread == LWP_THREAD_NULL) + return; + + // wait for thread to finish + while (!LWP_ThreadIsSuspended(Thread)) + usleep(100); +} + +void BannerAsync::ThreadInit(void) +{ + if (Thread == LWP_THREAD_NULL) + { + LWP_MutexInit(&ListLock, false); + LWP_CreateThread(&Thread, BannerAsyncThread, NULL, NULL, 65536, 70); + } +} + +void BannerAsync::ThreadExit(void) +{ + if(Thread != LWP_THREAD_NULL) + { + ClearQueue(); + CloseThread = true; + LWP_ResumeThread(Thread); + LWP_JoinThread(Thread, NULL); + LWP_MutexUnlock(ListLock); + LWP_MutexDestroy(ListLock); + Thread = LWP_THREAD_NULL; + ListLock = LWP_MUTEX_NULL; + } +} + diff --git a/source/banner/BannerAsync.h b/source/banner/BannerAsync.h new file mode 100644 index 00000000..49ffdb2d --- /dev/null +++ b/source/banner/BannerAsync.h @@ -0,0 +1,64 @@ +/* +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#ifndef _BANNERASYNC_H_ +#define _BANNERASYNC_H_ + +#include +#include +#include +#include "usbloader/GameList.h" +#include "Banner.h" + +using namespace std; + +class BannerAsync : public Banner +{ +public: + + BannerAsync( const discHdr *header ); + virtual ~BannerAsync(); + + static void ThreadInit(void); + static void ThreadExit(void); + static void HaltThread(void); + static void ResumeThread(void); + static void RemoveBanner(BannerAsync *banner); + static void ClearQueue(); + static void PushFront(BannerAsync *banner); +private: + const discHdr *header; + + static void * BannerAsyncThread(void *arg); + static void ThreadAdd(BannerAsync* banner); + static void ThreadRemove(BannerAsync* banner); + + static vector List; + static queue DeleteList; + static lwp_t Thread; + static mutex_t ListLock; + static BannerAsync * InUse; + static bool SleepThread; + static bool CloseThread; +}; + +#endif /*_GUIIMAGEASYNC_H_*/ diff --git a/source/banner/BannerTools.h b/source/banner/BannerTools.h new file mode 100644 index 00000000..1f42c6e7 --- /dev/null +++ b/source/banner/BannerTools.h @@ -0,0 +1,62 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#ifndef BANNER_TOOLS_H_ +#define BANNER_TOOLS_H_ + +#include +#include "utils/gx_addons.h" +#include "gecko.h" + +#define MAKE_FOURCC(a, b, c, d) ((a) * (1 << 24) + (b) * (1 << 16) + (c) * (1 << 8) + (d) * (1 << 0)) + +typedef struct +{ + u32 magic; + u32 size; +} section_t; + +typedef struct +{ + float x, y; +} Vec2f; + +typedef struct +{ + float x, y, z; +} Vec3f; + +#define ALIGN32(x) (((x) + 31) & ~31) +#define LIMIT(x, min, max) \ + ({ \ + typeof( x ) _x = x; \ + typeof( min ) _min = min; \ + typeof( max ) _max = max; \ + ( ( ( _x ) < ( _min ) ) ? ( _min ) : ( ( _x ) > ( _max ) ) ? ( _max) : ( _x ) ); \ + }) + +#define MultiplyAlpha(a1, a2) ((u16) (a1) * (u16) (a2) / 0xFF) +#define FLOAT_2_U8(x) ((u8)((x) > 255.0f ? 255.0f : ((x) < 0.0f ? 0.0f : (x) + 0.5f))) +#define FLOAT_2_S16(x) ((s16)((x) > 32767.0f ? 32767.0f : ((x) < -32768.0f ? 32768.0f : (x) + 0.5f))) + +#endif diff --git a/source/banner/CustomBanner.cpp b/source/banner/CustomBanner.cpp new file mode 100644 index 00000000..1fc1b84b --- /dev/null +++ b/source/banner/CustomBanner.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include +#include "CustomBanner.h" +#include "ImageOperations/TextureConverter.h" +#include "utils/StringTools.h" + + +CustomBanner::CustomBanner() +{ +} + +CustomBanner::~CustomBanner() +{ + for(u32 i = 0; i < text_list.size(); i++) + delete [] text_list[i]; +} + +void CustomBanner::SetBannerTexture(const char *tex_name, const u8 *data, float width, float height, u8 fmt) +{ + if(!layout_banner) + return; + + Texture *texture = layout_banner->FindTexture( tex_name ); + if(texture != NULL && data != NULL) { + texture->LoadTextureData(data, width, height, fmt); + } +} + +void CustomBanner::SetBannerText(const char *text_name, const char *text) +{ + if(!layout_banner || !text) + return; + + Textbox *tbox = dynamic_cast(layout_banner->FindPane(text_name)); + if(tbox) + { + const wchar_t *wText = wfmt("%s", text); + int len = wcslen(wText); + u16 *text_char16 = new u16[len+1]; + + for(int i = 0; i < len; i++) + text_char16[i] = (u16) wText[i]; + text_char16[len] = 0; + + tbox->SetText(text_char16); + text_list.push_back(text_char16); + } +} + +void CustomBanner::SetIconTexture(const char *tex_name, const u8 *data, float width, float height, u8 fmt) +{ + if(!layout_icon) + return; + + Texture *texture = layout_icon->FindTexture( tex_name ); + if(texture != NULL && data != NULL) { + texture->LoadTextureData(data, width, height, fmt); + } +} + +u8 *CustomBanner::LoadTextureFromPng(const u8 * img, u32 imgSize, int *width, int *height) +{ + if(!img) + return NULL; + + // load png + gdImagePtr gdImg = gdImageCreateFromPngPtr(imgSize, (u8 *)img); + if(!gdImg) + return NULL; + + u8 *texture = GDImageToRGBA8(&gdImg, width, height); + + // free image object + gdImageDestroy( gdImg ); + + return texture; +} + +void CustomBanner::SetBannerPngImage(const char *tex_name, const u8 * img, u32 imgSize) +{ + int pngWidth; + int pngHeight; + + u8 * texData = LoadTextureFromPng(img, imgSize, &pngWidth, &pngHeight); + + if(texData) + { + SetBannerTexture(tex_name, texData, pngWidth, pngHeight, GX_TF_RGBA8); + free(texData); + } +} + +void CustomBanner::SetIconPngImage(const char *tex_name, const u8 * img, u32 imgSize) +{ + int pngWidth; + int pngHeight; + + u8 * texData = LoadTextureFromPng(img, imgSize, &pngWidth, &pngHeight); + + if(texData) + { + SetIconTexture(tex_name, texData, pngWidth, pngHeight, GX_TF_RGBA8); + free(texData); + } +} + +void CustomBanner::SetBannerTextureScale(float scale) +{ + Pane *pane = layout_banner->FindPane("HBPic"); + if(pane) + pane->SetScale(scale); + + pane = layout_banner->FindPane("HBPicSha"); + if(pane) + pane->SetScale(scale); +} + +void CustomBanner::SetBannerPaneVisible(const char *pane_name, bool visible) +{ + Pane *pane = layout_banner->FindPane(pane_name); + if(pane) + pane->SetVisible(visible); +} diff --git a/source/banner/CustomBanner.h b/source/banner/CustomBanner.h new file mode 100644 index 00000000..f3d4eea4 --- /dev/null +++ b/source/banner/CustomBanner.h @@ -0,0 +1,43 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef CUSTOM_BANNER_H_ +#define CUSTOM_BANNER_H_ + +#include +#include "Banner.h" + +using namespace std; + +class CustomBanner : public Banner +{ +public: + + CustomBanner(); + virtual ~CustomBanner(); + void SetBannerText(const char *text_name, const char *text); + void SetBannerPngImage(const char *tex_name, const u8 * img, u32 imgSize); + void SetIconPngImage(const char *tex_name, const u8 * img, u32 imgSize); + void SetBannerTexture(const char *tex_name, const u8 *data, float width, float height, u8 fmt); + void SetIconTexture(const char *tex_name, const u8 *data, float width, float height, u8 fmt); + void SetBannerTextureScale(float scale); + void SetBannerPaneVisible(const char *pane, bool visible); +private: + u8 *LoadTextureFromPng(const u8 * img, u32 imgSize, int *width, int *height); + vector text_list; +}; + +#endif /*CUSTOM_BANNER_H_*/ diff --git a/source/banner/Layout.cpp b/source/banner/Layout.cpp new file mode 100644 index 00000000..4cf4b916 --- /dev/null +++ b/source/banner/Layout.cpp @@ -0,0 +1,391 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#include "SystemMenu/SystemMenuResources.h" +#include "Layout.h" +#include "WiiFont.h" + +Layout::Layout() + : header(0) +{ +} + +Layout::~Layout() +{ + for(u32 i = 0; i < panes.size(); ++i) + delete panes[i]; + + for(u32 i = 0; i < resources.materials.size(); ++i) + delete resources.materials[i]; + + for(u32 i = 0; i < resources.textures.size(); ++i) + delete resources.textures[i]; + + for(u32 i = 0; i < resources.fonts.size(); ++i) { + + if(resources.fonts[i] == SystemMenuResources::Instance()->GetWbf1() || + resources.fonts[i] == SystemMenuResources::Instance()->GetWbf2()) + continue; + + delete resources.fonts[i]; + } +} + +bool Layout::Load(const u8 *brlyt) +{ + if(!brlyt) + return false; + + const BRLYT_Header *brlytFile = (const BRLYT_Header *) brlyt; + + if(brlytFile->magic != BRLYT_MAGIC || brlytFile->version != BRLYT_VERSION) + return false; + + Group* last_group = NULL; + std::stack*> group_stack; + group_stack.push(&groups); + + Pane* last_pane = NULL; + std::stack*> pane_stack; + pane_stack.push(&panes); + + const u8 *position = brlyt + brlytFile->header_len; + + for(u32 i = 0; i < brlytFile->section_count; ++i) + { + section_t *section = (section_t *) position; + position += section->size; + + if(section->magic == Layout::MAGIC) + { + header = (Layout::Header *) (section + 1); + } + else if (section->magic == TextureList::MAGIC) + { + const LytItemList *txl1 = (const LytItemList *) (section+1); + const char *nameoffset = ((const char *)(txl1+1)); + const LytStringTable *stringTable = (const LytStringTable *) (((const u8 *)(txl1+1))+txl1->offset_to_first); + + for(u32 i = 0; i < txl1->num_items; ++i) + { + const char *name = nameoffset+stringTable[i].offset_filename; + + Texture *texture = new Texture; + texture->setName(name); + resources.textures.push_back(texture); + } + } + else if (section->magic == MaterialList::MAGIC) + { + const LytItemList *mat1 = (const LytItemList *) (section+1); + const u32 *mat_offsets = (const u32 *) (((const u8 *)(mat1+1))+mat1->offset_to_first); + + for(u32 i = 0; i < mat1->num_items; ++i) + { + Material *material = new Material; + material->Load((Material::Header *) (((const u8 *) section)+mat_offsets[i])); + resources.materials.push_back(material); + } + } + else if (section->magic == FontList::MAGIC) + { + // load font list + const LytItemList *fnl1 = (const LytItemList *) (section+1); + const char *nameoffset = ((const char *)(fnl1+1)); + const LytStringTable *stringTable = (const LytStringTable *) (((const u8 *)(fnl1+1))+fnl1->offset_to_first); + + for(u32 i = 0; i < fnl1->num_items; i++) + { + const char *name = nameoffset+stringTable[i].offset_filename; + + WiiFont *font; + + if(strcmp(name, "wbf1.brfna") == 0 || strcmp(name, "RevoIpl_RodinNTLGPro_DB_32_I4.brfnt") == 0) { + //! 1st system font or alias for it + font = SystemMenuResources::Instance()->GetWbf1(); + if(!font) continue; + } + else if(strcmp(name, "wbf2.brfna") == 0) { + //! 2nd system font + font = SystemMenuResources::Instance()->GetWbf2(); + if(!font) continue; + } + else { + //! font from banner + font = new WiiFont; + font->SetName(name); + } + resources.fonts.push_back(font); + } + } + else if (section->magic == Pane::MAGIC) + { + Pane* pane = new Pane; + pane->Load((Pane::Header *) section); + pane_stack.top()->push_back(last_pane = pane); + } + else if (section->magic == Bounding::MAGIC) + { + Bounding* pane = new Bounding; + pane->Load((Pane::Header *) section); + pane_stack.top()->push_back(last_pane = pane); + } + else if (section->magic == Picture::MAGIC) + { + Picture* pane = new Picture; + pane->Load((Pane::Header *) section); + pane_stack.top()->push_back(last_pane = pane); + } + else if (section->magic == Window::MAGIC) + { + Window* pane = new Window; + pane->Load((Pane::Header *) section); + pane_stack.top()->push_back(last_pane = pane); + } + else if (section->magic == Textbox::MAGIC) + { + Textbox* pane = new Textbox; + pane->Load((Pane::Header *) section); + pane_stack.top()->push_back(last_pane = pane); + } + else if (section->magic == Layout::MAGIC_PANE_PUSH) + { + if (last_pane) + pane_stack.push(&last_pane->panes); + } + else if (section->magic == Layout::MAGIC_PANE_POP) + { + if (pane_stack.size() > 1) + pane_stack.pop(); + } + else if (section->magic == Group::MAGIC) + { + const char *grp = (const char *) (section + 1); + std::string group_name(grp, 0, Layout::Group::NAME_LENGTH); + Group& group_ref = (*group_stack.top())[group_name]; + grp += Layout::Group::NAME_LENGTH; + + u16 sub_count = *(u16 *) grp; + grp += 4; // 2 bytes reserved + + while (sub_count--) + { + std::string pane_name(grp, 0, Layout::Group::NAME_LENGTH); + group_ref.panes.push_back(pane_name); + grp += Layout::Group::NAME_LENGTH; + } + + last_group = &group_ref; + } + else if (section->magic == Layout::MAGIC_GROUP_PUSH) + { + if (last_group) + group_stack.push(&last_group->groups); + } + else if (section->magic == Layout::MAGIC_GROUP_POP) + { + if (group_stack.size() > 1) + group_stack.pop(); + } + else { + gprintf("Uknown layout section: %08X\n", section->magic); + } + } + return true; +} + +bool Layout::LoadTextures(const U8Archive &banner_file) +{ + bool success = false; + + for(u32 i = 0; i < resources.textures.size(); ++i) + { + const u8 *file = banner_file.GetFile("/arc/timg/" + resources.textures[i]->getName()); + if (file) + resources.textures[i]->Load(file); + else + success = false; + } + + return success; +} + +bool Layout::LoadFonts(const U8Archive &banner_file) +{ + bool success = false; + + for(u32 i = 0; i < resources.fonts.size(); ++i) + { + if(resources.fonts[i]->IsLoaded()) + continue; + + const u8 *file = banner_file.GetFile("/arc/font/" + resources.fonts[i]->getName()); + if (file) + resources.fonts[i]->Load(file); + else + success = false; + } + + return success; +} + +void Layout::Render(Mtx &modelview, const Vec2f &ScreenProps, bool widescreen, u8 render_alpha) const +{ + if(!header) + return; + + Mtx mv; + // we draw inverse + guMtxScaleApply(modelview, mv, 1.0f, -1.0f, 1.0f); + + // centered draw + if(header->centered) + guMtxTransApply(mv, mv, ScreenProps.x * 0.5f, ScreenProps.y * 0.5f, 0.f); + + // render all panes + for(u32 i = 0; i < panes.size(); ++i) + panes[i]->Render(resources, render_alpha, mv, widescreen); +} + +void Layout::SetFrame(FrameNumber frame_number) +{ + frame_current = frame_number; + + const u8 key_set = (frame_current >= frame_loop_start); + if (key_set) + frame_number -= frame_loop_start; + + resources.cur_set = key_set; + + for(u32 i = 0; i < panes.size(); ++i) + panes[i]->SetFrame(frame_number, key_set); + + for(u32 i = 0; i < resources.materials.size(); ++i) + resources.materials[i]->SetFrame(frame_number, key_set); +} + +void Layout::AdvanceFrame() +{ + ++frame_current; + + if (frame_current >= frame_loop_end) + frame_current = frame_loop_start; + + SetFrame(frame_current); +} + +void Layout::SetLanguage(const std::string& language) +{ + if(!header) + return; + + // check if that language is found + bool lang_found = false; + + // hide panes of non-matching languages + std::map::iterator itr; + for(itr = groups["RootGroup"].groups.begin(); itr != groups["RootGroup"].groups.end(); itr++) + { + // check if that language exists + if(!lang_found && itr->first == language) + lang_found = true; + + // some hax, there are some odd "Rso0" "Rso1" groups that shouldn't be hidden + // only the 3 character language groups should be + if (itr->first != language && itr->first.length() == 3) + { + std::list::iterator itr2; + for(itr2 = itr->second.panes.begin(); itr2 != itr->second.panes.end(); itr2++) + { + Pane* const found = FindPane(*itr2); + if (found) + found->SetHide(true); + } + } + } + + // show english if langauge is not found + if(!lang_found && language != "ENG") + { + SetLanguage("ENG"); + return; + } + + // unhide panes of matching language, some banners list language specific panes in multiple language groups + std::list::iterator itr2; + for(itr2 = groups["RootGroup"].groups[language].panes.begin(); itr2 != groups["RootGroup"].groups[language].panes.end(); itr2++) + { + Pane* const found = FindPane(*itr2); + if (found) + { + found->SetHide(false); + found->SetVisible(true); // all games with languages start as visible + } + } +} + +void Layout::AddPalette(const std::string &name, u8 key_set) +{ + resources.palettes[key_set].push_back(name); + + if(FindTexture(name) != 0) + return; + + Texture *texture = new Texture; + texture->setName(name); + resources.textures.push_back(texture); +} + +Pane* Layout::FindPane(const std::string& find_name) +{ + for(u32 i = 0; i < panes.size(); ++i) + { + Pane* found = panes[i]->FindPane(find_name); + if(found) + return found; + } + + return NULL; +} + +Material* Layout::FindMaterial(const std::string& find_name) +{ + for(u32 i = 0; i < resources.materials.size(); ++i) + { + if (find_name.compare(0, 20, resources.materials[i]->getName()) == 0) + return resources.materials[i]; + } + + return NULL; +} + +Texture* Layout::FindTexture(const std::string& find_name) +{ + for(u32 i = 0; i < resources.textures.size(); ++i) + { + if (find_name == resources.textures[i]->getName()) + return resources.textures[i]; + } + + return NULL; +} diff --git a/source/banner/Layout.h b/source/banner/Layout.h new file mode 100644 index 00000000..a1cac479 --- /dev/null +++ b/source/banner/Layout.h @@ -0,0 +1,144 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#ifndef LAYOUT_H_ +#define LAYOUT_H_ + +#include +#include +#include +#include +#include + +#include "Texture.h" +#include "Material.h" +#include "Pane.h" +#include "Picture.h" +#include "Window.h" +#include "WiiFont.h" +#include "Textbox.h" +#include "utils/U8Archive.h" + +typedef std::vector PaletteList; + +struct BannerResources +{ + MaterialList materials; + TextureList textures; + FontList fonts; + PaletteList palettes[2]; + u8 cur_set; +}; + +class Layout +{ +public: + Layout(); + virtual ~Layout(); + + static const u32 BRLYT_MAGIC = MAKE_FOURCC('R', 'L', 'Y', 'T'); + static const u32 BRLYT_VERSION = 0xFEFF0008; + static const u32 MAGIC = MAKE_FOURCC('l', 'y', 't', '1'); + static const u32 MAGIC_PANE_PUSH = MAKE_FOURCC('p', 'a', 's', '1'); + static const u32 MAGIC_PANE_POP = MAKE_FOURCC('p', 'a', 'e', '1'); + static const u32 MAGIC_GROUP_PUSH = MAKE_FOURCC('g', 'r', 's', '1'); + static const u32 MAGIC_GROUP_POP = MAKE_FOURCC('g', 'r', 'e', '1'); + + bool Load(const u8 *brlyt); + bool LoadTextures(const U8Archive &banner_file); + bool LoadFonts(const U8Archive &banner_file); + + void Render(Mtx &modelview, const Vec2f &ScreenProps, bool widescreen, u8 render_alpha = 0xFF) const; + + FrameNumber GetFrame() const { return frame_current; } + void SetFrame(FrameNumber frame_number); + void AdvanceFrame(); + + void SetLoopStart(FrameNumber loop_start) { frame_loop_start = loop_start; } + void SetLoopEnd(FrameNumber loop_end) { frame_loop_end = loop_end; } + + float GetWidth() const { return header->width; } + void SetWidth(float _width) { header->width = _width; } + + float GetHeight() const { return header->height; } + void SetHeight(float _height) { header->height = _height; } + + bool isCentered() const { return header->centered; } + + void SetLanguage(const std::string& language); + + Pane* FindPane(const std::string& name); + Material* FindMaterial(const std::string& name); + Texture *FindTexture(const std::string &name); + + void AddPalette(const std::string &name, u8 key_set); +protected: + struct BRLYT_Header + { + u32 magic; + u32 version; + u32 filesize; + u16 header_len; + u16 section_count; + } __attribute__((packed)); + + struct Header + { + u8 centered; + u8 pad[3]; + float width; + float height; + } __attribute__((packed)); + + struct LytItemList + { + u16 num_items; + u16 offset_to_first; + } __attribute__((packed)); + + struct LytStringTable + { + u32 offset_filename; + u32 pad; + } __attribute__((packed)); + + struct Group + { + static const u32 MAGIC = MAKE_FOURCC('g', 'r', 'p', '1'); + static const u32 NAME_LENGTH = 0x10; + + std::map groups; + std::list panes; + }; + + Layout::Header *header; + + BannerResources resources; + PaneList panes; + + FrameNumber frame_current, frame_loop_start, frame_loop_end; + + std::map groups; +}; + +#endif diff --git a/source/banner/Material.cpp b/source/banner/Material.cpp new file mode 100644 index 00000000..e1143226 --- /dev/null +++ b/source/banner/Material.cpp @@ -0,0 +1,591 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok and giantpune + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include +#include +#include "Layout.h" +#include "Material.h" + +Material::Material() + : flags(0) + , texture_maps(0) + , texture_srts(0) + , texture_coord_gens(0) + , chan_control(0) + , mat_color(0) + , tev_swap_table(0) + , ind_srt(0) + , ind_stage(0) + , tev_stages(0) + , alpha_compare(0) + , blend_mode(0) + , header(0) +{ + for( int i = 0; i < 8; i++ ) + palette_texture[i] = DEFAULT_PALETTE; +} + +void Material::Load(Material::Header *file) +{ + header = file; + + // Flags + flags = (MatFlags *) &header->flags; + + flags->texture_map = std::min((int)MAX_TEX_MAP, (int)flags->texture_map); + flags->texture_srt = std::min((int)MAX_TEX_SRT, (int)flags->texture_srt); + flags->texture_coord_gen = std::min((int)MAX_TEX_GEN, (int)flags->texture_coord_gen); + flags->ind_srt = flags->ind_srt; + flags->ind_stage = std::min((int)MAX_IND_STAGES, (int)flags->ind_stage); + flags->tev_stages = std::min((int)MAX_TEV_STAGES, (int)flags->tev_stages); + + u8 *buf_offset = (u8 *) (header+1); + + // texture map + if(flags->texture_map) + { + texture_maps = (TextureMap *) buf_offset; + buf_offset += sizeof(TextureMap) * flags->texture_map; + } + + // texture srt + if(flags->texture_srt) + { + texture_srts = (TextureSrt *) buf_offset; + buf_offset += sizeof(TextureSrt) * flags->texture_srt; + } + + // texture coord gen + if(flags->texture_coord_gen) + { + texture_coord_gens = (TextureCoordGen *) buf_offset; + buf_offset += sizeof(TextureCoordGen) * flags->texture_coord_gen; + } + + // channel control + if (flags->channel_control) + { + chan_control = (ChannelControl *) buf_offset; + buf_offset += sizeof(ChannelControl); + } + + // material color + if (flags->material_color) + { + mat_color = (GXColor *) buf_offset; + buf_offset += sizeof(GXColor); + } + //else Default to 0xFFFFFFFF + + // tev swap table + if (flags->tev_swap_table) + { + tev_swap_table = (TevSwap *) buf_offset; + buf_offset += sizeof(TevSwap) * 4; + } + + // ind srt + if(flags->ind_srt) + { + ind_srt = (IndSrt *) buf_offset; + buf_offset += sizeof(IndSrt) * flags->ind_srt; + } + + // ind stage + if(flags->ind_stage) + { + ind_stage = (IndStage *) buf_offset; + buf_offset += sizeof(IndStage) * flags->ind_stage; + } + + // tev stage + if(flags->tev_stages) + { + tev_stages = (TevStage *) buf_offset; + buf_offset += sizeof(TevStage) * flags->tev_stages; + } + + // alpha compare + if (flags->alpha_compare) + { + alpha_compare = (AlphaCompareModes *) buf_offset; + buf_offset += sizeof(AlphaCompareModes); + } + + // blend mode + if (flags->blend_mode) + { + blend_mode = (BlendModes *) buf_offset; + buf_offset += sizeof(BlendModes); + } +} + +inline void Material::ApplyChannelControl(u8 render_alpha, bool &modulate_colors) const +{ + if(flags->channel_control) + { + GX_SetChanCtrl(0, 0, 0, chan_control->color_matsrc, 0, 0, 2 ); + GX_SetChanCtrl(2, 0, 0, chan_control->alpha_matsrc, 0, 0, 2 ); + + if(chan_control->alpha_matsrc != 1 && chan_control->color_matsrc != 1) + modulate_colors = false; + + if(!chan_control->alpha_matsrc || !chan_control->color_matsrc) + { + GXColor matColor = (GXColor){0xff, 0xff, 0xff, MultiplyAlpha(0xff, render_alpha) }; + + if(flags->material_color) + matColor = (GXColor){ mat_color->r, mat_color->g, mat_color->b, + MultiplyAlpha(mat_color->a, render_alpha) }; + + GX_SetChanMatColor(4, matColor); + + if((*(u32 *)&matColor) == 0xFFFFFFFF) + modulate_colors = true; + } + } + else + { + GX_SetChanCtrl(4, 0, 0, 1, 0, 0, 2); + } + + GX_SetNumChans(1); +} + +inline void Material::ApplyTexCoordGens(void) const +{ + // texture coord gen + for(u32 i = 0; i != flags->texture_coord_gen; ++i) + { + const TextureCoordGen &tcg = texture_coord_gens[i]; + GX_SetTexCoordGen(GX_TEXCOORD0 + i, tcg.tgen_typ, tcg.tgen_src, tcg.mtxsrc); + + const u8 mtrx = (tcg.mtxsrc - GX_TEXMTX0) / 3; + + if (tcg.tgen_typ == 1 && tcg.mtxsrc != GX_IDENTITY && mtrx < flags->texture_srt) + { + const TextureSrt& srt = texture_srts[mtrx]; + + const float rotate_rad = DegToRad(srt.rotate); + const float cosF = cosf(rotate_rad); + const float sinF = sinf(rotate_rad); + + // setup texture matrix + Mtx m; + m[0][0] = srt.scale_x * cosF; + m[0][1] = srt.scale_y * -sinF; + m[0][2] = 0.0f; + m[0][3] = -0.5f * (m[0][0] + m[0][1]) + srt.translate_x + 0.5f; + m[1][0] = srt.scale_x * sinF; + m[1][1] = srt.scale_y * cosF; + m[1][2] = 0.0f; + m[1][3] = -0.5f * (m[1][0] + m[1][1]) + srt.translate_y + 0.5f; + m[2][0] = 0.0f; + m[2][1] = 0.0f; + m[2][2] = 1.0f; + m[2][3] = 0.0f; + + GX_LoadTexMtxImm(m, tcg.mtxsrc, GX_MTX3x4); + } + } + + GX_SetNumTexGens(flags->texture_coord_gen); +} + +inline void Material::ApplyTevSwapTable(void) const +{ + if (flags->tev_swap_table) + { + for(int i = 0; i < 4; i++) + GX_SetTevSwapModeTable(GX_TEV_SWAP0 + i, + tev_swap_table[i].r, tev_swap_table[i].g, + tev_swap_table[i].b, tev_swap_table[i].a); + } + else + { + GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); + GX_SetTevSwapModeTable(GX_TEV_SWAP1, GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_ALPHA); + GX_SetTevSwapModeTable(GX_TEV_SWAP2, GX_CH_GREEN, GX_CH_GREEN, GX_CH_GREEN, GX_CH_ALPHA); + GX_SetTevSwapModeTable(GX_TEV_SWAP3, GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE, GX_CH_ALPHA); + } +} + +inline void Material::ApplyTevStages(bool modulate_colors) const +{ + u32 tev_stages_cnt = 0; + + if(flags->tev_stages) + { + // tev stages + for(u32 i = 0; i < flags->tev_stages; ++i) + { + const TevStage &ts = tev_stages[i]; + + GX_SetTevOrder(i, ts.texcoord, ts.tex_map | (ts.lowBit << 8), ts.color ); + GX_SetTevSwapMode(i, ts.ras_sel, ts.tex_sel); + + GX_SetTevColorIn(i, ts.color_in.a, ts.color_in.b, ts.color_in.c, ts.color_in.d); + GX_SetTevColorOp(i, ts.color_in.tevop, ts.color_in.tevbias, ts.color_in.tevscale, ts.color_in.clamp, ts.color_in.tevregid ); + GX_SetTevKColorSel(i, ts.color_in.sel ); + + GX_SetTevAlphaIn(i, ts.alpha_in.a, ts.alpha_in.b, ts.alpha_in.c, ts.alpha_in.d); + GX_SetTevAlphaOp(i, ts.alpha_in.tevop, ts.alpha_in.tevbias, ts.alpha_in.tevscale, ts.alpha_in.clamp, ts.alpha_in.tevregid ); + GX_SetTevKAlphaSel(i, ts.alpha_in.sel ); + + GX_SetTevIndirect(i, ts.ind.indtexid, ts.ind.format, ts.ind.bias, ts.ind.mtxid, + ts.ind.wrap_s, ts.ind.wrap_t, ts.ind.addprev, ts.ind.utclod, ts.ind.a); + + tev_stages_cnt++; + } + } + else + { + if(flags->texture_map == 0) + { + // 1st stage + GX_SetTevOrder(GX_TEVSTAGE0, 0xFF, 0xFF, 4); + GX_SetTevColorIn(GX_TEVSTAGE0, 0xF, 4, 0xA, 0xF); + GX_SetTevAlphaIn(GX_TEVSTAGE0, 0x7, 2, 0x5, 0x7); + tev_stages_cnt++; + } + else if(flags->texture_map == 1) + { + // 1st stage + GX_SetTevOrder(GX_TEVSTAGE0, 0, 0, 0xFF); + GX_SetTevColorIn(GX_TEVSTAGE0, 2, 4, 8, 0xF); + GX_SetTevAlphaIn(GX_TEVSTAGE0, 1, 2, 4, 7); + tev_stages_cnt++; + + // 2nd stage + if(modulate_colors) + { + GX_SetTevOrder(GX_TEVSTAGE0 + tev_stages_cnt, 0xFF, 0xFF, 4); + GX_SetTevColorIn(GX_TEVSTAGE0 + tev_stages_cnt, 0xF, 0, 0xA, 0xF); + GX_SetTevAlphaIn(GX_TEVSTAGE0 + tev_stages_cnt, 7, 0, 5, 7); + tev_stages_cnt++; + } + } + else if(flags->texture_map == 2) + { + // 1st stage + GX_SetTevOrder(GX_TEVSTAGE0, 0, 0, 0xFF); + GX_SetTevColorIn(GX_TEVSTAGE0, 0xF, 0xF, 0xF, 8); + GX_SetTevAlphaIn(GX_TEVSTAGE0, 7, 7, 7, 4); + tev_stages_cnt++; + + // 2nd stage + GX_SetTevOrder(GX_TEVSTAGE0 + tev_stages_cnt, 1, 1, 0xFF); + GX_SetTevColorIn(GX_TEVSTAGE0 + tev_stages_cnt, 8, 0, 0xE, 0xF); + GX_SetTevAlphaIn(GX_TEVSTAGE0 + tev_stages_cnt, 4, 0, 6, 7); + GX_SetTevKColorSel(GX_TEVSTAGE0 + tev_stages_cnt, 0x1f); + GX_SetTevKAlphaSel(GX_TEVSTAGE0 + tev_stages_cnt, 0x1f); + tev_stages_cnt++; + + // 3rd stage + if(modulate_colors) + { + GX_SetTevOrder(GX_TEVSTAGE0 + tev_stages_cnt, 0xFF, 0xFF, 4); + GX_SetTevColorIn(GX_TEVSTAGE0 + tev_stages_cnt, 0xF, 0, 0xA, 0xF); + GX_SetTevAlphaIn(GX_TEVSTAGE0 + tev_stages_cnt, 7, 0, 5, 7); + tev_stages_cnt++; + } + } + else + { + u32 TevKDefault[] = { 0x1F, 0x1B, 0x17, 0x13, 0x1E, 0x1A, 0x16, 0x12 }; + + for(int i = 0; i < flags->texture_map; i++) + { + GX_SetTevOrder(i, i, i, 0xff ); + + GX_SetTevColorIn(i, 0xf, 8, 0xe, i ? 0xf : 0 ); + GX_SetTevAlphaIn(i, 7, 4, 6, i ? 7 : 0 ); + GX_SetTevKColorSel(i, TevKDefault[i] ); + GX_SetTevKAlphaSel(i, TevKDefault[i] ); + tev_stages_cnt++; + } + + GX_SetTevOrder(GX_TEVSTAGE0 + tev_stages_cnt, 0xff, 0xff, 0xff ); + GX_SetTevColorIn(GX_TEVSTAGE0 + tev_stages_cnt, 2, 4, 0, 0xf ); + GX_SetTevAlphaIn(GX_TEVSTAGE0 + tev_stages_cnt, 1, 2, 0, 7 ); + tev_stages_cnt++; + + if(modulate_colors) + { + GX_SetTevOrder(GX_TEVSTAGE0 + tev_stages_cnt, 0xFF, 0xFF, 4); + GX_SetTevColorIn(GX_TEVSTAGE0 + tev_stages_cnt, 0xF, 0, 0xA, 0xF); + GX_SetTevAlphaIn(GX_TEVSTAGE0 + tev_stages_cnt, 7, 0, 5, 7); + tev_stages_cnt++; + } + } + + for(u32 i = 0; i < tev_stages_cnt; i++) + { + GX_SetTevColorOp(GX_TEVSTAGE0 + i, 0, 0, 0, 1, 0); + GX_SetTevAlphaOp(GX_TEVSTAGE0 + i, 0, 0, 0, 1, 0); + GX_SetTevDirect(GX_TEVSTAGE0 + i); + GX_SetTevSwapMode(GX_TEVSTAGE0 + i, 0, 0); + } + } + + // enable correct number of tev stages + GX_SetNumTevStages(tev_stages_cnt); +} + +inline void Material::ApplyIndStages(void) const +{ + for( int i = 0; i < flags->ind_srt; i++ ) + { + const IndSrt &ind = ind_srt[i]; + + const float rotate_rad = DegToRad(ind.rotate); + // maybe add a look up table + float cosF = cosf(rotate_rad); + float sinF = sinf(rotate_rad); + + int scale_exp = 0; + f32 mtx23[2][3]; + f32 mtxabs23[2][3]; + + mtx23[0][0] = ind.scale_x * cosF; + mtx23[0][1] = ind.scale_y * -sinF; + mtx23[0][2] = ind.translate_x; + + mtx23[1][0] = ind.scale_x * sinF; + mtx23[1][1] = ind.scale_y * cosF; + mtx23[1][2] = ind.translate_y; + + // create matrix with abs values + // compiler will optimize the loops + for(int n = 0; n < 2; n++) + for(int m = 0; m < 3; m++) + mtxabs23[n][m] = fabs(mtx23[n][m]); + + // hardcore clamping going on here + if( (mtxabs23[0][0] >= 1.0f) + || (mtxabs23[0][1] >= 1.0f) + || (mtxabs23[0][2] >= 1.0f) + || (mtxabs23[1][0] >= 1.0f) + || (mtxabs23[1][1] >= 1.0f) + || (mtxabs23[1][2] >= 1.0f)) + { + while( scale_exp < 0x2E + && ((mtxabs23[0][0] >= 1.0f) + || (mtxabs23[0][1] >= 1.0f) + || (mtxabs23[0][2] >= 1.0f) + || (mtxabs23[1][0] >= 1.0f) + || (mtxabs23[1][1] >= 1.0f) + || (mtxabs23[1][2] >= 1.0f))) + { + for(int n = 0; n < 2; n++) + { + for(int m = 0; m < 3; m++) + { + mtx23[n][m] *= 0.5f; + mtxabs23[n][m] *= 0.5f; + } + } + + scale_exp++; + } + } + else if( (mtxabs23[0][0] < 0.5f) + && (mtxabs23[0][1] < 0.5f) + && (mtxabs23[0][2] < 0.5f) + && (mtxabs23[1][0] < 0.5f) + && (mtxabs23[1][1] < 0.5f) + && (mtxabs23[1][2] < 0.5f)) + { + while( scale_exp > -0x11 + && (mtxabs23[0][0] < 0.5f) + && (mtxabs23[0][1] < 0.5f) + && (mtxabs23[0][2] < 0.5f) + && (mtxabs23[1][0] < 0.5f) + && (mtxabs23[1][1] < 0.5f) + && (mtxabs23[1][2] < 0.5f)) + { + for(int n = 0; n < 2; n++) + { + for(int m = 0; m < 3; m++) + { + mtx23[n][m] *= 2.0f; + mtxabs23[n][m] *= 2.0f; + } + } + + scale_exp--; + } + } + + GX_SetIndTexMatrix(GX_ITM_0 + i, mtx23, scale_exp); + } + + for( int i = 0; i < flags->ind_stage; i++ ) + { + const IndStage &stage = ind_stage[i]; + GX_SetIndTexOrder(i, stage.texcoord, stage.tex_map); + GX_SetIndTexCoordScale(i, stage.scale_s, stage.scale_t); + } + + GX_SetNumIndStages(flags->ind_stage); +} + +inline void Material::ApplyTextures(const BannerResources& resources) const +{ + u8 tlut_name = 0; + + for(u32 i = 0; i < flags->texture_map; ++i) + { + const TextureMap &tr = texture_maps[i]; + + if(palette_texture[i] == DEFAULT_PALETTE) + { + if (tr.tex_index < resources.textures.size()) + resources.textures[tr.tex_index]->Apply(tlut_name, i, tr.wrap_s, tr.wrap_t); + } + else + { + // find texture from palette + if(palette_texture[i] >= resources.palettes[resources.cur_set].size()) + { + gprintf( "palette index is out of range %i\n", palette_texture[i]); + return; + } + for(u32 n = 0; n < resources.textures.size(); n++) + { + if(resources.textures[n]->getName() == resources.palettes[resources.cur_set][palette_texture[i]]) + { + resources.textures[n]->Apply(tlut_name, i, tr.wrap_s, tr.wrap_t); + break; + } + } + } + } + + // invalidate texture cache + GX_InvalidateTexAll(); +} + +void Material::Apply(const BannerResources& resources, u8 render_alpha, bool modulate_colors) const +{ + // channel control and material color + ApplyChannelControl(render_alpha, modulate_colors); + + // texture coordinates gen + ApplyTexCoordGens(); + + // bind textures + ApplyTextures(resources); + + for (u32 i = 0; i < 4; ++i) + { + // tev reg colors + if(i < 3) + GX_SetTevColorS10(GX_TEVREG0 + i, header->color_regs[i]); + + // tev k colors + GX_SetTevKColor(GX_KCOLOR0 + i, header->color_constants[i]); + } + + // tev swap colors + ApplyTevSwapTable(); + + // tev stages + ApplyTevStages(modulate_colors); + + // ind stages + ApplyIndStages(); + + // alpha compare + if(flags->alpha_compare) + GX_SetAlphaCompare(alpha_compare->compare & 0xf, alpha_compare->ref0, + alpha_compare->op, alpha_compare->compare >> 4, alpha_compare->ref1); + else + GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); + + // blend mode + if (flags->blend_mode) + GX_SetBlendMode(blend_mode->type, blend_mode->src_factor, blend_mode->dst_factor, blend_mode->logical_op); + else + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_SET); +} + +void Material::ProcessHermiteKey(const KeyType& type, float value) +{ + if (type.type == ANIMATION_TYPE_TEXTURE_SRT) // texture scale/rotate/translate + { + if (type.target < 5 && type.index < flags->texture_srt) + { + (&texture_srts[type.index].translate_x)[type.target] = value; + return; + } + // TODO: Something is still here: target 0-4 and index 1-9 while texture_srt is 1, value is always 0 or 1 + return; // TODO remove this + } + else if (type.type == ANIMATION_TYPE_IND_MATERIAL) // ind texture crap + { + if (type.target < 5 && type.index < flags->ind_srt) + { + (&ind_srt[type.index].translate_x)[type.target] = value; + return; + } + return; // TODO remove this + } + else if (type.type == ANIMATION_TYPE_MATERIAL_COLOR) // material color + { + if (type.target < 4) + { + // mat_color + if(flags->material_color) + (&mat_color->r)[type.target] = FLOAT_2_U8(value); + return; + } + else if (type.target < 0x10) + { + (&header->color_regs->r)[type.target - 4] = FLOAT_2_S16(value); + return; + } + else if (type.target < 0x20) + { + (&header->color_constants->r)[type.target - 0x10] = FLOAT_2_U8(value); + return; + } + } + + Base::ProcessHermiteKey(type, value); +} + +void Material::ProcessStepKey(const KeyType& type, StepKeyHandler::KeyData data) +{ + if (type.type == ANIMATION_TYPE_TEXTURE_PALETTE) // tpl palette + { + if(type.index < MAX_TEX_MAP) + { + palette_texture[type.index] = data.data2; + return; + } + } + + Base::ProcessStepKey(type, data); +} diff --git a/source/banner/Material.h b/source/banner/Material.h new file mode 100644 index 00000000..50d744ce --- /dev/null +++ b/source/banner/Material.h @@ -0,0 +1,253 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok and giantpune + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef WII_BNR_MATERIAL_H_ +#define WII_BNR_MATERIAL_H_ + +#include "Animator.h" +#include "Texture.h" + +struct BannerResources; + +class Material : public Animator +{ +public: + typedef Animator Base; + + struct Header + { + char name[20]; + GXColorS10 color_regs[3]; + GXColor color_constants[4]; + u32 flags; + } __attribute__((packed)); + + Material(); + + void Load(Material::Header *mat); + void Apply(const BannerResources& resources, u8 render_alpha, bool modulate) const; + const char *getName() const { return header->name; } + + const Material::Header *GetHeader() const { return header; } +protected: + void ProcessHermiteKey(const KeyType& type, float value); + void ProcessStepKey(const KeyType& type, StepKeyHandler::KeyData data); + +private: + void ApplyChannelControl(u8 render_alpha, bool &modulate_colors) const; + void ApplyTevSwapTable(void) const; + void ApplyTexCoordGens(void) const; + void ApplyTevStages(bool modulate_colors) const; + void ApplyIndStages(void) const; + void ApplyTextures(const BannerResources& resources) const; + + enum + { + MAX_TEX_MAP = 8, + MAX_TEX_SRT = 10, + MAX_TEX_GEN = 8, + MAX_IND_STAGES = 4, + MAX_TEV_STAGES = 16, + DEFAULT_PALETTE = 0xFF, + }; + + struct MatFlags + { + u32 pad2 : 4; + u32 material_color : 1; + u32 pad : 1; + u32 channel_control : 1; + u32 blend_mode : 1; + u32 alpha_compare : 1; + u32 tev_stages : 5; + u32 ind_stage : 3; + u32 ind_srt : 2; + u32 tev_swap_table : 1; + u32 texture_coord_gen : 4; + u32 texture_srt : 4; + u32 texture_map : 4; + } __attribute__((packed)); + + struct TextureMap + { + u16 tex_index; + u8 wrap_s; + u8 wrap_t; + } __attribute__((packed)); + + struct TextureSrt + { + f32 translate_x; + f32 translate_y; + f32 rotate; + f32 scale_x; + f32 scale_y; + } __attribute__((packed)); + + struct TextureCoordGen + { + u8 tgen_typ; + u8 tgen_src; + u8 mtxsrc; + u8 pad; + } __attribute__((packed)); + + struct ChannelControl + { + u8 color_matsrc; + u8 alpha_matsrc; + u16 pad; + } __attribute__((packed)); + + struct IndSrt + { + f32 translate_x; + f32 translate_y; + f32 rotate; + f32 scale_x; + f32 scale_y; + } __attribute__((packed)); + + struct IndStage + { + u8 texcoord; + u8 tex_map; + u8 scale_s; + u8 scale_t; + } __attribute__((packed)); + + struct BlendModes + { + u8 type, src_factor, dst_factor, logical_op; + + } __attribute__((packed)); + + struct AlphaCompareModes + { + u8 compare, op, ref0, ref1; + + } __attribute__((packed)); + + struct TevSwap + { + u32 a : 2; + u32 b : 2; + u32 g : 2; + u32 r : 2; + } __attribute__((packed)); + + struct TevStage + { + u32 texcoord : 8; + u32 color : 8; + u32 tex_map : 8; + + u32 unk : 3; + u32 tex_sel : 2; + u32 ras_sel : 2; + u32 lowBit : 1; + + struct + { + u32 b : 4; + u32 a : 4; + + u32 d : 4; + u32 c : 4; + + u32 tevscale : 2; + u32 tevbias : 2; + u32 tevop : 4; + + u32 sel : 5; + u32 tevregid : 2; + u32 clamp : 1; + + } __attribute__((packed)) color_in, __attribute__((packed)) alpha_in; + + struct + { + u32 unk1 : 6; + u32 indtexid : 2; + + u32 unk2 : 1; + u32 mtxid : 4; + u32 bias : 3; + + u32 unk3 : 2; + u32 wrap_t : 3; + u32 wrap_s : 3; + + u32 unk4 : 2; + u32 a : 2; + u32 utclod : 1; + u32 addprev : 1; + u32 format : 2; + + } __attribute__((packed)) ind; + } __attribute__((packed)); + + // Material flags + MatFlags *flags; + + // Texture + TextureMap *texture_maps; + TextureSrt *texture_srts; + TextureCoordGen *texture_coord_gens; + + // Color channels + ChannelControl *chan_control; + + // Material colors + GXColor *mat_color; + + // Tev color swap + TevSwap *tev_swap_table; + + // Indirect textures + IndSrt *ind_srt; + IndStage *ind_stage; + + // Texture environment + TevStage *tev_stages; + + // Blending and alpha compare + AlphaCompareModes *alpha_compare; + BlendModes *blend_mode; + + // palette animation + u8 palette_texture[MAX_TEX_MAP]; + + //! Material header + Material::Header *header; +}; + +class MaterialList : public std::vector +{ +public: + static const u32 MAGIC = MAKE_FOURCC('m', 'a', 't', '1'); +}; + + +#endif diff --git a/source/banner/OpeningBNR.cpp b/source/banner/OpeningBNR.cpp index 4344f94f..8d7be2f5 100644 --- a/source/banner/OpeningBNR.cpp +++ b/source/banner/OpeningBNR.cpp @@ -1,15 +1,46 @@ +/* +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ #include #include #include "Channels/channels.h" +#include "GameCube/GCGames.h" +#include "libs/libwbfs/gcdisc.h" +#include "FileOperations/fileops.h" +#include "language/gettext.h" #include "usbloader/disc.h" #include "usbloader/wbfs.h" +#include "usbloader/wdvd.h" +#include "usbloader/wbfs/wbfs_rw.h" #include "utils/uncompress.h" +#include "themes/CTheme.h" +#include "settings/GameTitles.h" +#include "wstring.hpp" #include "OpeningBNR.hpp" BNRInstance * BNRInstance::instance = NULL; OpeningBNR::OpeningBNR() - : imetHdr(0) + : imetHdr(0), filesize(0) { memset(gameID, 0, sizeof(gameID)); } @@ -20,68 +51,142 @@ OpeningBNR::~OpeningBNR() free(imetHdr); } -bool OpeningBNR::Load(const u64 &tid, const char *pathPrefix) +bool OpeningBNR::LoadCachedBNR(const char *id) { - if(tid == 0) + char path[255]; + snprintf(path, sizeof(path), "%s%.6s.bnr", Settings.BNRCachePath, id); + if((filesize = FileSize(path)) == 0) + { + snprintf(path, sizeof(path), "%s%.3s.bnr", Settings.BNRCachePath, id); + if((filesize = FileSize(path)) == 0) + return false; + } + + FILE *f = fopen(path, "rb"); + if(!f) return false; - u32 tidLow = (u32) (tid & 0xFFFFFFFF); - char id[6]; - memset(id, 0, sizeof(id)); - memcpy(id, &tidLow, 4); - - if(memcmp(gameID, id, 6) == 0) - return true; - - memcpy(gameID, id, 6); - - if(imetHdr) - free(imetHdr); - imetHdr = NULL; - - imetHdr = (IMETHeader*) Channels::GetOpeningBnr(tid, pathPrefix); + imetHdr = (IMETHeader *) malloc(filesize); if(!imetHdr) + { + fclose(f); return false; + } + + fread(imetHdr, 1, filesize, f); + fclose(f); if (imetHdr->fcc != 'IMET') { - free(imetHdr); - imetHdr = NULL; - return false; + //! check if it's a channel .app file + IMETHeader *channelImet = (IMETHeader *)(((u8 *)imetHdr) + 0x40); + if(channelImet->fcc != 'IMET') + { + free(imetHdr); + imetHdr = NULL; + return false; + } + //! just move it 0x40 bytes back, the rest 0x40 bytes will just be unused + //! as it's a temporary file usually it's not worth to reallocate + filesize -= 0x40; + memcpy(imetHdr, channelImet, filesize); } return true; } -bool OpeningBNR::Load(const u8 * discid) +void OpeningBNR::WriteCachedBNR(const char *id, const u8 *buffer, u32 size) { - if(!discid) + char path[255]; + snprintf(path, sizeof(path), "%s%.6s.bnr", Settings.BNRCachePath, id); + + CreateSubfolder(Settings.BNRCachePath); + + FILE *f = fopen(path, "wb"); + if(!f) + return; + + fwrite(buffer, 1, size, f); + fclose(f); +} + +bool OpeningBNR::Load(const discHdr * header) +{ + if(!header) return false; - if(memcmp(gameID, discid, 6) == 0) + if(memcmp(gameID, header->id, 6) == 0) return true; - memcpy(gameID, discid, 6); + memcpy(gameID, header->id, 6); if(imetHdr) free(imetHdr); imetHdr = NULL; - wbfs_disc_t *disc = WBFS_OpenDisc((u8 *) gameID); - if (!disc) - return false; - - wiidisc_t *wdisc = wd_open_disc((int(*)(void *, u32, u32, void *)) wbfs_disc_read, disc); - if (!wdisc) + switch(header->type) { - WBFS_CloseDisc(disc); - return false; + case TYPE_GAME_WII_IMG: + case TYPE_GAME_WII_DISC: + return LoadWiiBanner(header); + case TYPE_GAME_NANDCHAN: + case TYPE_GAME_EMUNANDCHAN: + return LoadChannelBanner(header); + case TYPE_GAME_GC_IMG: + case TYPE_GAME_GC_DISC: + case TYPE_GAME_GC_EXTRACTED: + if(!Settings.CacheBNRFiles) + return false; + return LoadCachedBNR((char *)header->id); + default: + break; } - imetHdr = (IMETHeader*) wd_extract_file(wdisc, ALL_PARTITIONS, (char *) "opening.bnr"); + return false; +} - wd_close_disc(wdisc); - WBFS_CloseDisc(disc); +bool OpeningBNR::LoadWiiBanner(const discHdr * header) +{ + if(!header || ( (header->type != TYPE_GAME_WII_IMG) + && (header->type != TYPE_GAME_WII_DISC))) + return false; + + + if(Settings.CacheBNRFiles && LoadCachedBNR((const char *)header->id)) + return true; + + if(header->type == TYPE_GAME_WII_DISC) + { + wiidisc_t *wdisc = wd_open_disc(__ReadDVD, 0); + if (!wdisc) + return false; + + imetHdr = (IMETHeader*) wd_extract_file(wdisc, ALL_PARTITIONS, (char *) "opening.bnr"); + + filesize = wdisc->extracted_size; + + wd_close_disc(wdisc); + } + else + { + wbfs_disc_t *disc = WBFS_OpenDisc((u8 *) gameID); + if (!disc) + return false; + + wiidisc_t *wdisc = wd_open_disc((int(*)(void *, u32, u32, void *)) wbfs_disc_read, disc); + if (!wdisc) + { + WBFS_CloseDisc(disc); + return false; + } + + imetHdr = (IMETHeader*) wd_extract_file(wdisc, ALL_PARTITIONS, (char *) "opening.bnr"); + + filesize = wdisc->extracted_size; + + wd_close_disc(wdisc); + WBFS_CloseDisc(disc); + } if(!imetHdr) return false; @@ -93,6 +198,38 @@ bool OpeningBNR::Load(const u8 * discid) return false; } + if(Settings.CacheBNRFiles) + WriteCachedBNR((const char *) header->id, (u8 *) imetHdr, filesize); + + return true; +} + +bool OpeningBNR::LoadChannelBanner(const discHdr *header) +{ + if(!header || (header->tid == 0) || ( (header->type != TYPE_GAME_NANDCHAN) + && (header->type != TYPE_GAME_EMUNANDCHAN))) + return false; + + if(Settings.CacheBNRFiles && LoadCachedBNR((char *) header->id)) + return true; + + const u64 &tid = header->tid; + const char *pathPrefix = (header->type == TYPE_GAME_EMUNANDCHAN) ? Settings.NandEmuChanPath : ""; + + imetHdr = (IMETHeader*) Channels::GetOpeningBnr(tid, &filesize, pathPrefix); + if(!imetHdr) + return false; + + if (imetHdr->fcc != 'IMET') + { + free(imetHdr); + imetHdr = NULL; + return false; + } + + if(Settings.CacheBNRFiles) + WriteCachedBNR((char *) header->id, (u8 *) imetHdr, filesize); + return true; } @@ -101,56 +238,251 @@ const u16 * OpeningBNR::GetIMETTitle(int lang) if(!imetHdr || lang < 0 || lang >= 10) return NULL; + if (imetHdr->fcc != 'IMET') + return NULL; + if(imetHdr->names[lang][0] == 0) lang = CONF_LANG_ENGLISH; return imetHdr->names[lang]; } -const u8 * OpeningBNR::GetBannerSound(u32 * size) +static int GC_Disc_Read(void *fp, u32 offset, u32 count, void*iobuf) { - if(!imetHdr) - return NULL; - - const U8Header *bnrArcHdr = (U8Header *) (imetHdr + 1); - const U8Entry *fst = (const U8Entry *) (((const u8 *) bnrArcHdr) + bnrArcHdr->rootNodeOffset); - - u32 i; - for (i = 1; i < fst[0].numEntries; ++i) - if (fst[i].fileType == 0 && strcasecmp(u8Filename(fst, i), "sound.bin") == 0) break; - - if (i >= fst[0].numEntries) + if(fp) { - return NULL; + fseek((FILE *)fp, offset, SEEK_SET); + return fread(iobuf, 1, count, (FILE *)fp); } - const u8 *sound_bin = ((const u8 *) bnrArcHdr) + fst[i].fileOffset; - if (((IMD5Header *) sound_bin)->fcc != 'IMD5') - { - return NULL; - } - const u8 *soundChunk = sound_bin + sizeof(IMD5Header); - u32 soundChunkSize = fst[i].fileLength - sizeof(IMD5Header); + return __ReadDVDPlain(iobuf, count, offset); +} - if (*((u32*) soundChunk) == 'LZ77') +u8 *OpeningBNR::LoadGCBNR(const discHdr * header, u32 *len) +{ + if(!header || ( (header->type != TYPE_GAME_GC_IMG) + && (header->type != TYPE_GAME_GC_DISC) + && (header->type != TYPE_GAME_GC_EXTRACTED))) + return NULL; + + const char *path = GCGames::Instance()->GetPath((char *) header->id); + if(!path) + return NULL; + + FILE *file = NULL; + GC_OpeningBnr *openingBnr = NULL; + + // read from file + if((header->type == TYPE_GAME_GC_IMG) || (header->type == TYPE_GAME_GC_DISC)) { - u32 uncSize = 0; - u8 * uncompressed_data = uncompressLZ77(soundChunk, soundChunkSize, &uncSize); - if (!uncompressed_data) + //! open iso file if it's iso + if(header->type == TYPE_GAME_GC_IMG) { + file = fopen(path, "rb"); + if(!file) + return NULL; + } + + gcdisc_t *disc = gc_open_disc(GC_Disc_Read, file); + if(!disc) { + fclose(file); return NULL; } - if (size) *size = uncSize; - return uncompressed_data; + if(!strcmp(Settings.db_language, "JA")) { + bool loaded = gc_extract_file(disc, "openingJA.bnr"); + if(!loaded) + loaded = gc_extract_file(disc, "opening.bnr"); + if(!loaded) + loaded = gc_extract_file(disc, "openingUS.bnr"); + if(!loaded) + loaded = gc_extract_file(disc, "openingEU.bnr"); + } + else if(!strcmp(Settings.db_language, "EN")) { + bool loaded = gc_extract_file(disc, "openingUS.bnr"); + if(!loaded) + loaded = gc_extract_file(disc, "opening.bnr"); + if(!loaded) + loaded = gc_extract_file(disc, "openingEU.bnr"); + if(!loaded) + loaded = gc_extract_file(disc, "openingJA.bnr"); + } + else { + bool loaded = gc_extract_file(disc, "openingEU.bnr"); + if(!loaded) + loaded = gc_extract_file(disc, "opening.bnr"); + if(!loaded) + loaded = gc_extract_file(disc, "openingUS.bnr"); + if(!loaded) + loaded = gc_extract_file(disc, "openingJA.bnr"); + } + + openingBnr = (GC_OpeningBnr *) disc->extracted_buffer; + if(len) + *len = disc->extracted_size; + + gc_close_disc(disc); } - - u8 *out = (u8 *) malloc(soundChunkSize); - if (out) + else if(header->type == TYPE_GAME_GC_EXTRACTED) { - memcpy(out, soundChunk, soundChunkSize); - if (size) *size = soundChunkSize; + string gamePath = path; + gamePath += "root/"; + //! open default file first + file = fopen((gamePath + "opening.bnr").c_str(), "rb"); + + // if not found try the region specific ones + if(!strcmp(Settings.db_language, "JA")) { + if(!file) + file = fopen((gamePath + "openingJA.bnr").c_str(), "rb"); + if(!file) + file = fopen((gamePath + "openingUS.bnr").c_str(), "rb"); + if(!file) + file = fopen((gamePath + "openingEU.bnr").c_str(), "rb"); + } + else if(!strcmp(Settings.db_language, "EN")) { + if(!file) + file = fopen((gamePath + "openingUS.bnr").c_str(), "rb"); + if(!file) + file = fopen((gamePath + "openingEU.bnr").c_str(), "rb"); + if(!file) + file = fopen((gamePath + "openingJA.bnr").c_str(), "rb"); + } + else { + if(!file) + file = fopen((gamePath + "openingEU.bnr").c_str(), "rb"); + if(!file) + file = fopen((gamePath + "openingUS.bnr").c_str(), "rb"); + if(!file) + file = fopen((gamePath + "openingJA.bnr").c_str(), "rb"); + } + + // file not found + if(!file) + return NULL; + + fseek(file, 0, SEEK_END); + int size = ftell(file); + rewind(file); + + openingBnr = (GC_OpeningBnr *) malloc(size); + if(openingBnr) + { + if(len) + *len = size; + fread(openingBnr, 1, size, file); + } + } - return out; + if(file) + fclose(file); + + if(!openingBnr) + return NULL; + + // check magic of the opening bnr + if(openingBnr->magic != 'BNR1' && openingBnr->magic != 'BNR2') { + free(openingBnr); + return NULL; + } + + return (u8 *) openingBnr; +} + +CustomBanner *OpeningBNR::CreateGCBanner(const discHdr * header) +{ + int language = 0; + u32 openingBnrSize; + GC_OpeningBnr *openingBnr = (GC_OpeningBnr *) LoadGCBNR(header, &openingBnrSize); + + CustomBanner *banner = new CustomBanner; + banner->LoadBanner(Resources::GetFile("custom_banner.bnr"), Resources::GetFileSize("custom_banner.bnr")); + banner->SetBannerPngImage("bg.tpl", Resources::GetFile("gc_banner_bg.png"), Resources::GetFileSize("gc_banner_bg.png")); + + banner->SetBannerText("T_PF", tr("GameCube")); + + if(openingBnr) + { + banner->SetBannerTexture("HBPic.tpl", openingBnr->tpl_data, 96, 32, GX_TF_RGB5A3); + + // European opening bnr file + if(openingBnr->magic == 'BNR2') + { + if(!strcmp(Settings.db_language, "DE")) { + language = 1; + } + else if(!strcmp(Settings.db_language, "FR")) { + language = 2; + } + else if(!strcmp(Settings.db_language, "ES")) { + language = 3; + } + else if(!strcmp(Settings.db_language, "IT")) { + language = 4; + } + else if(!strcmp(Settings.db_language, "NL")) { + language = 5; + } + + if((0x1820 + sizeof(openingBnr->description[0]) * language) > openingBnrSize) { + language = 0; + } + } + + wString str; + str.resize(strlen((char *) openingBnr->description[language].developer)); + for(u32 i = 0; i < str.size(); i++) + str[i] = *(openingBnr->description[language].developer + i); + + banner->SetBannerText("T_Coded_by", tr("Developer:")); + banner->SetBannerText("T_coder", str.toUTF8().c_str()); + + str.resize(strlen((char *) openingBnr->description[language].long_description)); + for(u32 i = 0; i < str.size(); i++) + str[i] = *(openingBnr->description[language].long_description + i); + + banner->SetBannerText("T_short_descript", str.toUTF8().c_str()); + + // free buffer + free(openingBnr); + } + else + { + banner->SetBannerPngImage("HBPic.tpl", Resources::GetFile("gc_icon_bg.png"), Resources::GetFileSize("gc_icon_bg.png")); + banner->SetBannerText("T_Coded_by", tr("Developer:")); + banner->SetBannerText("T_coder", tr("Unknown")); + banner->SetBannerText("T_short_descript", " "); + } + + banner->SetBannerText("T_name", GameTitles.GetTitle(header)); + banner->SetBannerPaneVisible("Line1", false); + banner->SetBannerPaneVisible("Line2", false); + banner->SetBannerPaneVisible("T_Released", false); + banner->SetBannerPaneVisible("T_release_date", false); + banner->SetBannerPaneVisible("T_versiontext", false); + banner->SetBannerPaneVisible("T_version", false); + banner->SetBannerTextureScale(Settings.GCBannerScale); + + return banner; +} + +CustomBanner *OpeningBNR::CreateGCIcon(const discHdr * header) +{ + GC_OpeningBnr *openingBnr = (GC_OpeningBnr *) LoadGCBNR(header); + + CustomBanner *newBanner = new CustomBanner; + newBanner->LoadIcon(Resources::GetFile("custom_banner.bnr"), Resources::GetFileSize("custom_banner.bnr")); + + if(openingBnr) + newBanner->SetIconTexture("Iconpng.tpl", openingBnr->tpl_data, 96, 32, GX_TF_RGB5A3); + else + newBanner->SetIconPngImage("Iconpng.tpl", Resources::GetFile("gc_icon_bg.png"), Resources::GetFileSize("gc_icon_bg.png")); + + newBanner->SetIconPngImage("HBLogo.tpl", Resources::GetFile("gc_icon_bg.png"), Resources::GetFileSize("gc_icon_bg.png")); + + // free buffer + if(openingBnr) + free(openingBnr); + + return newBanner; } diff --git a/source/banner/OpeningBNR.hpp b/source/banner/OpeningBNR.hpp index e0f4a963..3c25a8b1 100644 --- a/source/banner/OpeningBNR.hpp +++ b/source/banner/OpeningBNR.hpp @@ -1,7 +1,46 @@ +/* +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ #ifndef OPENING_BNR_HPP_ #define OPENING_BNR_HPP_ #include +#include "usbloader/disc.h" +#include "CustomBanner.h" + +typedef struct _GC_OpeningBnr +{ + u32 magic; // BNR1 or BNR2 + u8 pad[0x1C]; + u8 tpl_data[0x1800]; // 96x32 pixel format GX_TF_RGB5A3 + struct + { + u8 disc_title[0x20]; // Gamename + u8 developer_short[0x20]; // Company/Developer + u8 full_title[0x40]; // Full Game Title + u8 developer[0x40]; // Company/Developer Full name, or description + u8 long_description[0x80]; // Game Description + } description[6]; // 6 only on BNR2 => English, German, French, Spanish, Italian, Dutch ?? +} GC_OpeningBnr; typedef struct _IMETHeader { @@ -60,26 +99,35 @@ class OpeningBNR public: OpeningBNR(); ~OpeningBNR(); - bool Load(const u8 * gameID); - bool Load(const u64 &tid, const char *pathPrefix); + bool Load(const discHdr * header); + bool LoadWiiBanner(const discHdr * header); + bool LoadChannelBanner(const discHdr *header); + CustomBanner *CreateGCBanner(const discHdr * header); + CustomBanner *CreateGCIcon(const discHdr * header); + + const u8 * Get() const { return (const u8*) imetHdr; } + u32 GetSize() const { return filesize; } + + bool LoadCachedBNR(const char *id); + void WriteCachedBNR(const char *id, const u8 *buffer, u32 size); + const u16 * GetIMETTitle(int lang); - const u16 * GetIMETTitle(const u8 * gameID, int lang) { Load(gameID); return GetIMETTitle(lang); }; - const u8 * GetBannerSound(u32 * size); - const u8 * GetBannerSound(const u8 * gameID, u32 * size) { Load(gameID); return GetBannerSound(size); }; - const u8 * GetBannerSound(const u64 &tid, u32 * size, const char *pathPrefix) { Load(tid, pathPrefix); return GetBannerSound(size); }; + const u16 * GetIMETTitle(const discHdr * header, int lang) { Load(header); return GetIMETTitle(lang); } private: + u8 *LoadGCBNR(const discHdr * header, u32 *len = 0); IMETHeader *imetHdr; + u32 filesize; char gameID[7]; }; class BNRInstance : public OpeningBNR { public: - static BNRInstance * Instance() { if(!instance) instance = new BNRInstance; return instance; }; - static void DestroyInstance() { delete instance; instance = NULL; }; + static BNRInstance * Instance() { if(!instance) instance = new BNRInstance; return instance; } + static void DestroyInstance() { delete instance; instance = NULL; } private: - BNRInstance() { }; - ~BNRInstance() { }; + BNRInstance() { } + ~BNRInstance() { } static BNRInstance * instance; }; diff --git a/source/banner/Pane.cpp b/source/banner/Pane.cpp new file mode 100644 index 00000000..84139607 --- /dev/null +++ b/source/banner/Pane.cpp @@ -0,0 +1,165 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "Pane.h" +#include "Layout.h" + +void Pane::Load(Pane::Header *pan) +{ + if(!pan) + return; + + header = pan; + hide = false; + RootPane = !strcmp( header->name, "RootPane" ); + AlignHor = header->origin % 3; + AlignVer = 2 - header->origin / 3; +} + +Pane::~Pane() +{ + // delete children + for(u32 i = 0; i < panes.size(); ++i) + delete panes[i]; +} + +void Pane::SetFrame(FrameNumber frame, u8 key_set) +{ + // setframe on self + Animator::SetFrame(frame, key_set); + + // setframe on children + for(u32 i = 0; i < panes.size(); ++i) + panes[i]->SetFrame(frame, key_set); +} + +void Pane::Render(const BannerResources& resources, u8 parent_alpha, Mtx &modelview, + bool widescreen, bool modify_alpha) const +{ + if (!header || !GetVisible() || GetHide()) + return; + + u8 render_alpha = header->alpha; + + if(RootPane && parent_alpha != 0xFF) + { + modify_alpha = true; + render_alpha = MultiplyAlpha(header->alpha, parent_alpha); + } + if(!RootPane && modify_alpha) + { + render_alpha = MultiplyAlpha(header->alpha, parent_alpha); + } + else if(GetInfluencedAlpha() && header->alpha != 0xff) + { + modify_alpha = true; + parent_alpha = MultiplyAlpha(header->alpha, parent_alpha); + } + + float ws_scale = 1.0f; + + if(widescreen && GetWidescren()) + { + ws_scale *= 0.82f; // should actually be 0.75? + widescreen = false; + } + + Mtx m1,m2,m3,m4, mv; + guMtxIdentity (m1); + + // Scale + guMtxScaleApply(m1,m1, header->scale.x * ws_scale, header->scale.y, 1.f); + + // Rotate + guMtxRotDeg ( m2, 'x', header->rotate.x ); + guMtxRotDeg ( m3, 'y', header->rotate.y ); + guMtxRotDeg ( m4, 'z', header->rotate.z ); + guMtxConcat(m2, m3, m2); + guMtxConcat(m2, m4, m2); + guMtxConcat(m1, m2, m1); + + // Translate + guMtxTransApply(m1,m1, header->translate.x, header->translate.y, header->translate.z); + + guMtxConcat (modelview, m1, mv); + + // render self + Draw(resources, render_alpha, ws_scale, mv); + + // render children + for(u32 i = 0; i < panes.size(); ++i) + panes[i]->Render(resources, render_alpha, mv, widescreen, modify_alpha); +} + +Pane* Pane::FindPane(const std::string& find_name) +{ + if(!header) + return NULL; + + if (find_name.compare(0, 0x10, getName()) == 0) + return this; + + for(u32 i = 0; i < panes.size(); ++i) + { + Pane *found = panes[i]->FindPane(find_name); + if (found) + return found; + } + + return NULL; +} + +void Pane::ProcessHermiteKey(const KeyType& type, float value) +{ + if (type.type == ANIMATION_TYPE_VERTEX_COLOR) // vertex color + { + // only alpha is supported for Panes afaict + if (0x10 == type.target) + { + header->alpha = FLOAT_2_U8(value); + return; + } + } + else if (type.type == ANIMATION_TYPE_PANE) // pane animation + { + if (type.target < 10) + { + (&header->translate.x)[type.target] = value; + return; + } + } + + Base::ProcessHermiteKey(type, value); +} + +void Pane::ProcessStepKey(const KeyType& type, StepKeyHandler::KeyData data) +{ + if (type.type == ANIMATION_TYPE_VISIBILITY) // visibility + { + SetVisible(!!data.data2); + return; + } + + Base::ProcessStepKey(type, data); +} diff --git a/source/banner/Pane.h b/source/banner/Pane.h new file mode 100644 index 00000000..b0374bfe --- /dev/null +++ b/source/banner/Pane.h @@ -0,0 +1,143 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef WII_BNR_PANE_H_ +#define WII_BNR_PANE_H_ + +#include +#include + +#include "Animator.h" + +struct BannerResources; + +class Pane; +typedef std::vector PaneList; + +class Pane : public Animator +{ +public: + typedef Animator Base; + + static const u32 MAGIC = MAKE_FOURCC('p', 'a', 'n', '1'); + + struct Header + { + u32 magic; + u32 size_section; + u8 flags; + u8 origin; + u8 alpha; + u8 padding; + char name [0x10]; + char user_data [0x08]; + Vec3f translate; + Vec3f rotate; + Vec2f scale; + float width; + float height; + } __attribute__((packed)); + + Pane() : header(NULL) {} + virtual ~Pane(); + + void Load(Pane::Header *file); + + const char *getName() const { if(!header) return ""; return header->name; } + + void Render(const BannerResources& resources, u8 parent_alpha, Mtx &modelview, + bool widescreen, bool modify_alpha = false) const; + + void SetFrame(FrameNumber frame, u8 key_set); + + void SetScale(float scale) { if(header) header->scale.x = header->scale.y = scale; } + + bool GetHide() const { return hide; } + void SetHide(bool _hide) { hide = _hide; } + + bool GetVisible() const { if(!header) return false; return ((header->flags & (1 << FLAG_VISIBLE)) != 0); } + void SetVisible(bool visible) + { + if(!header) + return; + + if(visible) + header->flags |= (1 << FLAG_VISIBLE); + else + header->flags &= ~(1 << FLAG_VISIBLE); + } + + u8 GetOriginX() const { return AlignHor; } + u8 GetOriginY() const { return AlignVer; } + + float GetWidth() const { if(!header) return 0.f; return header->width; } + float GetHeight() const { if(!header) return 0.f; return header->height; } + + bool GetInfluencedAlpha() const { if(!header) return false; return ((header->flags & (1 << FLAG_INFLUENCED_ALPHA)) != 0); } + void SetInfluencedAlpha(bool influenced) + { + if(!header) + return; + + if(influenced) + header->flags |= (1 << FLAG_INFLUENCED_ALPHA); + else + header->flags &= ~(1 << FLAG_INFLUENCED_ALPHA); + } + + bool GetWidescren() const { return ((header->flags & (1 << FLAG_WIDESCREEN)) != 0); } + + Pane* FindPane(const std::string& name); // recursive + + PaneList panes; + +protected: + void ProcessHermiteKey(const KeyType& type, float value); + void ProcessStepKey(const KeyType& type, StepKeyHandler::KeyData data); + +private: + virtual void Draw(const BannerResources&, u8, const float, Mtx&) const {} + + enum + { + FLAG_VISIBLE = 0x00, + FLAG_INFLUENCED_ALPHA = 0x01, + FLAG_WIDESCREEN = 0x02 + }; + + Pane::Header *header; + bool hide; // used by the groups + bool RootPane; + u8 AlignVer; + u8 AlignHor; +}; + +// apparently Bounding is just a regular pane +class Bounding : public Pane +{ +public: + static const u32 MAGIC = MAKE_FOURCC('b', 'n', 'd', '1'); +}; + +#endif diff --git a/source/banner/Picture.cpp b/source/banner/Picture.cpp new file mode 100644 index 00000000..1583899b --- /dev/null +++ b/source/banner/Picture.cpp @@ -0,0 +1,34 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "Picture.h" + +void Picture::Load(Pane::Header * file) +{ + if(!file) + return; + + Pane::Load(file); + QuadPane::Load((QuadPane::Header *) (file+1)); +} diff --git a/source/banner/Picture.h b/source/banner/Picture.h new file mode 100644 index 00000000..9c957c2b --- /dev/null +++ b/source/banner/Picture.h @@ -0,0 +1,40 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef WII_BNR_PICTURE_H_ +#define WII_BNR_PICTURE_H_ + +#include "QuadPane.h" + +class Picture : public QuadPane +{ +public: + typedef QuadPane Base; + + static const u32 MAGIC = MAKE_FOURCC('p', 'i', 'c', '1'); + + void Load(Pane::Header *hdr); +}; + +#endif diff --git a/source/banner/QuadPane.cpp b/source/banner/QuadPane.cpp new file mode 100644 index 00000000..c8890994 --- /dev/null +++ b/source/banner/QuadPane.cpp @@ -0,0 +1,126 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#include "QuadPane.h" +#include "Layout.h" + +void QuadPane::Load(QuadPane::Header* file) +{ + if(!file) + return; + + header = file; + tex_coords = (const TexCoords *) (header+1); +} + +inline void QuadPane::SetVertex(int ind, float x, float y, u8 render_alpha) const +{ + // position + GX_Position3f32(x, y, 0.f); + + const GXColor &vertex_color = header->vertex_colors[ind]; + // color + GX_Color4u8(vertex_color.r, vertex_color.g, vertex_color.b, + MultiplyAlpha(vertex_color.a, render_alpha)); + + // texture coord + for(u32 i = 0; i < header->tex_coord_count; i++) + GX_TexCoord2f32(tex_coords[i].coords[ind].s, tex_coords[i].coords[ind].t); +} + +static inline bool IsModulateColor(GXColor *colors, u8 render_alpha) +{ + if(render_alpha != 0xFF) + return true; + + u32 *colorPtr = (u32 *) colors; + + for(int i = 0; i < 4; ++i) + { + if(colorPtr[i] != 0xFFFFFFFF) + return true; + } + + return false; +} + +void QuadPane::Draw(const BannerResources& resources, u8 render_alpha, const float ws_scale, Mtx &modelview, u16 material_index, u8 texture_flip) const +{ + if(!header) + return; + + if (material_index < resources.materials.size()) + { + bool modulate_color = IsModulateColor(header->vertex_colors, render_alpha); + resources.materials[material_index]->Apply(resources, render_alpha, modulate_color); + } + + Mtx m, mv; + guMtxIdentity (m); + + guMtxTransApply(m,m, -0.5f * GetOriginX(), -0.5f * GetOriginY(), 0.f); + guMtxScaleApply(m,m, GetWidth(), GetHeight(), 1.f); + + guMtxConcat (modelview, m, mv); + + GX_LoadPosMtxImm (mv, GX_PNMTX0); + + GX_ClearVtxDesc(); + GX_InvVtxCache(); + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); + for(u32 i = 0; i < header->tex_coord_count; i++) + GX_SetVtxDesc(GX_VA_TEX0+i, GX_DIRECT); + + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + if(texture_flip) + { + SetVertex(0, 0.f, 0.f, render_alpha); + SetVertex(1, 1.f, 0.f, render_alpha); + SetVertex(3, 1.f, 1.f, render_alpha); + SetVertex(2, 0.f, 1.f, render_alpha); + } + else + { + SetVertex(2, 0.f, 0.f, render_alpha); + SetVertex(3, 1.f, 0.f, render_alpha); + SetVertex(1, 1.f, 1.f, render_alpha); + SetVertex(0, 0.f, 1.f, render_alpha); + } + GX_End(); +} + +void QuadPane::ProcessHermiteKey(const KeyType& type, float value) +{ + if (type.type == ANIMATION_TYPE_VERTEX_COLOR) // vertex color + { + if (type.target < 0x10) + { + // vertex colors + (&header->vertex_colors->r)[type.target] = FLOAT_2_U8(value); + return; + } + } + + Base::ProcessHermiteKey(type, value); +} diff --git a/source/banner/QuadPane.h b/source/banner/QuadPane.h new file mode 100644 index 00000000..c0302449 --- /dev/null +++ b/source/banner/QuadPane.h @@ -0,0 +1,73 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#ifndef _QUAD_PANE_H_ +#define _QUAD_PANE_H_ + +#include "Pane.h" + +// used by Picture and Window +class QuadPane : public Pane +{ +public: + typedef Pane Base; + + struct Header + { + GXColor vertex_colors[4]; + u16 material_index; + u8 tex_coord_count; + u8 pad; + } __attribute__((packed)); + + QuadPane() : header(NULL) {} + void Load(QuadPane::Header *file); + +protected: + void ProcessHermiteKey(const KeyType& type, float value); + //! overload + void Draw(const BannerResources& resources, u8 render_alpha, const float ws_scale, Mtx &view, + u16 material_index, u8 texture_flip) const; + //! main virtual draw function + void Draw(const BannerResources& resources, u8 render_alpha, const float ws_scale, Mtx &view) const { + Draw(resources, render_alpha, ws_scale, view, header->material_index, 0); + } + +private: + void SetVertex(int ind, float x, float y, u8 render_alpha) const; + + struct TexCoords + { + struct TexCoord + { + float s; + float t; + + } coords[4]; + }; + + QuadPane::Header *header; + const TexCoords *tex_coords; +}; + +#endif diff --git a/source/banner/Textbox.cpp b/source/banner/Textbox.cpp new file mode 100644 index 00000000..2202cc6c --- /dev/null +++ b/source/banner/Textbox.cpp @@ -0,0 +1,280 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - giantpune +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#include "Textbox.h" +#include "Layout.h" + +void Textbox::Load(Pane::Header *file) +{ + if(!file) + return; + + Pane::Load(file); + header = (Textbox::Header *) (file + 1); + text = (const u16 *) (header + 1); + + textAlignVer = (header->text_alignment / 3); + textAlignHor = (header->text_alignment % 3); +} + +void Textbox::SetTextWidth(WiiFont *font) +{ + lineWidths.clear(); + frameWidth = 0.f; + frameHeight = header->font_size; + float currentLine = 0.f; + float scale = header->font_size /(float)font->CharacterHeight(); + + for(const u16 *txtString = text; *txtString != 0; txtString++) + { + if(*txtString == '\n') + { + currentLine *= scale; + lineWidths.push_back(currentLine); + frameWidth = MAX(frameWidth, currentLine); + frameHeight += header->font_size + header->space_line; + currentLine = 0.f; + continue; + } + + const WiiFont::CharInfo *charInfo = font->GetCharInfo(*txtString); + if(!charInfo) + continue; + + if(charInfo->unk) + currentLine += (float) charInfo->advanceKerning; + + currentLine += (float) charInfo->advanceGlyphX; + } + + currentLine *= scale; + lineWidths.push_back(currentLine); + frameWidth = MAX(frameWidth, currentLine); +} + +void Textbox::SetupGX(const BannerResources& resources) const +{ + GX_ClearVtxDesc(); + GX_InvVtxCache(); + + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + + // channel control + GX_SetNumChans(1); + GX_SetChanCtrl(GX_COLOR0A0,GX_DISABLE,GX_SRC_REG,GX_SRC_VTX,GX_LIGHTNULL,GX_DF_NONE,GX_AF_NONE); + + // texture gen. + GX_SetNumTexGens(1); + GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + // texture environment + GX_SetNumTevStages(1); + GX_SetNumIndStages(0); + GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE); + GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GX_SetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0); + GX_SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_1_4); + GX_SetTevKAlphaSel(GX_TEVSTAGE0, GX_TEV_KASEL_1); + GX_SetTevDirect(GX_TEVSTAGE0); + // swap table + GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); + GX_SetTevSwapModeTable(GX_TEV_SWAP1, GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_ALPHA); + GX_SetTevSwapModeTable(GX_TEV_SWAP2, GX_CH_GREEN, GX_CH_GREEN, GX_CH_GREEN, GX_CH_ALPHA); + GX_SetTevSwapModeTable(GX_TEV_SWAP3, GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE, GX_CH_ALPHA); + // alpha compare and blend mode + GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_SET); + + if(header->material_index < resources.materials.size()) + { + const Material::Header *matHead = resources.materials[header->material_index]->GetHeader(); + if(!matHead) + return; + + //GX_SetFog(0, 0.0f, 0.0f, 0.0f, 0.0f, (GXColor){0xff, 0xff, 0xff, 0xff}); + GX_SetTevSwapModeTable(0, 0, 1, 2, 3); + //GX_SetZTexture(0, 0x11, 0); + GX_SetNumChans(1 ); + GX_SetChanCtrl(4, 0, 0, 1, 0, 0, 2); + GX_SetChanCtrl(5, 0, 0, 0, 0, 0, 2); + GX_SetNumTexGens(1); + GX_SetTexCoordGen2(0, 1, 4, 0x3c, 0, 0x7D); + GX_SetNumIndStages(0); + GX_SetBlendMode(1, 4, 5, 0xf); + GX_SetNumTevStages(2); + GX_SetTevDirect(0); + GX_SetTevDirect(1); + GX_SetTevSwapMode(0, 0, 0); + GX_SetTevSwapMode(1, 0, 0); + GX_SetTevOrder(0, 0, 0, 0xff); + + for( int i = 0; i < 2; i++ ) + { + GX_SetTevColor(i + 1, (GXColor){ LIMIT(matHead->color_regs[i].r, 0, 0xFF), + LIMIT(matHead->color_regs[i].g, 0, 0xFF), + LIMIT(matHead->color_regs[i].b, 0, 0xFF), + LIMIT(matHead->color_regs[i].a, 0, 0xFF) }); + } + + GX_SetTevColorIn(0, 2, 4, 8, 0xf); + GX_SetTevAlphaIn(0, 1, 2, 4, 7); + GX_SetTevColorOp(0, 0, 0, 0, 1, 0); + GX_SetTevAlphaOp(0, 0, 0, 0, 1, 0); + GX_SetTevOrder(1, 0xff, 0xff, 4); + GX_SetTevColorIn(1, 0xf, 0, 0xa, 0xf); + GX_SetTevAlphaIn(1, 7, 0, 5, 7); + GX_SetTevColorOp(1, 0, 0, 0, 1, 0); + GX_SetTevAlphaOp(1, 0, 0, 0, 1, 0); + } +} + +void Textbox::Draw(const BannerResources& resources, u8 parent_alpha, const float ws_scale, Mtx &modelview) const +{ + if(!text) + return; + + if(header->font_index >= resources.fonts.size()) + return; + + WiiFont *font = resources.fonts[header->font_index]; + if(!font->IsLoaded()) + return; + + // Ugly...but doing it by going through all panes is more ugly + // TODO: move it to somewhere else + if(lineWidths.empty()) + ((Textbox *) this)->SetTextWidth(font); + + if(lineWidths.empty()) + return; + + SetupGX(resources); + + GX_LoadPosMtxImm(modelview, GX_PNMTX0); + + // Setup text color + GXColor color0 = { header->color[0].r, + header->color[0].g, + header->color[0].b, + MultiplyAlpha(header->color[0].a, parent_alpha) }; + + GXColor color1 = { header->color[1].r, + header->color[1].g, + header->color[1].b, + MultiplyAlpha(header->color[1].a, parent_alpha) }; + + u32 lastSheetIdx = 0xffff; + float scale = header->font_size /(float)font->CharacterHeight(); + + // use complete text width if not aligned to middle + float textWidth = (GetAlignHor() == 1) ? lineWidths[0] : frameWidth; + + // position offset calculation for first line...why the hell is it that complex? + float xPos = -0.5f * ( GetOriginX() * GetWidth() * ws_scale + + GetAlignHor() * (-GetWidth() * ws_scale + textWidth) ); + float yPos = -0.5f * ( GetAlignVer() * -frameHeight + + GetHeight() * (GetAlignVer() - (2 - GetOriginY())) ) + - header->font_size; + + // store the character width here for later use, it's constant over the text + float charWidth = scale * (float)font->CharacterWidth(); + int lineNumber = 0; + + for(const u16 *txtString = text; *txtString != 0; txtString++) + { + if(*txtString == '\n') + { + lineNumber++; + // use complete text width if not aligned to middle + textWidth = (GetAlignHor() == 1) ? lineWidths[lineNumber] : frameWidth; + // calculate text position depending on line width + xPos = -0.5f * (GetOriginX() * GetWidth() * ws_scale + + GetAlignHor() * (-GetWidth() * ws_scale + textWidth)); + // go one line down + yPos -= (header->font_size + header->space_line); + continue; + } + + const WiiFont::CharInfo *charInfo = font->GetCharInfo(*txtString); + if(!charInfo) + continue; + + if(charInfo->sheetIdx != lastSheetIdx) + { + lastSheetIdx = charInfo->sheetIdx; + + if(!font->Apply(charInfo->sheetIdx)) + continue; + } + + if(charInfo->unk) + xPos += scale * (float)charInfo->advanceKerning; + + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + + GX_Position3f32(xPos, yPos, 0.f); + GX_Color4u8(color1.r, color1.g, color1.b, color1.a); + GX_TexCoord2f32(charInfo->s1, charInfo->t2); + + GX_Position3f32(xPos + charWidth, yPos, 0.f); + GX_Color4u8(color1.r, color1.g, color1.b, color1.a); + GX_TexCoord2f32(charInfo->s2, charInfo->t2); + + GX_Position3f32(xPos + charWidth, yPos + header->font_size, 0.f); + GX_Color4u8(color0.r, color0.g, color0.b, color0.a); + GX_TexCoord2f32(charInfo->s2, charInfo->t1); + + GX_Position3f32(xPos, yPos + header->font_size, 0.f); + GX_Color4u8(color0.r, color0.g, color0.b, color0.a); + GX_TexCoord2f32(charInfo->s1, charInfo->t1); + + GX_End(); + + xPos += scale * (float)charInfo->advanceGlyphX; + } +} + +void Textbox::ProcessHermiteKey(const KeyType& type, float value) +{ + if (type.type == ANIMATION_TYPE_VERTEX_COLOR) // vertex color + { + if(type.target < 4) + { + (&header->color[0].r)[type.target] = FLOAT_2_U8(value); + return; + } + else if(type.target >= 8 && type.target < 12) + { + (&header->color[1].r)[type.target - 8] = FLOAT_2_U8(value); + return; + } + } + Base::ProcessHermiteKey(type, value); +} + +void Textbox::ProcessStepKey(const KeyType& type, StepKeyHandler::KeyData data) +{ + Base::ProcessStepKey(type, data); +} diff --git a/source/banner/Textbox.h b/source/banner/Textbox.h new file mode 100644 index 00000000..96c815f8 --- /dev/null +++ b/source/banner/Textbox.h @@ -0,0 +1,85 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - giantpune +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef WII_BNR_TEXTBOX_H_ +#define WII_BNR_TEXTBOX_H_ + +#include "Pane.h" + +class WiiFont; + +class Textbox : public Pane +{ +public: + typedef Pane Base; + + Textbox() : header(NULL), text(NULL), frameWidth(0.f), frameHeight(0.f) + { } + + static const u32 MAGIC = MAKE_FOURCC('t', 'x', 't', '1'); + + void Load(Pane::Header *file); + + u8 GetAlignHor() const { return textAlignHor; } + u8 GetAlignVer() const { return textAlignVer; } + + void SetText(const u16 *t) { text = t; lineWidths.clear(); } + +protected: + void ProcessHermiteKey(const KeyType& type, float value); + void ProcessStepKey(const KeyType& type, StepKeyHandler::KeyData data); + +private: + void Draw(const BannerResources& resources, u8 render_alpha, const float ws_scale, Mtx &view) const; + void SetupGX(const BannerResources& resources) const; + void SetTextWidth(WiiFont *font); + + struct Header + { + u16 text_buf_bytes; + u16 text_str_bytes; + u16 material_index; + u16 font_index; + u8 text_alignment; + u8 pad1; // ? + u8 pad2[2]; + u32 text_str_offset; + GXColor color[2]; + float font_size; + float height; // seems to work better for offset calculation + float space_char; + float space_line; + } __attribute__((packed)); + + Textbox::Header *header; + const u16 *text; + float frameWidth; + float frameHeight; + u8 textAlignVer; + u8 textAlignHor; + std::vector lineWidths; +}; + +#endif diff --git a/source/banner/Texture.cpp b/source/banner/Texture.cpp new file mode 100644 index 00000000..6d5db07a --- /dev/null +++ b/source/banner/Texture.cpp @@ -0,0 +1,149 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok +Copyright (c) 2012 - giantpune + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include +#include +#include +#include "Texture.h" +#include "video.h" + +Texture::~Texture() +{ + if(texture_data) + free(texture_data); +} + +void Texture::Load(const u8 *file ) +{ + if(!file) + return; + + header = (Texture::Header *) file; + + if (header->magic != MAGIC) { + header = NULL; + return; // bad header + } + + u32 texture_count = header->num_textures; + // only support a single texture + if (texture_count > 1) + { + // Never saw it happen + texture_count = 1; + gprintf("texture count > 1\n"); + } + + // read textures + const TPL_Texture *tpl_list = (const TPL_Texture *) (file + header->header_size); + + for(u32 i = 0; i < texture_count; i++) + { + // seek to texture header + const TPL_Texture_Header *texture = (const TPL_Texture_Header *) (file + tpl_list[i].texture_offset); + + u8 mipmap = 0; + u8 bias_clamp = 0; + + if(texture->max_lod > 0) + mipmap = GX_TRUE; + if(texture->lod_bias > 0.0f) + bias_clamp = GX_ENABLE; + + // texture data + u8 *texture_data = (u8 *) (file + texture->offset); + + // seek to/read palette header + if (tpl_list[i].palette_offset != 0) + { + palette = (TPL_Palette_Header *) (file + tpl_list[i].palette_offset); + + // load the texture + GX_InitTexObjCI(&texobj, texture_data, texture->width, texture->height, texture->format, + texture->wrap_s, texture->wrap_t, mipmap, 0); + } + else + { + // load the texture + GX_InitTexObj(&texobj, texture_data, texture->width, texture->height, texture->format, + texture->wrap_s, texture->wrap_t, mipmap); + } + + // filter mode + if(mipmap) + { + GX_InitTexObjLOD(&texobj, texture->min, texture->mag, texture->min_lod, texture->max_lod, + texture->lod_bias, bias_clamp, bias_clamp, texture->edge_lod); + } + } +} + +//! This function is to load custom texture data and replace the original one. +void Texture::LoadTextureData(const u8 *data, u16 width, u16 height, u8 fmt) +{ + if(texture_data) + free(texture_data); + + int tex_size = GX_GetTexBufferSize(width, height, fmt, GX_FALSE, 0); + texture_data = (u8*) memalign(32, tex_size); + if(!texture_data) + return; + + memcpy(texture_data, data, tex_size); + DCFlushRange(texture_data, tex_size); + + GX_InitTexObj(&texobj, texture_data, width, height, fmt, 0, 0, GX_FALSE); +} + +void Texture::Apply(u8 &tlutName, u8 map_id, u8 wrap_s, u8 wrap_t) const +{ + if(!header) + return; + + if(tlutName >= 20 || map_id >= 8) + return; + + // create a temporary texture object to not modify the original with the wrap_s and wrap_t parameters + GXTexObj tmpTexObj; + for(int i = 0; i < 8; ++i) + tmpTexObj.val[i] = texobj.val[i]; + + // assume that if there is a palette header, then this format is a CIx one + if(palette) + { + // seek to/read palette data + u8 *tlut_data = (u8 *) (((u8 *) header) + palette->offset); + + // load tlut + GXTlutObj tlutobj; + GX_InitTlutObj(&tlutobj, tlut_data, palette->format, palette->num_items ); + GX_LoadTlut(&tlutobj, tlutName); + GX_InitTexObjTlut((GXTexObj *) &tmpTexObj, tlutName); + tlutName++; + } + + GX_InitTexObjWrapMode((GXTexObj *) &tmpTexObj, wrap_s, wrap_t); + GX_LoadTexObj((GXTexObj *) &tmpTexObj, map_id); +} diff --git a/source/banner/Texture.h b/source/banner/Texture.h new file mode 100644 index 00000000..de6cfa72 --- /dev/null +++ b/source/banner/Texture.h @@ -0,0 +1,102 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok +Copyright (c) 2012 - giantpune + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#ifndef TEXTURE_H_ +#define TEXTURE_H_ + +#include +#include +#include +#include "BannerTools.h" + +class Texture +{ +public: + static const u32 MAGIC = MAKE_FOURCC(0x00, ' ', 0xAF, 0x30); + + Texture() : header(NULL), palette(NULL), texture_data(NULL) {} + virtual ~Texture(); + + void Load(const u8 *texture); + const std::string &getName() const { return name; } + void setName(const std::string& _name) { name = _name; } + + // load custom data into the texture object + void LoadTextureData(const u8 *data, u16 width, u16 height, u8 fmt); + + void Apply(u8 &tlutName, u8 map_id, u8 wrap_s, u8 wrap_t) const; +private: + struct Header + { + u32 magic; + u32 num_textures; + u32 header_size; + } __attribute__((packed)) ; + + struct TPL_Texture + { + u32 texture_offset; + u32 palette_offset; + } __attribute__((packed)) ; + + struct TPL_Texture_Header + { + u16 height; + u16 width; + u32 format; + u32 offset; + u32 wrap_s; + u32 wrap_t; + u32 min; + u32 mag; + f32 lod_bias; + u8 edge_lod; + u8 min_lod; + u8 max_lod; + u8 unpacked; + } __attribute__((packed)); + + struct TPL_Palette_Header + { + u16 num_items; + u8 unpacked; + u8 pad; + u32 format; + u32 offset; + } __attribute__((packed)); + + Texture::Header *header; + TPL_Palette_Header *palette; + u8 *texture_data; + GXTexObj texobj; + std::string name; +}; + +class TextureList : public std::vector +{ + public: + static const u32 MAGIC = MAKE_FOURCC('t', 'x', 'l', '1'); +}; + +#endif diff --git a/source/banner/WiiFont.cpp b/source/banner/WiiFont.cpp new file mode 100644 index 00000000..c9b4bf6d --- /dev/null +++ b/source/banner/WiiFont.cpp @@ -0,0 +1,387 @@ +/* +Copyright (c) 2012 - giantpune +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include +#include "WiiFont.h" + +WiiFont::WiiFont() + : header(NULL) + , finf(NULL) + , tglp(NULL) + , cwdh(NULL) + , font_loaded(false) +{ +} + +WiiFont::~WiiFont() +{ + std::map::iterator itr; + + for (itr = textureMap.begin(); itr != textureMap.end(); itr++) + { + if(itr->second.allocated && itr->second.texture_data) + free(itr->second.texture_data); + } +} + +bool WiiFont::Load(const u8 *file) +{ + if(!file) + return false; + + header = (WiiFont::Header *) file; + + if((header->magic != MAGIC_FONT && header->magic != MAGIC_FONT_ARCHIVE) + || header->version != MAGIC_VERSION) + { + header = NULL; + return false; + } + + const u8 *position = ((const u8 *)header) + header->header_len; + + for(u32 i = 0; i < header->section_count; ++i) + { + section_t *section = (section_t *) position; + position += section->size; + + switch( section->magic ) + { + case MAGIC_GLYPH_GROUP: + glgr = (GlgrHeader *) section; + break; + case MAGIC_FONT_INFORMATION: + finf = (FinfHeader *) section; + break; + case MAGIC_TEXTURE_GLYPH: + tglp = (TglpHeader *) section; + break; + case MAGIC_CHARACTER_WIDTH: + cwdh = (CwdhHeader *) section; + break; + case MAGIC_CHARACTER_CODE_MAP: + ParseCmap((CmapEntry *) (section + 1)); + break; + default: + // ignore + gprintf("Uknown section %.4s\n", (char *) §ion->magic); + break; + } + } + + // Some sanity checks + if(!finf || !tglp || !cwdh) + return false; + + if(finf->tglpOffset > header->filesize || finf->cwdhOffset > header->filesize + || finf->cmapOffset > header->filesize) + return false; + + font_loaded = true; + + return true; +} + +inline bool WiiFont::CheckCmap(u16 charCode, u16 mapValue) +{ + std::map::iterator it = cmap.find(charCode); + if(it != cmap.end()) + { + if((*it).second != mapValue) + { + gprintf("Duplicate characters\n"); + return false; + } + } + return true; +} + +bool WiiFont::ParseCmap(CmapEntry *cmapEntry) +{ + if(!cmapEntry) + return false; + + while(true) + { + switch(cmapEntry->type) + { + case 0: + { + for(u16 i = cmapEntry->charCode, j = cmapEntry->start; j < cmapEntry->end; j++, i++) + { + if(!CheckCmap(j, i)) + return false; + cmap[j] = i; + } + break; + } + case 1: + { + u16 idx = 0; + u16 *idxPointer = &cmapEntry->charCode; + for(u32 i = cmapEntry->start; i < cmapEntry->end; i++) + { + u16 m_idx = idxPointer[idx++]; + if(m_idx == 0xffff) + continue; + + if(!CheckCmap(i, m_idx)) + return false; + + cmap[i] = m_idx; + } + break; + } + case 2: + { + u16 ind, character; + u16 *charData = (u16 *) (cmapEntry + 1); + for(u32 i = 0; i < cmapEntry->charCode; i++) + { + character = charData[0]; + ind = charData[1]; + charData += 2; + + if(!CheckCmap(character, ind)) + return false; + + cmap[character] = ind; + } + break; + } + default: + gprintf( "unknown cmap type\n" ); + return false; + } + + if(cmapEntry->pos == 0) + return true; + + cmapEntry = (CmapEntry *)(((u8 *)header) + cmapEntry->pos); + } +} + +const WiiFont::CharInfo *WiiFont::GetCharInfo(u16 charCode) +{ + if(!finf || !tglp || !cwdh) + return NULL; + + // see if the character already exists in our cache + std::map::iterator itr = charInfoMap.find(charCode); + if(itr != charInfoMap.end()) + return &itr->second; + + u16 idx = CharToIdx(charCode); + if(idx > cwdh->endIdx) + { + gprintf( "idx > cwdh->endIdx" ); + return NULL; + } + + Cwdh *cwdh2 = (Cwdh*) (((u8 *)header) + finf->cwdhOffset + 8); + + u32 chars_per_texture = (tglp->charColumns * tglp->charRows); + u32 tex_idx = idx / chars_per_texture; + u32 row = (idx - chars_per_texture * tex_idx) / tglp->charColumns; + u32 col = (idx - chars_per_texture * tex_idx) - ( tglp->charColumns * row ); + + f32 _s1 = ((tglp->cellWidth + 1) * col) / (f32)tglp->width; + f32 _s2 = _s1 + CharacterWidth() / (f32)tglp->width; + + // this is good vertically but horizontal without it it looks clearer + f32 _t1 = ((tglp->cellHeight + 1) * row + 0.5f * (CharacterHeight() - (tglp->cellHeight + 1))) / (f32)tglp->height; + f32 _t2 = _t1 + CharacterHeight() / (f32)tglp->height; + + CharInfo charInfo; + charInfo.sheetIdx = tex_idx; + charInfo.s1 = _s1; + charInfo.s2 = _s2; + charInfo.t1 = _t1; + charInfo.t2 = _t2; + charInfo.advanceGlyphX = cwdh2[idx].advanceGlyphX; + charInfo.unk = cwdh2[idx].unk; + charInfo.advanceKerning = cwdh2[idx].advanceKerning; + + charInfoMap[charCode] = charInfo; + return &charInfoMap[charCode]; +} + +GXTexObj *WiiFont::LoadTextureObj(u16 tex_idx) +{ + if(!font_loaded || tex_idx >= tglp->texCnt) + return NULL; + + // init texture and add it to the list + TextureCache chacheStruct; + + if(header->magic == MAGIC_FONT_ARCHIVE) + { + chacheStruct.texture_data = GetUnpackedTexture(tex_idx); + chacheStruct.allocated = true; + } + else + { + chacheStruct.texture_data = ((u8 *) header) + tglp->dataOffset + tex_idx * tglp->texSize; + chacheStruct.allocated = false; + } + + if(!chacheStruct.texture_data) + return NULL; + + GX_InitTexObj( &chacheStruct.texObj, chacheStruct.texture_data, tglp->width, tglp->height, 0, 0, 0, true ); + GX_InitTexObjLOD( &chacheStruct.texObj, GX_LINEAR,GX_LINEAR, 0.0f, 0.0f, 0.0f, GX_DISABLE, GX_FALSE, GX_ANISO_1 ); + + textureMap[tex_idx] = chacheStruct; + return &textureMap[tex_idx].texObj; +} + +bool WiiFont::Apply(u16 tex_indx) +{ + if(!font_loaded) + return false; + + if(tex_indx >= tglp->texCnt) + return false; + + GXTexObj *tex_obj; + + // Load character texture from cache if available otherwise create cache + std::map::iterator itr = textureMap.find(tex_indx); + if(itr != textureMap.end()) + tex_obj = &itr->second.texObj; + else + tex_obj = LoadTextureObj(tex_indx); + + // no texture for this character found + if(!tex_obj) + return false; + + GX_LoadTexObj(tex_obj, GX_TEXMAP0); + GX_InvalidateTexAll(); + return true; +} + +// giantpunes little magic function +bool WiiFont::Decompress_0x28( unsigned char *outBuf, u32 outLen, const unsigned char *inBuf, u32 inLen ) +{ + if( outLen & 3 )// this copies 32 bits at a time, so it probably needs to be aligned + { + gprintf( "length not aligned to 32 bits\n" ); + return false; + } + const u32 root_offset = 5; + u32 symbol = 0; + u32 counter = 0; + u32 inIdx = 0; + u32 outIdx = 0; + u32 *in32 = (u32*)( ( inBuf + 6 ) + ( ( inBuf[ 4 ] << 1 ) ) ); + u32 *out32 = (u32*)outBuf; + u8 *p = (u8*)inBuf + root_offset; + + outLen >>= 2; //we are copying 4 bytes at a time + while( outLen ) + { + for( u32 i = 0, leaf = p[ 0 ], bits = __builtin_bswap32( in32[ inIdx ] ) + ; i < 0x20 && outLen + ; i++, leaf = p[ 0 ], bits <<= 1 ) + { + u32 topBit = bits >> 31; + u8 d = 2 - ((u64)p & 1); + p += topBit + ( ( leaf << 1 ) & 0x7e ) + d; + + if( !( p > (u8*)inBuf ) && ( p < (u8*)inBuf + inLen ) ) + { + gprintf( "out of range 1\n" ); + return false; + } + + if( ( ( leaf << ( topBit ) ) & ( 1 << 7 ) ) ) // p->isLeaf + { + symbol = ( ( p[ 0 ] << 0x18 ) | ( symbol >> 8 ) ); + p = (u8*)inBuf + root_offset; // p = root + if( counter++ > 2 ) + { + out32[ outIdx++ ] = __builtin_bswap32( symbol );// buf[bufcur++] = p->symbol + counter = 0; // reset counter + outLen--; // decrease amount to copy + } + } + } + if( inIdx++ >= ( inLen >> 2 ) ) + { + gprintf( "out of range 2\n" ); + return false; + } + } + return true; +} + +u8 *WiiFont::GetUnpackedTexture(u16 sheetNo) +{ + u8 *data = (u8 *) header; + u32 compressedSize; + u32 off = 0; + + // skip over all the sheets till we get the one we want + for( u32 i = 0; i < sheetNo; i++ ) + { + compressedSize = *(u32*)( data + tglp->dataOffset + off ); + off += compressedSize + 4; + } + + compressedSize = *(u32*)( data + tglp->dataOffset + off ); + + u32 uncompressedSize = *(u32*)( data + tglp->dataOffset + off + 4 ); + if( (uncompressedSize & 0xff000000) != 0x28000000 )// looks like all the sheets in wbf1 and wbf2.brfna are 0x28 + { + gprintf( "Brfna::LoadSheets(): unknown data type\n" ); + return NULL; + } + uncompressedSize = ( __builtin_bswap32( uncompressedSize ) >> 8 ); + if( !uncompressedSize )// is this right? it looks like it is. but it SHOULD only happen for files over 0xffffff bytes + { + uncompressedSize = __builtin_bswap32(*(u32*)( data + tglp->dataOffset + off + 8 )); + } + if( uncompressedSize != glgr->sheet_size ) + { + gprintf( "uncompressedSize != glgr->sheet_size %08x\n", uncompressedSize ); + return NULL; + } + + // decompress + u8* sheetData = (u8*)memalign( 32, uncompressedSize );// buffer needs to be 32bit aligned + if( !sheetData ) + return NULL; + + if( !Decompress_0x28( sheetData, uncompressedSize, ( data + tglp->dataOffset + off + 4 ), compressedSize ) ) + { + free( sheetData ); + return NULL; + } + + // Flush cache + DCFlushRange(sheetData, uncompressedSize); + + return sheetData; +} diff --git a/source/banner/WiiFont.h b/source/banner/WiiFont.h new file mode 100644 index 00000000..3861c08f --- /dev/null +++ b/source/banner/WiiFont.h @@ -0,0 +1,226 @@ +/* +Copyright (c) 2012 - giantpune +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef __WII_FONT_H_ +#define __WII_FONT_H_ + +#include +#include + +#include "Pane.h" + +class WiiFont +{ +public: + static const u32 MAGIC_FONT = MAKE_FOURCC('R', 'F', 'N', 'T'); + static const u32 MAGIC_FONT_ARCHIVE = MAKE_FOURCC('R', 'F', 'N', 'A'); + static const u32 MAGIC_VERSION = 0xFEFF0104; + static const u32 MAGIC_GLYPH_GROUP = MAKE_FOURCC('G', 'L', 'G', 'R'); + static const u32 MAGIC_FONT_INFORMATION = MAKE_FOURCC('F', 'I', 'N', 'F'); + static const u32 MAGIC_TEXTURE_GLYPH = MAKE_FOURCC('T', 'G', 'L', 'P'); + static const u32 MAGIC_CHARACTER_CODE_MAP = MAKE_FOURCC('C', 'M', 'A', 'P'); + static const u32 MAGIC_CHARACTER_WIDTH = MAKE_FOURCC('C', 'W', 'D', 'H'); + + WiiFont(); + ~WiiFont(); + + // load the file + bool Load(const u8 *file); + + // apply texture, non-const because we load texture on demand + bool Apply(u16 tex_idx); + + // struct to hold info for a character to keep from searching and calculating it all every frame + struct CharInfo + { + u32 sheetIdx; + f32 s1; + f32 t1; + f32 s2; + f32 t2; + s8 advanceKerning; + u8 unk; + s8 advanceGlyphX; + }; + + // get the character information + const CharInfo *GetCharInfo(u16 charCode); + + // check if the font was loaded correctly + bool IsLoaded() const { return font_loaded; } + + // get some parameters from the FINF header + u8 CharacterWidth() const { return finf ? finf->charWidth : 0; } + u8 CharacterHeight() const { return finf ? finf->height : 0; } + + const std::string& getName() const { return name; } + void SetName(const std::string& _name) { name = _name; } + +private: + struct Header + { + u32 magic; + u32 version; + u32 filesize; + u16 header_len; + u16 section_count; + } __attribute__((packed)); + + struct GlgrHeader + { + u32 magic; + u32 sectionSize; + u32 sheet_size; + u16 glyphs_per_sheet; + u16 set_count; + u16 sheet_count; + u16 cwdh_count; + u16 cmap_count; + }__attribute__(( packed )); + + struct FinfHeader + { + u32 magic; + u32 headerSize; // finf size + u8 unk8_1; // font type? + u8 leading; // + u16 defaultChar; + u8 leftMargin; + u8 charWidth; + u8 fullWidth; + u8 encoding; + u32 tglpOffset; // TLGP offset + u32 cwdhOffset; // CWDH offset + u32 cmapOffset; // CMAP offset + u8 height; + u8 width; + u8 ascent; + u8 unk8_10; + } __attribute__(( packed )); + + struct TglpHeader + { + u32 magic; + u32 tglpSize; // TGLP size + + u8 cellWidth; // font width - 1 + u8 cellHeight; // font heigh - 1 + u8 baselinePos; + u8 maxCharWidth; + + u32 texSize; // length of 1 image + + u16 texCnt; // number of images + u16 texType; // + u16 charColumns; // character per row + u16 charRows; // characters per column + u16 width; // width of image + u16 height; // height of image + u32 dataOffset; // data offset + } __attribute__(( packed )); + + struct CwdhHeader + { + u32 magic; + u32 length; // section length? + u16 startIdx; // + u16 endIdx; // + u32 next; // + + } __attribute__(( packed )); + + struct CmapEntry + { + u16 start; + u16 end; + u16 type; + u16 pad; + u32 pos; + u16 charCode; + } __attribute__(( packed )); + + struct Cwdh + { + s8 advanceKerning; + u8 unk; + s8 advanceGlyphX; + } __attribute__(( packed )); + + // font texture decompress functions + static bool Decompress_0x28( unsigned char *outBuf, u32 outLen, const unsigned char *inBuf, u32 inLen ); + u8 *GetUnpackedTexture(u16 sheetNo); + + // load a GX texture from index + GXTexObj *LoadTextureObj(u16 texture_idx); + + // cmap parser + bool ParseCmap(CmapEntry *cmapEntry); + + // just a duplicate check + bool CheckCmap(u16 charCode, u16 mapValue); + + // get index of a character + u16 CharToIdx(u16 charCode) + { + std::map::iterator itr = cmap.find(charCode); + if(itr != cmap.end()) + return itr->second; + + return finf->defaultChar; + } + + WiiFont::Header *header; + + // pointers to each sections + FinfHeader *finf; + TglpHeader *tglp; + CwdhHeader *cwdh; + GlgrHeader *glgr; + + // struct to contain a decompressed texture caches + struct TextureCache + { + u8* texture_data; + bool allocated; + GXTexObj texObj; + }; + std::map textureMap; + + // character info cache map + std::map charInfoMap; + + // holds all the character codes and their index within the font + std::map cmap; + + std::string name; + bool font_loaded; +}; + +class FontList : public std::vector +{ +public: + static const u32 MAGIC = MAKE_FOURCC('f', 'n', 'l', '1'); +}; + +#endif diff --git a/source/banner/Window.cpp b/source/banner/Window.cpp new file mode 100644 index 00000000..d3a89e1e --- /dev/null +++ b/source/banner/Window.cpp @@ -0,0 +1,51 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#include "Window.h" + +void Window::Load(Pane::Header *file) +{ + if(!file) + return; + + const u8 *section_start = (const u8 *)file; + + Pane::Load(file); + header = (Window::Header *) (file+1); + + // read content + QuadPane::Load((QuadPane::Header *)(section_start + header->content_offset)); + + // read frames + const u32 *frame_offsets = (const u32 *) (section_start + header->frame_table_offset); + for(u32 i = 0; i < header->frame_count; i++) + frames.push_back((Frame *) (section_start + frame_offsets[i])); +} + +void Window::Draw(const BannerResources& resources, u8 render_alpha, const float ws_scale, Mtx &view) const +{ + // TODO: handle "inflation" + // TODO: handle "frames" and "texture_flip" + + QuadPane::Draw(resources, render_alpha, ws_scale, view); +} diff --git a/source/banner/Window.h b/source/banner/Window.h new file mode 100644 index 00000000..97880818 --- /dev/null +++ b/source/banner/Window.h @@ -0,0 +1,65 @@ +/* +Copyright (c) 2010 - Wii Banner Player Project +Copyright (c) 2012 - Dimok + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ +#ifndef WII_BNR_WINDOW_H_ +#define WII_BNR_WINDOW_H_ + +#include "QuadPane.h" + +class Window : public QuadPane +{ +public: + typedef QuadPane Base; + + static const u32 MAGIC = MAKE_FOURCC('w', 'n', 'd', '1'); + + void Load(Pane::Header *file); + +private: + void Draw(const BannerResources& resources, u8 render_alpha, const float ws_scale, Mtx &view) const; + + struct inflation + { + float l, r, t, b; + }; + + struct Header + { + inflation infl; + u8 frame_count; + u8 pad[3]; + u32 content_offset; + u32 frame_table_offset; + } __attribute__((packed)); + + struct Frame + { + u16 material_index; + u8 texture_flip; + } __attribute__((packed)); + + Header *header; + std::vector frames; +}; + +#endif diff --git a/source/cheats/cheatmenu.cpp b/source/cheats/cheatmenu.cpp index 33d79bc6..10b2f54b 100644 --- a/source/cheats/cheatmenu.cpp +++ b/source/cheats/cheatmenu.cpp @@ -51,8 +51,8 @@ int CheatMenu(const char * gameID) char txtfilename[55]; snprintf(txtfilename, sizeof(txtfilename), "%s%s.txt", Settings.TxtCheatcodespath, gameID); - GCTCheats c; - int check = c.openTxtfile(txtfilename); + GCTCheats gctCheats; + int check = gctCheats.openTxtfile(txtfilename); int download = 0; @@ -66,28 +66,38 @@ int CheatMenu(const char * gameID) if (download == 1) { download = CodeDownload(gameID); - if (download < 0 || c.openTxtfile(txtfilename) != 1) + if (download < 0 || gctCheats.openTxtfile(txtfilename) != 1) break; } else break; case 1: - int cntcheats = c.getCnt(); + int cntcheats = gctCheats.getCnt(); OptionList cheatslst; GuiOptionBrowser chtBrowser(400, 280, &cheatslst, "bg_options_settings.png"); chtBrowser.SetPosition(0, 90); - chtBrowser.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + chtBrowser.SetAlignment(ALIGN_CENTER, ALIGN_TOP); chtBrowser.SetClickable(true); - GuiText titleTxt(c.getGameName().c_str(), 28, ( GXColor ) {0, 0, 0, 255}); - titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + GuiText titleTxt(gctCheats.getGameName().c_str(), 28, ( GXColor ) {0, 0, 0, 255}); + titleTxt.SetAlignment(ALIGN_CENTER, ALIGN_TOP); titleTxt.SetMaxWidth(350, SCROLL_HORIZONTAL); titleTxt.SetPosition(12, 40); + char gctPath[200]; + snprintf(gctPath, sizeof(gctPath), "%s%.6s.gct", Settings.Cheatcodespath, gameID); + u8 *gctBuf = NULL; + u32 gctSize = 0; + LoadFileToMem(gctPath, &gctBuf, &gctSize); + for (int i = 0; i < cntcheats; i++) { - cheatslst.SetValue(i, "%s", c.getCheatName(i).c_str()); - cheatslst.SetName(i, "OFF"); + cheatslst.SetValue(i, "%s", gctCheats.getCheatName(i).c_str()); + // search after header and before footer + if(gctBuf && gctCheats.IsCheatIncluded(i, gctBuf, gctSize)) + cheatslst.SetName(i, tr("ON")); + else + cheatslst.SetName(i, tr("OFF")); } HaltGui(); @@ -104,54 +114,57 @@ int CheatMenu(const char * gameID) while (!exit) { - VIDEO_WaitVSync(); + usleep(100000); ret = chtBrowser.GetClickedOption(); if (ret >= 0) { const char *strCheck = cheatslst.GetName(ret); - if (strCheck && strncmp(strCheck, "ON", 2) == 0) + if (strCheck && strncmp(strCheck, tr("ON"), 2) == 0) { - cheatslst.SetName(ret, "%s", "OFF"); + cheatslst.SetName(ret, "%s", tr("OFF")); } - else if (strCheck && strncmp(strCheck, "OFF", 3) == 0) + else if (strCheck && strncmp(strCheck, tr("OFF"), 3) == 0) { - cheatslst.SetName(ret, "%s", "ON"); + cheatslst.SetName(ret, "%s", tr("ON")); } } if (createBtn.GetState() == STATE_CLICKED) { - createBtn.ResetState(); if (cntcheats > 0) { - int selectednrs[30]; - int x = 0; + vector vActiveCheats; for (int i = 0; i < cntcheats; i++) { const char *strCheck = cheatslst.GetName(i); - if (strCheck && strncmp(strCheck, "ON", 2) == 0) - { - selectednrs[x] = i; - x++; - } + if (strCheck && strncmp(strCheck, tr("ON"), 2) == 0) + vActiveCheats.push_back(i); } - if (x == 0) + if (vActiveCheats.size() == 0) { - WindowPrompt(tr( "Error" ), tr( "No cheats were selected" ), tr( "OK" )); + if(WindowPrompt(tr( "Error" ), tr( "No cheats were selected! Should the GCT file be deleted?" ), tr("Yes"), tr("Cancel"))) + { + RemoveFile(gctPath); + w.Remove(&chtBrowser); + for (int i = 0; i < gctCheats.getCnt(); i++) + cheatslst.SetName(i, tr("OFF")); + w.Append(&chtBrowser); + } } else { CreateSubfolder(Settings.Cheatcodespath); - string chtpath = Settings.Cheatcodespath; - string gctfname = chtpath + c.getGameID() + ".gct"; - c.createGCT(selectednrs, x, gctfname.c_str()); + gctCheats.createGCT(vActiveCheats, gctPath); WindowPrompt(tr( "GCT File created" ), NULL, tr( "OK" )); - exit = true; - break; } } - else WindowPrompt(tr( "Error" ), tr( "Could not create GCT file" ), tr( "OK" )); + else + WindowPrompt(tr( "Error" ), tr( "Could not create GCT file" ), tr( "OK" )); + + mainWindow->SetState(STATE_DISABLED); + w.SetState(STATE_DEFAULT); + createBtn.ResetState(); } if (backBtn.GetState() == STATE_CLICKED) @@ -164,21 +177,27 @@ int CheatMenu(const char * gameID) if(updateBtn.GetState() == STATE_CLICKED) { download = CodeDownload(gameID); - if (download >= 0 && c.openTxtfile(txtfilename) == 1) + if (download >= 0 && gctCheats.openTxtfile(txtfilename) == 1) { w.Remove(&chtBrowser); cheatslst.ClearList(); - cntcheats = c.getCnt(); + cntcheats = gctCheats.getCnt(); for (int i = 0; i < cntcheats; i++) { - cheatslst.SetValue(i, "%s", c.getCheatName(i).c_str()); - cheatslst.SetName(i, "OFF"); + cheatslst.SetValue(i, "%s", gctCheats.getCheatName(i).c_str()); + // search after header and before footer + if(gctBuf && gctCheats.IsCheatIncluded(i, gctBuf, gctSize)) + cheatslst.SetName(i, tr("ON")); + else + cheatslst.SetName(i, tr("OFF")); } w.Append(&chtBrowser); } updateBtn.ResetState(); } } + if(gctBuf) + free(gctBuf); HaltGui(); mainWindow->SetState(STATE_DEFAULT); mainWindow->Remove(&w); diff --git a/source/cheats/gct.cpp b/source/cheats/gct.cpp index ccab216f..80a80e19 100644 --- a/source/cheats/gct.cpp +++ b/source/cheats/gct.cpp @@ -1,35 +1,47 @@ -/* - * gct.h - * Class to handle Ocarina TXT Cheatfiles - * nIxx - */ - -#include -#include +/**************************************************************************** + * Copyright (C) 2009 nIxx + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include #include #include #include "gct.h" #define ERRORRANGE "Error: CheatNr out of range" +//Header and Footer +static const char GCT_Header[] = { 0x00, 0xd0, 0xc0, 0xde, 0x00, 0xd0, 0xc0, 0xde }; +static const char GCT_Footer[] = { 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + GCTCheats::GCTCheats(void) { - iCntCheats = 0; } GCTCheats::~GCTCheats(void) { - - string sGameID = ""; - string sGameTitle = ""; - /*string sCheatName[MAXCHEATS]; - string sCheats[MAXCHEATS]; - string sCheatComment[MAXCHEATS];*/ } -int GCTCheats::getCnt() +void GCTCheats::Clear(void) { - return iCntCheats; + sGameID.clear(); + sCheatName.clear(); + sGameTitle.clear(); + sCheatComment.clear(); + sCheats.clear(); + } string GCTCheats::getGameName(void) @@ -42,227 +54,157 @@ string GCTCheats::getGameID(void) return sGameID; } -string GCTCheats::getCheat(int nr) +vector GCTCheats::getCheat(int nr) { - if (nr <= (iCntCheats - 1)) - { - return sCheats[nr]; - } - else - { - return ERRORRANGE; - } + if((unsigned int)nr >= sCheats.size()) + return vector(); + + return sCheats[nr]; } string GCTCheats::getCheatName(int nr) { - if (nr <= (iCntCheats - 1)) - { - return sCheatName[nr]; - } - else - { + if((unsigned int)nr >= sCheatName.size()) return ERRORRANGE; - } + + return sCheatName[nr]; } string GCTCheats::getCheatComment(int nr) { - if (nr <= (iCntCheats - 1)) - { - return sCheatComment[nr]; - } - else - { + if((unsigned int)nr >= sCheatComment.size()) return ERRORRANGE; - } + + return sCheatComment[nr]; } -int GCTCheats::createGCT(int nr, const char * filename) +int GCTCheats::createGCT(const vector &vCheats, const char * filename) { + if (vCheats.size() == 0 || !filename) + return 0; - if (nr == 0) return 0; + FILE *pFile = fopen(filename, "wb"); - ofstream filestr; - filestr.open(filename); + if (!pFile) + return 0; - if (filestr.fail()) return 0; - - //Header and Footer - char header[] = { 0x00, 0xd0, 0xc0, 0xde, 0x00, 0xd0, 0xc0, 0xde }; - char footer[] = { 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - - string buf = getCheat(nr); - filestr.write(header, sizeof(header)); - - int x = 0; - long int li; - int len = buf.size(); - - while (x < len) - { - string temp = buf.substr(x, 2); - li = strtol(temp.c_str(), NULL, 16); - temp = li; - filestr.write(temp.c_str(), 1); - x += 2; - } - filestr.write(footer, sizeof(footer)); - - filestr.close(); - return 1; -} - -int GCTCheats::createGCT(const char * chtbuffer, const char * filename) -{ - - ofstream filestr; - filestr.open(filename); - - if (filestr.fail()) return 0; - - //Header and Footer - char header[] = { 0x00, 0xd0, 0xc0, 0xde, 0x00, 0xd0, 0xc0, 0xde }; - char footer[] = { 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - - string buf = chtbuffer; - filestr.write(header, sizeof(header)); - - int x = 0; - long int li; - int len = buf.size(); - - while (x < len) - { - string temp = buf.substr(x, 2); - li = strtol(temp.c_str(), NULL, 16); - temp = li; - filestr.write(temp.c_str(), 1); - x += 2; - } - - filestr.write(footer, sizeof(footer)); - - filestr.close(); - - return 1; -} - -int GCTCheats::createGCT(int nr[], int cnt, const char * filename) -{ - - if (cnt == 0) return 0; - - ofstream filestr; - filestr.open(filename); - - if (filestr.fail()) return 0; - - //Header and Footer - char header[] = { 0x00, 0xd0, 0xc0, 0xde, 0x00, 0xd0, 0xc0, 0xde }; - char footer[] = { 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - - filestr.write(header, sizeof(header)); + fwrite(GCT_Header, sizeof(GCT_Header), 1, pFile); + int cnt = vCheats.size(); int c = 0; - while (c != cnt) + while (c < cnt) { - int actnr = nr[c]; - string buf = getCheat(actnr); - long int li; - int len = buf.size(); - int x = 0; + if((unsigned int)vCheats[c] > sCheats.size()) + continue; - while (x < len) + vector &cheatBuf = sCheats[vCheats[c]]; + unsigned int x = 0; + + while (x < cheatBuf.size()) { - string temp = buf.substr(x, 2); - li = strtol(temp.c_str(), NULL, 16); - temp = li; - filestr.write(temp.c_str(), 1); - x += 2; + fwrite((char*)&cheatBuf[x], 4, 1, pFile); + x++; } c++; } - filestr.write(footer, sizeof(footer)); - filestr.close(); + fwrite(GCT_Footer, sizeof(GCT_Footer), 1, pFile); + fclose(pFile); return 1; } +static inline void RemoveLineEnds(char *str) +{ + const char *strPtr = str; + while(*strPtr != 0) + { + if(*strPtr == '\n' || *strPtr == '\r') + { + strPtr++; + continue; + } + + *str = *strPtr; + str++; + strPtr++; + } + *str = 0; +} + int GCTCheats::openTxtfile(const char * filename) { - ifstream filestr; - int i = 0; - string str; - filestr.open(filename); + //! clear already loaded things + Clear(); - if (filestr.fail()) return 0; + FILE *pFile = fopen(filename, "rb"); + if (!pFile) + return 0; - filestr.seekg(0, ios_base::end); - int size = filestr.tellg(); - if (size <= 0) return -1; - filestr.seekg(0, ios_base::beg); + fseek(pFile, 0, SEEK_END); + int size = ftell(pFile); + fseek(pFile, 0, SEEK_SET); - getline(filestr, sGameID); - if (sGameID[sGameID.length() - 1] == '\r') sGameID.erase(sGameID.length() - 1); + if (size <= 0) { + fclose(pFile); + return -1; + } - getline(filestr, sGameTitle); - if (sGameTitle[sGameTitle.length() - 1] == '\r') sGameTitle.erase(sGameTitle.length() - 1); + const int max_line_size = 4096; + char *line = new char[max_line_size]; - getline(filestr, sCheatName[i]); // skip first line if file uses CRLF - if (!sGameTitle[sGameTitle.length() - 1] == '\r') filestr.seekg(0, ios_base::beg); + fgets(line, max_line_size, pFile); + RemoveLineEnds(line); + sGameID = line; + fgets(line, max_line_size, pFile); + RemoveLineEnds(line); + sGameTitle = line; - while (!filestr.eof()) + while (fgets(line, max_line_size, pFile)) { - getline(filestr, sCheatName[i]); // '\n' delimiter by default - if (sCheatName[i][sCheatName[i].length() - 1] == '\r') sCheatName[i].erase(sCheatName[i].length() - 1); + RemoveLineEnds(line); - string cheatdata; - bool emptyline = false; + if(*line == 0) + continue; - do + sCheatName.push_back(line); + + vector cheatdata; + + while (fgets(line, max_line_size, pFile)) { - getline(filestr, str); - if (str[str.length() - 1] == '\r') str.erase(str.length() - 1); + RemoveLineEnds(line); - if (str == "" || str[0] == '\r' || str[0] == '\n') - { - emptyline = true; + if(*line == 0) // empty line means start of new cheat break; - } - if (IsCode(str)) + if (IsCode(line)) { // remove any garbage (comment) after code - while (str.size() > 17) - { - str.erase(str.length() - 1); - } - cheatdata.append(str); - size_t found = cheatdata.find(' '); - cheatdata.replace(found, 1, ""); + line[8] = 0; + line[17] = 0; + + cheatdata.push_back(strtoul(&line[0], 0, 16)); + cheatdata.push_back(strtoul(&line[9], 0, 16)); } else { - //printf("%i",str.size()); - sCheatComment[i] = str; + sCheatComment.push_back(line); } - if (filestr.eof()) break; + } - } while (!emptyline); + if(cheatdata.empty()) + continue; - sCheats[i] = cheatdata; - i++; - if (i == MAXCHEATS) break; + sCheats.push_back(cheatdata); } - iCntCheats = i; - filestr.close(); + fclose(pFile); + delete [] line; return 1; } -bool GCTCheats::IsCode(const std::string& str) +bool GCTCheats::IsCode(const char *str) { - if (str[8] == ' ' && str.size() >= 17) + if (strlen(str) >= 17 && str[8] == ' ') { // accept strings longer than 17 in case there is a comment on the same line as the code char part1[9]; @@ -278,3 +220,19 @@ bool GCTCheats::IsCode(const std::string& str) } return false; } + +bool GCTCheats::IsCheatIncluded(int iCheat, const unsigned char *gctBuf, unsigned int gctSize) +{ + if(!gctBuf || (unsigned int)iCheat >= sCheats.size()) + return false; + + vector &Cheat = sCheats[iCheat]; + int len = Cheat.size() * sizeof(unsigned int); + + for(unsigned int i = sizeof(GCT_Header); i + len <= gctSize - sizeof(GCT_Footer); i += 4) + { + if(memcmp(&Cheat[0], &gctBuf[i], len) == 0) + return true; + } + return false; +} diff --git a/source/cheats/gct.h b/source/cheats/gct.h index 9acc1a41..d0f2526f 100644 --- a/source/cheats/gct.h +++ b/source/cheats/gct.h @@ -1,15 +1,25 @@ -/* - * gct.h - * Class to handle Ocarina TXT Cheatfiles +/**************************************************************************** + * Copyright (C) 2009 nIxx + * Copyright (C) 2012 Dimok * - */ - + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ #ifndef _GCT_H #define _GCT_H -#include - -#define MAXCHEATS 300 +#include +#include using namespace std; @@ -19,11 +29,9 @@ class GCTCheats private: string sGameID; string sGameTitle; - string sCheatName[MAXCHEATS]; - string sCheats[MAXCHEATS]; - string sCheatComment[MAXCHEATS]; - int iCntCheats; - + vector< string > sCheatName; + vector< string > sCheatComment; + vector< vector > sCheats; public: //!Constructor GCTCheats(void); @@ -33,25 +41,15 @@ class GCTCheats //!\param filename name of TXT file //!\return error code int openTxtfile(const char * filename); - //!Creates GCT file for one cheat - //!\param nr selected Cheat Numbers - //!\param filename name of GCT file - //!\return error code - int createGCT(int nr, const char * filename); - //!Creates GCT file from a buffer - //!\param chtbuffer buffer that holds the cheat data - //!\param filename name of GCT file - //!\return error code - int createGCT(const char * chtbuffer, const char * filename); //!Creates GCT file //!\param nr[] array of selected Cheat Numbers //!\param cnt size of array //!\param filename name of GCT file //!\return error code - int createGCT(int nr[], int cnt, const char * filename); + int createGCT(const vector &vCheats, const char * filename); //!Gets Count cheats //!\return Count cheats - int getCnt(); + int getCnt() const { return sCheats.size(); } //!Gets Game Name //!\return Game Name string getGameName(void); @@ -60,16 +58,20 @@ class GCTCheats string getGameID(void); //!Gets cheat data //!\return cheat data - string getCheat(int nr); + vector getCheat(int nr); //!Gets Cheat Name //!\return Cheat Name string getCheatName(int nr); //!Gets Cheat Comment //!\return Cheat Comment string getCheatComment(int nr); + //!Clear all loaded cheats + void Clear(void); //!Check if string is a code //!\return true/false - bool IsCode(const std::string& s); + bool IsCode(const char *s); + //!Check if cheat is included in GCT buffer + bool IsCheatIncluded(int iCheat, const unsigned char *gctBuf, unsigned int gctSize); }; #endif /* _GCT_H */ diff --git a/source/gecko.c b/source/gecko.c index 3d7d954b..bf72ad8c 100644 --- a/source/gecko.c +++ b/source/gecko.c @@ -10,8 +10,10 @@ static bool geckoinit = false; void gprintf(const char *format, ...) { + #ifndef DEBUG_TO_FILE if (!geckoinit) return; + #endif static char stringBuf[4096]; int len; @@ -19,14 +21,17 @@ void gprintf(const char *format, ...) va_start(va, format); if((len = vsnprintf(stringBuf, sizeof(stringBuf), format, va)) > 0) { - usb_sendbuffer(1, stringBuf, len); #ifdef DEBUG_TO_FILE FILE *debugF = fopen("sd:/debug.txt", "a"); if(!debugF) debugF = fopen("sd:/debug.txt", "w"); if(debugF) - fprintf(debugF, tmp); - fclose(debugF); + { + fwrite(stringBuf, 1, strlen(stringBuf), debugF); + fclose(debugF); + } + #else + usb_sendbuffer(1, stringBuf, len); #endif } va_end(va); diff --git a/source/homebrewboot/HomebrewBrowser.cpp b/source/homebrewboot/HomebrewBrowser.cpp index 2da0ef6b..35591f7e 100644 --- a/source/homebrewboot/HomebrewBrowser.cpp +++ b/source/homebrewboot/HomebrewBrowser.cpp @@ -395,7 +395,7 @@ int HomebrewBrowser::ReceiveFile() // It's a zip file, unzip to the apps directory // Zip archive, ask for permission to install the zip char zippath[255]; - sprintf(zippath, "%s%s", Settings.homebrewapps_path, filename); + snprintf(zippath, sizeof(zippath), "%s%s", Settings.homebrewapps_path, filename); FILE *fp = fopen(zippath, "wb"); if (!fp) diff --git a/source/input.cpp b/source/input.cpp index f9af0ee5..678c3203 100644 --- a/source/input.cpp +++ b/source/input.cpp @@ -106,7 +106,7 @@ bool ControlActivityTimeout(void) minTime = pointer[i]->getLastActivCounter(); // not very accurate but it's not required here - return (minTime/60 > ScreensaverTime(Settings.screensaver)); + return (minTime/(Settings.PAL50 ? 50 : 60) > ScreensaverTime(Settings.screensaver)); } /**************************************************************************** * SetupPads diff --git a/source/libs/libwbfs/gcdisc.c b/source/libs/libwbfs/gcdisc.c new file mode 100644 index 00000000..0910b803 --- /dev/null +++ b/source/libs/libwbfs/gcdisc.c @@ -0,0 +1,144 @@ +// Copyright 2012 Dimok based on wiidisc.c: +// Licensed under the terms of the GNU GPL, version 2 +// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +#include "gcdisc.h" + +static void disc_read(gcdisc_t *d, u32 offset, u8 *data, u32 len) +{ + if (data) + { + int ret = 0; + if (len == 0) return; + ret = d->read(d->fp, offset, len, data); + if (ret < 0) + wbfs_fatal( "error reading disc" ); + } +} + +static u32 do_fst(gcdisc_t *d, u8 *fst, const char *names, u32 i) +{ + u32 offset; + u32 size; + const char *name; + u32 j; + + name = names + (wbfs_be32(fst + 12 * i) & 0x00ffffff); + size = wbfs_be32(fst + 12 * i + 8); + + if (i == 0) + { + for (j = 1; j < size && !d->extracted_buffer;) + { + j = do_fst(d, fst, names, j); + } + return size; + } + + if (fst[12 * i]) + { + + for (j = i + 1; j < size && !d->extracted_buffer;) + j = do_fst(d, fst, names, j); + + return size; + } + else + { + offset = wbfs_be32(fst + 12 * i + 4); + + if (d->extract_pathname && strcasecmp(name, d->extract_pathname) == 0) + { + d->extracted_buffer = wbfs_ioalloc( size ); + d->extracted_size = size; + disc_read(d, offset, d->extracted_buffer, size); + } + return i + 1; + } +} + +static void do_files(gcdisc_t*d) +{ + u8 *b = wbfs_ioalloc( 0x480 ); // XXX: determine actual header size + //u32 dol_offset; + u32 fst_offset; + u32 fst_size; + u8 *fst; + u32 n_files; + disc_read(d, 0, b, 0x480); + + //dol_offset = wbfs_be32(b + 0x0420); + fst_offset = wbfs_be32(b + 0x0424); + fst_size = wbfs_be32(b + 0x0428); + + if (fst_size) + { + fst = wbfs_ioalloc( fst_size ); + if (fst == 0) + wbfs_fatal( "malloc fst" ); + disc_read(d, fst_offset, fst, fst_size); + n_files = wbfs_be32(fst + 8); + + + if (d->extract_pathname && strcmp(d->extract_pathname, "FST") == 0) + { + // if empty pathname requested return fst + d->extracted_buffer = fst; + d->extracted_size = fst_size; + d->extract_pathname = NULL; + // skip do_fst if only fst requested + n_files = 0; + } + + if (12 * n_files <= fst_size) + { + if (n_files > 1) do_fst(d, fst, (char *) fst + 12 * n_files, 0); + } + + if (fst != d->extracted_buffer) wbfs_iofree( fst ); + } + wbfs_iofree( b ); +} + +static void do_disc(gcdisc_t*d) +{ + u8 *b = wbfs_ioalloc( 0x100 ); + u32 magic; + disc_read(d, 0, b, 0x100); + magic = wbfs_be32(b + 28); + if (magic != 0xC2339F3D) + { + wbfs_iofree( b ); + wbfs_error( "not a gc disc" ); + return; + } + wbfs_iofree( b ); + + do_files(d); +} + +gcdisc_t *gc_open_disc(read_wiidisc_callback_t read, void*fp) +{ + gcdisc_t *d = wbfs_malloc( sizeof( gcdisc_t ) ); + if (!d) return 0; + wbfs_memset( d, 0, sizeof( gcdisc_t ) ); + d->read = read; + d->fp = fp; + + return d; +} +void gc_close_disc(gcdisc_t *d) +{ + wbfs_free( d ); +} + +u8 * gc_extract_file(gcdisc_t *d, const char *pathname) +{ + u8 *retval = 0; + d->extract_pathname = pathname; + d->extracted_buffer = 0; + do_disc(d); + d->extract_pathname = 0; + retval = d->extracted_buffer; + return retval; +} diff --git a/source/libs/libwbfs/gcdisc.h b/source/libs/libwbfs/gcdisc.h new file mode 100644 index 00000000..f46ae4c1 --- /dev/null +++ b/source/libs/libwbfs/gcdisc.h @@ -0,0 +1,32 @@ +#ifndef GCDISC_H +#define GCDISC_H + +#include +#include "libwbfs_os.h" // this file is provided by the project wanting to compile libwbfs and wiidisc +#include "wiidisc.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + typedef struct gcdisc_s + { + read_wiidisc_callback_t read; + void *fp; + + const char *extract_pathname; + u8 *extracted_buffer; + int extracted_size; + } gcdisc_t; + + gcdisc_t *gc_open_disc(read_wiidisc_callback_t read, void*fp); + void gc_close_disc(gcdisc_t *); + // returns a buffer allocated with wbfs_ioalloc() or NULL if not found of alloc error + u8 * gc_extract_file(gcdisc_t *d, const char *pathname); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/source/lstub.cpp b/source/lstub.cpp index ef09cd35..459975ba 100644 --- a/source/lstub.cpp +++ b/source/lstub.cpp @@ -53,10 +53,11 @@ s32 Set_Stub(u64 reqID) s32 Set_Stub_Split(u32 type, const char* reqID) { - char tmp[4]; - u32 lower; - sprintf(tmp, "%c%c%c%c", reqID[0], reqID[1], reqID[2], reqID[3]); - memcpy(&lower, tmp, 4); + const u32 lower = ((u32)reqID[0] << 24) | + ((u32)reqID[1] << 16) | + ((u32)reqID[2] << 8) | + ((u32)reqID[3]); + u64 reqID64 = TITLE_ID( type, lower ); return Set_Stub(reqID64); @@ -96,7 +97,6 @@ u64 getStubDest() u8 hbcStubAvailable() { char * sig = (char *) 0x80001804; - return (sig[0] == 'S' && sig[1] == 'T' && sig[2] == 'U' && sig[3] == 'B' && sig[4] == 'H' && sig[5] == 'A' - && sig[6] == 'X' && sig[7] == 'X') ? 1 : 0; + return (strncmp(sig, "STUBHAXX", 8) == 0); } diff --git a/source/memory/mem2.h b/source/memory/mem2.h index 8bad5e2b..9e0b848b 100644 --- a/source/memory/mem2.h +++ b/source/memory/mem2.h @@ -8,6 +8,8 @@ extern "C" { #endif +#define isMEM2Buffer(p) (((u32) p & 0x10000000) != 0) + void *MEM1_alloc(unsigned int s); void *MEM1_memalign(unsigned int a, unsigned int s); void *MEM1_realloc(void *p, unsigned int s); diff --git a/source/memory/memory.h b/source/memory/memory.h index 14b847d5..60641454 100644 --- a/source/memory/memory.h +++ b/source/memory/memory.h @@ -10,7 +10,7 @@ extern "C" #define Disc_Region ((vu32*) 0x80000003) #define Disc_Magic ((vu32*) 0x80000018) #define Sys_Magic ((vu32*) 0x80000020) -#define Version ((vu32*) 0x80000024) +#define Sys_Version ((vu32*) 0x80000024) #define Mem_Size ((vu32*) 0x80000028) #define Board_Model ((vu32*) 0x8000002C) #define Arena_L ((vu32*) 0x80000030) diff --git a/source/menu.cpp b/source/menu.cpp index 6a2e4d31..923318ec 100644 --- a/source/menu.cpp +++ b/source/menu.cpp @@ -174,9 +174,12 @@ int MainMenu(int menu) if (Settings.autonetwork) ResumeNetworkThread(); - btnSoundClick = new GuiSound(Resources::GetFile("button_click.wav"), Resources::GetFileSize("button_click.wav"), Settings.sfxvolume); - btnSoundClick2 = new GuiSound(Resources::GetFile("button_click2.wav"), Resources::GetFileSize("button_click2.wav"), Settings.sfxvolume); - btnSoundOver = new GuiSound(Resources::GetFile("button_over.wav"), Resources::GetFileSize("button_over.wav"), Settings.sfxvolume); + btnSoundClick = new GuiSound(NULL, 0, Settings.sfxvolume); + btnSoundClick->LoadSoundEffect(Resources::GetFile("button_click.wav"), Resources::GetFileSize("button_click.wav")); + btnSoundClick2 = new GuiSound(NULL, 0, Settings.sfxvolume); + btnSoundClick2->LoadSoundEffect(Resources::GetFile("button_click2.wav"), Resources::GetFileSize("button_click2.wav")); + btnSoundOver = new GuiSound(NULL, 0, Settings.sfxvolume); + btnSoundOver->LoadSoundEffect(Resources::GetFile("button_over.wav"), Resources::GetFileSize("button_over.wav")); pointer[0] = new WiiPointer("player1_point.png"); pointer[1] = new WiiPointer("player2_point.png"); diff --git a/source/menu/GameBrowseMenu.cpp b/source/menu/GameBrowseMenu.cpp index ee51d209..44f3395e 100644 --- a/source/menu/GameBrowseMenu.cpp +++ b/source/menu/GameBrowseMenu.cpp @@ -4,11 +4,13 @@ #include "GUI/gui_gamelist.h" #include "GUI/gui_gamegrid.h" #include "GUI/gui_gamecarousel.h" +#include "GUI/GuiBannerGrid.h" #include "GUI/LoadCoverImage.h" #include "prompts/PromptWindows.h" #include "prompts/gameinfo.h" #include "prompts/DiscBrowser.h" #include "prompts/GameWindow.hpp" +#include "prompts/BannerWindow.hpp" #include "prompts/CategorySwitchPrompt.hpp" #include "prompts/CheckboxPrompt.hpp" #include "themes/CTheme.h" @@ -23,6 +25,8 @@ #include "settings/CGameStatistics.h" #include "settings/CGameSettings.h" #include "settings/GameTitles.h" +#include "SystemMenu/SystemMenuResources.h" +#include "system/IosLoader.h" #include "utils/StringTools.h" #include "utils/rockout.h" #include "utils/ShowError.h" @@ -84,6 +88,8 @@ GameBrowseMenu::GameBrowseMenu() imgarrangeList_gray = Resources::GetImageData("arrangeList_gray.png"); imgarrangeCarousel = Resources::GetImageData("arrangeCarousel.png"); imgarrangeCarousel_gray = Resources::GetImageData("arrangeCarousel_gray.png"); + imgBannerGrid = Resources::GetImageData("arrangeBannerGrid.png"); + imgBannerGrid_gray = Resources::GetImageData("arrangeBannerGrid_gray.png"); imgdvd = Resources::GetImageData("dvd.png"); imgdvd_gray = Resources::GetImageData("dvd_gray.png"); imgLock = Resources::GetImageData("lock.png"); @@ -261,6 +267,16 @@ GameBrowseMenu::GameBrowseMenu() carouselBtn = new GuiButton(carouselBtnImg_g, carouselBtnImg_g, ALIGN_LEFT, ALIGN_TOP, 0, 0, trigA, btnSoundOver, btnSoundClick2, 1, carouselBtnTT, 15, 52, 1, 3); carouselBtn->SetSelectable(false); + bannerGridBtnTT = new GuiTooltip(tr( "Display as a channel grid" )); + if (Settings.wsprompt) bannerGridBtnTT->SetWidescreen(Settings.widescreen); + bannerGridBtnTT->SetAlpha(thInt("255 - tooltip alpha")); + bannerGridBtnImg = new GuiImage(imgBannerGrid); + bannerGridBtnImg->SetWidescreen(Settings.widescreen); + bannerGridBtnImg_g = new GuiImage(imgBannerGrid_gray); + bannerGridBtnImg_g->SetWidescreen(Settings.widescreen); + bannerGridBtn = new GuiButton(bannerGridBtnImg_g, bannerGridBtnImg_g, ALIGN_LEFT, ALIGN_TOP, 0, 0, trigA, btnSoundOver, btnSoundClick2, 1, bannerGridBtnTT, 15, 52, 1, 3); + bannerGridBtn->SetSelectable(false); + lockBtnTT = new GuiTooltip(NULL); if (Settings.wsprompt) lockBtnTT->SetWidescreen(Settings.widescreen); lockBtnTT->SetAlpha(thInt("255 - tooltip alpha")); @@ -390,6 +406,8 @@ GameBrowseMenu::~GameBrowseMenu() delete imgarrangeGrid_gray; delete imgarrangeCarousel; delete imgarrangeCarousel_gray; + delete imgBannerGrid; + delete imgBannerGrid_gray; delete imgarrangeList; delete imgarrangeList_gray; delete imgdvd; @@ -431,6 +449,8 @@ GameBrowseMenu::~GameBrowseMenu() delete gridBtnImg_g; delete carouselBtnImg; delete carouselBtnImg_g; + delete bannerGridBtnImg; + delete bannerGridBtnImg_g; delete lockBtnImg; delete lockBtnImg_g; delete unlockBtnImg; @@ -464,6 +484,7 @@ GameBrowseMenu::~GameBrowseMenu() delete listBtn; delete gridBtn; delete carouselBtn; + delete bannerGridBtn; delete lockBtn; delete dvdBtn; delete categBtn; @@ -483,6 +504,7 @@ GameBrowseMenu::~GameBrowseMenu() delete listBtnTT; delete gridBtnTT; delete carouselBtnTT; + delete bannerGridBtnTT; delete lockBtnTT; delete dvdBtnTT; delete categBtnTT; @@ -513,7 +535,7 @@ int GameBrowseMenu::Execute() while(retMenu == MENU_NONE) { - usleep(1000); + usleep(50000); if (shutdown) Sys_Shutdown(); @@ -631,6 +653,9 @@ void GameBrowseMenu::ReloadBrowser() if (Settings.gameDisplay == LIST_MODE) { + //! only one image, reload it since it won't be changeable later + if(gameList.size() == 1) + LoadCover(gameList[0]); if(gameList.size() > 0) Append(gameCoverImg); DownloadBtn->SetSize(160, 224); @@ -640,31 +665,35 @@ void GameBrowseMenu::ReloadBrowser() gridBtn->SetImageOver(gridBtnImg_g); carouselBtn->SetImage(carouselBtnImg_g); carouselBtn->SetImageOver(carouselBtnImg_g); + bannerGridBtn->SetImage(bannerGridBtnImg_g); + bannerGridBtn->SetImageOver(bannerGridBtnImg_g); - favoriteBtn->SetPosition(Settings.widescreen ? thInt("230 - list layout favorite btn pos x widescreen") : thInt("188 - list layout favorite btn pos x"), + favoriteBtn->SetPosition(Settings.widescreen ? thInt("214 - list layout favorite btn pos x widescreen") : thInt("168 - list layout favorite btn pos x"), thInt("13 - list layout favorite btn pos y")); - searchBtn->SetPosition(Settings.widescreen ? thInt("262 - list layout search btn pos x widescreen") : thInt("228 - list layout search btn pos x"), + searchBtn->SetPosition(Settings.widescreen ? thInt("246 - list layout search btn pos x widescreen") : thInt("208 - list layout search btn pos x"), thInt("13 - list layout search btn pos y")); - sortBtn->SetPosition(Settings.widescreen ? thInt("294 - list layout abc/sort btn pos x widescreen") : thInt("268 - list layout abc/sort btn pos x"), + sortBtn->SetPosition(Settings.widescreen ? thInt("278 - list layout abc/sort btn pos x widescreen") : thInt("248 - list layout abc/sort btn pos x"), thInt("13 - list layout abc/sort btn pos y")); - loaderModeBtn->SetPosition(Settings.widescreen ? thInt("326 - list layout loadermode btn pos x widescreen") : thInt("308 - list layout loadermode btn pos x"), + loaderModeBtn->SetPosition(Settings.widescreen ? thInt("310 - list layout loadermode btn pos x widescreen") : thInt("288 - list layout loadermode btn pos x"), thInt("13 - list layout loadermode btn pos y")); - categBtn->SetPosition(Settings.widescreen ? thInt("358 - list layout category btn pos x widescreen") : thInt("348 - list layout category btn pos x"), + categBtn->SetPosition(Settings.widescreen ? thInt("342 - list layout category btn pos x widescreen") : thInt("328 - list layout category btn pos x"), thInt("13 - list layout category btn pos y")); - listBtn->SetPosition(Settings.widescreen ? thInt("390 - list layout list btn pos x widescreen") : thInt("388 - list layout list btn pos x"), + listBtn->SetPosition(Settings.widescreen ? thInt("374 - list layout list btn pos x widescreen") : thInt("368 - list layout list btn pos x"), thInt("13 - list layout list btn pos y")); - gridBtn->SetPosition(Settings.widescreen ? thInt("422 - list layout grid btn pos x widescreen") : thInt("428 - list layout grid btn pos x"), + gridBtn->SetPosition(Settings.widescreen ? thInt("406 - list layout grid btn pos x widescreen") : thInt("408 - list layout grid btn pos x"), thInt("13 - list layout grid btn pos y")); - carouselBtn->SetPosition(Settings.widescreen ? thInt("454 - list layout carousel btn pos x widescreen") : thInt("468 - list layout carousel btn pos x"), + carouselBtn->SetPosition(Settings.widescreen ? thInt("438 - list layout carousel btn pos x widescreen") : thInt("448 - list layout carousel btn pos x"), thInt("13 - list layout carousel btn pos y")); - lockBtn->SetPosition(Settings.widescreen ? thInt("486 - list layout lock btn pos x widescreen") : thInt("508 - list layout lock btn pos x"), + bannerGridBtn->SetPosition(Settings.widescreen ? thInt("470 - list layout bannergrid btn pos x widescreen") : thInt("488 - list bannergrid btn pos x"), + thInt("13 - list layout bannergrid btn pos y")); + lockBtn->SetPosition(Settings.widescreen ? thInt("502 - list layout lock btn pos x widescreen") : thInt("528 - list layout lock btn pos x"), thInt("13 - list layout lock btn pos y")); - dvdBtn->SetPosition(Settings.widescreen ? thInt("518 - list layout dvd btn pos x widescreen") : thInt("548 - list layout dvd btn pos x"), + dvdBtn->SetPosition(Settings.widescreen ? thInt("534 - list layout dvd btn pos x widescreen") : thInt("568 - list layout dvd btn pos x"), thInt("13 - list layout dvd btn pos y")); gameBrowser = new GuiGameList(thInt("396 - game list layout width"), thInt("280 - game list layout height"), Settings.GameListOffset); gameBrowser->SetPosition(thInt("200 - game list layout pos x"), thInt("49 - game list layout pos y")); - gameBrowser->SetAlignment(ALIGN_LEFT, ALIGN_CENTRE); + gameBrowser->SetAlignment(ALIGN_LEFT, ALIGN_CENTER); gameBrowser->SetSelectedOption(Settings.SelectedGame); //! Setup optional background image @@ -685,31 +714,35 @@ void GameBrowseMenu::ReloadBrowser() listBtn->SetImageOver(listBtnImg_g); carouselBtn->SetImage(carouselBtnImg_g); carouselBtn->SetImageOver(carouselBtnImg_g); + bannerGridBtn->SetImage(bannerGridBtnImg_g); + bannerGridBtn->SetImageOver(bannerGridBtnImg_g); - favoriteBtn->SetPosition(Settings.widescreen ? thInt("160 - grid layout favorite btn pos x widescreen") : thInt("120 - grid layout favorite btn pos x"), + favoriteBtn->SetPosition(Settings.widescreen ? thInt("144 - grid layout favorite btn pos x widescreen") : thInt("100 - grid layout favorite btn pos x"), thInt("13 - grid layout favorite btn pos y")); - searchBtn->SetPosition(Settings.widescreen ? thInt("192 - grid layout search btn pos x widescreen") : thInt("160 - grid layout search btn pos x"), + searchBtn->SetPosition(Settings.widescreen ? thInt("176 - grid layout search btn pos x widescreen") : thInt("140 - grid layout search btn pos x"), thInt("13 - grid layout search btn pos y")); - sortBtn->SetPosition(Settings.widescreen ? thInt("224 - grid layout abc/sort btn pos x widescreen") : thInt("200 - grid layout abc/sort btn pos x"), + sortBtn->SetPosition(Settings.widescreen ? thInt("208 - grid layout abc/sort btn pos x widescreen") : thInt("180 - grid layout abc/sort btn pos x"), thInt("13 - grid layout abc/sort btn pos y")); - loaderModeBtn->SetPosition(Settings.widescreen ? thInt("256 - grid layout loadermode btn pos x widescreen") : thInt("240 - grid layout loadermode btn pos x"), + loaderModeBtn->SetPosition(Settings.widescreen ? thInt("240 - grid layout loadermode btn pos x widescreen") : thInt("220 - grid layout loadermode btn pos x"), thInt("13 - grid layout loadermode btn pos y")); - categBtn->SetPosition(Settings.widescreen ? thInt("288 - grid layout category btn pos x widescreen") : thInt("280 - grid layout category btn pos x"), + categBtn->SetPosition(Settings.widescreen ? thInt("272 - grid layout category btn pos x widescreen") : thInt("260 - grid layout category btn pos x"), thInt("13 - grid layout category btn pos y")); - listBtn->SetPosition(Settings.widescreen ? thInt("320 - grid layout list btn pos x widescreen") : thInt("320 - grid layout list btn pos x"), + listBtn->SetPosition(Settings.widescreen ? thInt("304 - grid layout list btn pos x widescreen") : thInt("300 - grid layout list btn pos x"), thInt("13 - grid layout list btn pos y")); - gridBtn->SetPosition(Settings.widescreen ? thInt("352 - grid layout grid btn pos x widescreen") : thInt("360 - grid layout grid btn pos x"), + gridBtn->SetPosition(Settings.widescreen ? thInt("336 - grid layout grid btn pos x widescreen") : thInt("340 - grid layout grid btn pos x"), thInt("13 - grid layout grid btn pos y")); - carouselBtn->SetPosition(Settings.widescreen ? thInt("384 - grid layout carousel btn pos x widescreen") : thInt("400 - grid layout carousel btn pos x"), + carouselBtn->SetPosition(Settings.widescreen ? thInt("368 - grid layout carousel btn pos x widescreen") : thInt("380 - grid layout carousel btn pos x"), thInt("13 - grid layout carousel btn pos y")); - lockBtn->SetPosition(Settings.widescreen ? thInt("416 - grid layout lock btn pos x widescreen") : thInt("440 - grid layout lock btn pos x"), + bannerGridBtn->SetPosition(Settings.widescreen ? thInt("400 - grid layout bannergrid btn pos x widescreen") : thInt("420 - grid bannergrid btn pos x"), + thInt("13 - grid layout bannergrid btn pos y")); + lockBtn->SetPosition(Settings.widescreen ? thInt("432 - grid layout lock btn pos x widescreen") : thInt("460 - grid layout lock btn pos x"), thInt("13 - grid layout lock btn pos y")); - dvdBtn->SetPosition(Settings.widescreen ? thInt("448 - grid layout dvd btn pos x widescreen") : thInt("480 - grid layout dvd btn pos x"), + dvdBtn->SetPosition(Settings.widescreen ? thInt("464 - grid layout dvd btn pos x widescreen") : thInt("500 - grid layout dvd btn pos x"), thInt("13 - grid layout dvd btn pos y")); gameBrowser = new GuiGameGrid(thInt("640 - game grid layout width"), thInt("400 - game grid layout height"), Settings.theme_path, Settings.GameListOffset); gameBrowser->SetPosition(thInt("0 - game grid layout pos x"), thInt("20 - game grid layout pos y")); - gameBrowser->SetAlignment(ALIGN_LEFT, ALIGN_CENTRE); + gameBrowser->SetAlignment(ALIGN_LEFT, ALIGN_CENTER); //! Setup optional background image if(!gridBackground) @@ -729,31 +762,35 @@ void GameBrowseMenu::ReloadBrowser() listBtn->SetImageOver(listBtnImg_g); gridBtn->SetImage(gridBtnImg_g); gridBtn->SetImageOver(gridBtnImg_g); + bannerGridBtn->SetImage(bannerGridBtnImg_g); + bannerGridBtn->SetImageOver(bannerGridBtnImg_g); - favoriteBtn->SetPosition(Settings.widescreen ? thInt("160 - carousel layout favorite btn pos x widescreen") : thInt("120 - carousel layout favorite btn pos x"), + favoriteBtn->SetPosition(Settings.widescreen ? thInt("144 - carousel layout favorite btn pos x widescreen") : thInt("100 - carousel layout favorite btn pos x"), thInt("13 - carousel layout favorite btn pos y")); - searchBtn->SetPosition(Settings.widescreen ? thInt("192 - carousel layout search btn pos x widescreen") : thInt("160 - carousel layout search btn pos x"), + searchBtn->SetPosition(Settings.widescreen ? thInt("176 - carousel layout search btn pos x widescreen") : thInt("140 - carousel layout search btn pos x"), thInt("13 - carousel layout search btn pos y")); - sortBtn->SetPosition(Settings.widescreen ? thInt("224 - carousel layout abc/sort btn pos x widescreen") : thInt("200 - carousel layout abc/sort btn pos x"), + sortBtn->SetPosition(Settings.widescreen ? thInt("208 - carousel layout abc/sort btn pos x widescreen") : thInt("180 - carousel layout abc/sort btn pos x"), thInt("13 - carousel layout abc/sort btn pos y")); - loaderModeBtn->SetPosition(Settings.widescreen ? thInt("256 - carousel layout loadermode btn pos x widescreen") : thInt("240 - carousel layout loadermode btn pos x"), + loaderModeBtn->SetPosition(Settings.widescreen ? thInt("240 - carousel layout loadermode btn pos x widescreen") : thInt("220 - carousel layout loadermode btn pos x"), thInt("13 - carousel layout loadermode btn pos y")); - categBtn->SetPosition(Settings.widescreen ? thInt("288 - carousel layout category btn pos x widescreen") : thInt("280 - carousel layout category btn pos x"), + categBtn->SetPosition(Settings.widescreen ? thInt("272 - carousel layout category btn pos x widescreen") : thInt("260 - carousel layout category btn pos x"), thInt("13 - carousel layout category btn pos y")); - listBtn->SetPosition(Settings.widescreen ? thInt("320 - carousel layout list btn pos x widescreen") : thInt("320 - carousel layout list btn pos x"), + listBtn->SetPosition(Settings.widescreen ? thInt("304 - carousel layout list btn pos x widescreen") : thInt("300 - carousel layout list btn pos x"), thInt("13 - carousel layout list btn pos y")); - gridBtn->SetPosition(Settings.widescreen ? thInt("352 - carousel layout grid btn pos x widescreen") : thInt("360 - carousel layout grid btn pos x"), + gridBtn->SetPosition(Settings.widescreen ? thInt("336 - carousel layout grid btn pos x widescreen") : thInt("340 - carousel layout grid btn pos x"), thInt("13 - carousel layout grid btn pos y")); - carouselBtn->SetPosition(Settings.widescreen ? thInt("384 - carousel layout carousel btn pos x widescreen") : thInt("400 - carousel layout carousel btn pos x"), + carouselBtn->SetPosition(Settings.widescreen ? thInt("368 - carousel layout carousel btn pos x widescreen") : thInt("380 - carousel layout carousel btn pos x"), thInt("13 - carousel layout carousel btn pos y")); - lockBtn->SetPosition(Settings.widescreen ? thInt("416 - carousel layout lock btn pos x widescreen") : thInt("440 - carousel layout lock btn pos x"), + bannerGridBtn->SetPosition(Settings.widescreen ? thInt("400 - carousel layout bannergrid btn pos x widescreen") : thInt("420 - carousel bannergrid btn pos x"), + thInt("13 - carousel layout bannergrid btn pos y")); + lockBtn->SetPosition(Settings.widescreen ? thInt("432 - carousel layout lock btn pos x widescreen") : thInt("460 - carousel layout lock btn pos x"), thInt("13 - carousel layout lock btn pos y")); - dvdBtn->SetPosition(Settings.widescreen ? thInt("448 - carousel layout dvd btn pos x widescreen") : thInt("480 - carousel layout dvd btn pos x"), + dvdBtn->SetPosition(Settings.widescreen ? thInt("464 - carousel layout dvd btn pos x widescreen") : thInt("500 - carousel layout dvd btn pos x"), thInt("13 - carousel layout dvd btn pos y")); gameBrowser = new GuiGameCarousel(thInt("640 - game carousel layout width"), thInt("400 - game carousel layout height"), Settings.theme_path, Settings.GameListOffset); gameBrowser->SetPosition(thInt("0 - game carousel layout pos x"), thInt("-20 - game carousel layout pos y")); - gameBrowser->SetAlignment(ALIGN_LEFT, ALIGN_CENTRE); + gameBrowser->SetAlignment(ALIGN_LEFT, ALIGN_CENTER); //! Setup optional background image if(!carouselBackground) @@ -763,7 +800,45 @@ void GameBrowseMenu::ReloadBrowser() else bgImg->SetImage(background); } + else if(Settings.gameDisplay == BANNERGRID_MODE) + { + DownloadBtn->SetSize(0, 0); + UpdateGameInfoText(NULL); + bannerGridBtn->SetImage(bannerGridBtnImg); + bannerGridBtn->SetImageOver(bannerGridBtnImg); + listBtn->SetImage(listBtnImg_g); + listBtn->SetImageOver(listBtnImg_g); + gridBtn->SetImage(gridBtnImg_g); + gridBtn->SetImageOver(gridBtnImg_g); + carouselBtn->SetImage(carouselBtnImg_g); + carouselBtn->SetImageOver(carouselBtnImg_g); + favoriteBtn->SetPosition(Settings.widescreen ? thInt("144 - bannergrid layout favorite btn pos x widescreen") : thInt("100 - bannergrid layout favorite btn pos x"), + thInt("13 - bannergrid layout favorite btn pos y")); + searchBtn->SetPosition(Settings.widescreen ? thInt("176 - bannergrid layout search btn pos x widescreen") : thInt("140 - bannergrid layout search btn pos x"), + thInt("13 - bannergrid layout search btn pos y")); + sortBtn->SetPosition(Settings.widescreen ? thInt("208 - bannergrid layout abc/sort btn pos x widescreen") : thInt("180 - bannergrid layout abc/sort btn pos x"), + thInt("13 - bannergrid layout abc/sort btn pos y")); + loaderModeBtn->SetPosition(Settings.widescreen ? thInt("240 - bannergrid layout loadermode btn pos x widescreen") : thInt("220 - bannergrid layout loadermode btn pos x"), + thInt("13 - bannergrid layout loadermode btn pos y")); + categBtn->SetPosition(Settings.widescreen ? thInt("272 - bannergrid layout category btn pos x widescreen") : thInt("260 - bannergrid layout category btn pos x"), + thInt("13 - bannergrid layout category btn pos y")); + listBtn->SetPosition(Settings.widescreen ? thInt("304 - bannergrid layout list btn pos x widescreen") : thInt("300 - bannergrid layout list btn pos x"), + thInt("13 - bannergrid layout list btn pos y")); + gridBtn->SetPosition(Settings.widescreen ? thInt("336 - bannergrid layout grid btn pos x widescreen") : thInt("340 - bannergrid layout grid btn pos x"), + thInt("13 - bannergrid layout grid btn pos y")); + carouselBtn->SetPosition(Settings.widescreen ? thInt("368 - bannergrid layout carousel btn pos x widescreen") : thInt("380 - bannergrid layout carousel btn pos x"), + thInt("13 - bannergrid layout carousel btn pos y")); + bannerGridBtn->SetPosition(Settings.widescreen ? thInt("400 - bannergrid layout bannergrid btn pos x widescreen") : thInt("420 - bannergrid bannergrid btn pos x"), + thInt("13 - bannergrid layout bannergrid btn pos y")); + lockBtn->SetPosition(Settings.widescreen ? thInt("432 - bannergrid layout lock btn pos x widescreen") : thInt("460 - bannergrid layout lock btn pos x"), + thInt("13 - bannergrid layout lock btn pos y")); + dvdBtn->SetPosition(Settings.widescreen ? thInt("464 - bannergrid layout dvd btn pos x widescreen") : thInt("500 - bannergrid layout dvd btn pos x"), + thInt("13 - bannergrid layout dvd btn pos y")); + + gameBrowser = new GuiBannerGrid(Settings.GameListOffset + Settings.SelectedGame); + gameBrowser->SetPosition(thInt("0 - game bannergrid layout pos x"), thInt("0 - game bannergrid layout pos y")); + } if (thInt("1 - show hdd info: 1 for on and 0 for off") == 1) //force show hdd info Append(usedSpaceTxt); @@ -785,7 +860,7 @@ void GameBrowseMenu::ReloadBrowser() if (Settings.godmode || !(Settings.ParentalBlocks & BLOCK_COVER_DOWNLOADS)) Append(DownloadBtn); - if (Settings.godmode || !(Settings.ParentalBlocks & BLOCK_GAMEID_CHANGE)) + if ((Settings.gameDisplay == LIST_MODE) && (Settings.godmode || !(Settings.ParentalBlocks & BLOCK_GAMEID_CHANGE))) Append(idBtn); Append(favoriteBtn); @@ -796,6 +871,7 @@ void GameBrowseMenu::ReloadBrowser() Append(gridBtn); Append(loaderModeBtn); Append(carouselBtn); + Append(bannerGridBtn); Append(lockBtn); Append(dvdBtn); @@ -853,7 +929,7 @@ int GameBrowseMenu::MainLoop() int choice = WindowPrompt(0, fmt("%s %sGameList ?", tr( "Save Game List to" ), Settings.update_path), "TXT", "CSV", tr( "Back" )); if (choice) { - if (save_gamelist(choice - 1)) + if (save_gamelist(choice == 2)) WindowPrompt(0, tr( "Saved" ), tr( "OK" )); else WindowPrompt(tr( "Error" ), tr( "Could not save." ), tr( "OK" )); @@ -905,21 +981,24 @@ int GameBrowseMenu::MainLoop() else if (sdcardBtn->GetState() == STATE_CLICKED) { gprintf("\tsdCardBtn Clicked\n"); - HaltGui(); - bgMusic->Pause(); - Settings.Save(); - DeviceHandler::Instance()->UnMountSD(); - DeviceHandler::Instance()->MountSD(); - gprintf("\tLoading config...%s\n", Settings.Load() ? "done" : "failed"); - gprintf("\tLoading language...%s\n", Settings.LoadLanguage(Settings.language_path, CONSOLE_DEFAULT) ? "done" : "failed"); - gprintf("\tLoading game settings...%s\n", GameSettings.Load(Settings.ConfigPath) ? "done" : "failed"); - gprintf("\tLoading game statistics...%s\n", GameStatistics.Load(Settings.ConfigPath) ? "done" : "failed"); - gprintf("\tLoading font...%s\n", Theme::LoadFont(Settings.theme_path) ? "done" : "failed (using default)"); - gprintf("\tLoading theme...%s\n", Theme::Load(Settings.theme) ? "done" : "failed (using default)"); - bgMusic->Resume(); - gameList.FilterList(); - ReloadBrowser(); - ResumeGui(); + if(WindowPrompt(tr("Are you sure you want to remount SD?"), tr("The application might crash if there is currently a read/write access to the SD card!"), tr("Yes"), tr("Cancel"))) + { + HaltGui(); + bgMusic->Pause(); + Settings.Save(); + DeviceHandler::Instance()->UnMountSD(); + DeviceHandler::Instance()->MountSD(); + gprintf("\tLoading config...%s\n", Settings.Load() ? "done" : "failed"); + gprintf("\tLoading language...%s\n", Settings.LoadLanguage(Settings.language_path, CONSOLE_DEFAULT) ? "done" : "failed"); + gprintf("\tLoading game settings...%s\n", GameSettings.Load(Settings.ConfigPath) ? "done" : "failed"); + gprintf("\tLoading game statistics...%s\n", GameStatistics.Load(Settings.ConfigPath) ? "done" : "failed"); + gprintf("\tLoading font...%s\n", Theme::LoadFont(Settings.theme_path) ? "done" : "failed (using default)"); + gprintf("\tLoading theme...%s\n", Theme::Load(Settings.theme) ? "done" : "failed (using default)"); + bgMusic->Resume(); + gameList.FilterList(); + ReloadBrowser(); + ResumeGui(); + } sdcardBtn->ResetState(); } @@ -1079,6 +1158,22 @@ int GameBrowseMenu::MainLoop() carouselBtn->ResetState(); } + else if (bannerGridBtn->GetState() == STATE_CLICKED) + { + gprintf("\tbannerGridBtn Clicked\n"); + if(!SystemMenuResources::Instance()->IsLoaded()) { + WindowPrompt(tr( "Error:" ), tr( "Banner grid layout is only available with AHBPROT! Please consider installing new HBC version." ), tr( "OK" )); + bannerGridBtn->ResetState(); + return MENU_NONE; + } + if (Settings.gameDisplay != BANNERGRID_MODE) + { + Settings.gameDisplay = BANNERGRID_MODE; + ReloadBrowser(); + } + bannerGridBtn->ResetState(); + } + else if (homebrewBtn->GetState() == STATE_CLICKED) { gprintf("\thomebrewBtn Clicked\n"); @@ -1172,7 +1267,7 @@ int GameBrowseMenu::MainLoop() return returnMenu; } - int choice = CheckboxWindow(tr( "Select titles sources." ), 0, tr( "Wii Games" ), tr("GC Games"), tr( "Nand Channels" ), tr("EmuNand Channels"), 0, 0, Settings.LoaderMode); + int choice = CheckboxWindow(tr( "Select titles sources." ), 0, tr( "Wii Games" ), tr( "Nand Channels" ), tr("EmuNand Channels"), tr("GC Games"), 0, 0, Settings.LoaderMode); if(choice != CheckedNone && choice != Settings.LoaderMode) { Settings.LoaderMode = choice; @@ -1407,11 +1502,6 @@ void GameBrowseMenu::UpdateGameInfoText(const u8 * gameId) int GameBrowseMenu::OpenClickedGame(struct discHdr *header) { int choice = -1; - int gameSelected = GetSelectedGame(); - if(header == dvdheader) - gameSelected = 0; - else if(gameSelected < 0 || gameSelected >= gameList.size()) - return -1; if (searchBar) { @@ -1422,6 +1512,7 @@ int GameBrowseMenu::OpenClickedGame(struct discHdr *header) rockout(header); + SetAllowDim(false); SetState(STATE_DISABLED); if(gameBrowser) gameBrowser->SetState(STATE_DISABLED); @@ -1432,16 +1523,20 @@ int GameBrowseMenu::OpenClickedGame(struct discHdr *header) if (Settings.quickboot) { //quickboot game GameWindow::BootGame(header); } - else + else if(Settings.GameWindowMode == GAMEWINDOW_BANNER) { - GameWindow * GamePrompt = new GameWindow(gameSelected, dvdheader == header ? dvdheader : 0); - GamePrompt->SetGameBrowseMenu(this); - mainWindow->Append(GamePrompt); + BannerWindow GamePrompt(this, header); + mainWindow->Append(&GamePrompt); - choice = GamePrompt->Run(); - gameSelected = GamePrompt->GetSelectedGame(); + choice = GamePrompt.Run(); + } + else if(Settings.GameWindowMode == GAMEWINDOW_DISC) + { + SetAllowDim(true); + GameWindow GamePrompt(this, header); + mainWindow->Append(&GamePrompt); - delete GamePrompt; + choice = GamePrompt.Run(); } if (choice == 1) @@ -1459,6 +1554,8 @@ int GameBrowseMenu::OpenClickedGame(struct discHdr *header) rockout(0); SetState(STATE_DEFAULT); + SetAllowDim(true); + if(gameBrowser) gameBrowser->SetState(STATE_DEFAULT); @@ -1501,7 +1598,7 @@ void GameBrowseMenu::UpdateCallback(void * e) void GameBrowseMenu::UpdateFreeSpace(void * arg) { char spaceinfo[30]; - memset(spaceinfo, 0, 30); + spaceinfo[0] = 0; if(Settings.ShowFreeSpace) { @@ -1512,11 +1609,11 @@ void GameBrowseMenu::UpdateFreeSpace(void * arg) if (strcmp(Settings.db_language, "JA") == 0) { // needs to be "total...used" for Japanese - sprintf(spaceinfo, "%.2fGB %s %.2fGB %s", (freespace + used), tr( "of" ), freespace, tr( "free" )); + snprintf(spaceinfo, sizeof(spaceinfo), "%.2fGB %s %.2fGB %s", (freespace + used), tr( "of" ), freespace, tr( "free" )); } else { - sprintf(spaceinfo, "%.2fGB %s %.2fGB %s", freespace, tr( "of" ), (freespace + used), tr( "free" )); + snprintf(spaceinfo, sizeof(spaceinfo), "%.2fGB %s %.2fGB %s", freespace, tr( "of" ), (freespace + used), tr( "free" )); } } } diff --git a/source/menu/GameBrowseMenu.hpp b/source/menu/GameBrowseMenu.hpp index b2c0bca1..55c58c38 100644 --- a/source/menu/GameBrowseMenu.hpp +++ b/source/menu/GameBrowseMenu.hpp @@ -12,6 +12,7 @@ class GameBrowseMenu : public GuiWindow virtual ~GameBrowseMenu(); static int Execute(); void ReloadBrowser(); + GuiGameBrowser *GetGameBrowser() { return gameBrowser; } private: int MainLoop(); int OpenClickedGame(struct discHdr *header); @@ -66,6 +67,8 @@ class GameBrowseMenu : public GuiWindow GuiImageData * imgarrangeList_gray; GuiImageData * imgdvd; GuiImageData * imgdvd_gray; + GuiImageData * imgBannerGrid; + GuiImageData * imgBannerGrid_gray; GuiImageData * imgLock; GuiImageData * imgLock_gray; GuiImageData * imgUnlock; @@ -103,6 +106,8 @@ class GameBrowseMenu : public GuiWindow GuiImage * gridBtnImg_g; GuiImage * carouselBtnImg; GuiImage * carouselBtnImg_g; + GuiImage * bannerGridBtnImg; + GuiImage * bannerGridBtnImg_g; GuiImage * lockBtnImg; GuiImage * lockBtnImg_g; GuiImage * unlockBtnImg; @@ -136,6 +141,7 @@ class GameBrowseMenu : public GuiWindow GuiButton * listBtn; GuiButton * gridBtn; GuiButton * carouselBtn; + GuiButton * bannerGridBtn; GuiButton * lockBtn; GuiButton * dvdBtn; GuiButton * categBtn; @@ -155,6 +161,7 @@ class GameBrowseMenu : public GuiWindow GuiTooltip * listBtnTT; GuiTooltip * gridBtnTT; GuiTooltip * carouselBtnTT; + GuiTooltip * bannerGridBtnTT; GuiTooltip * lockBtnTT; GuiTooltip * dvdBtnTT; GuiTooltip * categBtnTT; diff --git a/source/menu/WDMMenu.cpp b/source/menu/WDMMenu.cpp index c7a80c43..09367ae1 100644 --- a/source/menu/WDMMenu.cpp +++ b/source/menu/WDMMenu.cpp @@ -65,7 +65,7 @@ WDMMenu::WDMMenu(const struct discHdr * header) optionBrowser = new GuiOptionBrowser(396, 280, Options, "bg_options_settings.png"); optionBrowser->SetPosition(0, 90); - optionBrowser->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + optionBrowser->SetAlignment(ALIGN_CENTER, ALIGN_TOP); Append(optionBrowser); SetEffect(EFFECT_FADE, 50); diff --git a/source/menu/menu_install.cpp b/source/menu/menu_install.cpp index 2b95a116..8b85f31b 100644 --- a/source/menu/menu_install.cpp +++ b/source/menu/menu_install.cpp @@ -1,29 +1,169 @@ +#include #include "menus.h" +#include "GameCube/GCGames.h" +#include "GameCube/GCDumper.hpp" #include "usbloader/usbstorage2.h" #include "usbloader/wbfs.h" #include "usbloader/disc.h" #include "usbloader/GameList.h" #include "prompts/ProgressWindow.h" +#include "prompts/GCMultiDiscMenu.h" #include "themes/CTheme.h" #include "utils/tools.h" +#define WII_MAGIC 0x5D1C9EA3 + extern int install_abort_signal; float gamesize = 0.0f; +/**************************************************************************** + * MenuGCInstall + ***************************************************************************/ +int MenuGCInstall() +{ + GCDumper gcDumper; + if(gcDumper.ReadDiscHeader() < 0) + { + WindowPrompt(tr("Error"), tr("Error reading disc"), tr("OK")); + return MENU_DISCLIST; + } + + std::vector installGames; + + if(gcDumper.GetDiscHeaders().size() == 0) + { + WindowPrompt(tr("Error"), tr("No games found on the disc"), tr("OK")); + return MENU_DISCLIST; + } + else if(gcDumper.GetDiscHeaders().size() > 1) + { + //! Multi game disc, lets ask the user which games to install + GCMultiDiscMenu gcMenu(gcDumper.GetDiscHeaders()); + gcMenu.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); + gcMenu.SetEffect(EFFECT_FADE, 20); + mainWindow->SetState(STATE_DISABLED); + mainWindow->Append(&gcMenu); + + int choice = gcMenu.ShowSelection(); + + gcMenu.SetEffect(EFFECT_FADE, -20); + while(gcMenu.GetEffect() > 0) usleep(1000); + + mainWindow->Remove(&gcMenu); + mainWindow->SetState(STATE_DEFAULT); + + installGames = gcMenu.GetSelectedGames(); + + if(choice == 0 || installGames.size() == 0) + return MENU_DISCLIST; + } + else + { + if(!WindowPrompt(tr( "Continue to install game?" ), gcDumper.GetDiscHeaders().at(0).title, tr("Yes"), tr( "Cancel" ))) + return MENU_DISCLIST; + + installGames.push_back(0); + } + + //! setup dumper settings + gcDumper.SetCompressed(Settings.GCInstallCompressed); + gcDumper.SetCompressed(Settings.GCInstallAligned); + + //! If a different main path then the SD path is selected ask where to install + int destination = 1; + if(strcmp(Settings.GameCubePath, Settings.GameCubeSDPath) != 0) + destination = WindowPrompt(tr("Where should the game be installed to?"), 0, tr("Main Path"), tr("SD Path"), tr("Cancel")); + if(!destination) + return MENU_DISCLIST; + + const char *InstallPath = destination == 1 ? Settings.GameCubePath : Settings.GameCubeSDPath; + + //! Start of install process, enable wii slot light + wiilight(1); + + int result = 0; + int installed_games = 0; + + for(u32 i = 0; i < installGames.size(); ++i) + { + //! check if the game is already installed on SD/USB + if(GCGames::Instance()->IsInstalled((char *)gcDumper.GetDiscHeaders().at(installGames[i]).id)) + { + WindowPrompt(tr("Game is already installed:"), gcDumper.GetDiscHeaders().at(installGames[i]).title, tr("OK")); + if(i+1 < installGames.size()) { + continue; + } + else if(i == 0) + { + result = MENU_DISCLIST; + break; + } + } + + // game is not yet installed so let's install it + int ret = gcDumper.InstallGame(InstallPath, installGames[i]); + if(ret >= 0) { + //! success + installed_games++; + } + else if(ret == PROGRESS_CANCELED) + { + result = MENU_DISCLIST; + break; + } + else if(ret < 0) + { + //! Error occured, ask the user what to do if there are more games to install + if(i+1 < installGames.size()) + { + if(!WindowPrompt(tr( "Install Error!" ), tr("Do you want to continue with next game?"), tr("Yes"), tr( "Cancel" ))) + { + result = MENU_DISCLIST; + break; + } + } + else + { + WindowPrompt(tr( "Install Error!" ), 0, tr( "Back" )); + result = MENU_DISCLIST; + break; + } + } + } + + wiilight(0); + GCGames::Instance()->LoadAllGames(); + + //! no game was installed so don't show successfully installed prompt + if(installed_games == 0) + return result; + + bgMusic->Pause(); + GuiSound instsuccess(Resources::GetFile("success.ogg"), Resources::GetFileSize("success.ogg"), Settings.sfxvolume); + instsuccess.SetVolume(Settings.sfxvolume); + instsuccess.SetLoop(0); + instsuccess.Play(); + char gamesTxt[20]; + snprintf(gamesTxt, sizeof(gamesTxt), "%i %s", installed_games, tr("Games")); + WindowPrompt(tr("Successfully installed:"), installGames.size() > 1 ? gamesTxt : gcDumper.GetDiscHeaders().at(installGames[0]).title, tr( "OK" )); + instsuccess.Stop(); + bgMusic->Resume(); + + return MENU_DISCLIST; +} + /**************************************************************************** * MenuInstall ***************************************************************************/ - int MenuInstall() { - gprintf("\nMenuInstall()"); + gprintf("\nMenuInstall()\n"); static struct discHdr headerdisc ATTRIBUTE_ALIGN( 32 ); Disc_SetUSB(NULL); int ret, choice = 0; - char name[200]; ret = DiscWait(tr( "Insert Disk" ), tr( "Waiting..." ), tr( "Cancel" ), 0, 0); if (ret < 0) @@ -38,24 +178,28 @@ int MenuInstall() return MENU_DISCLIST; } - ret = Disc_IsWii(); - if (ret < 0) - { - choice = WindowPrompt(tr( "Not a Wii Disc" ), tr( "Insert a Wii Disc!" ), tr( "OK" ), tr( "Back" )); + memset(&headerdisc, 0, sizeof(struct discHdr)); + Disc_ReadHeader(&headerdisc); + + if ((headerdisc.magic != WII_MAGIC) && (headerdisc.gc_magic != GCGames::MAGIC)) + { + choice = WindowPrompt(tr( "Not a Wii or a Game Cube Disc" ), tr( "Insert a Wii or a Game Cube Disc!" ), tr( "OK" ), tr( "Back" )); if (choice == 1) return MenuInstall(); else return MENU_DISCLIST; } - Disc_ReadHeader(&headerdisc); - snprintf(name, sizeof(name), "%s", headerdisc.title); + if(headerdisc.gc_magic == GCGames::MAGIC) + { + return MenuGCInstall(); + } ret = WBFS_CheckGame(headerdisc.id); if (ret) { - WindowPrompt(tr( "Game is already installed:" ), name, tr( "Back" )); + WindowPrompt(tr( "Game is already installed:" ), headerdisc.title, tr( "Back" )); return MENU_DISCLIST; } @@ -64,9 +208,8 @@ int MenuInstall() WBFS_DiskSpace(&used, &freespace); gamesize = (float) WBFS_EstimeGameSize(); - char gametxt[50]; - - sprintf(gametxt, "%s : %.2fGB", name, gamesize/GB_SIZE); + char gametxt[strlen(headerdisc.title) + 16]; + snprintf(gametxt, sizeof(gametxt), "%s : %.2fGB", headerdisc.title, gamesize/GB_SIZE); wiilight(1); choice = WindowPrompt(tr( "Continue to install game?" ), gametxt, tr( "OK" ), tr( "Cancel" )); @@ -83,7 +226,7 @@ int MenuInstall() } else { - StartProgress(gametxt, name, 0, true, true); + StartProgress(gametxt, headerdisc.title, 0, true, true); ProgressCancelEnable(true); ret = WBFS_AddGame(); ProgressCancelEnable(false); @@ -99,18 +242,16 @@ int MenuInstall() } else { - ShowProgress(tr("Install finished"), name, tr("Reloading game list now, please wait..."), gamesize, gamesize, true, true); + ShowProgress(tr("Install finished"), headerdisc.title, tr("Reloading game list now, please wait..."), gamesize, gamesize, true, true); gameList.ReadGameList(); //get the entries again gameList.FilterList(); - GuiSound * instsuccess = NULL; bgMusic->Pause(); - instsuccess = new GuiSound(Resources::GetFile("success.ogg"), Resources::GetFileSize("success.ogg"), Settings.sfxvolume); - instsuccess->SetVolume(Settings.sfxvolume); - instsuccess->SetLoop(0); - instsuccess->Play(); - WindowPrompt(tr( "Successfully installed:" ), name, tr( "OK" )); - instsuccess->Stop(); - delete instsuccess; + GuiSound instsuccess(Resources::GetFile("success.ogg"), Resources::GetFileSize("success.ogg"), Settings.sfxvolume); + instsuccess.SetVolume(Settings.sfxvolume); + instsuccess.SetLoop(0); + instsuccess.Play(); + WindowPrompt(tr( "Successfully installed:" ), headerdisc.title, tr( "OK" )); + instsuccess.Stop(); bgMusic->Resume(); } } diff --git a/source/menu/menu_partition_selection.cpp b/source/menu/menu_partition_selection.cpp index 44605d10..be88573b 100644 --- a/source/menu/menu_partition_selection.cpp +++ b/source/menu/menu_partition_selection.cpp @@ -73,7 +73,7 @@ int SelectPartitionMenu() GuiOptionBrowser optionBrowser(396, 280, &options, "bg_options_settings.png"); optionBrowser.SetPosition(0, 40); - optionBrowser.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + optionBrowser.SetAlignment(ALIGN_CENTER, ALIGN_TOP); HaltGui(); GuiWindow w(screenwidth, screenheight); diff --git a/source/mload/modules/ehcmodule_5.c b/source/mload/modules/ehcmodule_5.c index 8161ed9e..d87302d5 100644 --- a/source/mload/modules/ehcmodule_5.c +++ b/source/mload/modules/ehcmodule_5.c @@ -1,944 +1,977 @@ -#define size_ehcmodule_5 26234 +#define size_ehcmodule_5 27134 -unsigned char ehcmodule_5[26234] __attribute__((aligned (32)))={ - 127, 69, 76, 70, 1, 2, 1, 97, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 40, 0, 0, 0, 1, 19, 112, 0, 0, 0, 0, 0, 52, 0, 0, 0, - 0, 0, 0, 6, 6, 0, 52, 0, 32, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, - 0, 0, 0, 160, 0, 240, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 212, 0, 0, 0, 160, 0, 0, 0, 160, 0, 0, 0, 52, 0, 0, 0, - 52, 0, 240, 0, 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 0, 0, 0, 212, 0, 240, 0, - 0, 0, 0, 64, 0, 0, 0, 0, 1, 0, 0, 1, 8, 19, 112, 0, 0, 19, 112, 0, 0, 0, 0, 90, 208, 0, 0, 90, 208, 0, 240, 0, 5, 0, - 0, 0, 4, 0, 0, 0, 1, 0, 0, 91, 216, 19, 112, 96, 0, 19, 112, 96, 0, 0, 0, 10, 162, 0, 2, 153, 40, 0, 240, 0, 6, 0, 0, - 0, 4, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 6, 0, 0, 0, 11, 0, 0, 0, 4, 0, 0, 0, 9, 19, 112, 0, 0, 0, 0, 0, 125, 0, 0, 0, - 120, 0, 0, 0, 126, 0, 0, 48, 0, 0, 0, 0, 127, 19, 114, 247, 40, 227, 160, 0, 0, 227, 160, 16, 0, 229, 159, 49, 0, 225, - 47, 255, 19, 229, 159, 192, 252, 229, 156, 192, 0, 225, 160, 0, 0, 231, 156, 193, 11, 225, 160, 0, 0, 225, 47, 255, - 28, 225, 160, 0, 0, 225, 160, 0, 0, 227, 160, 176, 63, 234, 255, 255, 245, 225, 160, 0, 0, 225, 160, 0, 0, 227, 160, - 176, 64, 234, 255, 255, 241, 227, 160, 0, 0, 238, 7, 15, 21, 225, 47, 255, 30, 225, 160, 0, 0, 225, 160, 0, 0, 225, - 160, 0, 0, 227, 24, 0, 16, 10, 0, 0, 18, 227, 200, 128, 16, 227, 160, 32, 16, 229, 135, 32, 0, 225, 160, 0, 0, 225, - 160, 32, 13, 225, 160, 0, 0, 229, 159, 208, 144, 225, 160, 0, 0, 233, 45, 95, 254, 225, 160, 0, 0, 235, 0, 0, 15, 232, - 189, 95, 254, 225, 160, 0, 0, 225, 160, 208, 2, 227, 16, 0, 1, 10, 0, 0, 2, 225, 160, 0, 0, 227, 160, 0, 4, 235, 0, - 0, 5, 227, 24, 0, 1, 10, 0, 0, 1, 229, 159, 240, 88, 225, 160, 0, 0, 229, 159, 240, 84, 225, 160, 0, 0, 229, 159, 240, - 80, 225, 160, 0, 0, 229, 159, 32, 76, 225, 47, 255, 18, 225, 160, 0, 0, 238, 19, 15, 16, 225, 47, 255, 30, 225, 160, - 0, 0, 225, 160, 0, 0, 238, 3, 15, 16, 225, 47, 255, 30, 225, 160, 0, 0, 225, 160, 0, 0, 69, 72, 67, 95, 67, 70, 71, - 0, 18, 52, 0, 1, 0, 0, 0, 0, 19, 112, 16, 157, 19, 114, 195, 128, 19, 114, 249, 40, 255, 255, 30, 128, 255, 255, 30, - 156, 255, 255, 29, 68, 19, 112, 13, 61, 225, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 0, 40, - 2, 208, 16, 40, 2, 216, 4, 40, 0, 208, 7, 40, 1, 209, 19, 224, 7, 40, 16, 208, 12, 40, 17, 209, 14, 224, 11, 75, 8, - 104, 24, 224, 11, 72, 7, 224, 9, 75, 7, 34, 1, 96, 26, 32, 0, 224, 4, 72, 6, 224, 2, 72, 6, 224, 0, 72, 6, 188, 2, 71, - 8, 19, 112, 96, 48, 19, 112, 73, 29, 19, 112, 106, 200, 19, 112, 66, 25, 19, 112, 67, 97, 255, 255, 253, 102, 181, 112, - 28, 12, 28, 5, 240, 5, 252, 105, 28, 6, 32, 1, 66, 64, 240, 5, 252, 88, 14, 43, 115, 35, 12, 43, 115, 99, 10, 43, 115, - 163, 124, 34, 124, 99, 6, 18, 4, 27, 67, 19, 124, 162, 115, 229, 2, 18, 67, 19, 124, 226, 6, 45, 67, 19, 28, 32, 96, - 29, 48, 12, 33, 4, 240, 5, 252, 88, 124, 35, 124, 96, 6, 27, 4, 0, 67, 24, 124, 163, 33, 4, 2, 27, 67, 24, 124, 227, - 67, 24, 240, 5, 252, 75, 28, 48, 240, 5, 252, 48, 32, 0, 188, 112, 188, 2, 71, 8, 181, 8, 75, 12, 28, 1, 34, 32, 104, - 24, 240, 5, 252, 81, 40, 0, 209, 13, 72, 9, 240, 5, 252, 40, 240, 4, 252, 99, 32, 200, 240, 0, 255, 33, 240, 4, 252, - 104, 32, 200, 240, 0, 255, 28, 231, 244, 188, 8, 188, 2, 71, 8, 19, 112, 96, 48, 19, 112, 96, 84, 181, 8, 75, 4, 28, - 1, 104, 24, 240, 5, 251, 252, 188, 8, 188, 1, 71, 0, 70, 192, 19, 112, 96, 48, 181, 240, 176, 139, 144, 4, 145, 5, 28, - 8, 28, 17, 28, 23, 146, 7, 240, 5, 252, 8, 76, 129, 104, 35, 43, 0, 209, 0, 224, 241, 168, 9, 240, 3, 254, 8, 75, 126, - 96, 24, 40, 0, 208, 4, 35, 128, 154, 9, 1, 27, 66, 154, 208, 4, 35, 1, 74, 122, 66, 91, 96, 19, 224, 224, 78, 121, 72, - 122, 104, 51, 24, 26, 42, 0, 208, 104, 43, 0, 219, 102, 75, 116, 104, 26, 28, 81, 209, 98, 28, 53, 75, 112, 104, 48, - 104, 28, 33, 31, 34, 16, 52, 31, 24, 18, 67, 140, 146, 2, 33, 16, 28, 34, 240, 3, 255, 166, 40, 0, 209, 0, 224, 195, - 120, 35, 43, 67, 209, 11, 120, 99, 43, 73, 209, 8, 120, 163, 43, 83, 209, 5, 120, 227, 43, 79, 209, 2, 75, 103, 96, - 51, 224, 8, 104, 43, 43, 0, 208, 2, 35, 0, 96, 43, 231, 218, 35, 1, 66, 91, 96, 43, 121, 98, 121, 163, 2, 18, 4, 27, - 24, 211, 121, 34, 77, 95, 24, 155, 121, 226, 28, 40, 6, 18, 24, 155, 10, 219, 147, 3, 154, 3, 75, 91, 33, 0, 96, 26, - 34, 128, 1, 18, 240, 5, 250, 143, 75, 84, 104, 26, 75, 85, 66, 154, 209, 30, 32, 7, 35, 0, 70, 132, 70, 102, 64, 30, - 209, 4, 16, 218, 152, 2, 73, 82, 0, 146, 80, 80, 24, 226, 122, 18, 42, 0, 208, 10, 16, 218, 92, 168, 33, 1, 64, 177, - 67, 8, 84, 168, 154, 2, 28, 16, 154, 3, 24, 128, 144, 2, 34, 128, 51, 1, 1, 210, 66, 147, 209, 227, 75, 63, 34, 31, - 104, 27, 153, 5, 51, 31, 67, 147, 34, 128, 2, 18, 147, 3, 145, 2, 78, 60, 146, 6, 224, 97, 155, 4, 32, 15, 10, 93, 104, - 51, 67, 133, 66, 157, 208, 63, 73, 56, 75, 57, 104, 10, 96, 53, 66, 154, 209, 33, 75, 57, 28, 40, 104, 28, 28, 33, 240, - 5, 251, 33, 75, 55, 8, 193, 0, 138, 88, 210, 35, 0, 147, 1, 35, 7, 64, 24, 70, 140, 35, 0, 144, 0, 224, 9, 72, 47, 70, - 97, 92, 64, 28, 1, 65, 25, 32, 1, 66, 8, 208, 0, 25, 18, 51, 1, 153, 0, 66, 139, 211, 242, 60, 1, 64, 44, 25, 21, 75, - 34, 104, 28, 27, 100, 44, 16, 220, 10, 44, 16, 208, 9, 34, 128, 152, 3, 33, 0, 2, 18, 240, 5, 250, 34, 44, 0, 221, 8, - 224, 0, 36, 16, 28, 40, 28, 33, 154, 3, 240, 3, 254, 255, 40, 0, 208, 29, 154, 4, 152, 6, 4, 209, 12, 73, 26, 67, 28, - 60, 66, 187, 216, 0, 28, 28, 154, 3, 152, 2, 24, 137, 28, 34, 240, 5, 249, 198, 152, 2, 28, 33, 240, 5, 251, 28, 155, - 2, 152, 4, 25, 27, 27, 63, 16, 164, 25, 0, 147, 2, 144, 4, 47, 0, 209, 155, 224, 5, 32, 128, 2, 0, 176, 11, 188, 240, - 188, 2, 71, 8, 152, 5, 153, 7, 240, 5, 251, 39, 32, 0, 231, 245, 70, 192, 19, 112, 107, 88, 19, 112, 106, 208, 19, 112, - 96, 24, 19, 112, 96, 12, 128, 0, 0, 1, 127, 255, 255, 255, 19, 112, 123, 128, 19, 112, 106, 212, 19, 112, 131, 128, - 181, 56, 28, 12, 240, 5, 250, 214, 30, 5, 209, 38, 44, 0, 208, 36, 104, 34, 42, 0, 208, 33, 120, 17, 120, 83, 6, 9, - 4, 27, 67, 11, 120, 145, 2, 9, 67, 11, 120, 209, 67, 11, 43, 6, 209, 21, 123, 17, 123, 83, 6, 9, 4, 27, 67, 11, 123, - 145, 2, 9, 67, 11, 123, 209, 67, 11, 43, 122, 208, 2, 43, 136, 209, 7, 224, 2, 72, 5, 33, 21, 224, 1, 72, 4, 33, 20, - 240, 4, 250, 238, 28, 40, 188, 56, 188, 2, 71, 8, 70, 192, 19, 112, 1, 153, 181, 240, 176, 145, 75, 206, 33, 128, 104, - 24, 240, 5, 250, 97, 33, 32, 240, 5, 250, 144, 144, 8, 240, 0, 251, 131, 240, 5, 250, 211, 33, 120, 240, 5, 250, 140, - 153, 8, 72, 199, 240, 5, 250, 120, 72, 198, 154, 8, 28, 1, 35, 0, 240, 5, 250, 72, 73, 196, 144, 9, 32, 205, 240, 4, - 250, 109, 33, 0, 36, 1, 145, 7, 145, 11, 145, 6, 39, 0, 152, 8, 169, 15, 34, 0, 240, 5, 250, 122, 144, 14, 155, 14, - 43, 0, 209, 246, 44, 0, 208, 2, 152, 9, 240, 5, 250, 67, 74, 185, 158, 15, 35, 0, 96, 19, 46, 0, 209, 87, 75, 183, 104, - 27, 43, 0, 208, 5, 75, 182, 104, 27, 43, 0, 209, 1, 74, 181, 96, 19, 36, 0, 47, 0, 208, 222, 75, 178, 104, 27, 43, 0, - 208, 218, 75, 175, 104, 27, 43, 0, 209, 214, 75, 175, 104, 27, 43, 0, 208, 9, 240, 3, 253, 88, 40, 0, 208, 5, 240, 3, - 253, 84, 40, 0, 208, 1, 240, 3, 253, 80, 75, 168, 104, 27, 147, 4, 43, 0, 209, 36, 77, 167, 75, 167, 104, 42, 58, 1, - 66, 154, 216, 30, 78, 159, 76, 165, 35, 1, 96, 51, 104, 32, 33, 1, 74, 164, 240, 3, 254, 26, 155, 4, 96, 51, 40, 0, - 208, 9, 35, 128, 104, 42, 0, 155, 66, 154, 209, 4, 104, 35, 33, 128, 2, 9, 24, 91, 96, 35, 75, 154, 74, 156, 104, 25, - 104, 18, 66, 145, 211, 1, 34, 0, 96, 26, 75, 145, 36, 0, 104, 27, 43, 0, 209, 153, 152, 9, 73, 138, 240, 5, 249, 219, - 36, 1, 231, 147, 120, 51, 120, 112, 6, 27, 4, 0, 67, 24, 120, 179, 2, 27, 67, 24, 120, 243, 67, 24, 56, 1, 40, 6, 217, - 0, 226, 152, 240, 4, 255, 69, 0, 10, 0, 73, 2, 151, 2, 151, 2, 151, 0, 7, 0, 92, 38, 1, 37, 1, 226, 150, 123, 51, 123, - 116, 6, 27, 4, 36, 67, 28, 123, 179, 73, 119, 2, 27, 67, 28, 123, 243, 67, 28, 28, 32, 240, 5, 249, 53, 40, 0, 209, - 11, 125, 51, 125, 117, 6, 27, 4, 45, 67, 29, 125, 179, 2, 27, 67, 29, 125, 243, 38, 1, 67, 29, 226, 122, 28, 32, 73, - 120, 240, 5, 249, 35, 40, 0, 208, 0, 226, 108, 125, 51, 125, 117, 6, 27, 4, 45, 67, 29, 125, 179, 34, 1, 2, 27, 67, - 29, 125, 243, 39, 0, 67, 29, 75, 103, 38, 1, 96, 26, 75, 103, 96, 24, 72, 110, 240, 0, 251, 48, 75, 109, 34, 4, 104, - 27, 104, 155, 96, 154, 240, 1, 248, 39, 226, 87, 122, 50, 122, 115, 6, 18, 4, 27, 67, 19, 122, 178, 2, 18, 67, 19, 122, - 242, 67, 19, 154, 6, 66, 154, 209, 0, 226, 70, 240, 1, 248, 46, 38, 1, 37, 0, 226, 68, 126, 51, 126, 116, 6, 27, 4, - 36, 67, 28, 126, 179, 124, 50, 2, 27, 67, 28, 126, 243, 6, 18, 67, 28, 124, 115, 4, 27, 67, 19, 124, 178, 2, 18, 67, - 19, 124, 242, 67, 26, 146, 4, 125, 51, 125, 117, 6, 27, 4, 45, 67, 29, 125, 179, 2, 27, 67, 29, 125, 243, 67, 29, 155, - 6, 43, 0, 209, 16, 123, 51, 43, 0, 209, 13, 122, 50, 122, 115, 122, 176, 6, 18, 4, 27, 67, 19, 2, 0, 67, 3, 122, 240, - 67, 24, 240, 1, 248, 10, 28, 6, 224, 0, 38, 0, 153, 4, 28, 32, 25, 73, 145, 10, 0, 201, 240, 5, 249, 164, 34, 0, 28, - 37, 146, 5, 224, 7, 104, 40, 104, 105, 240, 5, 249, 156, 155, 5, 53, 8, 51, 1, 147, 5, 153, 5, 154, 10, 66, 145, 219, - 243, 73, 48, 104, 11, 147, 5, 43, 0, 208, 0, 225, 213, 154, 15, 123, 16, 123, 83, 6, 0, 4, 27, 67, 3, 123, 144, 2, 0, - 67, 3, 123, 208, 67, 3, 72, 51, 66, 131, 209, 0, 225, 38, 66, 131, 216, 31, 43, 6, 216, 11, 43, 5, 211, 0, 225, 180, - 43, 1, 209, 0, 224, 159, 43, 0, 208, 119, 43, 2, 208, 0, 225, 70, 225, 171, 43, 27, 216, 6, 43, 26, 211, 0, 224, 172, - 43, 12, 208, 0, 225, 61, 224, 156, 74, 37, 66, 147, 209, 0, 224, 167, 50, 1, 66, 147, 208, 0, 225, 52, 224, 246, 72, - 34, 66, 131, 209, 0, 224, 226, 66, 131, 216, 68, 74, 32, 66, 147, 209, 0, 224, 182, 66, 147, 216, 8, 58, 12, 66, 147, - 209, 0, 225, 17, 50, 2, 66, 147, 208, 0, 225, 31, 225, 20, 74, 25, 66, 147, 209, 0, 225, 22, 50, 1, 66, 147, 208, 0, - 225, 22, 224, 169, 70, 192, 19, 112, 96, 48, 19, 112, 96, 109, 0, 152, 150, 128, 19, 112, 1, 65, 19, 112, 106, 240, - 19, 112, 106, 196, 19, 112, 106, 200, 19, 112, 96, 20, 19, 112, 107, 84, 19, 114, 199, 32, 0, 0, 15, 254, 19, 112, 106, - 192, 19, 112, 107, 128, 19, 114, 199, 36, 19, 112, 96, 121, 19, 112, 21, 69, 19, 112, 96, 60, 85, 77, 83, 3, 85, 77, - 83, 1, 85, 77, 83, 130, 85, 77, 83, 16, 85, 77, 83, 128, 73, 196, 66, 139, 209, 0, 225, 63, 66, 139, 216, 7, 73, 194, - 66, 139, 208, 119, 73, 194, 66, 139, 208, 0, 224, 219, 224, 220, 74, 192, 66, 147, 209, 0, 225, 36, 50, 1, 66, 147, - 208, 0, 224, 210, 225, 20, 46, 0, 209, 0, 225, 55, 105, 162, 105, 35, 136, 21, 106, 34, 136, 27, 136, 16, 104, 34, 2, - 25, 10, 27, 120, 18, 67, 11, 2, 41, 10, 45, 67, 13, 146, 5, 4, 45, 104, 162, 12, 45, 120, 18, 149, 0, 2, 5, 10, 0, 67, - 40, 4, 0, 12, 0, 144, 1, 107, 32, 4, 27, 144, 2, 12, 27, 28, 48, 153, 5, 240, 1, 251, 44, 225, 3, 46, 0, 209, 0, 225, - 17, 104, 35, 28, 48, 120, 25, 104, 163, 136, 26, 105, 35, 240, 1, 251, 12, 224, 247, 46, 0, 208, 0, 225, 5, 104, 35, - 105, 34, 120, 24, 104, 163, 120, 25, 105, 163, 240, 0, 255, 46, 224, 235, 38, 0, 37, 1, 225, 4, 154, 11, 42, 0, 209, - 1, 240, 0, 254, 203, 240, 3, 249, 119, 39, 0, 28, 5, 40, 0, 219, 3, 75, 151, 34, 1, 96, 26, 39, 1, 155, 15, 38, 1, 122, - 25, 122, 90, 6, 9, 4, 18, 67, 10, 122, 153, 122, 219, 2, 9, 67, 10, 67, 19, 147, 6, 150, 11, 224, 230, 75, 141, 153, - 5, 38, 1, 96, 25, 37, 0, 39, 0, 224, 223, 104, 35, 104, 26, 75, 138, 224, 97, 104, 35, 37, 0, 120, 222, 75, 136, 120, - 27, 66, 179, 208, 16, 75, 135, 104, 27, 43, 0, 208, 12, 240, 3, 249, 40, 240, 0, 254, 206, 240, 0, 254, 180, 75, 129, - 112, 30, 240, 0, 254, 146, 240, 3, 249, 62, 28, 5, 75, 126, 112, 30, 224, 165, 35, 1, 96, 11, 154, 5, 75, 121, 72, 124, - 96, 26, 240, 0, 249, 164, 75, 123, 34, 4, 104, 27, 39, 0, 104, 155, 96, 154, 240, 0, 254, 154, 77, 120, 28, 40, 240, - 3, 250, 74, 75, 119, 96, 24, 104, 35, 43, 0, 208, 1, 104, 42, 96, 26, 75, 116, 104, 29, 224, 135, 104, 35, 105, 34, - 104, 24, 104, 163, 38, 1, 104, 25, 240, 3, 251, 254, 75, 111, 28, 5, 120, 26, 42, 0, 209, 0, 224, 148, 70, 105, 34, - 23, 92, 81, 112, 25, 104, 35, 40, 0, 221, 2, 104, 25, 72, 105, 224, 1, 104, 25, 72, 105, 240, 1, 248, 24, 224, 133, - 104, 35, 105, 34, 104, 24, 104, 163, 104, 25, 240, 3, 252, 134, 224, 97, 75, 100, 104, 26, 66, 81, 65, 74, 96, 26, 224, - 105, 104, 35, 104, 26, 75, 85, 96, 26, 38, 1, 231, 106, 122, 17, 122, 83, 6, 9, 4, 27, 67, 11, 122, 145, 122, 210, 2, - 9, 67, 11, 67, 26, 104, 37, 35, 0, 146, 6, 147, 13, 120, 42, 42, 95, 209, 26, 120, 106, 42, 68, 209, 23, 120, 170, 42, - 86, 209, 20, 120, 234, 42, 68, 209, 17, 77, 82, 74, 69, 96, 43, 104, 227, 33, 1, 96, 17, 43, 4, 209, 6, 104, 161, 168, - 13, 34, 4, 240, 4, 254, 134, 155, 13, 96, 43, 240, 3, 255, 12, 224, 17, 104, 227, 43, 4, 209, 4, 104, 161, 168, 13, - 34, 4, 240, 4, 254, 121, 28, 40, 153, 13, 240, 3, 255, 67, 144, 7, 40, 0, 208, 46, 75, 53, 34, 1, 96, 26, 38, 1, 37, - 0, 224, 43, 240, 3, 251, 66, 38, 1, 28, 5, 40, 0, 209, 42, 35, 1, 74, 60, 66, 91, 96, 19, 224, 37, 75, 45, 34, 1, 96, - 26, 104, 35, 105, 33, 104, 24, 104, 163, 104, 26, 247, 255, 251, 159, 28, 5, 38, 1, 224, 24, 154, 7, 42, 0, 208, 18, - 104, 35, 105, 34, 104, 25, 104, 163, 152, 7, 104, 27, 240, 4, 251, 17, 38, 1, 37, 0, 224, 10, 38, 1, 37, 6, 224, 6, - 37, 1, 38, 1, 66, 109, 39, 1, 224, 2, 38, 1, 37, 1, 66, 109, 153, 4, 0, 203, 24, 228, 224, 7, 104, 32, 104, 97, 240, - 4, 255, 141, 154, 4, 52, 8, 50, 1, 146, 4, 155, 4, 153, 10, 66, 139, 219, 243, 224, 9, 38, 1, 37, 1, 224, 1, 38, 1, - 37, 6, 66, 109, 224, 2, 37, 0, 38, 1, 149, 6, 74, 12, 36, 0, 104, 19, 43, 0, 208, 4, 152, 9, 73, 23, 240, 4, 255, 31, - 36, 1, 46, 0, 209, 0, 228, 213, 152, 15, 28, 41, 240, 4, 255, 117, 228, 208, 87, 70, 83, 2, 85, 77, 83, 131, 87, 70, - 83, 1, 87, 70, 83, 3, 19, 112, 96, 20, 19, 112, 106, 196, 19, 112, 1, 12, 19, 112, 107, 56, 19, 112, 21, 69, 19, 112, - 96, 60, 19, 114, 199, 32, 19, 114, 199, 36, 19, 112, 96, 16, 19, 112, 96, 137, 19, 112, 96, 164, 19, 112, 106, 204, - 19, 112, 96, 12, 19, 112, 96, 24, 0, 152, 150, 128, 74, 3, 35, 128, 104, 17, 2, 27, 67, 11, 96, 19, 71, 112, 70, 192, - 13, 4, 0, 204, 75, 2, 74, 3, 104, 25, 64, 10, 96, 26, 71, 112, 13, 4, 0, 204, 255, 255, 127, 255, 181, 16, 247, 255, - 255, 243, 32, 128, 240, 3, 254, 16, 33, 32, 240, 4, 255, 1, 76, 9, 96, 32, 32, 4, 240, 4, 255, 12, 104, 33, 34, 0, 32, - 4, 240, 4, 255, 47, 247, 255, 255, 215, 32, 4, 240, 4, 254, 216, 188, 16, 188, 1, 71, 0, 70, 192, 19, 112, 96, 32, 181, - 16, 28, 4, 75, 15, 28, 8, 0, 137, 24, 9, 104, 26, 0, 73, 35, 1, 240, 4, 254, 171, 75, 12, 33, 16, 96, 24, 72, 11, 240, - 3, 255, 18, 33, 16, 72, 10, 240, 3, 255, 25, 75, 10, 34, 55, 96, 28, 75, 9, 32, 4, 104, 27, 104, 155, 96, 154, 240, - 4, 254, 179, 188, 16, 188, 1, 71, 0, 19, 112, 96, 32, 19, 112, 96, 28, 13, 128, 0, 56, 13, 128, 0, 60, 19, 112, 106, - 224, 19, 112, 96, 60, 181, 112, 76, 21, 75, 21, 38, 2, 66, 118, 28, 33, 104, 24, 34, 0, 96, 38, 240, 4, 254, 191, 75, - 18, 77, 18, 104, 27, 104, 154, 35, 0, 96, 147, 74, 17, 104, 40, 96, 19, 240, 4, 254, 134, 104, 40, 240, 4, 254, 205, - 35, 1, 66, 91, 96, 43, 104, 35, 43, 0, 209, 3, 75, 11, 104, 27, 96, 35, 224, 0, 96, 38, 32, 4, 240, 4, 254, 125, 75, - 2, 104, 24, 188, 112, 188, 2, 71, 8, 19, 112, 106, 216, 19, 112, 96, 32, 19, 112, 96, 60, 19, 112, 96, 28, 19, 112, - 106, 224, 19, 112, 106, 220, 75, 2, 34, 0, 96, 24, 75, 2, 96, 26, 71, 112, 19, 112, 106, 228, 19, 112, 106, 224, 181, - 248, 76, 34, 38, 16, 104, 35, 77, 33, 67, 179, 96, 35, 104, 43, 73, 32, 104, 154, 121, 19, 121, 87, 6, 27, 4, 63, 67, - 31, 121, 147, 2, 27, 67, 31, 121, 211, 67, 31, 104, 11, 43, 0, 208, 18, 28, 56, 240, 0, 248, 56, 40, 0, 220, 20, 74, - 22, 35, 0, 96, 19, 75, 22, 96, 24, 32, 4, 240, 4, 254, 66, 104, 43, 34, 55, 104, 155, 64, 23, 96, 95, 224, 22, 75, 17, - 104, 27, 43, 0, 208, 8, 28, 56, 240, 0, 248, 33, 104, 43, 34, 55, 104, 155, 64, 23, 96, 95, 224, 2, 35, 55, 64, 31, - 96, 87, 104, 35, 67, 51, 96, 35, 75, 9, 104, 26, 67, 22, 96, 30, 32, 0, 188, 248, 188, 2, 71, 8, 70, 192, 13, 128, 0, - 60, 19, 112, 96, 60, 19, 112, 106, 224, 19, 112, 106, 220, 19, 112, 106, 228, 13, 128, 0, 56, 71, 24, 70, 192, 181, - 112, 28, 12, 28, 5, 240, 4, 254, 67, 28, 6, 32, 1, 66, 64, 240, 4, 254, 50, 104, 34, 75, 17, 66, 154, 209, 25, 75, 16, - 73, 17, 34, 1, 67, 19, 96, 75, 28, 32, 34, 8, 240, 4, 252, 219, 28, 32, 27, 100, 8, 164, 33, 8, 60, 2, 240, 4, 254, - 54, 2, 36, 35, 234, 10, 36, 6, 27, 67, 35, 96, 43, 28, 40, 33, 4, 240, 4, 254, 44, 28, 48, 240, 4, 254, 17, 188, 112, - 188, 1, 71, 0, 230, 0, 1, 112, 19, 112, 4, 153, 19, 112, 96, 36, 181, 56, 28, 4, 240, 4, 253, 202, 44, 38, 208, 72, - 44, 38, 216, 5, 44, 36, 208, 9, 44, 37, 208, 0, 224, 169, 224, 14, 44, 57, 208, 112, 44, 60, 208, 0, 224, 163, 224, - 110, 73, 83, 75, 84, 72, 84, 96, 75, 34, 8, 240, 4, 252, 163, 72, 82, 224, 150, 72, 82, 73, 82, 247, 255, 255, 171, - 76, 77, 75, 81, 77, 82, 96, 99, 34, 8, 28, 40, 28, 33, 240, 4, 252, 148, 28, 40, 33, 8, 240, 4, 253, 242, 75, 77, 77, - 78, 96, 99, 34, 8, 28, 40, 28, 33, 240, 4, 252, 136, 28, 40, 33, 8, 240, 4, 253, 230, 75, 73, 77, 74, 96, 99, 34, 8, - 28, 33, 28, 40, 240, 4, 252, 124, 28, 40, 33, 8, 240, 4, 253, 218, 75, 59, 72, 69, 96, 99, 28, 33, 34, 8, 240, 4, 252, - 113, 72, 66, 224, 100, 72, 66, 73, 66, 247, 255, 255, 121, 76, 52, 75, 65, 77, 57, 96, 99, 34, 8, 28, 40, 28, 33, 240, - 4, 252, 98, 28, 40, 33, 8, 240, 4, 253, 192, 75, 60, 77, 53, 96, 99, 34, 8, 28, 40, 28, 33, 240, 4, 252, 86, 28, 40, - 33, 8, 240, 4, 253, 180, 75, 55, 77, 49, 96, 99, 34, 8, 28, 33, 28, 40, 240, 4, 252, 74, 28, 40, 33, 8, 240, 4, 253, - 168, 75, 34, 72, 50, 96, 99, 28, 33, 34, 8, 240, 4, 252, 63, 72, 47, 224, 50, 72, 47, 224, 0, 72, 47, 73, 47, 247, 255, - 255, 69, 76, 26, 75, 46, 77, 31, 96, 99, 34, 8, 28, 40, 28, 33, 240, 4, 252, 46, 28, 40, 33, 8, 240, 4, 253, 140, 75, - 41, 77, 27, 96, 99, 34, 8, 28, 40, 28, 33, 240, 4, 252, 34, 28, 40, 33, 8, 240, 4, 253, 128, 75, 36, 77, 23, 96, 99, - 34, 8, 28, 33, 28, 40, 240, 4, 252, 22, 28, 40, 33, 8, 240, 4, 253, 116, 75, 8, 72, 31, 96, 99, 28, 33, 34, 8, 240, - 4, 252, 11, 72, 28, 33, 8, 240, 4, 253, 105, 32, 0, 188, 56, 188, 2, 71, 8, 70, 192, 19, 112, 96, 36, 19, 112, 0, 96, - 255, 255, 30, 120, 32, 32, 93, 232, 32, 32, 64, 140, 255, 255, 31, 112, 19, 112, 0, 188, 255, 255, 31, 140, 19, 112, - 0, 196, 255, 255, 30, 52, 19, 112, 0, 204, 255, 255, 31, 104, 32, 32, 91, 20, 32, 32, 62, 108, 255, 255, 30, 176, 255, - 255, 30, 204, 255, 255, 29, 116, 255, 255, 30, 168, 32, 32, 94, 132, 32, 32, 93, 148, 32, 32, 63, 96, 255, 255, 33, - 48, 255, 255, 33, 76, 255, 255, 31, 244, 255, 255, 33, 40, 181, 56, 77, 11, 35, 0, 104, 42, 73, 10, 240, 4, 252, 201, - 76, 10, 73, 10, 34, 0, 96, 32, 104, 40, 240, 4, 253, 4, 104, 32, 240, 4, 252, 211, 104, 32, 240, 4, 253, 26, 188, 56, - 188, 1, 71, 0, 70, 192, 19, 112, 96, 44, 0, 152, 150, 128, 19, 112, 96, 52, 19, 112, 106, 232, 181, 8, 1, 67, 26, 27, - 0, 155, 24, 24, 0, 192, 247, 255, 255, 216, 188, 8, 188, 1, 71, 0, 0, 0, 181, 0, 74, 5, 2, 192, 104, 19, 104, 17, 26, - 201, 213, 0, 104, 19, 66, 129, 217, 249, 188, 1, 71, 0, 13, 128, 0, 16, 181, 8, 75, 24, 120, 26, 30, 83, 65, 154, 75, - 23, 96, 26, 240, 3, 252, 204, 75, 22, 33, 4, 96, 24, 28, 24, 240, 4, 252, 225, 240, 3, 252, 206, 34, 0, 28, 1, 72, 18, - 240, 3, 253, 3, 33, 160, 2, 73, 72, 16, 240, 4, 252, 201, 75, 16, 33, 128, 96, 24, 240, 4, 252, 122, 33, 32, 240, 4, - 252, 169, 75, 13, 96, 24, 240, 3, 251, 37, 40, 0, 219, 3, 247, 255, 250, 7, 32, 0, 224, 1, 32, 1, 66, 64, 188, 8, 188, - 2, 71, 8, 19, 112, 1, 12, 19, 112, 107, 80, 19, 114, 195, 128, 19, 112, 14, 77, 19, 113, 131, 128, 19, 112, 96, 48, - 19, 112, 96, 44, 181, 240, 7, 65, 213, 27, 34, 128, 78, 14, 77, 15, 76, 15, 35, 0, 1, 146, 104, 49, 0, 152, 104, 137, - 104, 47, 49, 68, 24, 9, 104, 8, 66, 187, 209, 5, 7, 193, 212, 7, 73, 9, 32, 2, 96, 8, 224, 3, 64, 32, 40, 3, 209, 0, - 96, 10, 51, 1, 43, 4, 209, 233, 188, 240, 188, 1, 71, 0, 19, 112, 96, 60, 19, 112, 107, 80, 0, 0, 32, 3, 19, 112, 107, - 84, 181, 240, 176, 131, 147, 1, 155, 8, 76, 14, 0, 91, 104, 38, 28, 13, 28, 23, 147, 0, 32, 10, 247, 255, 255, 79, 104, - 43, 154, 1, 64, 59, 66, 147, 208, 9, 104, 35, 27, 155, 213, 0, 104, 38, 154, 0, 66, 147, 211, 240, 32, 2, 66, 64, 224, - 0, 32, 0, 176, 3, 188, 240, 188, 2, 71, 8, 70, 192, 13, 128, 0, 16, 181, 56, 28, 4, 240, 3, 251, 27, 33, 0, 28, 5, 34, - 96, 28, 32, 240, 4, 251, 64, 35, 64, 96, 163, 35, 128, 4, 91, 99, 101, 96, 35, 96, 99, 188, 56, 188, 1, 71, 0, 181, - 16, 75, 12, 36, 0, 104, 27, 108, 26, 42, 7, 220, 13, 73, 10, 104, 12, 0, 228, 24, 164, 0, 97, 25, 12, 106, 25, 1, 100, - 25, 12, 50, 1, 100, 26, 28, 32, 247, 255, 255, 215, 28, 32, 188, 16, 188, 2, 71, 8, 70, 192, 19, 112, 96, 60, 19, 112, - 107, 64, 181, 240, 28, 4, 176, 133, 28, 8, 28, 14, 147, 3, 28, 23, 240, 4, 250, 147, 35, 0, 98, 35, 5, 53, 35, 128, - 13, 45, 1, 91, 27, 93, 96, 224, 66, 175, 210, 1, 28, 61, 224, 49, 32, 128, 1, 64, 24, 54, 11, 54, 3, 54, 150, 2, 33, - 1, 28, 38, 54, 16, 145, 1, 224, 20, 152, 2, 240, 4, 250, 121, 34, 0, 198, 1, 97, 50, 155, 2, 33, 128, 28, 24, 35, 128, - 1, 91, 1, 73, 24, 192, 24, 109, 144, 2, 66, 189, 211, 0, 28, 61, 154, 1, 50, 1, 146, 1, 66, 189, 210, 8, 152, 1, 33, - 0, 15, 195, 34, 4, 66, 130, 65, 75, 6, 27, 43, 0, 209, 223, 66, 189, 208, 4, 28, 40, 153, 10, 240, 4, 251, 194, 26, - 109, 155, 3, 4, 40, 67, 24, 240, 4, 250, 80, 35, 128, 4, 91, 96, 160, 100, 37, 96, 35, 96, 99, 176, 5, 28, 40, 188, - 240, 188, 2, 71, 8, 0, 0, 181, 240, 176, 139, 28, 5, 247, 255, 255, 133, 28, 6, 32, 0, 46, 0, 209, 0, 224, 145, 99, - 245, 105, 42, 126, 107, 126, 47, 97, 104, 146, 3, 147, 7, 47, 0, 209, 25, 35, 8, 147, 0, 35, 160, 104, 105, 34, 8, 0, - 155, 28, 48, 247, 255, 255, 140, 247, 255, 255, 108, 28, 4, 28, 56, 44, 0, 208, 121, 107, 96, 99, 229, 240, 4, 250, - 30, 154, 3, 96, 48, 99, 180, 42, 0, 208, 4, 79, 58, 224, 3, 28, 52, 39, 128, 224, 0, 79, 56, 104, 235, 154, 7, 147, - 4, 42, 0, 208, 2, 35, 128, 0, 91, 67, 31, 105, 235, 150, 9, 5, 91, 13, 91, 147, 5, 154, 5, 35, 128, 4, 91, 58, 1, 147, - 6, 146, 8, 155, 5, 154, 3, 147, 0, 28, 32, 28, 59, 153, 4, 247, 255, 255, 92, 154, 3, 155, 4, 26, 18, 146, 3, 154, 7, - 24, 27, 147, 4, 42, 0, 208, 1, 155, 6, 96, 99, 154, 8, 155, 5, 24, 128, 66, 3, 209, 2, 34, 128, 6, 18, 24, 191, 155, - 3, 43, 0, 221, 11, 247, 255, 255, 38, 30, 6, 208, 52, 107, 112, 99, 245, 240, 4, 249, 218, 99, 166, 96, 32, 28, 52, - 231, 213, 35, 128, 4, 91, 96, 99, 105, 43, 158, 9, 148, 3, 43, 0, 208, 29, 126, 42, 146, 4, 42, 0, 209, 25, 247, 255, - 255, 14, 28, 4, 32, 0, 44, 0, 208, 27, 107, 96, 99, 229, 240, 4, 249, 192, 155, 3, 34, 128, 96, 24, 99, 156, 35, 128, - 0, 91, 6, 18, 64, 123, 67, 19, 154, 4, 28, 32, 146, 0, 33, 0, 34, 0, 247, 255, 255, 20, 104, 162, 35, 128, 4, 27, 67, - 19, 96, 163, 28, 48, 224, 0, 32, 0, 176, 11, 188, 240, 188, 2, 71, 8, 128, 0, 0, 128, 128, 0, 1, 128, 181, 56, 76, 19, - 37, 32, 104, 35, 104, 155, 120, 25, 120, 90, 6, 9, 4, 18, 67, 10, 120, 153, 120, 219, 2, 9, 67, 10, 67, 19, 224, 18, - 104, 34, 67, 171, 104, 146, 32, 10, 96, 19, 247, 255, 253, 243, 104, 35, 104, 155, 120, 25, 120, 90, 6, 9, 4, 18, 67, - 10, 120, 153, 120, 219, 2, 9, 67, 10, 67, 19, 66, 29, 209, 234, 188, 56, 188, 1, 71, 0, 19, 112, 96, 60, 181, 56, 76, - 19, 37, 32, 104, 35, 104, 155, 120, 25, 120, 90, 6, 9, 4, 18, 67, 10, 120, 153, 120, 219, 2, 9, 67, 10, 67, 19, 224, - 18, 104, 34, 67, 43, 104, 146, 32, 10, 96, 19, 247, 255, 253, 201, 104, 35, 104, 155, 120, 25, 120, 90, 6, 9, 4, 18, - 67, 10, 120, 153, 120, 219, 2, 9, 67, 10, 67, 19, 66, 29, 208, 234, 188, 56, 188, 1, 71, 0, 19, 112, 96, 60, 181, 248, - 28, 12, 40, 0, 209, 33, 77, 28, 76, 29, 79, 29, 38, 0, 104, 43, 104, 155, 126, 25, 126, 90, 6, 9, 4, 18, 67, 10, 126, - 153, 126, 219, 2, 9, 67, 10, 67, 19, 105, 34, 108, 82, 66, 147, 211, 6, 105, 98, 108, 82, 66, 147, 216, 2, 247, 255, - 255, 142, 224, 25, 32, 10, 54, 1, 247, 255, 253, 147, 66, 190, 209, 226, 224, 18, 40, 1, 209, 16, 224, 6, 32, 10, 54, - 1, 247, 255, 253, 137, 66, 190, 209, 3, 224, 8, 77, 6, 79, 8, 38, 0, 104, 43, 104, 155, 105, 154, 108, 99, 66, 154, - 208, 239, 32, 0, 188, 248, 188, 2, 71, 8, 70, 192, 19, 112, 96, 60, 19, 114, 195, 160, 0, 0, 19, 136, 181, 0, 7, 67, - 213, 21, 75, 12, 104, 27, 104, 154, 75, 11, 108, 81, 64, 11, 43, 3, 209, 2, 35, 128, 1, 155, 100, 83, 75, 6, 104, 27, - 104, 154, 75, 6, 108, 145, 64, 11, 43, 3, 209, 2, 35, 128, 1, 155, 100, 147, 188, 1, 71, 0, 70, 192, 19, 112, 96, 60, - 0, 0, 32, 3, 181, 16, 75, 19, 28, 4, 104, 27, 52, 16, 104, 155, 0, 164, 25, 28, 34, 128, 104, 99, 1, 146, 66, 19, 209, - 0, 64, 83, 34, 46, 67, 147, 96, 99, 32, 5, 247, 255, 253, 106, 34, 128, 104, 99, 1, 146, 66, 19, 208, 0, 64, 83, 34, - 46, 67, 147, 96, 99, 32, 5, 247, 255, 253, 94, 75, 4, 32, 5, 96, 99, 247, 255, 253, 89, 188, 16, 188, 1, 71, 0, 19, - 112, 96, 60, 0, 0, 24, 1, 181, 240, 176, 131, 75, 36, 28, 5, 104, 27, 53, 16, 104, 155, 0, 173, 25, 93, 104, 107, 74, - 33, 29, 44, 64, 26, 38, 4, 42, 1, 208, 4, 4, 154, 213, 51, 247, 255, 255, 192, 224, 48, 39, 136, 1, 127, 34, 4, 67, - 147, 67, 59, 96, 35, 32, 60, 247, 255, 253, 40, 104, 35, 74, 24, 32, 50, 64, 19, 96, 35, 247, 255, 253, 33, 75, 22, - 34, 128, 147, 0, 28, 32, 28, 33, 0, 82, 35, 0, 247, 255, 253, 156, 40, 0, 208, 2, 104, 107, 72, 17, 224, 20, 104, 35, - 34, 4, 66, 26, 209, 16, 62, 1, 46, 0, 209, 220, 34, 128, 1, 146, 67, 19, 33, 42, 67, 139, 96, 107, 32, 10, 96, 106, - 247, 255, 253, 2, 72, 8, 224, 1, 32, 1, 66, 64, 176, 3, 188, 240, 188, 2, 71, 8, 19, 112, 96, 60, 0, 0, 32, 1, 255, - 255, 254, 213, 0, 0, 19, 136, 255, 255, 248, 48, 255, 255, 251, 161, 181, 16, 75, 20, 28, 4, 104, 27, 52, 16, 104, 155, - 0, 164, 25, 28, 34, 128, 104, 99, 1, 146, 66, 19, 209, 0, 64, 83, 34, 46, 67, 147, 96, 99, 32, 5, 247, 255, 252, 230, - 34, 128, 104, 99, 1, 146, 66, 19, 208, 0, 64, 83, 34, 46, 67, 147, 96, 99, 32, 5, 247, 255, 252, 218, 75, 5, 32, 60, - 96, 99, 247, 255, 252, 213, 32, 1, 188, 16, 188, 2, 71, 8, 70, 192, 19, 112, 96, 60, 0, 0, 24, 1, 32, 0, 71, 112, 75, - 5, 48, 16, 104, 27, 0, 128, 104, 155, 24, 24, 35, 128, 1, 155, 96, 67, 32, 0, 71, 112, 70, 192, 19, 112, 96, 60, 74, - 5, 32, 0, 104, 19, 104, 153, 35, 128, 1, 155, 100, 75, 104, 18, 104, 146, 100, 147, 71, 112, 70, 192, 19, 112, 96, 60, - 181, 0, 75, 10, 104, 27, 104, 154, 35, 128, 108, 81, 1, 155, 66, 25, 209, 0, 100, 83, 75, 5, 104, 27, 104, 154, 35, - 128, 108, 145, 1, 155, 66, 25, 209, 0, 100, 147, 32, 0, 188, 2, 71, 8, 19, 112, 96, 60, 75, 6, 33, 0, 104, 26, 35, 1, - 66, 91, 28, 16, 103, 17, 103, 147, 48, 152, 50, 160, 96, 1, 96, 19, 71, 112, 70, 192, 19, 112, 96, 60, 75, 6, 120, 27, - 30, 90, 65, 147, 74, 5, 0, 152, 96, 19, 74, 5, 24, 195, 0, 219, 104, 16, 51, 88, 24, 192, 71, 112, 19, 112, 1, 12, 19, - 112, 107, 80, 19, 112, 96, 60, 181, 16, 73, 19, 120, 9, 30, 72, 65, 129, 72, 18, 0, 140, 96, 1, 72, 17, 24, 97, 104, - 0, 0, 201, 24, 64, 111, 4, 33, 0, 44, 0, 208, 16, 128, 25, 128, 89, 28, 1, 49, 96, 136, 8, 4, 0, 10, 4, 14, 0, 67, 32, - 128, 152, 136, 73, 4, 9, 10, 8, 14, 9, 67, 1, 128, 217, 33, 1, 32, 0, 112, 17, 188, 16, 188, 2, 71, 8, 70, 192, 19, - 112, 1, 12, 19, 112, 107, 80, 19, 112, 96, 60, 181, 0, 28, 3, 224, 8, 120, 90, 58, 4, 6, 18, 14, 18, 42, 1, 217, 4, - 120, 26, 26, 137, 24, 155, 41, 0, 209, 244, 26, 24, 188, 2, 71, 8, 181, 240, 176, 133, 124, 130, 124, 195, 6, 18, 4, - 27, 67, 19, 125, 2, 28, 5, 2, 18, 67, 19, 125, 66, 67, 26, 208, 103, 34, 0, 146, 3, 146, 1, 224, 83, 6, 36, 4, 9, 2, - 18, 67, 12, 67, 20, 158, 3, 67, 35, 25, 156, 122, 98, 122, 163, 6, 18, 4, 27, 67, 19, 122, 226, 2, 18, 67, 19, 123, - 34, 67, 26, 208, 58, 39, 0, 151, 2, 28, 46, 224, 36, 6, 0, 4, 9, 2, 18, 67, 8, 67, 16, 67, 3, 25, 221, 123, 170, 123, - 235, 6, 18, 4, 27, 67, 19, 124, 42, 124, 104, 2, 18, 67, 19, 67, 24, 208, 1, 240, 2, 255, 250, 122, 106, 122, 171, 6, - 18, 4, 27, 67, 19, 122, 234, 123, 40, 2, 18, 67, 19, 67, 24, 208, 1, 240, 2, 255, 237, 154, 2, 55, 18, 50, 1, 146, 2, - 121, 35, 157, 2, 70, 156, 122, 96, 122, 161, 122, 226, 123, 35, 69, 101, 219, 209, 28, 53, 6, 6, 4, 8, 67, 48, 2, 18, - 67, 16, 67, 24, 240, 2, 255, 215, 158, 1, 154, 3, 54, 1, 50, 13, 150, 1, 146, 3, 124, 104, 158, 1, 124, 172, 124, 233, - 125, 42, 125, 107, 66, 134, 219, 163, 6, 36, 4, 8, 67, 32, 2, 18, 67, 16, 67, 24, 240, 2, 255, 193, 176, 5, 188, 240, - 188, 1, 71, 0, 0, 0, 181, 248, 30, 5, 209, 4, 75, 28, 34, 48, 112, 26, 112, 93, 224, 47, 76, 26, 28, 46, 28, 48, 33, - 10, 240, 4, 248, 128, 28, 48, 28, 15, 33, 10, 240, 4, 248, 119, 23, 251, 24, 255, 64, 95, 55, 48, 75, 20, 112, 39, 60, - 1, 28, 6, 66, 156, 209, 236, 75, 15, 34, 0, 118, 154, 45, 0, 218, 2, 34, 45, 112, 26, 34, 1, 35, 16, 72, 10, 224, 0, - 51, 1, 92, 193, 41, 48, 208, 251, 66, 72, 65, 65, 26, 91, 73, 6, 224, 2, 84, 136, 51, 1, 50, 1, 92, 200, 40, 0, 209, - 249, 84, 136, 188, 248, 188, 1, 71, 0, 70, 192, 19, 114, 197, 96, 19, 114, 197, 121, 19, 114, 197, 111, 181, 248, 30, - 4, 209, 4, 75, 24, 34, 48, 112, 26, 112, 92, 224, 40, 77, 22, 28, 47, 63, 10, 28, 32, 33, 10, 240, 4, 248, 23, 28, 32, - 28, 14, 33, 10, 240, 4, 248, 30, 54, 48, 112, 46, 61, 1, 28, 4, 66, 189, 209, 240, 74, 12, 35, 0, 118, 147, 28, 17, - 35, 16, 224, 0, 51, 1, 92, 202, 42, 48, 208, 251, 66, 81, 65, 74, 26, 155, 73, 6, 34, 0, 224, 2, 84, 136, 51, 1, 50, - 1, 92, 200, 40, 0, 209, 249, 84, 136, 188, 248, 188, 1, 71, 0, 19, 114, 197, 96, 19, 114, 197, 121, 181, 16, 40, 0, - 209, 4, 75, 24, 34, 48, 112, 26, 112, 88, 224, 40, 75, 22, 33, 15, 28, 28, 60, 8, 28, 10, 64, 2, 9, 0, 42, 9, 221, 0, - 50, 7, 50, 48, 112, 26, 59, 1, 66, 163, 209, 244, 74, 14, 35, 0, 118, 19, 35, 48, 112, 19, 35, 120, 112, 83, 35, 16, - 224, 0, 51, 1, 92, 209, 41, 48, 208, 251, 66, 74, 65, 74, 26, 155, 73, 6, 34, 2, 224, 2, 84, 136, 51, 1, 50, 1, 92, - 200, 40, 0, 209, 249, 84, 136, 188, 16, 188, 1, 71, 0, 19, 114, 197, 96, 19, 114, 197, 119, 180, 15, 181, 240, 176, - 133, 171, 10, 203, 64, 34, 128, 1, 146, 169, 3, 128, 10, 147, 2, 36, 0, 77, 46, 79, 46, 224, 81, 28, 114, 146, 1, 43, - 37, 208, 12, 70, 106, 115, 19, 168, 3, 240, 3, 255, 41, 25, 4, 158, 1, 66, 172, 220, 68, 168, 3, 240, 3, 255, 222, 224, - 64, 120, 115, 43, 115, 208, 46, 43, 115, 216, 4, 43, 100, 208, 7, 43, 105, 209, 53, 224, 4, 43, 117, 208, 9, 43, 120, - 209, 48, 224, 20, 155, 2, 29, 26, 146, 2, 104, 24, 247, 255, 255, 26, 224, 19, 155, 2, 29, 26, 146, 2, 104, 24, 247, - 255, 255, 85, 28, 56, 240, 3, 255, 2, 25, 4, 28, 56, 66, 172, 220, 27, 224, 24, 155, 2, 29, 26, 146, 2, 104, 24, 247, - 255, 255, 127, 72, 16, 240, 3, 254, 244, 25, 4, 66, 172, 220, 14, 72, 13, 224, 10, 155, 2, 29, 26, 146, 2, 104, 30, - 28, 48, 240, 3, 254, 232, 25, 4, 66, 172, 220, 2, 28, 48, 240, 3, 255, 158, 158, 1, 54, 1, 120, 51, 43, 0, 209, 170, - 176, 5, 188, 240, 188, 8, 176, 4, 71, 24, 0, 0, 13, 171, 19, 114, 197, 96, 181, 240, 28, 12, 176, 133, 28, 6, 33, 0, - 32, 0, 247, 255, 252, 176, 126, 35, 43, 0, 209, 6, 74, 218, 33, 8, 96, 19, 104, 32, 240, 2, 254, 69, 96, 96, 105, 33, - 41, 0, 208, 9, 126, 99, 104, 160, 43, 0, 208, 2, 240, 2, 254, 59, 224, 1, 240, 2, 254, 64, 96, 224, 126, 35, 43, 0, - 209, 3, 75, 207, 104, 27, 105, 219, 224, 6, 126, 99, 43, 0, 208, 1, 75, 205, 224, 0, 75, 205, 104, 27, 147, 2, 77, 204, - 75, 205, 153, 2, 28, 40, 96, 25, 34, 96, 240, 2, 255, 104, 33, 0, 34, 48, 152, 2, 240, 3, 254, 69, 75, 195, 34, 0, 104, - 27, 28, 32, 100, 26, 75, 197, 34, 1, 104, 25, 64, 74, 96, 26, 247, 255, 251, 121, 75, 195, 100, 168, 96, 24, 126, 34, - 35, 15, 146, 1, 64, 26, 146, 3, 155, 3, 28, 7, 34, 128, 105, 176, 2, 29, 1, 146, 155, 1, 67, 2, 105, 225, 67, 42, 43, - 0, 209, 1, 72, 186, 224, 5, 5, 72, 35, 128, 9, 64, 5, 219, 67, 3, 28, 24, 67, 16, 240, 3, 253, 158, 77, 176, 35, 64, - 96, 171, 75, 180, 33, 255, 104, 27, 34, 255, 108, 91, 2, 9, 4, 18, 64, 25, 64, 26, 2, 9, 10, 18, 67, 10, 14, 25, 67, - 10, 33, 224, 64, 11, 33, 2, 67, 11, 6, 27, 67, 26, 96, 104, 96, 42, 107, 120, 240, 3, 253, 130, 35, 128, 153, 1, 4, - 91, 97, 40, 97, 107, 41, 0, 208, 16, 9, 203, 153, 3, 106, 114, 1, 27, 24, 91, 64, 218, 28, 19, 7, 218, 213, 3, 105, - 170, 35, 128, 67, 19, 224, 2, 105, 171, 34, 128, 67, 147, 97, 171, 77, 150, 75, 156, 105, 170, 28, 40, 64, 19, 97, 171, - 33, 96, 240, 2, 253, 195, 108, 173, 224, 4, 28, 40, 33, 96, 240, 2, 253, 189, 107, 173, 45, 0, 209, 248, 152, 2, 73, - 141, 34, 96, 240, 2, 254, 224, 75, 146, 120, 27, 43, 0, 208, 4, 75, 145, 72, 145, 104, 25, 247, 255, 254, 218, 75, 144, - 34, 1, 96, 26, 77, 130, 35, 0, 147, 1, 153, 1, 74, 139, 49, 1, 145, 1, 72, 140, 104, 17, 247, 254, 255, 192, 104, 43, - 33, 32, 105, 152, 240, 2, 253, 173, 104, 42, 73, 124, 39, 2, 108, 75, 105, 145, 34, 255, 2, 18, 64, 26, 2, 16, 34, 255, - 4, 18, 64, 26, 10, 18, 67, 2, 14, 24, 67, 2, 32, 224, 64, 3, 67, 59, 6, 24, 28, 19, 67, 3, 96, 11, 104, 43, 33, 32, - 105, 152, 240, 2, 253, 125, 247, 255, 251, 163, 247, 254, 255, 201, 154, 1, 42, 4, 208, 6, 28, 3, 51, 9, 209, 3, 72, - 118, 247, 255, 254, 158, 231, 200, 75, 114, 34, 0, 96, 26, 75, 109, 28, 7, 120, 27, 43, 0, 208, 3, 72, 113, 28, 57, - 247, 255, 254, 145, 47, 0, 209, 3, 75, 93, 104, 27, 43, 0, 208, 15, 75, 92, 104, 27, 104, 154, 75, 107, 50, 68, 104, - 27, 0, 155, 24, 211, 104, 27, 34, 5, 64, 19, 43, 5, 208, 2, 75, 84, 34, 1, 96, 26, 66, 121, 65, 121, 145, 1, 29, 122, - 208, 1, 41, 0, 208, 65, 247, 255, 251, 66, 77, 79, 33, 32, 104, 43, 105, 152, 240, 3, 254, 144, 104, 43, 33, 32, 105, - 152, 240, 2, 253, 39, 75, 82, 104, 42, 104, 27, 105, 145, 108, 91, 34, 255, 2, 18, 64, 26, 2, 16, 34, 255, 4, 18, 64, - 26, 10, 18, 67, 2, 14, 24, 67, 2, 32, 224, 64, 3, 32, 2, 67, 3, 6, 24, 28, 19, 67, 3, 96, 11, 104, 43, 33, 32, 105, - 152, 240, 3, 254, 78, 247, 255, 251, 66, 126, 34, 42, 0, 208, 98, 9, 211, 32, 15, 64, 16, 1, 27, 24, 27, 106, 113, 34, - 1, 64, 154, 67, 145, 28, 10, 73, 70, 104, 9, 6, 9, 15, 201, 64, 153, 28, 11, 67, 19, 98, 115, 224, 79, 247, 255, 251, - 0, 75, 49, 108, 157, 224, 4, 107, 104, 33, 96, 240, 2, 253, 15, 107, 173, 45, 0, 209, 248, 77, 44, 34, 32, 28, 40, 153, - 2, 240, 2, 254, 42, 126, 34, 42, 0, 208, 16, 9, 211, 32, 15, 64, 16, 1, 27, 24, 27, 106, 113, 34, 1, 64, 154, 67, 145, - 28, 10, 105, 169, 6, 9, 15, 201, 64, 153, 28, 11, 67, 19, 98, 115, 77, 28, 33, 32, 104, 43, 38, 2, 105, 152, 240, 3, - 254, 41, 104, 43, 33, 32, 105, 152, 240, 2, 252, 192, 75, 30, 104, 42, 104, 27, 105, 145, 108, 91, 34, 255, 2, 18, 64, - 26, 2, 16, 34, 255, 4, 18, 64, 26, 10, 18, 67, 2, 14, 24, 67, 2, 32, 224, 64, 3, 67, 51, 6, 24, 28, 19, 67, 3, 96, 11, - 104, 43, 33, 32, 105, 152, 240, 3, 253, 232, 247, 255, 250, 220, 73, 10, 32, 1, 247, 255, 251, 2, 105, 33, 41, 0, 208, - 48, 126, 99, 104, 224, 43, 0, 208, 42, 240, 2, 252, 175, 224, 41, 19, 112, 107, 84, 19, 112, 96, 60, 19, 112, 106, 244, - 19, 112, 107, 72, 19, 114, 197, 0, 19, 112, 106, 252, 19, 112, 107, 64, 19, 112, 107, 20, 64, 64, 64, 0, 19, 112, 107, - 68, 1, 0, 0, 128, 19, 112, 106, 248, 19, 112, 96, 56, 19, 112, 96, 194, 19, 112, 107, 16, 19, 112, 41, 229, 19, 112, - 98, 156, 19, 112, 96, 240, 19, 112, 107, 80, 19, 112, 107, 4, 240, 2, 252, 138, 126, 35, 43, 0, 209, 3, 104, 96, 33, - 8, 240, 2, 252, 125, 155, 1, 43, 0, 208, 0, 105, 103, 176, 5, 28, 56, 188, 240, 188, 2, 71, 8, 181, 48, 176, 137, 70, - 108, 118, 33, 9, 201, 118, 97, 33, 128, 0, 137, 37, 0, 145, 7, 70, 105, 149, 0, 146, 4, 147, 2, 247, 255, 253, 246, - 176, 9, 188, 48, 188, 2, 71, 8, 181, 240, 176, 139, 28, 6, 70, 104, 48, 70, 136, 4, 72, 43, 70, 156, 104, 0, 70, 107, - 51, 66, 48, 248, 104, 0, 136, 27, 112, 66, 4, 27, 70, 98, 147, 1, 2, 19, 10, 18, 67, 19, 4, 26, 12, 19, 14, 18, 112, - 1, 159, 18, 112, 130, 112, 195, 155, 1, 4, 36, 10, 26, 14, 27, 67, 19, 4, 27, 12, 37, 12, 26, 14, 27, 113, 3, 14, 36, - 2, 43, 67, 35, 4, 27, 113, 66, 14, 26, 12, 27, 113, 195, 172, 2, 35, 0, 113, 130, 118, 35, 35, 64, 97, 227, 75, 21, - 9, 201, 144, 2, 118, 97, 97, 37, 66, 159, 217, 22, 28, 40, 240, 2, 252, 52, 28, 42, 28, 57, 96, 160, 240, 3, 251, 237, - 28, 33, 28, 48, 247, 255, 253, 171, 104, 164, 28, 6, 28, 33, 28, 56, 28, 42, 240, 3, 251, 226, 28, 32, 240, 2, 252, - 63, 224, 5, 28, 48, 96, 167, 28, 33, 247, 255, 253, 155, 28, 6, 176, 11, 28, 48, 188, 240, 188, 2, 71, 8, 70, 192, 19, - 112, 96, 60, 19, 136, 0, 0, 181, 0, 176, 133, 145, 0, 35, 0, 33, 2, 34, 1, 147, 1, 147, 2, 247, 255, 255, 148, 176, - 5, 188, 2, 71, 8, 181, 0, 176, 133, 28, 19, 34, 0, 145, 0, 146, 1, 146, 2, 33, 1, 34, 11, 247, 255, 255, 134, 176, 5, - 188, 2, 71, 8, 181, 0, 176, 133, 34, 0, 146, 0, 146, 1, 146, 2, 28, 11, 34, 9, 33, 0, 247, 255, 255, 120, 176, 5, 188, - 2, 71, 8, 181, 112, 28, 4, 176, 132, 32, 1, 28, 14, 240, 2, 251, 224, 30, 5, 208, 18, 35, 0, 147, 0, 35, 1, 147, 1, - 28, 32, 33, 128, 34, 8, 35, 0, 149, 2, 247, 255, 255, 97, 30, 4, 219, 1, 120, 43, 112, 51, 28, 40, 240, 2, 251, 234, - 224, 1, 36, 4, 66, 100, 176, 4, 28, 32, 188, 112, 188, 2, 71, 8, 181, 16, 176, 132, 70, 108, 2, 18, 52, 27, 120, 36, - 67, 19, 34, 0, 146, 0, 145, 2, 34, 6, 33, 128, 148, 1, 247, 255, 255, 67, 176, 4, 188, 16, 188, 2, 71, 8, 0, 0, 181, - 240, 176, 137, 75, 65, 28, 2, 104, 30, 0, 129, 104, 179, 24, 9, 50, 16, 0, 201, 0, 146, 24, 154, 28, 11, 51, 88, 28, - 7, 24, 240, 144, 5, 24, 113, 32, 0, 103, 8, 104, 81, 29, 21, 74, 56, 64, 10, 42, 1, 209, 99, 34, 4, 146, 4, 147, 7, - 75, 54, 32, 10, 96, 43, 247, 254, 255, 156, 75, 52, 32, 100, 96, 43, 247, 254, 255, 151, 75, 51, 34, 128, 96, 43, 35, - 250, 0, 219, 147, 0, 0, 82, 35, 0, 28, 40, 28, 41, 247, 255, 248, 15, 104, 42, 75, 45, 28, 4, 64, 19, 43, 5, 209, 50, - 35, 192, 1, 27, 64, 26, 35, 128, 0, 219, 66, 154, 208, 43, 40, 0, 209, 43, 32, 100, 247, 254, 255, 121, 75, 38, 74, - 38, 104, 24, 96, 26, 154, 7, 35, 22, 147, 1, 24, 179, 147, 2, 35, 128, 144, 6, 33, 128, 152, 5, 34, 6, 0, 91, 148, 0, - 247, 255, 254, 231, 30, 4, 219, 12, 28, 123, 32, 0, 4, 27, 144, 0, 144, 1, 144, 2, 12, 27, 152, 5, 33, 0, 34, 5, 247, - 255, 254, 217, 28, 4, 75, 21, 154, 6, 96, 26, 44, 0, 219, 2, 224, 8, 36, 1, 66, 100, 155, 4, 59, 1, 147, 4, 43, 0, 209, - 169, 44, 0, 219, 10, 0, 187, 25, 219, 0, 219, 24, 246, 55, 1, 35, 0, 103, 243, 103, 55, 224, 1, 36, 1, 66, 100, 176, - 9, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 60, 0, 0, 32, 1, 0, 0, 24, 3, 0, 0, 25, 3, 0, 0, 16, 1, 0, - 0, 32, 5, 19, 112, 96, 56, 0, 6, 26, 128, 181, 248, 76, 25, 28, 6, 104, 35, 104, 157, 35, 1, 96, 171, 247, 255, 255, - 97, 30, 7, 218, 24, 104, 35, 33, 55, 104, 155, 28, 52, 104, 90, 32, 10, 64, 10, 96, 90, 52, 16, 247, 254, 255, 18, 0, - 164, 75, 15, 25, 44, 96, 99, 32, 50, 247, 254, 255, 11, 75, 13, 32, 100, 96, 99, 247, 254, 255, 6, 75, 11, 96, 99, 75, - 7, 32, 55, 104, 26, 104, 146, 104, 81, 64, 1, 96, 81, 104, 27, 34, 4, 104, 155, 28, 56, 96, 154, 188, 248, 188, 2, 71, - 8, 70, 192, 19, 112, 96, 60, 0, 0, 24, 3, 0, 0, 25, 3, 0, 0, 16, 1, 181, 240, 176, 137, 75, 126, 34, 0, 104, 31, 0, - 131, 24, 27, 0, 219, 28, 30, 147, 5, 54, 88, 24, 251, 28, 4, 25, 190, 103, 26, 32, 50, 247, 254, 254, 218, 37, 3, 150, - 6, 72, 118, 247, 255, 251, 219, 32, 50, 247, 254, 254, 210, 35, 22, 147, 1, 155, 6, 34, 0, 147, 2, 35, 128, 146, 0, - 28, 48, 33, 128, 34, 6, 0, 91, 247, 255, 254, 69, 40, 0, 218, 12, 72, 109, 247, 255, 251, 198, 32, 50, 247, 254, 254, - 189, 61, 1, 32, 100, 247, 254, 254, 185, 45, 0, 209, 222, 224, 2, 154, 5, 24, 187, 224, 38, 28, 32, 247, 255, 249, 106, - 75, 100, 66, 152, 209, 0, 224, 184, 32, 100, 247, 254, 254, 169, 72, 98, 247, 255, 251, 172, 35, 22, 147, 1, 155, 6, - 34, 0, 147, 2, 35, 128, 146, 0, 28, 48, 33, 128, 34, 6, 0, 91, 247, 255, 254, 25, 40, 0, 218, 6, 72, 90, 53, 1, 247, - 255, 251, 153, 45, 3, 209, 221, 224, 12, 0, 163, 25, 27, 0, 219, 24, 251, 110, 26, 75, 85, 66, 154, 209, 46, 28, 32, - 247, 255, 249, 199, 76, 83, 224, 144, 28, 32, 247, 255, 249, 14, 32, 100, 247, 254, 254, 125, 28, 32, 247, 255, 249, - 52, 74, 73, 66, 144, 209, 0, 224, 130, 32, 100, 247, 254, 254, 115, 72, 75, 247, 255, 251, 118, 35, 0, 154, 6, 147, - 0, 35, 22, 147, 1, 35, 128, 146, 2, 28, 48, 33, 128, 34, 6, 0, 91, 247, 255, 253, 227, 40, 0, 218, 7, 72, 63, 61, 1, - 247, 255, 251, 99, 45, 0, 209, 215, 76, 64, 224, 102, 72, 64, 247, 255, 251, 92, 28, 99, 147, 5, 4, 27, 12, 27, 147, - 7, 0, 163, 25, 28, 0, 228, 37, 0, 25, 63, 32, 50, 247, 254, 254, 73, 72, 57, 28, 41, 247, 255, 251, 75, 35, 0, 147, - 0, 147, 1, 147, 2, 28, 48, 33, 0, 34, 5, 155, 7, 247, 255, 253, 187, 30, 4, 218, 7, 76, 50, 28, 41, 72, 50, 27, 100, - 247, 255, 251, 57, 53, 1, 224, 3, 72, 48, 28, 41, 247, 255, 251, 51, 155, 5, 34, 0, 103, 250, 103, 59, 44, 0, 218, 47, - 33, 0, 28, 48, 247, 255, 254, 5, 32, 50, 247, 254, 254, 32, 72, 40, 247, 255, 251, 35, 35, 22, 147, 1, 155, 6, 34, 0, - 147, 2, 35, 128, 146, 0, 28, 48, 33, 128, 34, 6, 0, 91, 247, 255, 253, 144, 30, 4, 218, 6, 72, 32, 247, 255, 251, 17, - 34, 0, 103, 58, 76, 31, 224, 2, 72, 31, 247, 255, 251, 10, 53, 1, 44, 0, 218, 7, 15, 233, 34, 0, 35, 4, 66, 171, 65, - 74, 6, 18, 42, 0, 209, 172, 44, 0, 219, 4, 72, 24, 247, 255, 250, 250, 224, 0, 76, 6, 176, 9, 28, 32, 188, 240, 188, - 2, 71, 8, 70, 192, 19, 112, 96, 60, 19, 112, 97, 75, 19, 112, 97, 0, 255, 255, 251, 161, 19, 112, 97, 30, 19, 112, 97, - 69, 149, 11, 32, 119, 255, 255, 251, 160, 19, 112, 97, 106, 255, 255, 247, 103, 19, 112, 97, 155, 19, 112, 97, 181, - 255, 255, 224, 192, 19, 112, 97, 212, 19, 112, 97, 243, 19, 112, 98, 55, 19, 112, 98, 14, 255, 255, 247, 62, 19, 112, - 98, 52, 19, 112, 98, 87, 181, 16, 75, 10, 34, 1, 104, 27, 28, 4, 104, 155, 96, 154, 247, 255, 248, 117, 40, 0, 219, - 2, 28, 32, 247, 255, 254, 202, 75, 3, 34, 4, 104, 27, 104, 155, 96, 154, 188, 16, 188, 2, 71, 8, 19, 112, 96, 60, 181, - 240, 176, 141, 144, 6, 32, 22, 28, 13, 240, 2, 249, 151, 30, 7, 209, 0, 224, 214, 32, 10, 247, 254, 253, 159, 35, 18, - 147, 0, 152, 6, 28, 57, 34, 1, 35, 0, 247, 255, 253, 197, 40, 0, 218, 54, 72, 180, 247, 255, 250, 152, 76, 179, 104, - 32, 247, 255, 255, 202, 28, 6, 32, 20, 247, 254, 253, 138, 75, 176, 104, 32, 104, 27, 0, 130, 104, 155, 51, 68, 24, - 155, 104, 26, 46, 0, 219, 4, 75, 172, 64, 26, 75, 172, 66, 154, 208, 13, 247, 255, 254, 81, 32, 20, 247, 254, 253, 118, - 75, 166, 104, 27, 104, 154, 75, 164, 50, 68, 104, 27, 0, 155, 24, 211, 104, 27, 32, 30, 247, 254, 253, 106, 35, 18, - 147, 0, 152, 6, 28, 57, 34, 1, 35, 0, 247, 255, 253, 144, 30, 4, 218, 1, 72, 158, 224, 165, 28, 57, 34, 18, 28, 40, - 240, 3, 249, 9, 28, 56, 240, 2, 249, 102, 120, 170, 120, 235, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, 19, 4, 27, 12, - 26, 112, 234, 14, 27, 122, 42, 112, 171, 122, 107, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, 19, 4, 27, 12, 26, 114, - 106, 14, 27, 122, 170, 114, 43, 122, 235, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, 19, 4, 27, 12, 26, 114, 234, 14, - 27, 123, 42, 114, 171, 123, 107, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, 19, 4, 27, 12, 26, 14, 27, 115, 43, 124, - 107, 115, 106, 0, 88, 24, 192, 0, 128, 24, 192, 240, 2, 249, 13, 14, 3, 116, 171, 12, 3, 116, 235, 10, 3, 117, 43, 117, - 104, 40, 0, 209, 1, 72, 120, 224, 204, 124, 107, 33, 0, 0, 90, 24, 210, 0, 146, 24, 210, 240, 3, 248, 250, 33, 0, 145, - 10, 145, 5, 28, 46, 225, 51, 32, 9, 240, 2, 248, 242, 30, 4, 208, 49, 154, 5, 28, 33, 6, 19, 14, 27, 147, 3, 35, 9, - 147, 0, 34, 2, 155, 3, 152, 6, 247, 255, 253, 32, 124, 179, 124, 245, 6, 27, 4, 45, 67, 29, 125, 51, 28, 33, 2, 27, - 67, 29, 125, 115, 34, 9, 67, 29, 155, 10, 24, 237, 28, 40, 240, 3, 248, 145, 28, 32, 240, 2, 248, 238, 120, 170, 120, - 235, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, 19, 4, 27, 12, 28, 14, 27, 112, 171, 112, 236, 28, 32, 240, 2, 248, 192, - 30, 7, 209, 6, 28, 53, 72, 86, 36, 4, 247, 255, 249, 205, 66, 100, 225, 3, 120, 235, 152, 6, 147, 0, 28, 57, 34, 2, - 155, 3, 247, 255, 252, 235, 40, 0, 218, 5, 28, 4, 72, 78, 28, 53, 247, 255, 249, 188, 224, 238, 120, 41, 145, 2, 121, - 43, 0, 216, 24, 192, 0, 64, 240, 2, 248, 159, 14, 3, 114, 107, 12, 3, 114, 171, 10, 3, 114, 235, 115, 40, 40, 0, 209, - 2, 28, 53, 72, 68, 224, 93, 155, 2, 154, 2, 26, 228, 25, 210, 146, 3, 148, 4, 121, 43, 33, 0, 0, 218, 24, 210, 0, 82, - 240, 3, 248, 134, 33, 0, 145, 11, 145, 8, 224, 177, 122, 107, 122, 172, 6, 27, 4, 36, 67, 28, 122, 235, 154, 11, 2, - 27, 67, 28, 123, 43, 153, 3, 67, 28, 24, 164, 28, 32, 34, 9, 240, 3, 248, 47, 120, 35, 147, 2, 121, 35, 0, 216, 26, - 192, 240, 2, 248, 106, 14, 3, 115, 163, 12, 3, 115, 227, 10, 3, 116, 35, 116, 96, 40, 0, 209, 2, 28, 53, 72, 42, 224, - 40, 153, 3, 155, 4, 28, 10, 153, 2, 24, 82, 153, 2, 146, 3, 26, 91, 147, 4, 121, 35, 33, 0, 0, 218, 26, 210, 240, 3, - 248, 79, 152, 3, 153, 4, 247, 255, 248, 33, 35, 11, 144, 2, 70, 106, 92, 154, 115, 98, 155, 2, 43, 0, 208, 31, 28, 24, - 240, 2, 248, 64, 14, 3, 114, 99, 12, 3, 114, 163, 10, 3, 114, 227, 115, 32, 40, 0, 209, 6, 72, 23, 28, 53, 36, 4, 247, - 255, 249, 70, 66, 100, 224, 119, 153, 3, 154, 2, 240, 2, 255, 234, 153, 3, 155, 4, 28, 10, 153, 2, 24, 82, 153, 2, 146, - 3, 26, 91, 147, 4, 34, 0, 146, 7, 146, 9, 224, 68, 70, 192, 19, 112, 98, 121, 19, 112, 107, 80, 19, 112, 96, 60, 0, - 0, 49, 5, 0, 0, 16, 5, 19, 112, 98, 163, 19, 112, 98, 198, 19, 112, 98, 96, 19, 112, 98, 236, 19, 112, 99, 22, 19, 112, - 99, 56, 19, 112, 99, 89, 123, 162, 123, 227, 6, 18, 4, 27, 67, 19, 124, 34, 153, 7, 2, 18, 67, 19, 124, 98, 67, 19, - 24, 201, 145, 2, 28, 8, 34, 7, 153, 3, 240, 2, 255, 178, 154, 2, 153, 3, 120, 19, 24, 201, 145, 3, 153, 2, 121, 18, - 121, 75, 2, 18, 67, 26, 4, 18, 10, 17, 14, 19, 67, 11, 4, 27, 153, 2, 12, 26, 14, 27, 113, 11, 113, 74, 154, 9, 155, - 7, 50, 1, 51, 7, 146, 9, 147, 7, 121, 35, 153, 9, 66, 153, 211, 207, 154, 8, 155, 11, 50, 1, 51, 18, 146, 8, 147, 11, - 121, 43, 153, 8, 66, 153, 210, 0, 231, 72, 28, 56, 240, 1, 255, 230, 154, 5, 155, 10, 50, 1, 51, 13, 146, 5, 147, 10, - 39, 0, 124, 115, 153, 5, 66, 153, 210, 0, 230, 198, 28, 53, 36, 0, 47, 0, 208, 7, 28, 56, 240, 1, 255, 211, 44, 0, 208, - 2, 28, 40, 247, 254, 255, 151, 176, 13, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 181, 248, 28, 4, 38, 3, 39, 64, 224, - 79, 104, 160, 240, 2, 255, 38, 10, 3, 64, 51, 28, 5, 43, 2, 208, 7, 107, 227, 108, 34, 105, 89, 24, 138, 0, 65, 12, - 73, 26, 82, 97, 90, 108, 35, 43, 0, 208, 59, 66, 47, 208, 57, 75, 36, 120, 27, 43, 0, 208, 2, 72, 35, 247, 255, 248, - 157, 6, 235, 213, 6, 75, 32, 120, 27, 43, 0, 208, 2, 72, 32, 247, 255, 248, 148, 7, 107, 213, 6, 75, 27, 120, 27, 43, - 0, 208, 2, 72, 28, 247, 255, 248, 139, 6, 171, 213, 6, 75, 23, 120, 27, 43, 0, 208, 2, 72, 25, 247, 255, 248, 130, 7, - 43, 213, 6, 75, 18, 120, 27, 43, 0, 208, 2, 72, 21, 247, 255, 248, 121, 75, 15, 120, 27, 43, 0, 208, 2, 72, 19, 247, - 255, 248, 114, 75, 11, 120, 27, 43, 0, 208, 8, 72, 16, 247, 255, 248, 107, 224, 4, 107, 164, 44, 0, 209, 173, 32, 0, - 224, 3, 107, 227, 32, 1, 66, 64, 97, 88, 75, 11, 34, 0, 104, 27, 100, 26, 188, 248, 188, 2, 71, 8, 19, 112, 106, 248, - 19, 112, 99, 118, 19, 112, 99, 131, 19, 112, 99, 139, 19, 112, 99, 160, 19, 112, 99, 179, 19, 112, 99, 190, 19, 112, - 105, 116, 19, 112, 96, 60, 181, 240, 176, 131, 28, 5, 35, 9, 7, 193, 213, 60, 75, 58, 120, 27, 43, 0, 208, 2, 72, 57, - 247, 255, 248, 59, 75, 57, 104, 28, 44, 0, 208, 9, 107, 96, 33, 96, 240, 3, 248, 79, 107, 96, 33, 96, 240, 1, 254, 239, - 107, 164, 231, 243, 76, 51, 104, 35, 43, 0, 208, 24, 108, 88, 33, 32, 240, 3, 248, 65, 104, 35, 33, 32, 108, 88, 240, - 1, 254, 224, 104, 35, 34, 128, 4, 82, 97, 26, 74, 43, 108, 88, 104, 18, 33, 32, 107, 82, 97, 90, 240, 3, 248, 32, 104, - 35, 105, 154, 75, 39, 96, 26, 75, 35, 104, 24, 247, 255, 255, 78, 35, 0, 40, 0, 208, 4, 74, 36, 104, 18, 100, 19, 35, - 5, 66, 91, 7, 106, 213, 40, 75, 26, 120, 27, 43, 0, 208, 2, 72, 31, 247, 254, 255, 251, 33, 128, 1, 137, 79, 28, 78, - 29, 35, 1, 34, 0, 145, 1, 104, 56, 104, 49, 104, 128, 0, 148, 48, 68, 25, 0, 70, 140, 104, 4, 69, 98, 209, 7, 32, 1, - 66, 32, 209, 10, 75, 22, 96, 24, 35, 1, 66, 91, 224, 5, 73, 20, 64, 12, 44, 3, 209, 1, 153, 1, 96, 1, 50, 1, 42, 4, - 209, 228, 43, 0, 208, 4, 34, 18, 66, 42, 208, 1, 35, 6, 66, 91, 176, 3, 28, 24, 188, 240, 188, 2, 71, 8, 70, 192, 19, - 112, 106, 248, 19, 112, 99, 207, 19, 112, 107, 20, 19, 112, 106, 252, 19, 112, 107, 8, 19, 112, 107, 4, 19, 112, 96, - 60, 19, 112, 99, 240, 19, 112, 107, 80, 19, 112, 107, 84, 0, 0, 32, 3, 181, 240, 176, 131, 147, 1, 70, 107, 51, 39, - 120, 30, 28, 5, 30, 115, 6, 27, 14, 27, 145, 0, 28, 23, 43, 15, 216, 61, 28, 4, 52, 160, 104, 32, 40, 0, 208, 56, 33, - 0, 34, 31, 240, 2, 254, 139, 104, 35, 74, 29, 96, 26, 28, 43, 51, 152, 104, 24, 240, 2, 254, 7, 104, 35, 96, 88, 28, - 56, 240, 2, 254, 2, 104, 35, 70, 105, 96, 152, 29, 202, 120, 17, 104, 35, 70, 106, 115, 25, 28, 209, 104, 35, 120, 10, - 115, 90, 104, 34, 35, 6, 46, 6, 217, 0, 35, 10, 28, 44, 115, 147, 52, 160, 104, 32, 153, 8, 28, 50, 48, 15, 240, 2, - 254, 34, 28, 43, 51, 144, 104, 24, 123, 105, 104, 35, 34, 31, 247, 255, 249, 214, 40, 31, 208, 6, 40, 0, 219, 5, 72, - 5, 224, 3, 32, 3, 66, 64, 224, 0, 32, 0, 176, 3, 188, 240, 188, 2, 71, 8, 85, 83, 66, 67, 255, 255, 216, 238, 181, 240, - 28, 4, 176, 131, 52, 160, 28, 5, 145, 0, 28, 23, 33, 255, 34, 13, 104, 32, 240, 2, 254, 62, 28, 43, 51, 144, 104, 24, - 123, 41, 104, 35, 34, 13, 247, 255, 249, 176, 40, 13, 208, 1, 40, 0, 218, 40, 40, 0, 219, 43, 28, 43, 51, 160, 104, - 28, 104, 32, 240, 2, 253, 174, 144, 1, 104, 96, 240, 2, 253, 170, 28, 6, 104, 160, 240, 2, 253, 166, 74, 16, 153, 1, - 123, 35, 66, 145, 209, 21, 47, 0, 208, 0, 96, 56, 154, 0, 42, 0, 208, 0, 112, 19, 53, 152, 104, 43, 66, 158, 209, 12, - 28, 179, 4, 27, 12, 54, 12, 27, 4, 54, 67, 30, 96, 46, 32, 0, 224, 4, 72, 5, 224, 2, 72, 5, 224, 0, 72, 5, 176, 3, 188, - 240, 188, 2, 71, 8, 83, 66, 83, 85, 255, 255, 216, 237, 255, 255, 216, 236, 255, 255, 216, 235, 181, 240, 176, 135, - 74, 75, 75, 76, 120, 18, 28, 6, 104, 31, 42, 0, 208, 1, 73, 74, 224, 0, 73, 74, 96, 25, 28, 51, 51, 144, 104, 24, 75, - 72, 104, 27, 104, 153, 105, 195, 49, 68, 0, 155, 24, 203, 104, 25, 35, 1, 66, 11, 209, 6, 74, 68, 36, 1, 96, 19, 75, - 62, 66, 100, 96, 31, 224, 113, 75, 60, 42, 0, 208, 1, 74, 60, 224, 0, 74, 60, 96, 26, 75, 62, 120, 27, 43, 0, 208, 17, - 136, 243, 34, 255, 147, 0, 33, 33, 35, 0, 147, 1, 147, 2, 247, 255, 249, 80, 28, 4, 28, 33, 72, 55, 247, 254, 254, 209, - 32, 60, 247, 254, 249, 200, 224, 6, 33, 0, 247, 255, 249, 166, 32, 5, 247, 254, 249, 193, 36, 0, 75, 41, 120, 26, 75, - 41, 42, 0, 208, 1, 74, 41, 224, 0, 74, 41, 28, 53, 96, 26, 53, 144, 44, 0, 218, 10, 32, 50, 247, 254, 249, 176, 104, - 40, 33, 0, 247, 255, 249, 142, 32, 5, 247, 254, 249, 169, 224, 48, 123, 49, 104, 40, 247, 255, 249, 134, 28, 4, 32, - 5, 247, 254, 249, 160, 72, 33, 28, 33, 247, 254, 254, 162, 44, 0, 219, 34, 123, 113, 104, 40, 247, 255, 249, 120, 28, - 4, 32, 5, 247, 254, 249, 146, 72, 27, 28, 33, 247, 254, 254, 148, 44, 0, 219, 20, 32, 10, 247, 254, 249, 137, 70, 105, - 49, 23, 104, 40, 247, 255, 249, 143, 28, 4, 28, 33, 72, 20, 247, 254, 254, 133, 44, 0, 219, 5, 72, 19, 247, 254, 254, - 128, 75, 7, 96, 31, 224, 4, 75, 6, 34, 1, 96, 31, 75, 8, 96, 26, 176, 7, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, - 112, 1, 15, 19, 112, 96, 56, 0, 15, 66, 64, 0, 3, 13, 64, 19, 112, 96, 60, 19, 112, 107, 84, 19, 112, 1, 13, 19, 112, - 100, 17, 19, 112, 100, 50, 19, 112, 100, 89, 19, 112, 100, 129, 19, 112, 100, 176, 181, 240, 176, 141, 146, 6, 70, 106, - 50, 79, 120, 18, 28, 31, 70, 107, 51, 95, 146, 7, 70, 106, 120, 27, 50, 83, 120, 18, 6, 27, 22, 27, 146, 8, 70, 106, - 50, 47, 147, 4, 35, 0, 112, 19, 74, 147, 77, 148, 28, 6, 145, 5, 147, 10, 96, 19, 147, 3, 36, 0, 28, 163, 43, 1, 216, - 0, 225, 6, 44, 0, 208, 33, 28, 48, 33, 0, 247, 255, 255, 38, 30, 4, 219, 1, 32, 5, 224, 0, 32, 60, 247, 254, 249, 37, - 75, 137, 104, 27, 104, 154, 75, 136, 50, 68, 104, 27, 0, 155, 24, 211, 104, 27, 44, 0, 218, 9, 154, 3, 50, 1, 146, 3, - 42, 2, 221, 0, 224, 232, 32, 10, 247, 254, 249, 17, 224, 219, 35, 0, 147, 3, 155, 4, 154, 8, 59, 1, 6, 27, 22, 27, 147, - 4, 42, 0, 208, 59, 104, 43, 74, 123, 147, 2, 120, 19, 43, 0, 208, 1, 75, 121, 224, 0, 75, 121, 96, 43, 154, 7, 155, - 18, 146, 1, 147, 0, 28, 58, 35, 0, 28, 48, 153, 5, 247, 255, 254, 66, 75, 110, 154, 2, 28, 4, 96, 26, 28, 131, 43, 1, - 216, 0, 224, 190, 40, 0, 218, 0, 224, 178, 155, 6, 34, 144, 25, 146, 28, 60, 147, 2, 146, 9, 224, 18, 155, 9, 123, 113, - 104, 24, 28, 34, 155, 2, 247, 255, 248, 69, 28, 131, 43, 1, 216, 0, 224, 159, 40, 0, 219, 69, 66, 160, 209, 67, 154, - 2, 26, 36, 24, 18, 146, 2, 44, 0, 209, 234, 224, 62, 104, 43, 74, 93, 147, 2, 120, 19, 43, 0, 208, 1, 75, 91, 224, 0, - 75, 91, 96, 43, 154, 7, 155, 18, 146, 1, 147, 0, 28, 58, 35, 128, 28, 48, 153, 5, 247, 255, 254, 6, 75, 80, 154, 2, - 28, 4, 96, 26, 40, 0, 218, 3, 72, 83, 28, 33, 247, 254, 253, 178, 28, 163, 43, 1, 217, 125, 44, 0, 218, 22, 224, 113, - 28, 51, 51, 144, 104, 24, 123, 49, 28, 58, 155, 6, 247, 255, 248, 10, 30, 4, 218, 3, 72, 74, 28, 33, 247, 254, 253, - 158, 28, 163, 43, 1, 217, 96, 44, 0, 219, 5, 66, 188, 208, 5, 224, 2, 47, 0, 209, 231, 224, 1, 76, 68, 224, 86, 75, - 59, 74, 61, 104, 27, 147, 2, 120, 19, 43, 0, 208, 1, 75, 59, 224, 0, 75, 59, 74, 54, 70, 105, 96, 19, 28, 48, 49, 47, - 170, 10, 247, 255, 254, 33, 30, 4, 218, 34, 72, 58, 28, 33, 247, 254, 253, 121, 28, 99, 209, 3, 75, 46, 154, 2, 96, - 26, 224, 65, 28, 52, 52, 144, 104, 32, 123, 49, 247, 255, 248, 73, 123, 51, 32, 15, 104, 34, 64, 24, 9, 219, 1, 27, - 106, 81, 24, 27, 32, 1, 64, 152, 67, 129, 98, 81, 70, 105, 28, 48, 49, 47, 170, 10, 247, 255, 253, 253, 28, 4, 75, 31, - 154, 2, 96, 26, 28, 163, 43, 1, 217, 34, 155, 10, 43, 0, 208, 7, 75, 37, 104, 27, 43, 0, 208, 3, 70, 107, 34, 1, 51, - 47, 112, 26, 44, 0, 219, 12, 75, 32, 36, 0, 104, 27, 43, 0, 208, 7, 70, 107, 51, 47, 120, 27, 43, 0, 208, 2, 76, 28, - 224, 0, 28, 4, 155, 4, 43, 0, 221, 2, 44, 0, 218, 0, 230, 247, 34, 0, 44, 0, 218, 0, 34, 1, 75, 9, 96, 26, 154, 21, - 42, 0, 208, 3, 70, 107, 51, 47, 120, 27, 112, 19, 155, 22, 43, 0, 208, 2, 155, 10, 154, 22, 96, 19, 176, 13, 28, 32, - 188, 240, 188, 2, 71, 8, 19, 112, 107, 84, 19, 112, 96, 56, 19, 112, 96, 60, 19, 112, 107, 80, 19, 112, 1, 15, 0, 15, - 66, 64, 0, 3, 13, 64, 19, 112, 100, 186, 19, 112, 100, 205, 255, 255, 216, 233, 19, 112, 100, 229, 19, 112, 107, 28, - 255, 255, 216, 234, 181, 240, 28, 3, 176, 143, 51, 160, 145, 7, 104, 30, 33, 128, 70, 111, 1, 9, 55, 55, 37, 0, 172, - 9, 24, 118, 144, 6, 112, 61, 28, 32, 33, 0, 34, 16, 240, 2, 251, 218, 46, 0, 208, 87, 35, 6, 147, 1, 35, 1, 147, 2, - 35, 10, 147, 5, 153, 7, 35, 0, 152, 6, 34, 0, 149, 4, 148, 0, 151, 3, 247, 255, 254, 131, 28, 5, 28, 41, 72, 40, 247, - 254, 252, 216, 28, 107, 208, 69, 45, 0, 219, 2, 120, 59, 43, 0, 208, 64, 153, 7, 171, 9, 34, 3, 112, 26, 1, 74, 112, - 90, 34, 18, 113, 26, 34, 0, 113, 90, 28, 48, 33, 0, 34, 18, 240, 2, 251, 174, 75, 28, 53, 2, 209, 1, 74, 28, 224, 0, - 74, 28, 96, 26, 171, 9, 147, 0, 35, 6, 147, 1, 35, 0, 147, 2, 147, 3, 147, 4, 35, 10, 147, 5, 153, 7, 152, 6, 28, 50, - 35, 18, 247, 255, 254, 81, 28, 5, 28, 41, 72, 19, 247, 254, 252, 166, 45, 0, 219, 19, 120, 179, 33, 15, 70, 108, 64, - 25, 52, 55, 72, 15, 112, 33, 247, 254, 252, 155, 120, 35, 59, 2, 6, 27, 14, 27, 43, 2, 217, 3, 224, 3, 37, 4, 66, 109, - 224, 0, 77, 9, 176, 15, 28, 40, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 100, 244, 19, 112, 96, 56, 0, 38, 37, 160, - 0, 152, 150, 128, 19, 112, 101, 21, 19, 112, 101, 52, 255, 255, 216, 239, 181, 240, 176, 147, 70, 106, 35, 0, 50, 71, - 112, 19, 74, 197, 28, 4, 32, 16, 112, 16, 72, 196, 28, 34, 50, 152, 96, 16, 174, 12, 58, 8, 96, 17, 28, 8, 130, 115, - 28, 49, 130, 179, 247, 255, 249, 179, 28, 5, 28, 41, 72, 190, 247, 254, 252, 96, 45, 0, 218, 0, 225, 195, 75, 188, 104, - 27, 43, 0, 208, 37, 75, 187, 121, 50, 121, 153, 66, 145, 209, 23, 121, 217, 121, 114, 66, 145, 209, 19, 137, 25, 137, - 50, 66, 145, 209, 15, 137, 89, 137, 114, 66, 145, 209, 11, 123, 25, 123, 178, 66, 145, 209, 7, 123, 89, 123, 242, 66, - 145, 209, 3, 123, 154, 124, 51, 66, 154, 208, 8, 168, 12, 247, 254, 251, 12, 37, 1, 72, 171, 247, 254, 252, 53, 66, - 109, 225, 167, 170, 12, 121, 17, 75, 167, 38, 0, 113, 153, 121, 81, 113, 217, 137, 17, 129, 25, 137, 81, 129, 89, 123, - 145, 115, 25, 123, 209, 115, 89, 124, 18, 115, 154, 74, 161, 35, 128, 66, 91, 96, 19, 35, 0, 147, 10, 147, 5, 224, 221, - 138, 111, 138, 171, 154, 10, 4, 63, 67, 31, 24, 191, 122, 58, 72, 155, 0, 82, 153, 5, 247, 254, 252, 14, 34, 156, 35, - 0, 25, 18, 147, 9, 147, 6, 146, 11, 224, 191, 122, 123, 122, 189, 6, 27, 4, 45, 67, 29, 122, 251, 2, 27, 67, 29, 123, - 59, 67, 29, 155, 9, 24, 237, 121, 107, 43, 8, 209, 115, 121, 235, 43, 80, 209, 112, 121, 43, 43, 1, 217, 109, 154, 11, - 35, 1, 112, 19, 121, 169, 72, 137, 34, 1, 247, 254, 251, 234, 35, 0, 115, 102, 115, 38, 147, 8, 147, 7, 224, 49, 123, - 170, 123, 235, 6, 18, 4, 27, 67, 19, 124, 42, 2, 18, 67, 19, 124, 106, 67, 19, 154, 8, 24, 155, 120, 218, 42, 2, 209, - 28, 120, 154, 6, 17, 41, 0, 218, 11, 123, 33, 41, 0, 209, 8, 42, 0, 208, 6, 115, 34, 121, 25, 121, 91, 2, 9, 67, 25, - 72, 119, 224, 10, 123, 97, 41, 0, 209, 9, 42, 0, 208, 7, 115, 98, 121, 25, 121, 91, 2, 9, 72, 114, 67, 25, 247, 254, - 251, 184, 155, 7, 154, 8, 51, 1, 50, 7, 147, 7, 146, 8, 121, 43, 154, 7, 66, 154, 211, 201, 123, 33, 41, 0, 208, 93, - 123, 98, 42, 0, 208, 90, 72, 105, 247, 254, 251, 165, 75, 97, 123, 34, 168, 12, 116, 26, 123, 98, 116, 90, 121, 122, - 112, 34, 120, 170, 96, 98, 115, 218, 120, 235, 96, 163, 247, 254, 250, 105, 75, 91, 74, 97, 72, 97, 96, 26, 120, 33, - 104, 162, 247, 254, 251, 142, 28, 35, 51, 144, 70, 105, 104, 24, 49, 71, 247, 254, 254, 140, 40, 0, 218, 89, 224, 85, - 123, 170, 123, 235, 6, 18, 4, 27, 67, 19, 124, 42, 124, 104, 2, 18, 67, 19, 67, 24, 208, 1, 240, 1, 250, 129, 122, 106, - 122, 171, 6, 18, 4, 27, 67, 19, 122, 234, 123, 40, 2, 18, 67, 19, 115, 174, 115, 238, 116, 46, 116, 110, 67, 24, 208, - 1, 240, 1, 250, 112, 121, 107, 114, 110, 114, 174, 114, 238, 115, 46, 43, 9, 209, 4, 74, 72, 75, 63, 168, 12, 96, 26, - 224, 39, 43, 8, 209, 10, 121, 235, 43, 80, 209, 7, 121, 43, 43, 1, 217, 4, 121, 171, 74, 66, 26, 211, 74, 56, 96, 19, - 155, 6, 154, 9, 51, 1, 50, 18, 147, 6, 146, 9, 121, 59, 154, 6, 66, 154, 210, 0, 231, 58, 155, 5, 154, 10, 51, 1, 50, - 13, 147, 5, 146, 10, 173, 12, 124, 107, 154, 5, 66, 154, 210, 0, 231, 27, 72, 53, 247, 254, 251, 51, 28, 40, 247, 254, - 250, 3, 77, 49, 224, 148, 72, 50, 247, 254, 251, 43, 70, 107, 51, 71, 120, 25, 120, 34, 72, 48, 247, 254, 251, 36, 75, - 34, 74, 47, 96, 26, 28, 35, 51, 144, 104, 24, 120, 33, 247, 254, 254, 18, 40, 0, 218, 2, 72, 43, 247, 254, 251, 22, - 74, 42, 75, 27, 121, 225, 96, 26, 28, 35, 51, 144, 104, 24, 122, 226, 247, 254, 253, 245, 40, 0, 218, 2, 72, 37, 247, - 254, 251, 7, 72, 37, 70, 109, 247, 254, 251, 3, 53, 70, 35, 0, 112, 43, 28, 35, 51, 144, 104, 24, 136, 227, 33, 161, - 147, 0, 35, 1, 147, 1, 34, 254, 35, 0, 149, 2, 247, 254, 253, 109, 40, 0, 218, 56, 72, 27, 247, 254, 250, 238, 35, 8, - 115, 163, 224, 58, 70, 192, 19, 112, 96, 64, 44, 13, 224, 1, 19, 112, 101, 86, 19, 112, 107, 56, 19, 112, 107, 36, 19, - 112, 101, 128, 19, 112, 107, 0, 19, 112, 101, 166, 19, 112, 101, 205, 19, 112, 102, 4, 19, 112, 102, 18, 19, 112, 102, - 33, 255, 255, 251, 79, 19, 112, 102, 102, 255, 255, 177, 224, 255, 255, 216, 240, 19, 112, 102, 53, 19, 112, 102, 148, - 19, 112, 102, 189, 255, 255, 251, 78, 19, 112, 102, 222, 255, 255, 251, 77, 19, 112, 102, 252, 19, 112, 103, 43, 19, - 112, 103, 102, 120, 41, 72, 24, 49, 1, 6, 9, 14, 9, 115, 161, 247, 254, 250, 176, 28, 37, 53, 160, 104, 43, 43, 0, 209, - 7, 32, 129, 1, 64, 240, 1, 249, 147, 35, 31, 48, 31, 67, 152, 96, 40, 52, 160, 104, 35, 37, 0, 43, 0, 209, 4, 74, 12, - 75, 13, 37, 4, 96, 26, 66, 109, 74, 12, 75, 12, 96, 26, 45, 0, 208, 5, 75, 8, 72, 11, 104, 25, 247, 254, 250, 143, 224, - 2, 72, 9, 247, 254, 250, 139, 176, 19, 28, 40, 188, 240, 188, 2, 71, 8, 19, 112, 103, 141, 255, 255, 251, 75, 19, 112, - 107, 0, 0, 15, 66, 64, 19, 112, 96, 56, 19, 112, 103, 164, 19, 112, 103, 198, 181, 8, 33, 0, 247, 255, 251, 104, 188, - 8, 188, 2, 71, 8, 0, 0, 181, 240, 176, 139, 171, 8, 34, 18, 112, 26, 1, 74, 112, 90, 34, 0, 112, 154, 112, 218, 113, - 90, 28, 15, 28, 2, 33, 36, 50, 160, 113, 25, 104, 20, 34, 128, 1, 18, 24, 164, 144, 7, 44, 0, 208, 42, 38, 0, 33, 0, - 34, 36, 28, 32, 240, 2, 249, 63, 171, 8, 34, 6, 147, 0, 35, 10, 37, 0, 146, 1, 147, 5, 152, 7, 28, 57, 28, 34, 35, 36, - 149, 2, 149, 3, 149, 4, 247, 255, 251, 233, 40, 0, 219, 7, 120, 35, 34, 31, 64, 19, 43, 5, 208, 6, 43, 7, 209, 8, 224, - 3, 54, 1, 46, 2, 208, 9, 231, 220, 75, 6, 34, 1, 96, 26, 224, 4, 75, 4, 96, 29, 224, 1, 32, 4, 66, 64, 176, 11, 188, - 240, 188, 2, 71, 8, 19, 112, 107, 12, 181, 112, 176, 136, 28, 22, 28, 29, 34, 37, 171, 7, 112, 26, 1, 74, 112, 90, 28, - 2, 50, 160, 104, 20, 34, 128, 1, 18, 24, 164, 44, 0, 208, 42, 147, 0, 35, 2, 147, 1, 35, 0, 147, 2, 147, 3, 147, 4, - 35, 10, 147, 5, 28, 34, 35, 8, 247, 255, 251, 174, 40, 0, 219, 29, 168, 6, 28, 33, 34, 4, 240, 2, 248, 171, 45, 0, 208, - 1, 155, 6, 96, 43, 29, 33, 168, 6, 34, 4, 240, 2, 248, 162, 46, 0, 208, 1, 155, 6, 96, 51, 35, 128, 154, 6, 1, 27, 32, - 0, 66, 154, 209, 5, 75, 4, 34, 1, 96, 26, 224, 1, 32, 4, 66, 64, 176, 8, 188, 112, 188, 2, 71, 8, 19, 112, 107, 12, - 181, 240, 176, 131, 123, 131, 28, 15, 28, 5, 66, 187, 217, 72, 78, 40, 73, 40, 72, 41, 96, 49, 33, 0, 247, 254, 249, - 212, 75, 39, 28, 40, 96, 51, 28, 57, 247, 255, 252, 204, 30, 4, 219, 53, 74, 33, 28, 57, 96, 50, 28, 40, 247, 255, 255, - 86, 28, 4, 28, 33, 72, 32, 247, 254, 249, 193, 44, 0, 219, 40, 28, 43, 51, 160, 104, 26, 35, 128, 1, 27, 92, 211, 33, - 31, 64, 25, 72, 27, 247, 254, 249, 180, 28, 59, 29, 62, 51, 20, 0, 182, 0, 155, 25, 170, 147, 1, 28, 57, 24, 235, 28, - 40, 247, 255, 255, 130, 153, 1, 28, 4, 89, 75, 89, 114, 72, 18, 28, 33, 247, 254, 249, 160, 89, 114, 75, 17, 66, 154, - 217, 3, 154, 1, 89, 83, 43, 9, 216, 1, 36, 33, 66, 100, 74, 6, 75, 5, 96, 26, 224, 1, 36, 3, 66, 100, 176, 3, 28, 32, - 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 56, 0, 15, 66, 64, 19, 112, 103, 231, 1, 49, 45, 0, 19, 112, 104, 2, - 19, 112, 104, 22, 19, 112, 104, 43, 0, 0, 1, 255, 181, 240, 176, 141, 175, 9, 37, 40, 112, 61, 1, 77, 112, 125, 14, - 21, 112, 189, 70, 110, 12, 21, 36, 0, 112, 253, 113, 122, 10, 21, 54, 47, 10, 26, 112, 52, 113, 61, 113, 188, 113, 250, - 114, 59, 114, 124, 123, 130, 66, 138, 217, 28, 29, 10, 0, 146, 88, 18, 146, 7, 42, 0, 208, 22, 77, 15, 34, 1, 96, 42, - 154, 7, 151, 0, 67, 83, 34, 10, 146, 1, 34, 6, 146, 5, 154, 18, 148, 2, 150, 3, 148, 4, 247, 255, 250, 240, 96, 44, - 40, 0, 221, 7, 120, 51, 43, 0, 209, 3, 224, 3, 32, 3, 66, 64, 224, 0, 72, 3, 176, 13, 188, 240, 188, 2, 71, 8, 70, 192, - 19, 112, 107, 28, 255, 255, 216, 234, 181, 240, 176, 141, 147, 7, 1, 75, 175, 9, 37, 42, 70, 156, 112, 61, 35, 8, 70, - 101, 67, 43, 112, 123, 14, 21, 155, 7, 112, 189, 70, 110, 12, 21, 36, 0, 112, 253, 113, 122, 10, 21, 54, 47, 10, 26, - 112, 52, 113, 61, 113, 188, 113, 250, 114, 59, 114, 124, 123, 130, 66, 138, 217, 32, 29, 10, 0, 146, 88, 18, 70, 148, - 69, 164, 208, 26, 77, 17, 35, 1, 96, 43, 155, 7, 151, 0, 70, 98, 67, 90, 70, 148, 34, 1, 146, 2, 34, 6, 146, 5, 39, - 10, 154, 18, 70, 99, 151, 1, 150, 3, 148, 4, 247, 255, 250, 162, 96, 44, 40, 0, 221, 7, 120, 51, 43, 0, 209, 3, 224, - 3, 32, 3, 66, 64, 224, 0, 72, 3, 176, 13, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 28, 255, 255, 216, 234, 181, - 240, 176, 135, 28, 28, 147, 2, 28, 11, 51, 4, 0, 155, 145, 3, 88, 25, 28, 6, 28, 11, 67, 99, 32, 128, 2, 64, 28, 23, - 66, 131, 217, 2, 240, 2, 248, 151, 144, 2, 155, 3, 32, 1, 51, 4, 0, 155, 66, 64, 147, 5, 224, 28, 154, 2, 28, 37, 66, - 148, 217, 0, 28, 21, 4, 43, 12, 27, 147, 4, 155, 12, 28, 48, 147, 0, 153, 3, 28, 58, 155, 4, 247, 255, 255, 67, 40, - 0, 219, 12, 154, 4, 25, 127, 26, 164, 154, 5, 4, 36, 89, 147, 12, 36, 67, 93, 155, 12, 25, 91, 147, 12, 44, 0, 209, - 224, 176, 7, 188, 240, 188, 2, 71, 8, 181, 240, 176, 135, 28, 28, 147, 2, 28, 11, 51, 4, 0, 155, 145, 3, 88, 25, 28, - 6, 28, 11, 67, 99, 32, 128, 2, 64, 28, 23, 66, 131, 217, 2, 240, 2, 248, 89, 144, 2, 155, 3, 32, 1, 51, 4, 0, 155, 66, - 64, 147, 5, 224, 28, 154, 2, 28, 37, 66, 148, 217, 0, 28, 21, 4, 43, 12, 27, 147, 4, 155, 12, 28, 48, 147, 0, 153, 3, - 28, 58, 155, 4, 247, 255, 255, 73, 40, 0, 219, 12, 154, 4, 25, 127, 26, 164, 154, 5, 4, 36, 89, 147, 12, 36, 67, 93, - 155, 12, 25, 91, 147, 12, 44, 0, 209, 224, 176, 7, 188, 240, 188, 2, 71, 8, 181, 240, 176, 139, 74, 135, 35, 0, 128, - 19, 74, 135, 36, 0, 128, 19, 75, 134, 74, 135, 112, 28, 75, 135, 79, 135, 112, 28, 35, 120, 144, 8, 66, 91, 96, 19, - 28, 56, 153, 8, 247, 255, 251, 218, 40, 0, 218, 0, 224, 241, 34, 1, 75, 129, 115, 186, 104, 25, 41, 0, 208, 74, 121, - 91, 43, 0, 209, 3, 75, 126, 120, 27, 66, 26, 208, 29, 76, 122, 35, 0, 115, 163, 77, 124, 75, 124, 38, 1, 96, 43, 28, - 35, 51, 144, 104, 24, 136, 227, 33, 161, 147, 0, 28, 35, 51, 14, 147, 2, 34, 254, 35, 0, 150, 1, 247, 254, 250, 171, - 75, 117, 96, 43, 40, 0, 218, 1, 115, 166, 224, 188, 123, 163, 51, 1, 115, 163, 75, 108, 72, 113, 121, 29, 28, 41, 247, - 254, 248, 34, 78, 104, 28, 41, 28, 48, 247, 255, 254, 59, 28, 4, 28, 33, 72, 108, 247, 254, 248, 24, 28, 163, 43, 1, - 216, 9, 28, 48, 247, 255, 253, 154, 74, 94, 35, 121, 66, 91, 96, 19, 34, 0, 75, 91, 224, 61, 44, 0, 218, 0, 224, 153, - 75, 88, 34, 1, 112, 26, 75, 89, 112, 29, 224, 139, 38, 1, 113, 92, 37, 0, 28, 60, 150, 6, 55, 144, 28, 40, 30, 67, 65, - 152, 144, 9, 40, 0, 209, 5, 75, 84, 120, 27, 7, 217, 213, 1, 46, 0, 209, 41, 72, 87, 28, 41, 247, 253, 255, 236, 6, - 43, 14, 27, 28, 25, 72, 76, 147, 5, 247, 255, 254, 3, 144, 7, 153, 7, 72, 80, 247, 253, 255, 224, 155, 7, 51, 2, 43, - 1, 216, 16, 154, 9, 42, 0, 208, 13, 74, 73, 75, 70, 72, 67, 96, 26, 247, 255, 253, 91, 74, 63, 35, 121, 66, 91, 96, - 19, 75, 60, 34, 0, 112, 26, 224, 105, 155, 7, 43, 0, 218, 59, 46, 0, 208, 52, 78, 61, 75, 62, 73, 66, 96, 51, 136, 227, - 32, 0, 96, 8, 115, 160, 104, 56, 147, 0, 28, 35, 34, 1, 51, 14, 146, 1, 147, 2, 33, 161, 35, 0, 34, 254, 247, 254, 250, - 44, 75, 53, 144, 5, 96, 51, 40, 0, 218, 5, 73, 55, 35, 1, 32, 0, 115, 163, 96, 8, 224, 5, 123, 163, 34, 1, 51, 1, 115, - 163, 75, 42, 113, 90, 75, 40, 72, 49, 123, 155, 153, 5, 28, 26, 147, 6, 247, 253, 255, 153, 75, 38, 38, 0, 120, 27, - 7, 216, 213, 3, 153, 5, 15, 206, 224, 0, 53, 1, 154, 6, 66, 149, 219, 144, 224, 29, 33, 23, 70, 104, 74, 29, 92, 8, - 35, 1, 96, 19, 113, 16, 154, 8, 152, 8, 137, 17, 74, 19, 128, 17, 137, 65, 74, 19, 128, 17, 74, 19, 70, 105, 112, 19, - 34, 23, 75, 19, 92, 81, 112, 25, 74, 23, 75, 21, 32, 0, 96, 26, 75, 14, 34, 0, 96, 26, 224, 14, 74, 12, 35, 122, 66, - 91, 96, 19, 72, 12, 247, 255, 252, 239, 75, 8, 34, 0, 112, 26, 72, 20, 247, 253, 255, 97, 32, 3, 66, 64, 176, 11, 188, - 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 24, 19, 112, 107, 32, 19, 112, 107, 60, 19, 112, 107, 0, 19, 112, 96, 64, - 19, 114, 197, 128, 19, 112, 107, 36, 19, 112, 1, 14, 19, 112, 96, 56, 0, 152, 150, 128, 0, 15, 66, 64, 19, 112, 104, - 98, 19, 112, 104, 128, 19, 112, 104, 103, 19, 112, 107, 84, 19, 112, 104, 157, 19, 112, 104, 198, 181, 8, 74, 10, 104, - 19, 43, 0, 208, 13, 75, 9, 33, 16, 112, 25, 35, 0, 73, 8, 96, 19, 74, 8, 112, 11, 96, 19, 72, 7, 33, 0, 34, 20, 240, - 1, 254, 17, 188, 8, 188, 1, 71, 0, 19, 112, 107, 56, 19, 112, 96, 64, 19, 112, 107, 60, 19, 112, 107, 84, 19, 112, 107, - 36, 181, 240, 176, 135, 75, 120, 36, 0, 104, 29, 45, 0, 208, 0, 224, 229, 75, 118, 32, 0, 104, 27, 104, 155, 96, 157, - 247, 253, 248, 99, 74, 116, 35, 100, 66, 91, 96, 19, 74, 115, 35, 1, 96, 19, 74, 114, 72, 115, 112, 21, 74, 115, 112, - 19, 247, 253, 254, 251, 75, 114, 120, 27, 43, 1, 208, 4, 43, 2, 208, 6, 34, 0, 146, 4, 224, 6, 35, 1, 147, 4, 36, 1, - 224, 2, 34, 1, 146, 4, 36, 0, 0, 167, 224, 176, 75, 99, 74, 105, 104, 29, 0, 163, 25, 27, 0, 219, 24, 235, 103, 92, - 111, 27, 96, 20, 43, 0, 209, 54, 104, 171, 34, 1, 51, 68, 25, 219, 104, 27, 66, 26, 209, 2, 28, 32, 247, 253, 252, 223, - 78, 88, 34, 1, 104, 51, 104, 155, 51, 68, 25, 219, 104, 27, 66, 26, 208, 35, 28, 32, 247, 254, 251, 253, 35, 140, 0, - 219, 24, 192, 40, 1, 216, 4, 35, 100, 74, 80, 66, 91, 96, 19, 224, 130, 28, 32, 247, 254, 250, 140, 144, 3, 32, 20, - 247, 253, 249, 176, 104, 51, 104, 155, 51, 68, 25, 219, 104, 26, 155, 3, 43, 0, 219, 4, 75, 77, 64, 26, 75, 77, 66, - 154, 208, 2, 28, 32, 247, 254, 251, 220, 0, 160, 25, 0, 0, 192, 24, 43, 111, 27, 43, 0, 208, 100, 75, 64, 34, 1, 48, - 88, 96, 26, 24, 40, 247, 255, 254, 44, 144, 3, 40, 0, 209, 87, 75, 56, 34, 1, 96, 26, 79, 65, 74, 57, 96, 16, 28, 56, - 247, 252, 255, 230, 77, 52, 34, 4, 104, 43, 72, 62, 104, 155, 96, 154, 247, 253, 254, 132, 32, 100, 247, 253, 249, 123, - 32, 128, 1, 0, 240, 0, 253, 105, 78, 57, 75, 57, 34, 1, 96, 51, 75, 57, 144, 4, 112, 26, 104, 43, 154, 3, 104, 155, - 32, 0, 96, 154, 247, 252, 255, 201, 72, 53, 247, 253, 254, 108, 75, 52, 34, 0, 120, 25, 155, 4, 72, 51, 147, 0, 35, - 1, 247, 255, 253, 123, 144, 5, 28, 56, 247, 252, 255, 185, 104, 43, 34, 4, 104, 155, 96, 154, 70, 107, 34, 15, 92, 210, - 75, 40, 152, 4, 112, 26, 75, 43, 96, 51, 240, 0, 253, 91, 155, 5, 43, 0, 218, 7, 72, 40, 247, 253, 254, 75, 28, 32, - 247, 253, 252, 132, 76, 38, 224, 27, 72, 38, 247, 253, 254, 67, 75, 38, 104, 27, 43, 0, 208, 20, 52, 2, 224, 18, 28, - 32, 247, 253, 252, 118, 52, 1, 55, 4, 154, 4, 66, 148, 220, 0, 231, 74, 72, 18, 247, 252, 255, 139, 74, 7, 104, 19, - 34, 4, 104, 155, 96, 154, 75, 5, 104, 28, 176, 7, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 56, 19, 112, - 96, 60, 19, 112, 107, 0, 19, 112, 107, 84, 19, 112, 107, 60, 19, 112, 104, 227, 19, 112, 1, 15, 19, 112, 1, 12, 19, - 112, 107, 80, 0, 0, 57, 5, 0, 0, 16, 5, 19, 112, 17, 29, 19, 112, 105, 118, 19, 112, 96, 56, 0, 152, 150, 128, 19, 112, - 106, 248, 19, 112, 105, 167, 19, 112, 96, 64, 19, 114, 197, 128, 0, 15, 66, 64, 19, 112, 105, 140, 255, 255, 251, 162, - 19, 112, 105, 164, 19, 112, 107, 12, 181, 0, 30, 3, 208, 1, 34, 0, 96, 26, 74, 12, 32, 0, 120, 18, 42, 1, 209, 16, 74, - 10, 120, 18, 42, 16, 208, 12, 43, 0, 208, 4, 50, 4, 73, 8, 0, 146, 88, 82, 96, 26, 75, 5, 120, 26, 75, 5, 50, 20, 0, - 146, 88, 208, 188, 2, 71, 8, 70, 192, 19, 112, 107, 60, 19, 112, 96, 64, 19, 114, 197, 128, 181, 240, 176, 139, 75, - 78, 104, 27, 43, 0, 209, 0, 224, 139, 75, 77, 34, 16, 112, 26, 75, 76, 120, 26, 75, 76, 42, 0, 208, 1, 74, 76, 224, - 0, 74, 76, 78, 76, 96, 26, 172, 4, 35, 0, 28, 53, 130, 99, 130, 163, 53, 144, 28, 33, 104, 40, 247, 254, 251, 0, 75, - 71, 121, 34, 121, 153, 66, 145, 209, 23, 121, 217, 121, 98, 66, 145, 209, 19, 137, 25, 137, 34, 66, 145, 209, 15, 137, - 89, 137, 98, 66, 145, 209, 11, 123, 25, 123, 162, 66, 145, 209, 7, 123, 89, 123, 226, 66, 145, 209, 3, 123, 154, 124, - 35, 66, 154, 208, 6, 168, 4, 247, 253, 252, 101, 72, 56, 247, 253, 253, 143, 224, 80, 28, 32, 247, 253, 252, 94, 74, - 54, 75, 47, 104, 40, 96, 26, 120, 49, 247, 254, 248, 123, 40, 0, 219, 70, 104, 178, 42, 0, 208, 7, 6, 18, 104, 40, 121, - 241, 14, 18, 247, 254, 248, 98, 40, 0, 219, 61, 76, 41, 35, 0, 115, 163, 77, 36, 75, 42, 39, 1, 96, 43, 28, 35, 51, - 144, 104, 24, 136, 227, 33, 161, 147, 0, 28, 35, 51, 14, 147, 2, 34, 254, 35, 0, 151, 1, 247, 253, 255, 220, 75, 28, - 96, 43, 40, 0, 218, 6, 115, 167, 28, 32, 33, 0, 247, 254, 254, 77, 72, 30, 224, 34, 123, 163, 78, 25, 51, 1, 115, 163, - 75, 28, 28, 32, 96, 43, 121, 49, 247, 255, 248, 75, 75, 18, 96, 43, 40, 0, 219, 17, 121, 49, 28, 32, 247, 255, 250, - 213, 40, 0, 219, 13, 121, 50, 75, 9, 112, 26, 75, 19, 112, 31, 224, 8, 72, 19, 224, 6, 72, 19, 224, 4, 72, 19, 224, - 2, 72, 19, 224, 0, 72, 19, 176, 11, 188, 240, 188, 2, 71, 8, 19, 112, 107, 56, 19, 112, 96, 64, 19, 112, 1, 15, 19, - 112, 96, 56, 0, 15, 66, 64, 0, 3, 13, 64, 19, 114, 197, 128, 19, 112, 107, 36, 19, 112, 101, 128, 0, 152, 150, 128, - 255, 255, 252, 20, 1, 49, 45, 0, 19, 112, 107, 60, 255, 255, 252, 15, 255, 255, 252, 23, 255, 255, 252, 22, 255, 255, - 252, 19, 255, 255, 252, 18, 181, 240, 176, 131, 75, 57, 76, 58, 120, 26, 38, 1, 30, 83, 65, 154, 75, 56, 96, 34, 104, - 27, 43, 0, 208, 99, 75, 55, 0, 146, 104, 27, 104, 155, 28, 25, 49, 68, 24, 138, 104, 18, 66, 22, 208, 89, 34, 0, 32, - 0, 96, 154, 247, 252, 254, 74, 104, 32, 247, 254, 248, 191, 30, 7, 218, 3, 104, 32, 247, 254, 248, 186, 28, 7, 32, 60, - 247, 252, 255, 222, 77, 41, 72, 42, 104, 43, 28, 57, 104, 154, 75, 37, 50, 68, 104, 27, 38, 1, 0, 155, 24, 211, 104, - 27, 28, 26, 147, 1, 247, 253, 252, 212, 76, 32, 35, 1, 96, 35, 47, 0, 219, 48, 155, 1, 74, 32, 64, 26, 75, 32, 66, 154, - 209, 42, 74, 32, 35, 0, 112, 19, 34, 0, 96, 34, 247, 255, 254, 242, 74, 29, 75, 30, 96, 26, 40, 0, 219, 12, 74, 26, - 35, 0, 112, 22, 96, 35, 72, 27, 247, 252, 254, 16, 104, 43, 34, 4, 104, 155, 38, 0, 96, 154, 224, 17, 28, 57, 72, 23, - 96, 38, 247, 253, 252, 171, 75, 17, 34, 0, 112, 26, 32, 100, 247, 252, 255, 159, 72, 17, 247, 252, 253, 252, 104, 43, - 34, 4, 104, 155, 96, 154, 32, 100, 247, 252, 255, 149, 176, 3, 28, 48, 188, 240, 188, 2, 71, 8, 19, 112, 1, 12, 19, - 112, 107, 80, 19, 112, 107, 84, 19, 112, 96, 60, 19, 112, 105, 185, 0, 0, 49, 5, 0, 0, 16, 5, 19, 112, 107, 60, 0, 15, - 66, 64, 19, 112, 96, 56, 19, 112, 17, 29, 19, 112, 105, 213, 181, 240, 176, 131, 75, 30, 34, 0, 104, 27, 146, 1, 43, - 0, 208, 50, 247, 255, 255, 104, 75, 27, 104, 27, 43, 0, 209, 44, 75, 26, 120, 27, 43, 0, 208, 40, 77, 25, 79, 26, 76, - 26, 78, 27, 96, 47, 121, 49, 28, 32, 247, 255, 249, 243, 40, 0, 219, 29, 96, 47, 121, 49, 28, 32, 28, 11, 29, 10, 51, - 20, 0, 146, 0, 155, 25, 18, 25, 27, 247, 255, 250, 47, 40, 0, 219, 15, 75, 17, 104, 26, 42, 0, 208, 7, 121, 49, 35, - 128, 49, 4, 0, 137, 89, 9, 1, 27, 66, 153, 209, 3, 28, 19, 30, 90, 65, 147, 147, 1, 152, 1, 176, 3, 188, 240, 188, 2, - 71, 8, 19, 112, 107, 56, 19, 112, 107, 84, 19, 112, 107, 60, 19, 112, 96, 56, 0, 15, 66, 64, 19, 114, 197, 128, 19, - 112, 107, 36, 19, 112, 107, 12, 181, 240, 176, 135, 75, 66, 144, 3, 104, 27, 146, 4, 43, 0, 209, 7, 75, 64, 104, 27, - 43, 0, 208, 3, 0, 66, 75, 63, 8, 82, 96, 26, 75, 62, 104, 27, 43, 0, 208, 6, 75, 61, 104, 26, 42, 1, 221, 2, 34, 1, - 96, 26, 224, 101, 4, 9, 12, 9, 79, 56, 38, 4, 36, 0, 145, 5, 247, 255, 254, 255, 30, 67, 65, 152, 104, 59, 66, 64, 64, - 4, 43, 0, 208, 3, 74, 50, 104, 19, 43, 3, 208, 79, 44, 0, 219, 3, 75, 48, 120, 27, 43, 1, 208, 4, 75, 45, 34, 1, 36, - 1, 96, 26, 66, 100, 75, 43, 104, 27, 43, 0, 209, 61, 75, 38, 104, 26, 75, 42, 42, 0, 208, 1, 74, 41, 224, 0, 74, 41, - 96, 26, 28, 99, 208, 24, 32, 0, 247, 252, 253, 61, 77, 39, 34, 0, 104, 43, 72, 38, 104, 155, 96, 154, 154, 4, 75, 37, - 120, 25, 155, 5, 146, 0, 154, 3, 247, 255, 250, 237, 28, 4, 72, 34, 247, 252, 253, 43, 104, 43, 34, 4, 104, 155, 96, - 154, 74, 32, 75, 24, 96, 26, 44, 0, 218, 2, 75, 20, 34, 1, 96, 26, 75, 18, 104, 27, 43, 0, 208, 3, 75, 17, 104, 27, - 43, 0, 209, 15, 75, 15, 104, 27, 43, 0, 209, 5, 75, 14, 120, 27, 43, 1, 209, 1, 44, 0, 218, 7, 62, 1, 46, 0, 209, 162, - 67, 228, 15, 224, 224, 2, 32, 0, 224, 0, 32, 1, 176, 7, 188, 240, 188, 2, 71, 8, 19, 112, 106, 240, 19, 112, 107, 12, - 19, 112, 106, 192, 19, 112, 106, 196, 19, 112, 107, 84, 19, 112, 107, 60, 19, 112, 96, 56, 0, 152, 150, 128, 0, 45, - 198, 192, 19, 112, 96, 60, 19, 114, 197, 128, 19, 112, 96, 64, 19, 112, 17, 29, 0, 15, 66, 64, 181, 240, 176, 133, 75, - 39, 28, 7, 104, 27, 28, 22, 32, 0, 43, 0, 209, 67, 4, 9, 12, 9, 36, 0, 145, 3, 247, 255, 254, 111, 40, 0, 208, 2, 44, - 0, 219, 5, 224, 0, 36, 0, 74, 31, 120, 19, 43, 1, 208, 4, 75, 30, 34, 1, 36, 1, 96, 26, 66, 100, 75, 27, 104, 27, 43, - 0, 209, 234, 73, 26, 74, 27, 96, 17, 28, 98, 208, 22, 77, 26, 32, 0, 104, 42, 104, 146, 96, 147, 247, 252, 252, 182, - 75, 23, 28, 58, 120, 25, 72, 23, 155, 3, 150, 0, 247, 255, 250, 170, 28, 4, 72, 21, 247, 252, 252, 170, 104, 43, 34, - 4, 104, 155, 96, 154, 74, 18, 75, 13, 96, 26, 44, 0, 218, 2, 75, 9, 34, 1, 96, 26, 75, 7, 104, 27, 43, 0, 209, 194, - 44, 0, 219, 192, 32, 1, 176, 5, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 12, 19, 112, 107, 60, 19, 112, 107, - 84, 0, 45, 198, 192, 19, 112, 96, 56, 19, 112, 96, 60, 19, 112, 96, 64, 19, 114, 197, 128, 19, 112, 17, 29, 0, 15, 66, - 64, 181, 240, 176, 137, 74, 95, 104, 19, 43, 0, 209, 6, 75, 94, 104, 27, 105, 153, 96, 17, 106, 26, 75, 93, 96, 26, - 75, 92, 77, 90, 104, 28, 38, 0, 28, 55, 104, 43, 55, 8, 0, 191, 80, 252, 28, 32, 33, 0, 34, 96, 240, 1, 249, 245, 104, - 43, 33, 96, 88, 248, 240, 0, 249, 214, 52, 127, 33, 31, 54, 1, 67, 140, 46, 8, 209, 234, 37, 8, 38, 31, 28, 32, 33, - 0, 34, 96, 240, 1, 249, 227, 61, 1, 28, 32, 33, 96, 52, 127, 240, 0, 249, 195, 67, 180, 45, 0, 209, 241, 75, 72, 34, - 255, 96, 28, 75, 68, 39, 128, 104, 29, 35, 255, 2, 18, 4, 27, 76, 69, 38, 0, 5, 255, 146, 2, 147, 3, 33, 0, 96, 37, - 34, 96, 28, 40, 240, 1, 249, 198, 28, 40, 240, 0, 249, 152, 28, 50, 100, 104, 104, 35, 30, 81, 65, 138, 5, 210, 96, - 90, 104, 35, 34, 0, 96, 154, 104, 35, 33, 31, 97, 159, 53, 127, 67, 141, 104, 34, 28, 40, 146, 5, 240, 0, 249, 132, - 144, 6, 28, 40, 240, 0, 249, 128, 144, 1, 28, 40, 240, 0, 249, 124, 144, 7, 28, 40, 240, 0, 249, 120, 35, 255, 2, 27, - 153, 2, 147, 4, 34, 255, 155, 1, 4, 18, 64, 11, 146, 1, 153, 3, 154, 7, 2, 27, 64, 10, 10, 18, 67, 19, 14, 0, 154, 6, - 67, 3, 32, 224, 64, 16, 33, 2, 67, 8, 154, 5, 6, 0, 67, 3, 96, 19, 104, 34, 35, 128, 4, 91, 97, 19, 104, 34, 33, 96, - 97, 83, 204, 1, 54, 1, 240, 0, 249, 100, 46, 6, 209, 175, 76, 22, 77, 26, 104, 32, 105, 110, 240, 0, 249, 75, 144, 2, - 104, 32, 240, 0, 249, 71, 28, 7, 104, 32, 240, 0, 249, 67, 144, 3, 104, 32, 240, 0, 249, 63, 155, 4, 153, 1, 64, 31, - 155, 3, 2, 63, 64, 11, 10, 27, 67, 31, 154, 2, 14, 3, 28, 56, 67, 24, 35, 224, 64, 19, 33, 2, 67, 11, 6, 27, 67, 24, - 96, 48, 105, 104, 33, 96, 240, 0, 249, 57, 176, 9, 188, 240, 188, 1, 71, 0, 70, 192, 19, 112, 107, 76, 19, 112, 96, - 60, 19, 112, 106, 236, 19, 112, 107, 8, 19, 114, 195, 160, 181, 248, 75, 16, 39, 128, 104, 28, 38, 0, 4, 127, 28, 32, - 247, 252, 253, 241, 46, 3, 208, 15, 28, 37, 35, 31, 53, 127, 67, 157, 107, 104, 240, 1, 248, 182, 96, 103, 96, 32, 33, - 96, 28, 32, 240, 0, 249, 18, 54, 1, 28, 44, 231, 234, 28, 32, 33, 96, 240, 0, 249, 11, 188, 248, 188, 1, 71, 0, 19, - 112, 107, 8, 181, 240, 176, 131, 247, 255, 255, 12, 247, 255, 255, 214, 76, 65, 75, 66, 104, 34, 104, 24, 77, 65, 97, - 144, 104, 89, 78, 65, 97, 209, 104, 154, 79, 64, 96, 42, 104, 218, 105, 27, 96, 50, 96, 59, 33, 96, 240, 0, 249, 1, - 104, 35, 33, 0, 105, 154, 100, 211, 105, 154, 100, 145, 105, 155, 28, 24, 147, 1, 240, 0, 248, 209, 155, 1, 104, 34, - 100, 88, 104, 59, 105, 145, 108, 91, 34, 255, 2, 18, 64, 26, 2, 16, 34, 255, 4, 18, 64, 26, 10, 18, 67, 2, 14, 24, 67, - 2, 32, 224, 64, 3, 39, 2, 67, 59, 6, 24, 28, 19, 67, 3, 96, 11, 104, 35, 34, 128, 105, 153, 4, 18, 96, 74, 105, 154, - 33, 0, 96, 145, 105, 153, 34, 128, 5, 210, 97, 138, 105, 153, 34, 128, 4, 82, 97, 10, 105, 153, 97, 74, 105, 152, 33, - 96, 240, 0, 248, 179, 104, 35, 33, 96, 105, 216, 240, 0, 248, 194, 104, 35, 33, 0, 105, 218, 100, 211, 105, 218, 100, - 145, 105, 223, 28, 56, 240, 0, 248, 147, 33, 96, 100, 120, 104, 40, 240, 0, 248, 179, 104, 47, 104, 35, 28, 56, 100, - 251, 35, 0, 100, 187, 240, 0, 248, 134, 33, 96, 100, 120, 104, 40, 240, 0, 248, 146, 104, 48, 33, 96, 240, 0, 248, 162, - 104, 53, 104, 35, 33, 0, 100, 235, 100, 169, 28, 40, 240, 0, 248, 117, 33, 96, 100, 104, 104, 48, 240, 0, 248, 129, - 176, 3, 188, 240, 188, 1, 71, 0, 70, 192, 19, 112, 96, 60, 19, 114, 195, 160, 19, 112, 106, 244, 19, 112, 107, 72, 19, - 112, 107, 68, 181, 248, 76, 35, 75, 35, 96, 35, 240, 0, 248, 74, 40, 0, 219, 58, 240, 0, 249, 117, 74, 32, 104, 131, - 96, 32, 96, 26, 104, 32, 35, 1, 104, 130, 104, 17, 66, 11, 209, 252, 33, 252, 240, 0, 248, 91, 38, 128, 39, 128, 76, - 23, 37, 0, 4, 118, 1, 127, 104, 35, 33, 4, 108, 219, 81, 94, 104, 35, 108, 219, 89, 88, 53, 4, 240, 0, 248, 75, 66, - 189, 209, 243, 247, 255, 255, 65, 104, 35, 34, 2, 51, 84, 112, 26, 247, 252, 255, 185, 104, 35, 32, 0, 104, 154, 105, - 155, 108, 91, 97, 147, 104, 35, 34, 4, 104, 155, 96, 154, 104, 35, 74, 8, 104, 155, 96, 26, 104, 35, 104, 155, 104, - 27, 224, 1, 32, 1, 66, 64, 188, 248, 188, 2, 71, 8, 19, 112, 96, 60, 19, 114, 198, 36, 0, 1, 0, 32, 0, 1, 0, 33, 181, - 8, 33, 224, 72, 4, 2, 9, 240, 1, 249, 66, 75, 3, 96, 24, 23, 192, 188, 8, 188, 2, 71, 8, 19, 112, 163, 128, 19, 112, - 96, 68, 71, 112, 71, 112, 181, 16, 28, 4, 240, 1, 249, 62, 28, 32, 188, 16, 188, 2, 71, 8, 181, 16, 28, 4, 240, 1, 249, - 54, 28, 32, 188, 16, 188, 2, 71, 8, 181, 16, 28, 4, 240, 1, 249, 46, 28, 32, 188, 16, 188, 2, 71, 8, 181, 8, 240, 1, - 249, 71, 188, 8, 188, 1, 71, 0, 181, 8, 240, 1, 249, 65, 188, 8, 188, 1, 71, 0, 181, 8, 240, 1, 249, 59, 188, 8, 188, - 1, 71, 0, 181, 8, 75, 12, 28, 1, 34, 32, 104, 24, 240, 1, 249, 45, 40, 0, 209, 13, 72, 9, 240, 1, 249, 4, 240, 0, 249, - 63, 32, 200, 247, 252, 251, 253, 240, 0, 249, 68, 32, 200, 247, 252, 251, 248, 231, 244, 188, 8, 188, 2, 71, 8, 19, - 112, 96, 68, 19, 112, 105, 237, 181, 8, 75, 4, 28, 1, 104, 24, 240, 1, 248, 216, 188, 8, 188, 1, 71, 0, 70, 192, 19, - 112, 96, 68, 181, 248, 76, 13, 28, 15, 104, 33, 28, 30, 28, 24, 28, 21, 67, 81, 240, 1, 248, 228, 28, 56, 28, 41, 28, - 50, 247, 255, 252, 173, 35, 1, 40, 0, 208, 5, 104, 33, 28, 48, 67, 105, 240, 1, 248, 247, 35, 0, 28, 24, 188, 248, 188, - 2, 71, 8, 19, 114, 199, 32, 181, 16, 76, 11, 104, 32, 40, 0, 208, 3, 240, 0, 252, 39, 35, 0, 96, 35, 76, 8, 104, 35, - 43, 0, 209, 3, 72, 7, 247, 251, 252, 132, 96, 32, 74, 6, 35, 1, 66, 91, 96, 19, 188, 16, 188, 1, 71, 0, 19, 112, 107, - 96, 19, 112, 107, 88, 0, 0, 128, 32, 19, 112, 96, 24, 181, 56, 75, 14, 104, 24, 40, 0, 208, 1, 247, 251, 252, 139, 76, - 12, 75, 10, 104, 32, 37, 0, 96, 29, 40, 0, 208, 2, 240, 0, 251, 254, 96, 37, 75, 8, 104, 24, 40, 0, 208, 1, 240, 0, - 251, 91, 75, 5, 34, 0, 96, 26, 188, 56, 188, 1, 71, 0, 70, 192, 19, 112, 107, 88, 19, 112, 107, 96, 19, 112, 107, 92, - 181, 240, 176, 135, 75, 37, 28, 7, 104, 24, 145, 5, 40, 0, 208, 1, 247, 251, 252, 100, 75, 33, 77, 34, 34, 0, 96, 26, - 104, 43, 43, 0, 209, 23, 247, 255, 249, 93, 78, 31, 36, 0, 28, 48, 247, 255, 250, 124, 75, 29, 96, 24, 40, 0, 208, 43, - 154, 5, 104, 51, 33, 0, 144, 0, 146, 1, 72, 26, 34, 0, 148, 2, 240, 0, 249, 238, 96, 40, 40, 0, 208, 30, 77, 23, 104, - 44, 44, 0, 208, 11, 72, 22, 28, 57, 34, 6, 240, 0, 254, 210, 40, 0, 208, 19, 28, 32, 240, 0, 251, 179, 35, 0, 96, 43, - 75, 11, 28, 57, 104, 24, 240, 0, 251, 54, 75, 12, 28, 4, 96, 24, 40, 0, 208, 4, 72, 11, 28, 57, 34, 6, 240, 0, 254, - 234, 176, 7, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 88, 19, 112, 107, 92, 19, 114, 199, 32, 19, 114, - 199, 36, 19, 112, 72, 161, 19, 112, 107, 96, 19, 112, 96, 72, 181, 8, 28, 3, 28, 10, 32, 0, 28, 25, 35, 0, 240, 1, 248, - 56, 188, 8, 188, 1, 71, 0, 181, 8, 32, 1, 33, 0, 34, 0, 35, 0, 240, 1, 248, 46, 188, 8, 188, 2, 71, 8, 181, 8, 32, 17, - 33, 0, 34, 0, 35, 0, 240, 1, 248, 36, 188, 8, 188, 2, 71, 8, 181, 8, 32, 18, 33, 0, 34, 0, 35, 0, 240, 1, 248, 26, 188, - 8, 188, 2, 71, 8, 181, 56, 28, 5, 28, 12, 30, 19, 221, 4, 32, 2, 28, 41, 28, 34, 240, 1, 248, 13, 188, 56, 188, 1, 71, - 0, 181, 56, 28, 5, 28, 12, 30, 19, 221, 4, 32, 9, 28, 41, 28, 34, 240, 1, 248, 0, 188, 56, 188, 1, 71, 0, 181, 8, 28, - 3, 28, 10, 32, 5, 28, 25, 35, 0, 240, 0, 255, 245, 188, 8, 188, 1, 71, 0, 181, 8, 28, 3, 28, 10, 32, 6, 28, 25, 35, - 0, 240, 0, 255, 234, 188, 8, 188, 1, 71, 0, 181, 56, 28, 5, 28, 12, 28, 19, 28, 41, 28, 34, 32, 16, 240, 0, 255, 222, - 188, 56, 188, 2, 71, 8, 181, 8, 32, 128, 33, 0, 34, 0, 35, 0, 240, 0, 255, 212, 188, 8, 188, 1, 71, 0, 181, 8, 32, 129, - 33, 0, 34, 0, 35, 0, 240, 0, 255, 202, 188, 8, 188, 1, 71, 0, 181, 0, 35, 0, 224, 3, 51, 1, 6, 27, 14, 27, 8, 64, 40, - 0, 209, 249, 30, 88, 6, 0, 14, 0, 188, 2, 71, 8, 0, 0, 181, 240, 176, 133, 144, 1, 32, 84, 145, 3, 146, 2, 28, 30, 159, - 11, 247, 251, 251, 98, 28, 4, 30, 48, 209, 1, 32, 128, 0, 128, 247, 251, 251, 91, 28, 5, 32, 128, 2, 0, 97, 32, 247, - 255, 255, 218, 75, 125, 117, 32, 97, 227, 154, 10, 75, 124, 96, 37, 96, 30, 11, 211, 67, 115, 97, 163, 155, 12, 98, - 231, 43, 0, 208, 50, 33, 0, 28, 50, 28, 40, 240, 0, 254, 102, 35, 87, 112, 43, 35, 66, 112, 107, 35, 70, 112, 171, 35, - 83, 112, 235, 28, 48, 247, 255, 255, 188, 114, 40, 159, 10, 73, 111, 14, 59, 113, 43, 12, 59, 113, 107, 10, 59, 113, - 171, 113, 239, 105, 162, 35, 6, 66, 138, 217, 16, 73, 106, 35, 7, 66, 138, 217, 12, 73, 105, 35, 8, 66, 138, 217, 8, - 73, 104, 35, 9, 66, 138, 217, 4, 75, 103, 66, 147, 65, 155, 66, 91, 51, 10, 125, 34, 24, 155, 114, 107, 224, 6, 28, - 57, 152, 2, 34, 1, 28, 43, 159, 1, 240, 0, 248, 207, 120, 43, 120, 106, 6, 27, 4, 18, 67, 26, 120, 171, 2, 27, 67, 26, - 120, 235, 67, 26, 75, 91, 66, 154, 208, 4, 72, 90, 240, 0, 255, 49, 72, 90, 224, 40, 75, 90, 104, 27, 66, 95, 65, 95, - 46, 0, 208, 12, 47, 0, 208, 10, 28, 48, 247, 255, 255, 117, 122, 43, 66, 131, 208, 4, 72, 81, 240, 0, 255, 30, 72, 82, - 224, 21, 154, 10, 42, 0, 208, 31, 47, 0, 208, 29, 121, 42, 121, 107, 6, 18, 4, 27, 67, 19, 121, 170, 159, 10, 2, 18, - 67, 19, 121, 234, 67, 19, 66, 187, 208, 16, 72, 69, 240, 0, 255, 7, 72, 72, 240, 0, 255, 4, 72, 71, 240, 0, 255, 1, - 28, 32, 247, 251, 250, 232, 28, 40, 247, 251, 250, 229, 36, 0, 224, 100, 122, 46, 39, 1, 28, 58, 64, 178, 96, 98, 122, - 43, 28, 22, 147, 0, 114, 35, 121, 42, 121, 107, 6, 18, 4, 27, 67, 19, 121, 170, 121, 232, 2, 18, 67, 19, 67, 24, 105, - 33, 96, 224, 240, 0, 254, 168, 67, 112, 97, 160, 122, 107, 125, 34, 64, 159, 98, 99, 26, 155, 105, 226, 64, 216, 64, - 218, 4, 18, 12, 18, 4, 51, 12, 27, 133, 98, 50, 128, 4, 1, 133, 32, 0, 82, 30, 88, 24, 130, 66, 91, 64, 26, 155, 3, - 28, 37, 53, 72, 99, 99, 12, 201, 155, 0, 98, 39, 128, 42, 26, 127, 154, 1, 64, 223, 99, 34, 100, 39, 154, 2, 159, 12, - 99, 162, 47, 0, 209, 1, 100, 103, 224, 11, 30, 112, 24, 65, 66, 112, 64, 8, 247, 251, 250, 125, 141, 34, 100, 96, 8, - 210, 33, 255, 240, 0, 253, 155, 28, 35, 51, 72, 136, 25, 108, 32, 122, 35, 56, 1, 65, 25, 240, 0, 254, 104, 4, 2, 104, - 96, 12, 18, 28, 3, 59, 12, 135, 162, 66, 154, 217, 0, 135, 163, 247, 251, 250, 99, 35, 0, 100, 224, 101, 35, 176, 5, - 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 0, 4, 96, 144, 19, 112, 107, 108, 0, 63, 255, 255, 0, 127, 255, 255, 0, 255, - 255, 255, 1, 255, 255, 255, 3, 255, 255, 255, 87, 66, 70, 83, 19, 112, 106, 12, 19, 112, 106, 24, 19, 112, 107, 104, - 19, 112, 106, 34, 19, 112, 106, 63, 19, 112, 105, 116, 71, 56, 70, 192, 181, 240, 176, 165, 144, 9, 28, 24, 146, 10, - 147, 11, 145, 15, 247, 251, 250, 51, 35, 1, 74, 140, 77, 141, 66, 91, 96, 19, 104, 43, 28, 4, 43, 0, 209, 3, 152, 11, - 247, 251, 250, 39, 96, 40, 75, 136, 152, 11, 33, 0, 96, 24, 34, 1, 152, 10, 28, 35, 157, 9, 240, 0, 249, 24, 40, 0, - 208, 0, 224, 246, 35, 255, 0, 91, 92, 227, 43, 85, 209, 28, 51, 171, 51, 255, 92, 227, 43, 170, 209, 23, 28, 224, 73, - 125, 34, 4, 240, 0, 253, 138, 40, 0, 208, 16, 77, 123, 28, 32, 48, 54, 28, 41, 34, 3, 240, 0, 253, 129, 40, 0, 208, - 7, 28, 32, 48, 82, 28, 41, 34, 3, 240, 0, 253, 121, 40, 0, 209, 5, 168, 20, 33, 0, 34, 64, 240, 0, 253, 22, 224, 6, - 28, 33, 49, 191, 49, 255, 168, 20, 34, 64, 240, 0, 252, 204, 175, 32, 151, 5, 32, 228, 39, 1, 66, 127, 0, 64, 38, 0, - 173, 20, 144, 14, 151, 8, 122, 235, 122, 47, 122, 105, 122, 170, 147, 7, 120, 32, 120, 99, 6, 0, 4, 27, 67, 24, 120, - 163, 2, 27, 67, 24, 120, 227, 67, 24, 75, 97, 66, 152, 208, 3, 121, 40, 40, 0, 209, 0, 224, 162, 2, 9, 4, 18, 67, 17, - 67, 57, 159, 7, 6, 59, 67, 25, 145, 7, 121, 43, 43, 15, 209, 101, 33, 227, 32, 0, 0, 73, 159, 8, 144, 12, 35, 0, 145, - 13, 149, 4, 154, 7, 152, 10, 24, 210, 146, 8, 28, 17, 28, 35, 34, 1, 157, 9, 240, 0, 248, 169, 40, 0, 208, 0, 224, 135, - 72, 78, 153, 14, 92, 34, 92, 99, 2, 18, 4, 27, 67, 26, 77, 76, 155, 13, 152, 8, 92, 225, 93, 99, 67, 10, 6, 27, 67, - 19, 24, 192, 35, 235, 144, 8, 0, 91, 92, 227, 152, 10, 147, 16, 75, 69, 153, 8, 92, 227, 34, 1, 147, 17, 35, 236, 0, - 91, 92, 227, 157, 9, 147, 18, 75, 65, 92, 227, 147, 19, 28, 35, 240, 0, 248, 128, 40, 0, 209, 95, 120, 34, 120, 99, - 6, 18, 4, 27, 67, 19, 120, 162, 73, 54, 2, 18, 67, 19, 120, 226, 67, 19, 66, 139, 209, 10, 154, 43, 66, 150, 209, 2, - 144, 0, 155, 8, 224, 48, 152, 8, 66, 184, 208, 1, 54, 1, 28, 7, 153, 17, 157, 18, 2, 11, 4, 42, 152, 16, 153, 19, 67, - 19, 67, 3, 6, 10, 67, 19, 208, 53, 154, 12, 50, 1, 146, 12, 42, 8, 209, 162, 224, 47, 152, 10, 153, 7, 34, 1, 28, 35, - 159, 9, 240, 0, 248, 79, 40, 0, 209, 45, 120, 34, 120, 99, 6, 18, 4, 27, 67, 19, 120, 162, 73, 29, 2, 18, 67, 19, 120, - 226, 67, 19, 66, 139, 209, 27, 154, 43, 66, 150, 209, 15, 155, 7, 144, 0, 157, 44, 147, 1, 152, 9, 153, 15, 154, 10, - 155, 11, 149, 2, 247, 255, 253, 201, 28, 5, 28, 32, 247, 251, 249, 79, 224, 17, 159, 7, 152, 8, 66, 135, 208, 4, 54, - 1, 151, 8, 224, 1, 157, 4, 151, 8, 153, 5, 66, 141, 208, 1, 53, 16, 231, 65, 28, 32, 247, 251, 249, 61, 37, 0, 176, - 37, 28, 40, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 80, 19, 112, 107, 100, 19, 112, 107, 108, 19, 112, 106, 91, - 19, 112, 106, 96, 87, 66, 70, 83, 0, 0, 1, 199, 0, 0, 1, 201, 0, 0, 1, 215, 0, 0, 1, 217, 71, 40, 71, 56, 181, 16, 109, - 3, 28, 4, 43, 0, 208, 9, 72, 13, 240, 0, 253, 46, 72, 12, 240, 0, 253, 43, 72, 12, 240, 0, 253, 40, 224, 13, 104, 0, - 247, 251, 249, 14, 108, 224, 247, 251, 249, 11, 108, 96, 40, 0, 208, 1, 247, 251, 249, 6, 28, 32, 247, 251, 249, 3, - 188, 16, 188, 1, 71, 0, 19, 112, 106, 12, 19, 112, 106, 100, 19, 112, 105, 116, 181, 240, 28, 3, 51, 72, 176, 131, 136, - 26, 122, 3, 28, 5, 65, 26, 146, 1, 35, 1, 74, 48, 66, 91, 96, 19, 34, 0, 28, 14, 146, 0, 39, 0, 224, 79, 104, 43, 25, - 219, 123, 27, 43, 0, 208, 68, 106, 233, 155, 0, 49, 1, 24, 201, 34, 1, 107, 168, 108, 235, 107, 44, 240, 0, 248, 83, - 108, 233, 28, 48, 34, 6, 240, 0, 251, 106, 40, 0, 209, 51, 32, 12, 247, 251, 248, 177, 30, 6, 209, 9, 72, 32, 240, 0, - 252, 224, 72, 31, 240, 0, 252, 221, 72, 31, 240, 0, 252, 218, 224, 46, 28, 43, 51, 72, 96, 53, 96, 183, 136, 24, 247, - 251, 248, 158, 28, 3, 96, 112, 40, 0, 209, 12, 72, 21, 240, 0, 252, 203, 72, 21, 240, 0, 252, 200, 72, 20, 240, 0, 252, - 197, 28, 48, 247, 251, 248, 172, 224, 21, 106, 233, 154, 0, 49, 1, 24, 137, 107, 168, 107, 44, 154, 1, 240, 0, 248, - 28, 109, 43, 51, 1, 101, 43, 224, 9, 155, 0, 55, 1, 28, 26, 155, 1, 24, 210, 146, 0, 143, 171, 66, 159, 211, 172, 38, - 0, 176, 3, 28, 48, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 80, 19, 112, 106, 12, 19, 112, 106, 144, 19, 112, - 105, 116, 71, 32, 70, 192, 181, 16, 104, 3, 28, 4, 109, 26, 58, 1, 101, 26, 104, 64, 247, 251, 248, 123, 28, 32, 247, - 251, 248, 120, 188, 16, 188, 1, 71, 0, 0, 0, 181, 240, 176, 137, 104, 5, 147, 0, 106, 107, 146, 1, 28, 6, 30, 154, 28, - 8, 64, 208, 4, 2, 12, 18, 104, 104, 146, 4, 70, 132, 152, 4, 104, 116, 48, 128, 0, 64, 90, 32, 122, 47, 106, 42, 144, - 2, 36, 1, 40, 0, 209, 0, 224, 194, 58, 1, 64, 250, 27, 219, 146, 6, 63, 2, 28, 10, 64, 250, 147, 5, 155, 6, 28, 23, - 70, 96, 64, 31, 8, 131, 59, 1, 28, 26, 64, 10, 146, 7, 209, 0, 224, 133, 155, 2, 152, 5, 106, 233, 64, 131, 24, 121, - 147, 3, 24, 201, 75, 87, 104, 26, 108, 235, 66, 138, 209, 5, 74, 85, 28, 24, 104, 17, 74, 85, 104, 18, 224, 18, 107, - 44, 107, 168, 34, 1, 240, 0, 248, 165, 30, 4, 208, 0, 224, 149, 106, 235, 152, 3, 73, 76, 24, 251, 24, 27, 96, 11, 75, - 75, 108, 233, 104, 24, 75, 75, 104, 26, 240, 0, 250, 227, 154, 7, 104, 108, 0, 145, 155, 0, 26, 100, 66, 156, 217, 0, - 28, 28, 108, 235, 152, 1, 24, 89, 28, 34, 240, 0, 250, 213, 152, 0, 153, 1, 27, 0, 25, 9, 144, 0, 145, 1, 55, 1, 40, - 0, 208, 71, 154, 6, 66, 186, 210, 68, 155, 4, 104, 114, 51, 1, 4, 27, 12, 27, 147, 4, 51, 128, 0, 91, 90, 211, 36, 1, - 147, 2, 39, 0, 43, 0, 209, 54, 224, 95, 122, 43, 152, 0, 64, 216, 106, 43, 25, 194, 144, 3, 66, 154, 217, 1, 27, 219, - 147, 3, 154, 5, 106, 233, 155, 2, 24, 121, 64, 147, 24, 201, 107, 168, 107, 44, 154, 3, 155, 1, 240, 0, 248, 86, 40, - 0, 209, 70, 122, 43, 152, 3, 153, 0, 64, 152, 154, 3, 26, 9, 28, 3, 145, 0, 24, 191, 41, 0, 208, 15, 152, 6, 66, 184, - 210, 12, 154, 4, 104, 113, 50, 1, 4, 18, 12, 18, 146, 4, 50, 128, 0, 82, 90, 138, 146, 2, 42, 0, 208, 42, 39, 0, 153, - 1, 24, 201, 145, 1, 104, 107, 154, 0, 66, 154, 210, 197, 36, 0, 42, 0, 208, 34, 155, 5, 158, 2, 106, 233, 64, 158, 24, - 121, 107, 44, 107, 168, 25, 137, 108, 235, 34, 1, 240, 0, 248, 35, 30, 4, 209, 20, 106, 235, 108, 233, 24, 255, 75, - 11, 25, 190, 96, 30, 75, 11, 104, 24, 75, 11, 104, 26, 240, 0, 250, 99, 108, 233, 152, 1, 154, 0, 240, 0, 250, 94, 224, - 2, 36, 1, 224, 0, 28, 4, 176, 9, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 80, 19, 112, 107, 100, 19, 112, - 107, 108, 71, 32, 70, 192, 239, 0, 0, 204, 225, 47, 255, 30, 225, 160, 0, 0, 225, 160, 0, 0, 230, 0, 0, 16, 225, 47, - 255, 30, 230, 0, 0, 48, 225, 47, 255, 30, 230, 0, 0, 80, 225, 47, 255, 30, 230, 0, 0, 112, 225, 47, 255, 30, 230, 0, - 0, 144, 225, 47, 255, 30, 230, 0, 0, 176, 225, 47, 255, 30, 230, 0, 0, 208, 225, 47, 255, 30, 230, 0, 0, 240, 225, 47, - 255, 30, 230, 0, 1, 16, 225, 47, 255, 30, 230, 0, 1, 48, 225, 47, 255, 30, 230, 0, 1, 80, 225, 47, 255, 30, 230, 0, - 1, 112, 225, 47, 255, 30, 230, 0, 1, 144, 225, 47, 255, 30, 230, 0, 1, 176, 225, 47, 255, 30, 230, 0, 1, 208, 225, 47, - 255, 30, 230, 0, 1, 240, 225, 47, 255, 30, 230, 0, 2, 16, 225, 47, 255, 30, 230, 0, 2, 48, 225, 47, 255, 30, 230, 0, - 2, 80, 225, 47, 255, 30, 230, 0, 2, 112, 225, 47, 255, 30, 230, 0, 2, 144, 225, 47, 255, 30, 230, 0, 2, 176, 225, 47, - 255, 30, 230, 0, 2, 208, 225, 47, 255, 30, 230, 0, 2, 240, 225, 47, 255, 30, 230, 0, 3, 16, 225, 47, 255, 30, 230, 0, - 3, 48, 225, 47, 255, 30, 230, 0, 3, 80, 225, 47, 255, 30, 230, 0, 3, 112, 225, 47, 255, 30, 230, 0, 3, 144, 225, 47, - 255, 30, 230, 0, 3, 176, 225, 47, 255, 30, 230, 0, 3, 208, 225, 47, 255, 30, 230, 0, 3, 240, 225, 47, 255, 30, 230, - 0, 4, 16, 225, 47, 255, 30, 230, 0, 4, 48, 225, 47, 255, 30, 230, 0, 4, 80, 225, 47, 255, 30, 230, 0, 4, 112, 225, 47, - 255, 30, 230, 0, 4, 144, 225, 47, 255, 30, 230, 0, 4, 176, 225, 47, 255, 30, 230, 0, 4, 208, 225, 47, 255, 30, 230, - 0, 4, 240, 225, 47, 255, 30, 230, 0, 5, 16, 225, 47, 255, 30, 230, 0, 5, 48, 225, 47, 255, 30, 230, 0, 5, 80, 225, 47, - 255, 30, 230, 0, 6, 144, 225, 47, 255, 30, 230, 0, 7, 240, 225, 47, 255, 30, 230, 0, 8, 16, 225, 47, 255, 30, 230, 0, - 10, 16, 225, 47, 255, 30, 226, 144, 16, 0, 227, 176, 0, 4, 239, 0, 0, 171, 225, 47, 255, 30, 225, 160, 0, 0, 225, 160, - 0, 0, 180, 3, 70, 113, 8, 73, 0, 64, 0, 73, 90, 9, 0, 73, 68, 142, 188, 3, 71, 112, 226, 81, 32, 1, 1, 47, 255, 30, - 58, 0, 0, 54, 225, 80, 0, 1, 154, 0, 0, 34, 225, 17, 0, 2, 10, 0, 0, 35, 227, 17, 2, 14, 1, 160, 17, 129, 3, 160, 48, - 8, 19, 160, 48, 1, 227, 81, 2, 1, 49, 81, 0, 0, 49, 160, 18, 1, 49, 160, 50, 3, 58, 255, 255, 250, 227, 81, 1, 2, 49, - 81, 0, 0, 49, 160, 16, 129, 49, 160, 48, 131, 58, 255, 255, 250, 227, 160, 32, 0, 225, 80, 0, 1, 32, 64, 0, 1, 33, 130, - 32, 3, 225, 80, 0, 161, 32, 64, 0, 161, 33, 130, 32, 163, 225, 80, 1, 33, 32, 64, 1, 33, 33, 130, 33, 35, 225, 80, 1, - 161, 32, 64, 1, 161, 33, 130, 33, 163, 227, 80, 0, 0, 17, 176, 50, 35, 17, 160, 18, 33, 26, 255, 255, 239, 225, 160, - 0, 2, 225, 47, 255, 30, 3, 160, 0, 1, 19, 160, 0, 0, 225, 47, 255, 30, 227, 81, 8, 1, 33, 160, 24, 33, 35, 160, 32, - 16, 51, 160, 32, 0, 227, 81, 12, 1, 33, 160, 20, 33, 34, 130, 32, 8, 227, 81, 0, 16, 33, 160, 18, 33, 34, 130, 32, 4, - 227, 81, 0, 4, 130, 130, 32, 3, 144, 130, 32, 161, 225, 160, 2, 48, 225, 47, 255, 30, 225, 47, 255, 31, 225, 160, 0, - 0, 227, 80, 0, 0, 19, 224, 0, 0, 234, 0, 1, 11, 227, 81, 0, 0, 10, 255, 255, 248, 233, 45, 64, 3, 235, 255, 255, 188, - 232, 189, 64, 6, 224, 3, 0, 146, 224, 65, 16, 3, 225, 47, 255, 30, 227, 81, 0, 0, 10, 0, 0, 67, 224, 32, 192, 1, 66, - 97, 16, 0, 226, 81, 32, 1, 10, 0, 0, 39, 225, 176, 48, 0, 66, 96, 48, 0, 225, 83, 0, 1, 154, 0, 0, 38, 225, 17, 0, 2, - 10, 0, 0, 40, 227, 17, 2, 14, 1, 160, 17, 129, 3, 160, 32, 8, 19, 160, 32, 1, 227, 81, 2, 1, 49, 81, 0, 3, 49, 160, - 18, 1, 49, 160, 34, 2, 58, 255, 255, 250, 227, 81, 1, 2, 49, 81, 0, 3, 49, 160, 16, 129, 49, 160, 32, 130, 58, 255, - 255, 250, 227, 160, 0, 0, 225, 83, 0, 1, 32, 67, 48, 1, 33, 128, 0, 2, 225, 83, 0, 161, 32, 67, 48, 161, 33, 128, 0, - 162, 225, 83, 1, 33, 32, 67, 49, 33, 33, 128, 1, 34, 225, 83, 1, 161, 32, 67, 49, 161, 33, 128, 1, 162, 227, 83, 0, - 0, 17, 176, 34, 34, 17, 160, 18, 33, 26, 255, 255, 239, 227, 92, 0, 0, 66, 96, 0, 0, 225, 47, 255, 30, 225, 60, 0, 0, - 66, 96, 0, 0, 225, 47, 255, 30, 51, 160, 0, 0, 1, 160, 15, 204, 3, 128, 0, 1, 225, 47, 255, 30, 227, 81, 8, 1, 33, 160, - 24, 33, 35, 160, 32, 16, 51, 160, 32, 0, 227, 81, 12, 1, 33, 160, 20, 33, 34, 130, 32, 8, 227, 81, 0, 16, 33, 160, 18, - 33, 34, 130, 32, 4, 227, 81, 0, 4, 130, 130, 32, 3, 144, 130, 32, 161, 227, 92, 0, 0, 225, 160, 2, 51, 66, 96, 0, 0, - 225, 47, 255, 30, 225, 47, 255, 31, 225, 160, 0, 0, 227, 80, 0, 0, 195, 224, 1, 2, 179, 160, 1, 2, 234, 0, 0, 183, 227, - 81, 0, 0, 10, 255, 255, 247, 233, 45, 64, 3, 235, 255, 255, 177, 232, 189, 64, 6, 224, 3, 0, 146, 224, 65, 16, 3, 225, - 47, 255, 30, 71, 112, 70, 192, 33, 16, 6, 2, 14, 3, 65, 200, 67, 19, 6, 2, 14, 0, 67, 16, 65, 200, 67, 24, 71, 112, - 70, 192, 181, 112, 28, 4, 28, 13, 42, 3, 217, 33, 28, 11, 67, 3, 7, 158, 208, 18, 120, 32, 120, 41, 66, 136, 209, 29, - 58, 1, 35, 0, 224, 5, 52, 1, 51, 1, 120, 32, 92, 233, 66, 136, 209, 20, 66, 154, 209, 247, 32, 0, 188, 112, 188, 2, - 71, 8, 28, 13, 28, 4, 201, 8, 200, 64, 66, 158, 209, 4, 58, 4, 28, 4, 28, 13, 42, 3, 216, 244, 32, 0, 42, 0, 209, 222, - 231, 237, 26, 64, 231, 235, 70, 192, 181, 240, 28, 5, 28, 14, 28, 20, 42, 15, 217, 3, 28, 11, 67, 3, 7, 159, 208, 10, - 44, 0, 208, 5, 35, 0, 92, 242, 84, 234, 51, 1, 66, 163, 209, 250, 188, 240, 188, 2, 71, 8, 28, 21, 28, 12, 28, 3, 104, - 38, 96, 30, 104, 102, 96, 94, 104, 166, 96, 158, 104, 230, 61, 16, 96, 222, 52, 16, 51, 16, 45, 15, 216, 242, 58, 16, - 9, 23, 28, 126, 1, 63, 1, 54, 27, 215, 25, 133, 28, 60, 25, 142, 47, 3, 217, 217, 28, 52, 28, 59, 28, 42, 204, 2, 59, - 4, 194, 2, 43, 3, 216, 250, 63, 4, 8, 188, 28, 99, 0, 155, 0, 164, 24, 237, 24, 246, 27, 60, 231, 200, 70, 192, 181, - 112, 28, 3, 7, 132, 208, 13, 42, 0, 208, 64, 6, 13, 58, 1, 14, 45, 36, 3, 224, 2, 42, 0, 208, 57, 58, 1, 112, 29, 51, - 1, 66, 35, 209, 248, 42, 3, 217, 41, 37, 255, 64, 13, 2, 44, 67, 37, 4, 44, 28, 30, 67, 37, 42, 15, 217, 18, 28, 28, - 28, 22, 62, 16, 96, 37, 96, 101, 96, 165, 96, 229, 52, 16, 46, 15, 216, 247, 58, 16, 9, 22, 54, 1, 1, 54, 25, 158, 35, - 15, 64, 26, 42, 3, 217, 12, 28, 52, 28, 19, 59, 4, 196, 32, 43, 3, 216, 251, 58, 4, 8, 147, 51, 1, 0, 155, 24, 246, - 35, 3, 64, 26, 28, 51, 42, 0, 208, 6, 6, 9, 14, 12, 33, 0, 84, 92, 49, 1, 66, 138, 209, 251, 188, 112, 188, 2, 71, 8, - 120, 2, 120, 11, 48, 1, 49, 1, 42, 0, 208, 1, 66, 154, 208, 247, 26, 208, 71, 112, 35, 0, 92, 194, 51, 1, 42, 0, 209, - 251, 30, 88, 71, 112, 70, 192, 181, 240, 28, 3, 32, 0, 42, 0, 208, 72, 28, 8, 67, 24, 36, 3, 30, 85, 64, 4, 209, 42, - 28, 30, 28, 13, 42, 3, 217, 67, 104, 31, 104, 8, 66, 135, 209, 63, 58, 4, 28, 32, 42, 0, 208, 54, 72, 34, 24, 61, 67, - 189, 79, 33, 28, 32, 66, 61, 209, 47, 29, 28, 29, 8, 224, 11, 204, 8, 200, 2, 66, 139, 209, 45, 58, 4, 42, 0, 208, 40, - 77, 25, 25, 89, 67, 153, 66, 57, 209, 35, 28, 38, 28, 5, 42, 3, 216, 239, 28, 35, 28, 1, 42, 0, 208, 33, 30, 85, 120, - 28, 120, 8, 66, 132, 209, 18, 32, 0, 45, 0, 208, 16, 44, 0, 208, 14, 61, 1, 34, 0, 224, 4, 66, 170, 208, 12, 50, 1, - 44, 0, 208, 9, 24, 152, 120, 68, 24, 136, 120, 64, 66, 132, 208, 244, 26, 32, 188, 240, 188, 2, 71, 8, 32, 0, 231, 250, - 28, 41, 28, 51, 30, 85, 231, 222, 120, 36, 120, 0, 26, 32, 231, 242, 70, 192, 254, 254, 254, 255, 128, 128, 128, 128, - 0, 0, 0, 0, 71, 120, 70, 192, 234, 255, 254, 125, 71, 120, 70, 192, 234, 255, 254, 137, 71, 120, 70, 192, 234, 255, - 254, 123, 71, 120, 70, 192, 234, 255, 254, 253, 71, 120, 70, 192, 234, 255, 233, 149, 71, 120, 70, 192, 234, 255, 254, - 119, 71, 120, 70, 192, 234, 255, 254, 184, 71, 120, 70, 192, 234, 255, 254, 163, 71, 120, 70, 192, 234, 255, 233, 174, - 229, 159, 192, 0, 225, 47, 255, 28, 19, 112, 87, 81, 71, 120, 70, 192, 234, 255, 254, 124, 71, 120, 70, 192, 234, 255, - 254, 244, 71, 120, 70, 192, 234, 255, 255, 62, 71, 120, 70, 192, 234, 255, 254, 84, 71, 120, 70, 192, 234, 255, 254, - 80, 71, 120, 70, 192, 234, 255, 254, 112, 71, 120, 70, 192, 234, 255, 254, 86, 71, 120, 70, 192, 234, 255, 254, 88, - 71, 120, 70, 192, 234, 255, 233, 162, 71, 120, 70, 192, 234, 255, 254, 96, 71, 120, 70, 192, 234, 255, 254, 144, 71, - 120, 70, 192, 234, 255, 233, 152, 71, 120, 70, 192, 234, 255, 254, 136, 71, 120, 70, 192, 234, 255, 254, 84, 71, 120, - 70, 192, 234, 255, 233, 106, 71, 120, 70, 192, 234, 255, 254, 124, 71, 120, 70, 192, 234, 255, 254, 34, 71, 120, 70, - 192, 234, 255, 254, 66, 71, 120, 70, 192, 234, 255, 233, 94, 71, 120, 70, 192, 234, 255, 254, 82, 71, 120, 70, 192, - 234, 255, 254, 118, 71, 120, 70, 192, 234, 255, 254, 34, 0, 0, 0, 0, 73, 79, 83, 32, 109, 111, 100, 117, 108, 101, 0, - 0, 255, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 229, 31, - 240, 4, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 15, 66, 64, 19, 114, 198, 36, 16, - 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 87, 66, 70, 83, 32, 110, 111, 116, 32, 101, - 110, 111, 117, 103, 104, 32, 109, 101, 109, 111, 114, 121, 33, 10, 0, 47, 100, 101, 118, 47, 117, 115, 98, 49, 50, 51, - 0, 47, 100, 101, 118, 47, 117, 115, 98, 49, 50, 51, 47, 79, 70, 70, 0, 102, 105, 114, 115, 116, 32, 114, 101, 97, 100, - 32, 115, 101, 99, 116, 111, 114, 32, 40, 37, 105, 41, 32, 79, 75, 10, 0, 102, 105, 114, 115, 116, 32, 114, 101, 97, - 100, 32, 115, 101, 99, 116, 111, 114, 32, 40, 37, 105, 41, 32, 69, 82, 82, 79, 82, 10, 0, 101, 104, 99, 105, 95, 105, - 110, 116, 95, 119, 111, 114, 107, 105, 110, 103, 95, 99, 97, 108, 108, 98, 97, 99, 107, 95, 112, 97, 114, 116, 49, 44, - 32, 116, 105, 109, 101, 111, 117, 116, 58, 32, 37, 117, 10, 0, 117, 114, 98, 32, 114, 101, 116, 118, 97, 108, 58, 32, - 37, 105, 10, 0, 117, 110, 97, 98, 108, 101, 32, 116, 111, 32, 103, 101, 116, 32, 100, 101, 118, 105, 99, 101, 32, 100, - 101, 115, 99, 46, 46, 46, 10, 0, 103, 101, 116, 116, 105, 110, 103, 32, 85, 83, 66, 95, 82, 69, 81, 95, 71, 69, 84, - 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, 32, 45, 32, 114, 101, 115, 101, 116, 10, 0, 101, 114, 114, 111, 114, 32, 103, - 101, 116, 116, 105, 110, 103, 32, 85, 83, 66, 95, 82, 69, 81, 95, 71, 69, 84, 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, - 10, 0, 103, 101, 116, 116, 105, 110, 103, 32, 85, 83, 66, 95, 82, 69, 81, 95, 71, 69, 84, 68, 69, 83, 67, 82, 73, 80, - 84, 79, 82, 32, 45, 32, 97, 100, 113, 117, 105, 114, 101, 32, 45, 32, 114, 101, 115, 101, 116, 10, 0, 85, 83, 66, 95, - 82, 69, 81, 95, 71, 69, 84, 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, 32, 111, 107, 10, 0, 116, 114, 121, 105, 110, 103, - 32, 85, 83, 66, 95, 82, 69, 81, 95, 83, 69, 84, 65, 68, 68, 82, 69, 83, 83, 58, 32, 37, 100, 10, 0, 117, 110, 97, 98, - 108, 101, 32, 116, 111, 32, 115, 101, 116, 32, 100, 101, 118, 105, 99, 101, 32, 97, 100, 100, 114, 58, 32, 37, 100, - 10, 0, 85, 83, 66, 95, 82, 69, 81, 95, 83, 69, 84, 65, 68, 68, 82, 69, 83, 83, 32, 111, 107, 58, 32, 37, 100, 10, 0, - 101, 114, 114, 111, 114, 32, 99, 104, 101, 99, 107, 105, 110, 103, 32, 85, 83, 66, 95, 82, 69, 81, 95, 71, 69, 84, 68, - 69, 83, 67, 82, 73, 80, 84, 79, 82, 10, 0, 111, 107, 32, 99, 104, 101, 99, 107, 105, 110, 103, 32, 85, 83, 66, 95, 82, - 69, 81, 95, 71, 69, 84, 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, 10, 0, 105, 110, 105, 116, 32, 111, 107, 10, 0, 98, - 117, 102, 102, 101, 114, 32, 61, 61, 32, 78, 85, 76, 76, 32, 40, 110, 111, 32, 109, 101, 109, 41, 10, 0, 95, 95, 117, - 115, 98, 95, 103, 101, 116, 100, 101, 115, 99, 32, 101, 114, 114, 111, 114, 32, 85, 83, 66, 95, 68, 84, 95, 68, 69, - 86, 73, 67, 69, 58, 32, 114, 101, 116, 114, 121, 10, 0, 95, 95, 117, 115, 98, 95, 103, 101, 116, 100, 101, 115, 99, - 32, 101, 114, 114, 111, 114, 32, 85, 83, 66, 95, 68, 84, 95, 68, 69, 86, 73, 67, 69, 10, 0, 117, 100, 100, 45, 62, 99, - 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 115, 32, 61, 61, 32, 78, 85, 76, 76, 32, 40, 110, 111, 32, - 109, 101, 109, 41, 10, 0, 95, 95, 117, 115, 98, 95, 103, 101, 116, 100, 101, 115, 99, 32, 101, 114, 114, 111, 114, 32, - 85, 83, 66, 95, 68, 84, 95, 67, 79, 78, 70, 73, 71, 58, 32, 114, 101, 116, 114, 121, 10, 0, 117, 99, 100, 45, 62, 105, - 110, 116, 101, 114, 102, 97, 99, 101, 115, 32, 61, 61, 32, 78, 85, 76, 76, 32, 40, 110, 111, 32, 109, 101, 109, 41, - 10, 0, 117, 105, 100, 45, 62, 101, 110, 100, 112, 111, 105, 110, 116, 115, 32, 61, 61, 32, 78, 85, 76, 76, 32, 40, 110, - 111, 32, 109, 101, 109, 41, 10, 0, 117, 105, 100, 45, 62, 101, 120, 116, 114, 97, 32, 61, 61, 32, 78, 85, 76, 76, 32, - 40, 110, 111, 32, 109, 101, 109, 41, 10, 0, 10, 113, 116, 100, 32, 101, 114, 114, 111, 114, 33, 58, 0, 32, 66, 65, 66, - 66, 76, 69, 0, 32, 32, 109, 105, 115, 115, 101, 100, 32, 109, 105, 99, 114, 111, 32, 102, 114, 97, 109, 101, 0, 32, - 32, 100, 97, 116, 97, 98, 117, 102, 102, 101, 114, 32, 101, 114, 114, 111, 114, 0, 32, 119, 114, 111, 110, 103, 32, - 97, 99, 107, 0, 32, 116, 111, 111, 32, 109, 97, 110, 121, 32, 101, 114, 114, 111, 114, 115, 0, 105, 110, 116, 101, 114, - 114, 117, 112, 116, 95, 99, 97, 108, 108, 98, 97, 99, 107, 95, 104, 97, 110, 100, 32, 83, 84, 83, 95, 73, 78, 84, 10, - 0, 105, 110, 116, 101, 114, 114, 117, 112, 116, 95, 99, 97, 108, 108, 98, 97, 99, 107, 95, 104, 97, 110, 100, 32, 83, - 84, 83, 95, 80, 67, 68, 10, 0, 117, 115, 98, 115, 116, 111, 114, 97, 103, 101, 32, 114, 101, 115, 101, 116, 58, 32, - 66, 85, 76, 75, 32, 82, 69, 83, 69, 84, 32, 37, 105, 10, 0, 117, 115, 98, 115, 116, 111, 114, 97, 103, 101, 32, 114, - 101, 115, 101, 116, 58, 32, 99, 108, 101, 97, 114, 104, 97, 108, 116, 32, 105, 110, 32, 114, 101, 116, 32, 37, 105, - 10, 0, 117, 115, 98, 115, 116, 111, 114, 97, 103, 101, 32, 114, 101, 115, 101, 116, 58, 32, 99, 108, 101, 97, 114, 104, - 97, 108, 116, 32, 111, 117, 116, 32, 114, 101, 116, 32, 37, 105, 10, 0, 117, 115, 98, 115, 116, 111, 114, 97, 103, 101, - 32, 114, 101, 115, 101, 116, 58, 32, 85, 83, 66, 95, 71, 101, 116, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, - 111, 110, 32, 114, 101, 116, 32, 37, 105, 10, 0, 114, 101, 115, 101, 116, 32, 111, 107, 10, 0, 95, 95, 115, 101, 110, - 100, 95, 99, 98, 119, 32, 114, 101, 116, 32, 37, 105, 10, 0, 95, 95, 85, 83, 66, 95, 66, 108, 107, 77, 115, 103, 84, - 105, 109, 101, 111, 117, 116, 32, 37, 105, 10, 0, 95, 95, 114, 101, 97, 100, 95, 99, 115, 119, 32, 37, 105, 10, 0, 32, - 32, 32, 32, 83, 67, 83, 73, 95, 84, 69, 83, 84, 95, 85, 78, 73, 84, 95, 82, 69, 65, 68, 89, 32, 114, 101, 116, 32, 37, - 105, 10, 0, 32, 32, 32, 32, 83, 67, 83, 73, 95, 82, 69, 81, 85, 69, 83, 84, 95, 83, 69, 78, 83, 69, 32, 114, 101, 116, - 32, 37, 105, 10, 0, 32, 32, 32, 32, 83, 67, 83, 73, 95, 82, 69, 81, 85, 69, 83, 84, 95, 83, 69, 78, 83, 69, 32, 115, - 116, 97, 116, 117, 115, 32, 37, 120, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, - 41, 58, 32, 85, 83, 66, 95, 71, 101, 116, 68, 101, 115, 99, 114, 105, 112, 116, 111, 114, 115, 32, 37, 105, 10, 0, 85, - 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 100, 101, 118, 105, 99, 101, 32, 99, - 104, 97, 110, 103, 101, 100, 33, 33, 33, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, - 40, 41, 58, 32, 117, 99, 100, 32, 37, 105, 32, 80, 111, 119, 101, 114, 32, 37, 105, 32, 109, 65, 10, 0, 85, 83, 66, - 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 105, 110, 116, 101, 114, 102, 97, 99, 101, 32, - 115, 117, 98, 99, 108, 97, 115, 115, 32, 37, 105, 32, 97, 116, 97, 95, 112, 114, 111, 116, 32, 37, 105, 32, 10, 0, 73, - 110, 32, 80, 111, 105, 110, 116, 58, 32, 37, 105, 10, 0, 79, 117, 116, 32, 80, 111, 105, 110, 116, 58, 32, 37, 105, - 10, 0, 101, 112, 95, 105, 110, 32, 37, 120, 32, 101, 112, 95, 111, 117, 116, 32, 37, 120, 10, 0, 85, 83, 66, 83, 116, - 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 99, 97, 110, 110, 111, 116, 32, 102, 105, 110, 100, 32, - 97, 110, 121, 32, 105, 110, 116, 101, 114, 102, 97, 99, 101, 33, 33, 33, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, - 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 99, 111, 110, 102, 58, 32, 37, 120, 32, 97, 108, 116, 73, 110, 116, 101, - 114, 102, 97, 99, 101, 58, 32, 37, 120, 10, 0, 85, 83, 66, 95, 71, 101, 116, 67, 111, 110, 102, 105, 103, 117, 114, - 97, 116, 105, 111, 110, 40, 41, 32, 69, 114, 114, 111, 114, 46, 32, 67, 111, 110, 116, 105, 110, 117, 101, 46, 10, 0, - 65, 99, 116, 117, 97, 108, 32, 99, 111, 110, 102, 58, 32, 37, 120, 32, 32, 32, 110, 101, 120, 116, 32, 99, 111, 110, - 102, 58, 32, 37, 120, 10, 0, 85, 83, 66, 95, 83, 101, 116, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, - 110, 40, 41, 32, 69, 114, 114, 111, 114, 10, 0, 85, 83, 66, 95, 83, 101, 116, 65, 108, 116, 101, 114, 110, 97, 116, - 105, 118, 101, 73, 110, 116, 101, 114, 102, 97, 99, 101, 40, 41, 32, 69, 114, 114, 111, 114, 46, 32, 67, 111, 110, 116, - 105, 110, 117, 101, 10, 0, 85, 83, 66, 95, 83, 101, 116, 67, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, - 40, 41, 32, 38, 32, 85, 83, 66, 95, 83, 101, 116, 65, 108, 116, 101, 114, 110, 97, 116, 105, 118, 101, 73, 110, 116, - 101, 114, 102, 97, 99, 101, 40, 41, 32, 79, 75, 10, 0, 71, 101, 116, 95, 77, 97, 120, 95, 76, 117, 110, 40, 41, 58, - 32, 101, 114, 114, 44, 32, 100, 101, 102, 97, 117, 108, 116, 32, 109, 97, 120, 95, 108, 117, 110, 61, 56, 10, 0, 71, - 101, 116, 95, 77, 97, 120, 95, 76, 117, 110, 40, 41, 58, 32, 79, 75, 58, 32, 37, 105, 10, 0, 85, 83, 66, 83, 116, 111, - 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 116, 114, 121, 95, 115, 116, 97, 116, 117, 115, 32, 37, 105, - 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 79, 75, 44, 32, 114, 101, - 116, 117, 114, 110, 32, 48, 10, 0, 32, 32, 32, 32, 115, 116, 97, 114, 116, 95, 115, 116, 111, 112, 32, 99, 109, 100, - 32, 114, 101, 116, 32, 37, 105, 10, 0, 32, 32, 32, 32, 73, 110, 113, 117, 105, 114, 121, 32, 114, 101, 116, 32, 37, - 105, 10, 0, 32, 32, 32, 32, 68, 101, 118, 105, 99, 101, 32, 84, 121, 112, 101, 58, 32, 37, 120, 10, 0, 32, 32, 32, 32, - 82, 101, 97, 100, 67, 97, 112, 97, 99, 105, 116, 121, 32, 114, 101, 116, 32, 37, 105, 32, 32, 115, 101, 99, 116, 111, - 114, 95, 115, 105, 122, 101, 58, 32, 37, 117, 32, 32, 115, 101, 99, 116, 111, 114, 115, 58, 32, 37, 117, 10, 0, 70, - 97, 115, 116, 32, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 77, 111, 117, 110, 116, 76, 85, 78, 32, 37, 105, - 35, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 77, 111, 117, 110, 116, 76, 85, 78, 58, 32, 114, 101, 116, - 32, 37, 105, 10, 0, 85, 83, 66, 83, 84, 79, 82, 65, 71, 69, 95, 71, 69, 84, 95, 77, 65, 88, 95, 76, 85, 78, 32, 114, - 101, 116, 32, 37, 105, 32, 109, 97, 120, 108, 117, 110, 32, 37, 105, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, - 101, 95, 77, 111, 117, 110, 116, 76, 85, 78, 32, 102, 97, 105, 108, 33, 33, 33, 10, 0, 10, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 10, 82, 111, 100, 114, 105, 101, 115, 32, 101, 104, 99, - 109, 111, 100, 117, 108, 101, 32, 49, 46, 48, 10, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 73, 110, 105, 116, - 40, 41, 10, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 10, 10, 0, 85, 83, - 66, 83, 116, 111, 114, 97, 103, 101, 95, 73, 110, 105, 116, 40, 41, 32, 79, 107, 10, 0, 69, 114, 114, 111, 114, 32, - 82, 101, 97, 100, 105, 110, 103, 32, 115, 101, 99, 116, 111, 114, 32, 48, 10, 0, 79, 75, 32, 82, 101, 97, 100, 105, - 110, 103, 32, 115, 101, 99, 116, 111, 114, 32, 48, 10, 0, 85, 110, 112, 108, 117, 103, 58, 32, 114, 101, 115, 101, 116, - 32, 37, 105, 32, 115, 116, 97, 116, 117, 115, 32, 37, 120, 10, 0, 102, 97, 115, 116, 95, 114, 101, 109, 111, 117, 110, - 116, 32, 75, 79, 32, 114, 101, 116, 32, 37, 105, 10, 0, 85, 83, 66, 32, 65, 108, 108, 111, 99, 58, 32, 110, 111, 116, - 32, 101, 110, 111, 117, 103, 104, 32, 109, 101, 109, 111, 114, 121, 33, 10, 0, 119, 98, 102, 115, 32, 101, 114, 114, - 111, 114, 32, 0, 98, 97, 100, 32, 109, 97, 103, 105, 99, 0, 104, 100, 32, 115, 101, 99, 116, 111, 114, 32, 115, 105, - 122, 101, 32, 100, 111, 101, 115, 110, 39, 116, 32, 109, 97, 116, 99, 104, 0, 104, 100, 32, 110, 117, 109, 32, 115, - 101, 99, 116, 111, 114, 32, 100, 111, 101, 115, 110, 39, 116, 32, 109, 97, 116, 99, 104, 0, 78, 84, 70, 83, 0, 70, 65, - 84, 0, 116, 114, 121, 105, 110, 103, 32, 116, 111, 32, 99, 108, 111, 115, 101, 32, 119, 98, 102, 115, 32, 119, 104, - 105, 108, 101, 32, 100, 105, 115, 99, 115, 32, 115, 116, 105, 108, 108, 32, 111, 112, 101, 110, 0, 97, 108, 108, 111, - 99, 97, 116, 105, 110, 103, 32, 109, 101, 109, 111, 114, 121, 0 +unsigned char ehcmodule_5[27134] __attribute__((aligned (32)))={ + 127, 69, 76, 70, 1, 2, 1, 97, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 40, 0, 0, 0, 1, 19, 112, 0, 0, 0, 0, 0, 52, 0, 0, 0, + 0, 0, 0, 6, 6, 0, 52, 0, 32, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 160, + 0, 0, 0, 160, 0, 240, 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 212, 0, 0, 0, 160, 0, 0, 0, 160, 0, 0, 0, 52, 0, 0, 0, + 52, 0, 240, 0, 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 212, 0, 0, 0, 212, 0, 240, 0, + 0, 0, 0, 64, 0, 0, 0, 0, 1, 0, 0, 1, 8, 19, 112, 0, 0, 19, 112, 0, 0, 0, 0, 94, 48, 0, 0, 94, 48, 0, 240, 0, 5, 0, 0, + 0, 4, 0, 0, 0, 1, 0, 0, 95, 56, 19, 112, 96, 0, 19, 112, 96, 0, 0, 0, 10, 198, 0, 2, 154, 20, 0, 240, 0, 6, 0, 0, 0, + 4, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 6, 0, 0, 0, 11, 0, 0, 0, 4, 0, 0, 0, 9, 19, 112, 0, 0, 0, 0, 0, 125, 0, 0, 0, 120, + 0, 0, 0, 126, 0, 0, 48, 0, 0, 0, 0, 127, 19, 114, 248, 20, 227, 160, 0, 0, 227, 160, 16, 0, 229, 159, 49, 0, 225, 47, + 255, 19, 229, 159, 192, 252, 229, 156, 192, 0, 225, 160, 0, 0, 231, 156, 193, 11, 225, 160, 0, 0, 225, 47, 255, 28, + 225, 160, 0, 0, 225, 160, 0, 0, 227, 160, 176, 63, 234, 255, 255, 245, 225, 160, 0, 0, 225, 160, 0, 0, 227, 160, 176, + 64, 234, 255, 255, 241, 227, 160, 0, 0, 238, 7, 15, 21, 225, 47, 255, 30, 225, 160, 0, 0, 225, 160, 0, 0, 225, 160, + 0, 0, 227, 24, 0, 16, 10, 0, 0, 18, 227, 200, 128, 16, 227, 160, 32, 16, 229, 135, 32, 0, 225, 160, 0, 0, 225, 160, + 32, 13, 225, 160, 0, 0, 229, 159, 208, 144, 225, 160, 0, 0, 233, 45, 95, 254, 225, 160, 0, 0, 235, 0, 0, 15, 232, 189, + 95, 254, 225, 160, 0, 0, 225, 160, 208, 2, 227, 16, 0, 1, 10, 0, 0, 2, 225, 160, 0, 0, 227, 160, 0, 4, 235, 0, 0, 5, + 227, 24, 0, 1, 10, 0, 0, 1, 229, 159, 240, 88, 225, 160, 0, 0, 229, 159, 240, 84, 225, 160, 0, 0, 229, 159, 240, 80, + 225, 160, 0, 0, 229, 159, 32, 76, 225, 47, 255, 18, 225, 160, 0, 0, 238, 19, 15, 16, 225, 47, 255, 30, 225, 160, 0, + 0, 225, 160, 0, 0, 238, 3, 15, 16, 225, 47, 255, 30, 225, 160, 0, 0, 225, 160, 0, 0, 69, 72, 67, 95, 67, 70, 71, 0, + 18, 52, 0, 1, 0, 0, 0, 0, 19, 112, 17, 61, 19, 114, 195, 160, 19, 114, 250, 20, 255, 255, 30, 128, 255, 255, 30, 156, + 255, 255, 29, 68, 19, 112, 13, 221, 225, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 181, 0, 40, 2, 208, + 16, 40, 2, 216, 4, 40, 0, 208, 7, 40, 1, 209, 19, 224, 7, 40, 16, 208, 12, 40, 17, 209, 14, 224, 11, 75, 8, 104, 24, + 224, 11, 72, 7, 224, 9, 75, 7, 34, 1, 96, 26, 32, 0, 224, 4, 72, 6, 224, 2, 72, 6, 224, 0, 72, 6, 188, 2, 71, 8, 19, + 112, 96, 48, 19, 112, 76, 109, 19, 112, 106, 232, 19, 112, 68, 253, 19, 112, 70, 133, 255, 255, 253, 102, 181, 112, + 28, 12, 28, 5, 240, 5, 253, 211, 28, 6, 32, 1, 66, 64, 240, 5, 253, 210, 14, 43, 115, 35, 12, 43, 115, 99, 10, 43, 115, + 163, 124, 34, 124, 99, 6, 18, 4, 27, 67, 19, 124, 162, 115, 229, 2, 18, 67, 19, 124, 226, 6, 45, 67, 19, 28, 32, 96, + 29, 48, 12, 33, 4, 240, 5, 253, 216, 124, 35, 124, 96, 6, 27, 4, 0, 67, 24, 124, 163, 33, 4, 2, 27, 67, 24, 124, 227, + 67, 24, 240, 5, 253, 203, 28, 48, 240, 5, 253, 170, 32, 0, 188, 112, 188, 2, 71, 8, 181, 8, 75, 12, 28, 1, 34, 32, 104, + 24, 240, 5, 253, 249, 40, 0, 209, 13, 72, 9, 240, 5, 254, 4, 240, 4, 254, 15, 32, 200, 240, 0, 255, 113, 240, 4, 254, + 20, 32, 200, 240, 0, 255, 108, 231, 244, 188, 8, 188, 2, 71, 8, 19, 112, 96, 48, 19, 112, 96, 84, 181, 8, 75, 4, 28, + 1, 104, 24, 240, 5, 253, 118, 188, 8, 188, 1, 71, 0, 70, 192, 19, 112, 96, 48, 181, 240, 176, 139, 144, 4, 145, 5, 28, + 8, 28, 17, 28, 23, 146, 7, 240, 5, 253, 156, 76, 129, 104, 35, 43, 0, 209, 0, 224, 241, 168, 9, 240, 3, 255, 34, 75, + 126, 96, 24, 40, 0, 208, 4, 35, 128, 154, 9, 1, 27, 66, 154, 208, 4, 35, 1, 74, 122, 66, 91, 96, 19, 224, 224, 78, 121, + 72, 122, 104, 51, 24, 26, 42, 0, 208, 104, 43, 0, 219, 102, 75, 116, 104, 26, 28, 81, 209, 98, 28, 53, 75, 112, 104, + 48, 104, 28, 33, 31, 34, 16, 52, 31, 24, 18, 67, 140, 146, 2, 33, 16, 28, 34, 240, 4, 249, 24, 40, 0, 209, 0, 224, 195, + 120, 35, 43, 67, 209, 11, 120, 99, 43, 73, 209, 8, 120, 163, 43, 83, 209, 5, 120, 227, 43, 79, 209, 2, 75, 103, 96, + 51, 224, 8, 104, 43, 43, 0, 208, 2, 35, 0, 96, 43, 231, 218, 35, 1, 66, 91, 96, 43, 121, 98, 121, 163, 2, 18, 4, 27, + 24, 211, 121, 34, 77, 95, 24, 155, 121, 226, 28, 40, 6, 18, 24, 155, 10, 219, 147, 3, 154, 3, 75, 91, 33, 0, 96, 26, + 34, 128, 1, 18, 240, 5, 252, 63, 75, 84, 104, 26, 75, 85, 66, 154, 209, 30, 32, 7, 35, 0, 70, 132, 70, 102, 64, 30, + 209, 4, 16, 218, 152, 2, 73, 82, 0, 146, 80, 80, 24, 226, 122, 18, 42, 0, 208, 10, 16, 218, 92, 168, 33, 1, 64, 177, + 67, 8, 84, 168, 154, 2, 28, 16, 154, 3, 24, 128, 144, 2, 34, 128, 51, 1, 1, 210, 66, 147, 209, 227, 75, 63, 34, 31, + 104, 27, 153, 5, 51, 31, 67, 147, 34, 128, 2, 18, 147, 3, 145, 2, 78, 60, 146, 6, 224, 97, 155, 4, 32, 15, 10, 93, 104, + 51, 67, 133, 66, 157, 208, 63, 73, 56, 75, 57, 104, 10, 96, 53, 66, 154, 209, 33, 75, 57, 28, 40, 104, 28, 28, 33, 240, + 5, 252, 185, 75, 55, 8, 193, 0, 138, 88, 210, 35, 0, 147, 1, 35, 7, 64, 24, 70, 140, 35, 0, 144, 0, 224, 9, 72, 47, + 70, 97, 92, 64, 28, 1, 65, 25, 32, 1, 66, 8, 208, 0, 25, 18, 51, 1, 153, 0, 66, 139, 211, 242, 60, 1, 64, 44, 25, 21, + 75, 34, 104, 28, 27, 100, 44, 16, 220, 10, 44, 16, 208, 9, 34, 128, 152, 3, 33, 0, 2, 18, 240, 5, 251, 210, 44, 0, 221, + 8, 224, 0, 36, 16, 28, 40, 28, 33, 154, 3, 240, 4, 248, 113, 40, 0, 208, 29, 154, 4, 152, 6, 4, 209, 12, 73, 26, 67, + 28, 60, 66, 187, 216, 0, 28, 28, 154, 3, 152, 2, 24, 137, 28, 34, 240, 5, 251, 118, 152, 2, 28, 33, 240, 5, 252, 176, + 155, 2, 152, 4, 25, 27, 27, 63, 16, 164, 25, 0, 147, 2, 144, 4, 47, 0, 209, 155, 224, 5, 32, 128, 2, 0, 176, 11, 188, + 240, 188, 2, 71, 8, 152, 5, 153, 7, 240, 5, 252, 163, 32, 0, 231, 245, 70, 192, 19, 112, 107, 116, 19, 112, 106, 240, + 19, 112, 96, 24, 19, 112, 96, 12, 128, 0, 0, 1, 127, 255, 255, 255, 19, 112, 123, 160, 19, 112, 106, 244, 19, 112, 131, + 160, 181, 56, 28, 12, 240, 5, 252, 162, 30, 5, 209, 38, 44, 0, 208, 36, 104, 34, 42, 0, 208, 33, 120, 17, 120, 83, 6, + 9, 4, 27, 67, 11, 120, 145, 2, 9, 67, 11, 120, 209, 67, 11, 43, 6, 209, 21, 123, 17, 123, 83, 6, 9, 4, 27, 67, 11, 123, + 145, 2, 9, 67, 11, 123, 209, 67, 11, 43, 122, 208, 2, 43, 136, 209, 7, 224, 2, 72, 5, 33, 21, 224, 1, 72, 4, 33, 20, + 240, 4, 252, 154, 28, 40, 188, 56, 188, 2, 71, 8, 70, 192, 19, 112, 1, 153, 181, 240, 176, 145, 75, 206, 33, 128, 120, + 27, 36, 1, 147, 8, 75, 205, 104, 24, 240, 5, 252, 75, 33, 32, 240, 5, 252, 48, 144, 6, 240, 0, 251, 207, 240, 5, 252, + 25, 33, 120, 240, 5, 252, 72, 153, 6, 72, 198, 240, 5, 252, 6, 72, 197, 154, 6, 28, 1, 35, 0, 240, 5, 252, 94, 73, 195, + 28, 7, 32, 205, 240, 4, 252, 21, 33, 0, 145, 9, 145, 7, 145, 5, 152, 6, 169, 15, 34, 0, 240, 5, 252, 68, 144, 14, 155, + 14, 43, 0, 209, 246, 44, 0, 208, 2, 28, 56, 240, 5, 252, 79, 74, 185, 158, 15, 35, 0, 96, 19, 46, 0, 209, 93, 75, 183, + 104, 27, 43, 0, 208, 5, 75, 182, 104, 27, 43, 0, 209, 1, 74, 181, 96, 19, 155, 5, 36, 0, 43, 0, 208, 221, 75, 178, 104, + 27, 43, 0, 208, 217, 75, 175, 104, 27, 43, 0, 209, 213, 75, 175, 104, 27, 43, 0, 208, 9, 240, 3, 254, 179, 40, 0, 208, + 5, 240, 3, 254, 175, 40, 0, 208, 1, 240, 3, 254, 171, 75, 168, 104, 27, 147, 4, 43, 0, 209, 41, 77, 166, 75, 167, 104, + 42, 58, 1, 66, 154, 216, 35, 78, 158, 76, 165, 35, 1, 33, 1, 96, 51, 104, 32, 74, 163, 240, 3, 255, 137, 153, 4, 96, + 49, 40, 0, 208, 14, 75, 161, 104, 26, 75, 161, 0, 146, 88, 211, 43, 0, 209, 7, 32, 128, 4, 64, 104, 41, 240, 5, 251, + 143, 104, 35, 24, 192, 96, 32, 75, 151, 74, 155, 104, 25, 104, 18, 66, 145, 211, 1, 34, 0, 96, 26, 75, 142, 36, 0, 104, + 27, 43, 0, 209, 147, 28, 56, 73, 135, 240, 5, 251, 241, 36, 1, 231, 141, 120, 51, 120, 112, 6, 27, 4, 0, 67, 24, 120, + 179, 2, 27, 67, 24, 120, 243, 67, 24, 56, 1, 40, 6, 217, 0, 227, 2, 240, 5, 248, 237, 0, 10, 0, 74, 3, 1, 3, 1, 3, 1, + 0, 7, 0, 93, 38, 1, 37, 1, 227, 0, 123, 51, 123, 116, 6, 27, 4, 36, 67, 28, 123, 179, 73, 116, 2, 27, 67, 28, 123, 243, + 67, 28, 28, 32, 240, 5, 250, 221, 40, 0, 209, 11, 125, 51, 125, 117, 6, 27, 4, 45, 67, 29, 125, 179, 2, 27, 67, 29, + 125, 243, 38, 1, 67, 29, 226, 228, 28, 32, 73, 119, 240, 5, 250, 203, 40, 0, 208, 0, 226, 214, 125, 51, 125, 117, 6, + 27, 4, 45, 67, 29, 125, 179, 34, 1, 2, 27, 67, 29, 125, 243, 38, 1, 67, 29, 75, 100, 96, 26, 75, 100, 96, 24, 72, 109, + 240, 0, 251, 121, 75, 109, 34, 4, 104, 27, 104, 155, 96, 154, 240, 1, 248, 110, 34, 0, 146, 5, 226, 192, 122, 50, 122, + 115, 6, 18, 4, 27, 67, 19, 122, 178, 153, 7, 2, 18, 67, 19, 122, 242, 67, 19, 66, 153, 209, 0, 226, 175, 240, 1, 248, + 115, 38, 1, 37, 0, 226, 173, 126, 51, 126, 116, 6, 27, 4, 36, 67, 28, 126, 179, 124, 50, 2, 27, 67, 28, 126, 243, 6, + 18, 67, 28, 124, 115, 4, 27, 67, 19, 124, 178, 2, 18, 67, 19, 124, 242, 67, 26, 146, 4, 125, 51, 125, 117, 6, 27, 4, + 45, 67, 29, 125, 179, 154, 7, 2, 27, 67, 29, 125, 243, 67, 29, 42, 0, 209, 16, 123, 51, 43, 0, 209, 13, 122, 50, 122, + 115, 122, 176, 6, 18, 4, 27, 67, 19, 2, 0, 67, 3, 122, 240, 67, 24, 240, 1, 248, 79, 28, 6, 224, 0, 38, 0, 155, 4, 28, + 32, 25, 91, 0, 217, 147, 11, 240, 5, 251, 23, 33, 0, 28, 37, 145, 10, 224, 7, 104, 40, 104, 105, 240, 5, 251, 15, 154, + 10, 53, 8, 50, 1, 146, 10, 155, 10, 153, 11, 66, 139, 219, 243, 72, 45, 104, 1, 41, 0, 208, 0, 226, 63, 154, 15, 123, + 21, 123, 83, 6, 45, 4, 27, 67, 43, 123, 149, 2, 45, 67, 43, 123, 213, 67, 43, 77, 50, 66, 171, 209, 0, 225, 32, 66, + 171, 216, 33, 43, 6, 216, 12, 43, 5, 211, 0, 225, 247, 43, 1, 209, 0, 224, 165, 43, 0, 209, 0, 224, 124, 43, 2, 208, + 0, 225, 88, 225, 237, 43, 27, 216, 7, 38, 0, 43, 26, 211, 0, 224, 175, 43, 12, 208, 0, 225, 78, 224, 162, 74, 35, 66, + 147, 209, 0, 224, 169, 50, 1, 66, 147, 208, 0, 225, 69, 224, 238, 77, 32, 66, 171, 209, 0, 224, 218, 66, 171, 216, 71, + 74, 30, 66, 147, 209, 0, 224, 190, 66, 147, 216, 56, 58, 12, 66, 147, 209, 0, 225, 34, 50, 2, 66, 147, 208, 0, 225, + 48, 225, 37, 19, 112, 1, 12, 19, 112, 96, 48, 19, 112, 96, 109, 0, 152, 150, 128, 19, 112, 1, 65, 19, 112, 107, 12, + 19, 112, 106, 228, 19, 112, 106, 232, 19, 112, 96, 20, 19, 112, 107, 108, 19, 114, 200, 12, 0, 0, 15, 255, 19, 112, + 106, 224, 19, 112, 107, 160, 19, 112, 107, 104, 19, 112, 107, 44, 19, 114, 200, 16, 19, 112, 96, 121, 19, 112, 21, 229, + 19, 112, 96, 60, 85, 77, 83, 3, 85, 77, 83, 1, 85, 77, 83, 130, 85, 77, 83, 16, 74, 208, 66, 147, 209, 0, 224, 247, + 50, 1, 66, 147, 208, 0, 224, 247, 224, 130, 73, 205, 66, 139, 209, 0, 225, 100, 66, 139, 216, 8, 73, 203, 66, 139, 209, + 0, 224, 124, 73, 202, 66, 139, 208, 0, 224, 232, 224, 233, 74, 200, 66, 147, 209, 0, 225, 51, 50, 1, 66, 147, 208, 0, + 224, 223, 225, 34, 46, 0, 209, 0, 225, 116, 105, 162, 105, 35, 136, 21, 106, 34, 136, 27, 136, 16, 104, 34, 2, 25, 10, + 27, 120, 18, 67, 11, 2, 41, 10, 45, 67, 13, 146, 10, 4, 45, 104, 162, 12, 45, 120, 18, 149, 0, 2, 5, 10, 0, 67, 40, + 4, 0, 12, 0, 144, 1, 107, 32, 4, 27, 144, 2, 12, 27, 28, 48, 153, 10, 240, 1, 251, 132, 224, 10, 46, 0, 209, 0, 225, + 78, 104, 35, 28, 48, 120, 25, 104, 163, 136, 26, 105, 35, 240, 1, 251, 100, 28, 5, 38, 1, 225, 117, 104, 35, 105, 34, + 120, 24, 104, 163, 120, 25, 105, 163, 240, 0, 255, 121, 231, 243, 37, 1, 225, 106, 152, 8, 240, 3, 250, 131, 28, 5, + 40, 2, 216, 8, 40, 2, 208, 2, 34, 1, 40, 0, 209, 0, 34, 0, 75, 159, 96, 26, 224, 3, 34, 0, 146, 5, 40, 0, 219, 3, 75, + 157, 34, 1, 96, 26, 146, 5, 155, 15, 122, 25, 122, 90, 6, 9, 4, 18, 67, 10, 122, 153, 122, 219, 2, 9, 67, 10, 67, 19, + 147, 7, 231, 206, 75, 148, 37, 0, 96, 25, 240, 3, 250, 47, 38, 1, 149, 5, 225, 62, 104, 35, 104, 26, 75, 144, 224, 105, + 104, 35, 120, 219, 147, 8, 43, 1, 216, 2, 75, 139, 153, 8, 96, 25, 75, 137, 224, 27, 35, 1, 96, 3, 75, 136, 72, 138, + 96, 25, 240, 0, 249, 243, 75, 137, 34, 4, 104, 27, 104, 155, 96, 154, 240, 0, 254, 232, 34, 0, 146, 5, 77, 133, 28, + 40, 240, 3, 251, 98, 75, 132, 96, 24, 104, 35, 43, 0, 208, 1, 104, 42, 96, 26, 75, 129, 104, 29, 231, 154, 28, 123, + 208, 11, 75, 121, 104, 27, 43, 0, 208, 7, 28, 56, 240, 5, 249, 245, 28, 56, 240, 5, 249, 186, 39, 1, 66, 127, 104, 35, + 105, 34, 104, 24, 104, 163, 104, 25, 240, 3, 253, 97, 75, 118, 28, 5, 120, 26, 42, 0, 208, 11, 34, 0, 112, 26, 104, + 35, 40, 0, 221, 2, 104, 25, 72, 113, 224, 1, 104, 25, 72, 113, 240, 1, 248, 108, 28, 121, 208, 0, 231, 113, 75, 102, + 104, 27, 43, 0, 209, 0, 231, 108, 72, 108, 154, 6, 28, 1, 35, 0, 240, 5, 249, 194, 28, 7, 231, 100, 104, 35, 105, 34, + 104, 24, 104, 163, 104, 25, 240, 3, 253, 253, 231, 91, 75, 101, 104, 26, 66, 81, 65, 74, 96, 26, 224, 153, 104, 35, + 104, 26, 75, 87, 96, 26, 38, 1, 231, 91, 122, 17, 122, 83, 6, 9, 4, 27, 67, 11, 122, 145, 122, 210, 2, 9, 67, 11, 67, + 26, 104, 37, 35, 0, 146, 7, 147, 13, 120, 42, 42, 95, 209, 26, 120, 106, 42, 68, 209, 23, 120, 170, 42, 86, 209, 20, + 120, 234, 42, 68, 209, 17, 77, 83, 74, 72, 96, 43, 104, 227, 33, 1, 96, 17, 43, 4, 209, 6, 104, 161, 168, 13, 34, 4, + 240, 5, 248, 27, 155, 13, 96, 43, 240, 4, 248, 153, 224, 17, 104, 227, 43, 4, 209, 4, 104, 161, 168, 13, 34, 4, 240, + 5, 248, 14, 28, 40, 153, 13, 240, 4, 248, 208, 144, 9, 40, 0, 208, 94, 75, 56, 34, 1, 96, 26, 38, 1, 37, 0, 150, 5, + 224, 134, 240, 3, 252, 130, 38, 1, 28, 5, 40, 0, 208, 0, 224, 127, 35, 1, 74, 60, 66, 91, 96, 19, 224, 122, 75, 46, + 34, 1, 96, 26, 28, 122, 208, 7, 28, 56, 240, 5, 249, 94, 28, 56, 240, 5, 249, 35, 39, 1, 66, 127, 104, 35, 105, 33, + 104, 24, 104, 163, 38, 1, 104, 26, 247, 255, 251, 119, 75, 36, 28, 5, 104, 27, 43, 0, 208, 96, 72, 42, 154, 6, 28, 1, + 35, 0, 240, 5, 249, 62, 28, 7, 224, 88, 155, 9, 43, 0, 208, 82, 28, 121, 208, 11, 75, 27, 104, 27, 43, 0, 208, 7, 28, + 56, 240, 5, 249, 56, 28, 56, 240, 5, 248, 253, 39, 1, 66, 127, 104, 35, 105, 34, 104, 25, 104, 163, 152, 9, 104, 27, + 240, 4, 252, 125, 75, 17, 104, 27, 43, 0, 208, 6, 72, 24, 154, 6, 28, 1, 35, 0, 240, 5, 249, 25, 28, 7, 38, 1, 37, 0, + 224, 49, 38, 1, 37, 6, 224, 45, 37, 1, 34, 1, 38, 1, 66, 109, 146, 5, 224, 40, 85, 77, 83, 128, 87, 70, 83, 2, 85, 77, + 83, 131, 87, 70, 83, 1, 87, 70, 83, 3, 19, 112, 107, 104, 19, 112, 96, 20, 19, 112, 106, 228, 19, 112, 21, 229, 19, + 112, 96, 60, 19, 114, 200, 12, 19, 114, 200, 16, 19, 112, 96, 16, 19, 112, 96, 137, 19, 112, 96, 164, 0, 152, 150, 128, + 19, 112, 106, 236, 19, 112, 96, 12, 19, 112, 96, 24, 38, 1, 37, 1, 66, 109, 153, 4, 0, 203, 24, 228, 224, 7, 104, 32, + 104, 97, 240, 5, 248, 175, 154, 4, 52, 8, 50, 1, 146, 4, 155, 4, 153, 11, 66, 139, 219, 243, 224, 9, 38, 1, 37, 1, 224, + 1, 38, 1, 37, 6, 66, 109, 224, 2, 37, 0, 38, 1, 149, 7, 75, 8, 36, 0, 104, 27, 43, 0, 208, 4, 28, 56, 73, 6, 240, 5, + 248, 203, 36, 1, 46, 0, 209, 0, 228, 101, 152, 15, 28, 41, 240, 5, 248, 167, 228, 96, 19, 112, 96, 20, 0, 152, 150, + 128, 74, 3, 35, 128, 104, 17, 2, 27, 67, 11, 96, 19, 71, 112, 70, 192, 13, 4, 0, 204, 75, 2, 74, 3, 104, 25, 64, 10, + 96, 26, 71, 112, 13, 4, 0, 204, 255, 255, 127, 255, 181, 16, 247, 255, 255, 243, 32, 128, 240, 3, 255, 104, 33, 32, + 240, 5, 248, 85, 76, 9, 96, 32, 32, 4, 240, 5, 248, 92, 104, 33, 34, 0, 32, 4, 240, 5, 248, 79, 247, 255, 255, 215, + 32, 4, 240, 5, 248, 110, 188, 16, 188, 1, 71, 0, 70, 192, 19, 112, 96, 32, 181, 16, 28, 4, 75, 15, 28, 8, 0, 137, 24, + 9, 104, 26, 0, 73, 35, 1, 240, 5, 248, 117, 75, 12, 33, 16, 96, 24, 72, 11, 240, 4, 248, 110, 33, 16, 72, 10, 240, 4, + 248, 117, 75, 10, 34, 55, 96, 28, 75, 9, 32, 4, 104, 27, 104, 155, 96, 154, 240, 5, 248, 73, 188, 16, 188, 1, 71, 0, + 19, 112, 96, 32, 19, 112, 96, 28, 13, 128, 0, 56, 13, 128, 0, 60, 19, 112, 107, 0, 19, 112, 96, 60, 181, 112, 76, 21, + 75, 21, 38, 2, 66, 118, 28, 33, 104, 24, 34, 0, 96, 38, 240, 5, 248, 59, 75, 18, 77, 18, 104, 27, 104, 154, 35, 0, 96, + 147, 74, 17, 104, 40, 96, 19, 240, 5, 248, 68, 104, 40, 240, 5, 248, 9, 35, 1, 66, 91, 96, 43, 104, 35, 43, 0, 209, + 3, 75, 11, 104, 27, 96, 35, 224, 0, 96, 38, 32, 4, 240, 5, 248, 19, 75, 2, 104, 24, 188, 112, 188, 2, 71, 8, 19, 112, + 106, 248, 19, 112, 96, 32, 19, 112, 96, 60, 19, 112, 96, 28, 19, 112, 107, 0, 19, 112, 106, 252, 75, 2, 34, 0, 96, 24, + 75, 2, 96, 26, 71, 112, 19, 112, 107, 4, 19, 112, 107, 0, 181, 248, 76, 34, 38, 16, 104, 35, 77, 33, 67, 179, 96, 35, + 104, 43, 73, 32, 104, 154, 121, 19, 121, 87, 6, 27, 4, 63, 67, 31, 121, 147, 2, 27, 67, 31, 121, 211, 67, 31, 104, 11, + 43, 0, 208, 18, 28, 56, 240, 0, 248, 56, 40, 0, 220, 20, 74, 22, 35, 0, 96, 19, 75, 22, 96, 24, 32, 4, 240, 4, 255, + 216, 104, 43, 34, 55, 104, 155, 64, 23, 96, 95, 224, 22, 75, 17, 104, 27, 43, 0, 208, 8, 28, 56, 240, 0, 248, 33, 104, + 43, 34, 55, 104, 155, 64, 23, 96, 95, 224, 2, 35, 55, 64, 31, 96, 87, 104, 35, 67, 51, 96, 35, 75, 9, 104, 26, 67, 22, + 96, 30, 32, 0, 188, 248, 188, 2, 71, 8, 70, 192, 13, 128, 0, 60, 19, 112, 96, 60, 19, 112, 107, 0, 19, 112, 106, 252, + 19, 112, 107, 4, 13, 128, 0, 56, 71, 24, 70, 192, 181, 112, 28, 12, 28, 5, 240, 4, 255, 93, 28, 6, 32, 1, 66, 64, 240, + 4, 255, 92, 104, 34, 75, 17, 66, 154, 209, 25, 75, 16, 73, 17, 34, 1, 67, 19, 96, 75, 28, 32, 34, 8, 240, 4, 254, 59, + 28, 32, 27, 100, 8, 164, 33, 8, 60, 2, 240, 4, 255, 102, 2, 36, 35, 234, 10, 36, 6, 27, 67, 35, 96, 43, 28, 40, 33, + 4, 240, 4, 255, 92, 28, 48, 240, 4, 255, 59, 188, 112, 188, 1, 71, 0, 230, 0, 1, 112, 19, 112, 4, 153, 19, 112, 96, + 36, 181, 56, 28, 4, 240, 4, 255, 148, 44, 38, 208, 72, 44, 38, 216, 5, 44, 36, 208, 9, 44, 37, 208, 0, 224, 169, 224, + 14, 44, 57, 208, 112, 44, 60, 208, 0, 224, 163, 224, 110, 73, 83, 75, 84, 72, 84, 96, 75, 34, 8, 240, 4, 254, 3, 72, + 82, 224, 150, 72, 82, 73, 82, 247, 255, 255, 171, 76, 77, 75, 81, 77, 82, 96, 99, 34, 8, 28, 40, 28, 33, 240, 4, 253, + 244, 28, 40, 33, 8, 240, 4, 255, 34, 75, 77, 77, 78, 96, 99, 34, 8, 28, 40, 28, 33, 240, 4, 253, 232, 28, 40, 33, 8, + 240, 4, 255, 22, 75, 73, 77, 74, 96, 99, 34, 8, 28, 33, 28, 40, 240, 4, 253, 220, 28, 40, 33, 8, 240, 4, 255, 10, 75, + 59, 72, 69, 96, 99, 28, 33, 34, 8, 240, 4, 253, 209, 72, 66, 224, 100, 72, 66, 73, 66, 247, 255, 255, 121, 76, 52, 75, + 65, 77, 57, 96, 99, 34, 8, 28, 40, 28, 33, 240, 4, 253, 194, 28, 40, 33, 8, 240, 4, 254, 240, 75, 60, 77, 53, 96, 99, + 34, 8, 28, 40, 28, 33, 240, 4, 253, 182, 28, 40, 33, 8, 240, 4, 254, 228, 75, 55, 77, 49, 96, 99, 34, 8, 28, 33, 28, + 40, 240, 4, 253, 170, 28, 40, 33, 8, 240, 4, 254, 216, 75, 34, 72, 50, 96, 99, 28, 33, 34, 8, 240, 4, 253, 159, 72, + 47, 224, 50, 72, 47, 224, 0, 72, 47, 73, 47, 247, 255, 255, 69, 76, 26, 75, 46, 77, 31, 96, 99, 34, 8, 28, 40, 28, 33, + 240, 4, 253, 142, 28, 40, 33, 8, 240, 4, 254, 188, 75, 41, 77, 27, 96, 99, 34, 8, 28, 40, 28, 33, 240, 4, 253, 130, + 28, 40, 33, 8, 240, 4, 254, 176, 75, 36, 77, 23, 96, 99, 34, 8, 28, 33, 28, 40, 240, 4, 253, 118, 28, 40, 33, 8, 240, + 4, 254, 164, 75, 8, 72, 31, 96, 99, 28, 33, 34, 8, 240, 4, 253, 107, 72, 28, 33, 8, 240, 4, 254, 153, 32, 0, 188, 56, + 188, 2, 71, 8, 70, 192, 19, 112, 96, 36, 19, 112, 0, 96, 255, 255, 30, 120, 32, 32, 93, 232, 32, 32, 64, 140, 255, 255, + 31, 112, 19, 112, 0, 188, 255, 255, 31, 140, 19, 112, 0, 196, 255, 255, 30, 52, 19, 112, 0, 204, 255, 255, 31, 104, + 32, 32, 91, 20, 32, 32, 62, 108, 255, 255, 30, 176, 255, 255, 30, 204, 255, 255, 29, 116, 255, 255, 30, 168, 32, 32, + 94, 132, 32, 32, 93, 148, 32, 32, 63, 96, 255, 255, 33, 48, 255, 255, 33, 76, 255, 255, 31, 244, 255, 255, 33, 40, 181, + 56, 77, 11, 35, 0, 104, 42, 73, 10, 240, 4, 254, 147, 76, 10, 73, 10, 34, 0, 96, 32, 104, 40, 240, 4, 254, 128, 104, + 32, 240, 4, 254, 145, 104, 32, 240, 4, 254, 86, 188, 56, 188, 1, 71, 0, 70, 192, 19, 112, 96, 44, 0, 152, 150, 128, + 19, 112, 96, 52, 19, 112, 107, 8, 181, 8, 1, 67, 26, 27, 0, 155, 24, 24, 0, 192, 247, 255, 255, 216, 188, 8, 188, 1, + 71, 0, 0, 0, 181, 0, 74, 5, 2, 192, 104, 19, 104, 17, 26, 201, 213, 0, 104, 19, 66, 129, 217, 249, 188, 1, 71, 0, 13, + 128, 0, 16, 181, 8, 75, 23, 120, 26, 75, 23, 96, 26, 240, 3, 254, 42, 75, 22, 33, 4, 96, 24, 28, 24, 240, 4, 254, 39, + 240, 3, 254, 44, 34, 0, 28, 1, 72, 18, 240, 3, 254, 97, 33, 160, 2, 73, 72, 16, 240, 4, 254, 63, 75, 16, 33, 128, 96, + 24, 240, 4, 254, 26, 33, 32, 240, 4, 253, 255, 75, 13, 96, 24, 240, 3, 252, 127, 40, 0, 219, 3, 247, 255, 249, 185, + 32, 0, 224, 1, 32, 1, 66, 64, 188, 8, 188, 2, 71, 8, 19, 112, 1, 12, 19, 112, 107, 104, 19, 114, 195, 160, 19, 112, + 14, 237, 19, 113, 131, 160, 19, 112, 96, 48, 19, 112, 96, 44, 181, 240, 7, 67, 213, 29, 33, 128, 79, 15, 78, 16, 34, + 0, 35, 0, 1, 137, 104, 56, 104, 53, 104, 128, 70, 172, 48, 68, 24, 128, 104, 4, 69, 99, 209, 5, 7, 224, 212, 8, 72, + 9, 36, 2, 80, 132, 224, 4, 77, 8, 64, 44, 44, 3, 209, 0, 96, 1, 51, 1, 50, 4, 43, 4, 209, 231, 188, 240, 188, 1, 71, + 0, 19, 112, 96, 60, 19, 112, 107, 104, 19, 112, 107, 108, 0, 0, 32, 3, 181, 240, 176, 131, 147, 1, 155, 8, 76, 14, 0, + 91, 104, 38, 28, 13, 28, 23, 147, 0, 32, 10, 247, 255, 255, 79, 104, 43, 154, 1, 64, 59, 66, 147, 208, 9, 104, 35, 27, + 155, 213, 0, 104, 38, 154, 0, 66, 147, 211, 240, 32, 2, 66, 64, 224, 0, 32, 0, 176, 3, 188, 240, 188, 2, 71, 8, 70, + 192, 13, 128, 0, 16, 181, 56, 28, 4, 240, 3, 252, 115, 33, 0, 28, 5, 34, 96, 28, 32, 240, 4, 252, 160, 35, 64, 96, 163, + 35, 128, 4, 91, 99, 101, 96, 35, 96, 99, 188, 56, 188, 1, 71, 0, 181, 16, 75, 12, 36, 0, 104, 27, 108, 26, 42, 7, 220, + 13, 73, 10, 104, 12, 0, 228, 24, 164, 0, 97, 25, 12, 106, 25, 1, 100, 25, 12, 50, 1, 100, 26, 28, 32, 247, 255, 255, + 215, 28, 32, 188, 16, 188, 2, 71, 8, 70, 192, 19, 112, 96, 60, 19, 112, 107, 88, 181, 240, 28, 4, 176, 133, 28, 8, 28, + 14, 147, 3, 28, 23, 240, 4, 251, 243, 35, 0, 98, 35, 5, 53, 35, 128, 13, 45, 1, 91, 27, 93, 96, 224, 66, 175, 210, 1, + 28, 61, 224, 49, 32, 128, 1, 64, 24, 54, 11, 54, 3, 54, 150, 2, 33, 1, 28, 38, 54, 16, 145, 1, 224, 20, 152, 2, 240, + 4, 251, 217, 34, 0, 198, 1, 97, 50, 155, 2, 33, 128, 28, 24, 35, 128, 1, 91, 1, 73, 24, 192, 24, 109, 144, 2, 66, 189, + 211, 0, 28, 61, 154, 1, 50, 1, 146, 1, 66, 189, 210, 8, 152, 1, 33, 0, 15, 195, 34, 4, 66, 130, 65, 75, 6, 27, 43, 0, + 209, 223, 66, 189, 208, 4, 28, 40, 153, 10, 240, 4, 253, 62, 26, 109, 155, 3, 4, 40, 67, 24, 240, 4, 251, 176, 35, 128, + 4, 91, 96, 160, 100, 37, 96, 35, 96, 99, 176, 5, 28, 40, 188, 240, 188, 2, 71, 8, 0, 0, 181, 240, 176, 139, 28, 5, 247, + 255, 255, 133, 28, 6, 32, 0, 46, 0, 209, 0, 224, 145, 99, 245, 105, 42, 126, 107, 126, 47, 97, 104, 146, 3, 147, 7, + 47, 0, 209, 25, 35, 8, 147, 0, 35, 160, 104, 105, 34, 8, 0, 155, 28, 48, 247, 255, 255, 140, 247, 255, 255, 108, 28, + 4, 28, 56, 44, 0, 208, 121, 107, 96, 99, 229, 240, 4, 251, 126, 154, 3, 96, 48, 99, 180, 42, 0, 208, 4, 79, 58, 224, + 3, 28, 52, 39, 128, 224, 0, 79, 56, 104, 235, 154, 7, 147, 4, 42, 0, 208, 2, 35, 128, 0, 91, 67, 31, 105, 235, 150, + 9, 5, 91, 13, 91, 147, 5, 154, 5, 35, 128, 4, 91, 58, 1, 147, 6, 146, 8, 155, 5, 154, 3, 147, 0, 28, 32, 28, 59, 153, + 4, 247, 255, 255, 92, 154, 3, 155, 4, 26, 18, 146, 3, 154, 7, 24, 27, 147, 4, 42, 0, 208, 1, 155, 6, 96, 99, 154, 8, + 155, 5, 24, 128, 66, 3, 209, 2, 34, 128, 6, 18, 24, 191, 155, 3, 43, 0, 221, 11, 247, 255, 255, 38, 30, 6, 208, 52, + 107, 112, 99, 245, 240, 4, 251, 58, 99, 166, 96, 32, 28, 52, 231, 213, 35, 128, 4, 91, 96, 99, 105, 43, 158, 9, 148, + 3, 43, 0, 208, 29, 126, 42, 146, 4, 42, 0, 209, 25, 247, 255, 255, 14, 28, 4, 32, 0, 44, 0, 208, 27, 107, 96, 99, 229, + 240, 4, 251, 32, 155, 3, 34, 128, 96, 24, 99, 156, 35, 128, 0, 91, 6, 18, 64, 123, 67, 19, 154, 4, 28, 32, 146, 0, 33, + 0, 34, 0, 247, 255, 255, 20, 104, 162, 35, 128, 4, 27, 67, 19, 96, 163, 28, 48, 224, 0, 32, 0, 176, 11, 188, 240, 188, + 2, 71, 8, 128, 0, 0, 128, 128, 0, 1, 128, 181, 56, 76, 19, 37, 32, 104, 35, 104, 155, 120, 25, 120, 90, 6, 9, 4, 18, + 67, 10, 120, 153, 120, 219, 2, 9, 67, 10, 67, 19, 224, 18, 104, 34, 67, 171, 104, 146, 32, 10, 96, 19, 247, 255, 253, + 243, 104, 35, 104, 155, 120, 25, 120, 90, 6, 9, 4, 18, 67, 10, 120, 153, 120, 219, 2, 9, 67, 10, 67, 19, 66, 29, 209, + 234, 188, 56, 188, 1, 71, 0, 19, 112, 96, 60, 181, 56, 76, 19, 37, 32, 104, 35, 104, 155, 120, 25, 120, 90, 6, 9, 4, + 18, 67, 10, 120, 153, 120, 219, 2, 9, 67, 10, 67, 19, 224, 18, 104, 34, 67, 43, 104, 146, 32, 10, 96, 19, 247, 255, + 253, 201, 104, 35, 104, 155, 120, 25, 120, 90, 6, 9, 4, 18, 67, 10, 120, 153, 120, 219, 2, 9, 67, 10, 67, 19, 66, 29, + 208, 234, 188, 56, 188, 1, 71, 0, 19, 112, 96, 60, 181, 248, 28, 12, 40, 0, 209, 33, 77, 28, 76, 29, 79, 29, 38, 0, + 104, 43, 104, 155, 126, 25, 126, 90, 6, 9, 4, 18, 67, 10, 126, 153, 126, 219, 2, 9, 67, 10, 67, 19, 105, 34, 108, 82, + 66, 147, 211, 6, 105, 98, 108, 82, 66, 147, 216, 2, 247, 255, 255, 142, 224, 25, 32, 10, 54, 1, 247, 255, 253, 147, + 66, 190, 209, 226, 224, 18, 40, 1, 209, 16, 224, 6, 32, 10, 54, 1, 247, 255, 253, 137, 66, 190, 209, 3, 224, 8, 77, + 6, 79, 8, 38, 0, 104, 43, 104, 155, 105, 154, 108, 99, 66, 154, 208, 239, 32, 0, 188, 248, 188, 2, 71, 8, 70, 192, 19, + 112, 96, 60, 19, 114, 195, 192, 0, 0, 19, 136, 181, 0, 7, 67, 213, 21, 75, 12, 104, 27, 104, 154, 75, 11, 108, 81, 64, + 11, 43, 3, 209, 2, 35, 128, 1, 155, 100, 83, 75, 6, 104, 27, 104, 154, 75, 6, 108, 145, 64, 11, 43, 3, 209, 2, 35, 128, + 1, 155, 100, 147, 188, 1, 71, 0, 70, 192, 19, 112, 96, 60, 0, 0, 32, 3, 181, 16, 75, 19, 28, 4, 104, 27, 52, 16, 104, + 155, 0, 164, 25, 28, 34, 128, 104, 99, 1, 146, 66, 19, 209, 0, 64, 83, 34, 46, 67, 147, 96, 99, 32, 5, 247, 255, 253, + 106, 34, 128, 104, 99, 1, 146, 66, 19, 208, 0, 64, 83, 34, 46, 67, 147, 96, 99, 32, 5, 247, 255, 253, 94, 75, 4, 32, + 5, 96, 99, 247, 255, 253, 89, 188, 16, 188, 1, 71, 0, 19, 112, 96, 60, 0, 0, 24, 1, 181, 240, 176, 131, 75, 36, 28, + 5, 104, 27, 53, 16, 104, 155, 0, 173, 25, 93, 104, 107, 74, 33, 29, 44, 64, 26, 38, 4, 42, 1, 208, 4, 4, 154, 213, 51, + 247, 255, 255, 192, 224, 48, 39, 136, 1, 127, 34, 4, 67, 147, 67, 59, 96, 35, 32, 60, 247, 255, 253, 40, 104, 35, 74, + 24, 32, 50, 64, 19, 96, 35, 247, 255, 253, 33, 75, 22, 34, 128, 147, 0, 28, 32, 28, 33, 0, 82, 35, 0, 247, 255, 253, + 156, 40, 0, 208, 2, 104, 107, 72, 17, 224, 20, 104, 35, 34, 4, 66, 26, 209, 16, 62, 1, 46, 0, 209, 220, 34, 128, 1, + 146, 67, 19, 33, 42, 67, 139, 96, 107, 32, 10, 96, 106, 247, 255, 253, 2, 72, 8, 224, 1, 32, 1, 66, 64, 176, 3, 188, + 240, 188, 2, 71, 8, 19, 112, 96, 60, 0, 0, 32, 1, 255, 255, 254, 213, 0, 0, 19, 136, 255, 255, 248, 48, 255, 255, 251, + 161, 181, 16, 75, 20, 28, 4, 104, 27, 52, 16, 104, 155, 0, 164, 25, 28, 34, 128, 104, 99, 1, 146, 66, 19, 209, 0, 64, + 83, 34, 46, 67, 147, 96, 99, 32, 5, 247, 255, 252, 230, 34, 128, 104, 99, 1, 146, 66, 19, 208, 0, 64, 83, 34, 46, 67, + 147, 96, 99, 32, 5, 247, 255, 252, 218, 75, 5, 32, 60, 96, 99, 247, 255, 252, 213, 32, 1, 188, 16, 188, 2, 71, 8, 70, + 192, 19, 112, 96, 60, 0, 0, 24, 1, 75, 5, 48, 16, 104, 27, 0, 128, 104, 155, 24, 24, 35, 128, 1, 155, 96, 67, 32, 0, + 71, 112, 70, 192, 19, 112, 96, 60, 74, 5, 32, 0, 104, 19, 104, 153, 35, 128, 1, 155, 100, 75, 104, 18, 104, 146, 100, + 147, 71, 112, 70, 192, 19, 112, 96, 60, 181, 0, 75, 10, 104, 27, 104, 154, 35, 128, 108, 81, 1, 155, 66, 25, 209, 0, + 100, 83, 75, 5, 104, 27, 104, 154, 35, 128, 108, 145, 1, 155, 66, 25, 209, 0, 100, 147, 32, 0, 188, 2, 71, 8, 19, 112, + 96, 60, 75, 6, 33, 0, 104, 26, 35, 1, 66, 91, 28, 16, 103, 17, 103, 147, 48, 152, 50, 160, 96, 1, 96, 19, 71, 112, 70, + 192, 19, 112, 96, 60, 181, 112, 75, 13, 104, 25, 28, 11, 51, 84, 28, 10, 120, 29, 50, 120, 35, 0, 224, 9, 0, 156, 24, + 228, 0, 228, 104, 22, 52, 88, 25, 12, 50, 40, 66, 134, 208, 4, 51, 1, 66, 171, 219, 243, 32, 0, 224, 0, 28, 32, 188, + 112, 188, 2, 71, 8, 19, 112, 96, 60, 181, 240, 176, 133, 77, 24, 28, 1, 36, 0, 32, 0, 70, 172, 145, 1, 224, 26, 0, 166, + 25, 54, 0, 246, 25, 174, 111, 53, 45, 0, 208, 18, 0, 197, 83, 92, 39, 0, 25, 93, 128, 111, 54, 96, 136, 55, 48, 1, 4, + 63, 10, 57, 14, 63, 67, 15, 128, 175, 136, 118, 4, 54, 10, 55, 14, 54, 67, 62, 128, 238, 52, 1, 70, 103, 104, 61, 28, + 46, 54, 84, 120, 54, 66, 180, 218, 2, 153, 1, 66, 136, 219, 218, 112, 16, 176, 5, 188, 240, 188, 2, 71, 8, 70, 192, + 19, 112, 96, 60, 181, 0, 28, 3, 224, 8, 120, 90, 58, 4, 6, 18, 14, 18, 42, 1, 217, 4, 120, 26, 26, 137, 24, 155, 41, + 0, 209, 244, 26, 24, 188, 2, 71, 8, 181, 240, 176, 133, 124, 130, 124, 195, 6, 18, 4, 27, 67, 19, 125, 2, 28, 5, 2, + 18, 67, 19, 125, 66, 67, 26, 208, 103, 34, 0, 146, 3, 146, 1, 224, 83, 6, 36, 4, 9, 2, 18, 67, 12, 67, 20, 158, 3, 67, + 35, 25, 156, 122, 98, 122, 163, 6, 18, 4, 27, 67, 19, 122, 226, 2, 18, 67, 19, 123, 34, 67, 26, 208, 58, 39, 0, 151, + 2, 28, 46, 224, 36, 6, 0, 4, 9, 2, 18, 67, 8, 67, 16, 67, 3, 25, 221, 123, 170, 123, 235, 6, 18, 4, 27, 67, 19, 124, + 42, 124, 104, 2, 18, 67, 19, 67, 24, 208, 1, 240, 3, 249, 66, 122, 106, 122, 171, 6, 18, 4, 27, 67, 19, 122, 234, 123, + 40, 2, 18, 67, 19, 67, 24, 208, 1, 240, 3, 249, 53, 154, 2, 55, 18, 50, 1, 146, 2, 121, 35, 157, 2, 70, 156, 122, 96, + 122, 161, 122, 226, 123, 35, 69, 101, 219, 209, 28, 53, 6, 6, 4, 8, 67, 48, 2, 18, 67, 16, 67, 24, 240, 3, 249, 31, + 158, 1, 154, 3, 54, 1, 50, 13, 150, 1, 146, 3, 124, 104, 158, 1, 124, 172, 124, 233, 125, 42, 125, 107, 66, 134, 219, + 163, 6, 36, 4, 8, 67, 32, 2, 18, 67, 16, 67, 24, 240, 3, 249, 9, 176, 5, 188, 240, 188, 1, 71, 0, 0, 0, 181, 248, 30, + 5, 209, 4, 75, 28, 34, 48, 112, 26, 112, 93, 224, 47, 76, 26, 28, 46, 28, 48, 33, 10, 240, 4, 249, 236, 28, 48, 28, + 15, 33, 10, 240, 4, 249, 255, 23, 251, 24, 255, 64, 95, 55, 48, 75, 20, 112, 39, 60, 1, 28, 6, 66, 156, 209, 236, 75, + 15, 34, 0, 118, 154, 45, 0, 218, 2, 34, 45, 112, 26, 34, 1, 35, 16, 72, 10, 224, 0, 51, 1, 92, 193, 41, 48, 208, 251, + 66, 72, 65, 65, 26, 91, 73, 6, 224, 2, 84, 136, 51, 1, 50, 1, 92, 200, 40, 0, 209, 249, 84, 136, 188, 248, 188, 1, 71, + 0, 70, 192, 19, 114, 197, 128, 19, 114, 197, 153, 19, 114, 197, 143, 181, 248, 30, 4, 209, 4, 75, 24, 34, 48, 112, 26, + 112, 92, 224, 40, 77, 22, 28, 47, 63, 10, 28, 32, 33, 10, 240, 4, 249, 129, 28, 32, 28, 14, 33, 10, 240, 4, 249, 86, + 54, 48, 112, 46, 61, 1, 28, 4, 66, 189, 209, 240, 74, 12, 35, 0, 118, 147, 28, 17, 35, 16, 224, 0, 51, 1, 92, 202, 42, + 48, 208, 251, 66, 81, 65, 74, 26, 155, 73, 6, 34, 0, 224, 2, 84, 136, 51, 1, 50, 1, 92, 200, 40, 0, 209, 249, 84, 136, + 188, 248, 188, 1, 71, 0, 19, 114, 197, 128, 19, 114, 197, 153, 181, 16, 40, 0, 209, 4, 75, 24, 34, 48, 112, 26, 112, + 88, 224, 40, 75, 22, 33, 15, 28, 28, 60, 8, 28, 10, 64, 2, 9, 0, 42, 9, 221, 0, 50, 7, 50, 48, 112, 26, 59, 1, 66, 163, + 209, 244, 74, 14, 35, 0, 118, 19, 35, 48, 112, 19, 35, 120, 112, 83, 35, 16, 224, 0, 51, 1, 92, 209, 41, 48, 208, 251, + 66, 74, 65, 74, 26, 155, 73, 6, 34, 2, 224, 2, 84, 136, 51, 1, 50, 1, 92, 200, 40, 0, 209, 249, 84, 136, 188, 16, 188, + 1, 71, 0, 19, 114, 197, 128, 19, 114, 197, 151, 180, 15, 181, 240, 176, 133, 171, 10, 203, 64, 34, 128, 1, 146, 169, + 3, 128, 10, 147, 2, 36, 0, 77, 46, 79, 46, 224, 81, 28, 114, 146, 1, 43, 37, 208, 12, 70, 106, 115, 19, 168, 3, 240, + 4, 248, 121, 25, 4, 158, 1, 66, 172, 220, 68, 168, 3, 240, 4, 249, 90, 224, 64, 120, 115, 43, 115, 208, 46, 43, 115, + 216, 4, 43, 100, 208, 7, 43, 105, 209, 53, 224, 4, 43, 117, 208, 9, 43, 120, 209, 48, 224, 20, 155, 2, 29, 26, 146, + 2, 104, 24, 247, 255, 255, 26, 224, 19, 155, 2, 29, 26, 146, 2, 104, 24, 247, 255, 255, 85, 28, 56, 240, 4, 248, 82, + 25, 4, 28, 56, 66, 172, 220, 27, 224, 24, 155, 2, 29, 26, 146, 2, 104, 24, 247, 255, 255, 127, 72, 16, 240, 4, 248, + 68, 25, 4, 66, 172, 220, 14, 72, 13, 224, 10, 155, 2, 29, 26, 146, 2, 104, 30, 28, 48, 240, 4, 248, 56, 25, 4, 66, 172, + 220, 2, 28, 48, 240, 4, 249, 26, 158, 1, 54, 1, 120, 51, 43, 0, 209, 170, 176, 5, 188, 240, 188, 8, 176, 4, 71, 24, + 0, 0, 13, 171, 19, 114, 197, 128, 181, 240, 28, 12, 176, 133, 28, 6, 33, 0, 32, 0, 247, 255, 252, 160, 126, 35, 43, + 0, 209, 9, 74, 221, 104, 17, 74, 221, 0, 137, 80, 139, 104, 32, 33, 8, 240, 2, 255, 138, 96, 96, 105, 33, 41, 0, 208, + 9, 126, 99, 104, 160, 43, 0, 208, 2, 240, 2, 255, 128, 224, 1, 240, 2, 255, 133, 96, 224, 126, 35, 43, 0, 209, 3, 75, + 210, 104, 27, 105, 219, 224, 6, 126, 99, 43, 0, 208, 1, 75, 207, 224, 0, 75, 207, 104, 27, 147, 2, 77, 207, 75, 207, + 153, 2, 28, 40, 96, 25, 34, 96, 240, 3, 248, 177, 33, 0, 34, 48, 152, 2, 240, 3, 255, 146, 75, 197, 34, 0, 104, 27, + 28, 32, 100, 26, 75, 200, 34, 1, 104, 25, 64, 74, 96, 26, 247, 255, 251, 102, 75, 197, 100, 168, 96, 24, 126, 34, 35, + 15, 146, 1, 64, 26, 146, 3, 155, 3, 28, 7, 34, 128, 105, 176, 2, 29, 1, 146, 155, 1, 67, 2, 105, 225, 67, 42, 43, 0, + 209, 1, 72, 188, 224, 5, 5, 72, 35, 128, 9, 64, 5, 219, 67, 3, 28, 24, 67, 16, 240, 3, 254, 235, 77, 179, 35, 64, 96, + 171, 75, 182, 33, 255, 104, 27, 34, 255, 108, 91, 2, 9, 4, 18, 64, 25, 64, 26, 2, 9, 10, 18, 67, 10, 14, 25, 67, 10, + 33, 224, 64, 11, 33, 2, 67, 11, 6, 27, 67, 26, 96, 104, 96, 42, 107, 120, 240, 3, 254, 207, 35, 128, 153, 1, 4, 91, + 97, 40, 97, 107, 41, 0, 208, 16, 9, 203, 153, 3, 106, 114, 1, 27, 24, 91, 64, 218, 28, 19, 7, 218, 213, 3, 105, 170, + 35, 128, 67, 19, 224, 2, 105, 171, 34, 128, 67, 147, 97, 171, 77, 153, 75, 158, 105, 170, 28, 40, 64, 19, 97, 171, 33, + 96, 240, 2, 255, 8, 108, 173, 224, 4, 28, 40, 33, 96, 240, 2, 255, 2, 107, 173, 45, 0, 209, 248, 152, 2, 73, 143, 34, + 96, 240, 3, 248, 41, 75, 148, 120, 27, 43, 0, 208, 4, 75, 147, 72, 148, 104, 25, 247, 255, 254, 215, 75, 147, 34, 1, + 96, 26, 77, 132, 35, 0, 147, 1, 153, 1, 74, 141, 49, 1, 145, 1, 72, 143, 104, 17, 247, 254, 255, 173, 104, 43, 33, 32, + 105, 152, 240, 2, 254, 242, 104, 42, 73, 127, 39, 2, 108, 75, 105, 145, 34, 255, 2, 18, 64, 26, 2, 16, 34, 255, 4, 18, + 64, 26, 10, 18, 67, 2, 14, 24, 67, 2, 32, 224, 64, 3, 67, 59, 6, 24, 28, 19, 67, 3, 96, 11, 104, 43, 33, 32, 105, 152, + 240, 2, 254, 194, 247, 255, 251, 144, 247, 254, 255, 182, 154, 1, 42, 4, 208, 6, 28, 3, 51, 9, 209, 3, 72, 120, 247, + 255, 254, 155, 231, 200, 75, 116, 34, 0, 96, 26, 75, 112, 28, 7, 120, 27, 43, 0, 208, 3, 72, 115, 28, 57, 247, 255, + 254, 142, 47, 0, 209, 6, 75, 94, 104, 26, 75, 94, 0, 146, 88, 211, 43, 0, 208, 15, 74, 93, 75, 90, 104, 18, 104, 27, + 104, 146, 0, 155, 50, 68, 24, 210, 104, 18, 33, 5, 64, 10, 42, 5, 208, 2, 74, 85, 33, 1, 80, 153, 66, 121, 65, 121, + 145, 1, 29, 122, 208, 1, 41, 0, 208, 65, 247, 255, 251, 44, 77, 80, 33, 32, 104, 43, 105, 152, 240, 3, 255, 166, 104, + 43, 33, 32, 105, 152, 240, 2, 254, 105, 75, 83, 104, 42, 104, 27, 105, 145, 108, 91, 34, 255, 2, 18, 64, 26, 2, 16, + 34, 255, 4, 18, 64, 26, 10, 18, 67, 2, 14, 24, 67, 2, 32, 224, 64, 3, 32, 2, 67, 3, 6, 24, 28, 19, 67, 3, 96, 11, 104, + 43, 33, 32, 105, 152, 240, 3, 255, 124, 247, 255, 251, 44, 126, 34, 42, 0, 208, 98, 9, 211, 32, 15, 64, 16, 1, 27, 24, + 27, 106, 113, 34, 1, 64, 154, 67, 145, 28, 10, 73, 70, 104, 9, 6, 9, 15, 201, 64, 153, 28, 11, 67, 19, 98, 115, 224, + 79, 247, 255, 250, 234, 75, 50, 108, 157, 224, 4, 107, 104, 33, 96, 240, 2, 254, 81, 107, 173, 45, 0, 209, 248, 77, + 45, 34, 32, 28, 40, 153, 2, 240, 2, 255, 112, 126, 34, 42, 0, 208, 16, 9, 211, 32, 15, 64, 16, 1, 27, 24, 27, 106, 113, + 34, 1, 64, 154, 67, 145, 28, 10, 105, 169, 6, 9, 15, 201, 64, 153, 28, 11, 67, 19, 98, 115, 77, 29, 33, 32, 104, 43, + 38, 2, 105, 152, 240, 3, 255, 63, 104, 43, 33, 32, 105, 152, 240, 2, 254, 2, 75, 31, 104, 42, 104, 27, 105, 145, 108, + 91, 34, 255, 2, 18, 64, 26, 2, 16, 34, 255, 4, 18, 64, 26, 10, 18, 67, 2, 14, 24, 67, 2, 32, 224, 64, 3, 67, 51, 6, + 24, 28, 19, 67, 3, 96, 11, 104, 43, 33, 32, 105, 152, 240, 3, 255, 22, 247, 255, 250, 198, 73, 11, 32, 1, 247, 255, + 250, 236, 105, 33, 41, 0, 208, 48, 126, 99, 104, 224, 43, 0, 208, 42, 240, 2, 253, 241, 224, 41, 19, 112, 107, 104, + 19, 112, 107, 108, 19, 112, 96, 60, 19, 112, 107, 16, 19, 112, 107, 96, 19, 114, 197, 32, 19, 112, 107, 28, 19, 112, + 107, 88, 19, 112, 107, 56, 64, 64, 64, 0, 19, 112, 107, 92, 1, 0, 0, 128, 19, 112, 107, 20, 19, 112, 96, 56, 19, 112, + 96, 194, 19, 112, 107, 52, 19, 112, 42, 177, 19, 112, 98, 156, 19, 112, 96, 240, 19, 112, 107, 36, 240, 2, 253, 204, + 126, 35, 43, 0, 209, 3, 104, 96, 33, 8, 240, 2, 253, 191, 155, 1, 43, 0, 208, 0, 105, 103, 176, 5, 28, 56, 188, 240, + 188, 2, 71, 8, 181, 48, 176, 137, 70, 108, 118, 33, 9, 201, 118, 97, 33, 128, 0, 137, 37, 0, 145, 7, 70, 105, 149, 0, + 146, 4, 147, 2, 247, 255, 253, 240, 176, 9, 188, 48, 188, 2, 71, 8, 181, 240, 176, 139, 28, 6, 70, 104, 48, 70, 136, + 4, 72, 43, 70, 156, 104, 0, 70, 107, 51, 66, 48, 248, 104, 0, 136, 27, 112, 66, 4, 27, 70, 98, 147, 1, 2, 19, 10, 18, + 67, 19, 4, 26, 12, 19, 14, 18, 112, 1, 159, 18, 112, 130, 112, 195, 155, 1, 4, 36, 10, 26, 14, 27, 67, 19, 4, 27, 12, + 37, 12, 26, 14, 27, 113, 3, 14, 36, 2, 43, 67, 35, 4, 27, 113, 66, 14, 26, 12, 27, 113, 195, 172, 2, 35, 0, 113, 130, + 118, 35, 35, 64, 97, 227, 75, 21, 9, 201, 144, 2, 118, 97, 97, 37, 66, 159, 217, 22, 28, 40, 240, 2, 253, 118, 28, 42, + 28, 57, 96, 160, 240, 3, 253, 55, 28, 33, 28, 48, 247, 255, 253, 165, 104, 164, 28, 6, 28, 33, 28, 56, 28, 42, 240, + 3, 253, 44, 28, 32, 240, 2, 253, 129, 224, 5, 28, 48, 96, 167, 28, 33, 247, 255, 253, 149, 28, 6, 176, 11, 28, 48, 188, + 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 60, 19, 136, 0, 0, 181, 0, 176, 133, 145, 0, 35, 0, 33, 2, 34, 1, 147, 1, + 147, 2, 247, 255, 255, 148, 176, 5, 188, 2, 71, 8, 181, 0, 176, 133, 28, 19, 34, 0, 145, 0, 146, 1, 146, 2, 33, 1, 34, + 11, 247, 255, 255, 134, 176, 5, 188, 2, 71, 8, 181, 0, 176, 133, 34, 0, 146, 0, 146, 1, 146, 2, 28, 11, 34, 9, 33, 0, + 247, 255, 255, 120, 176, 5, 188, 2, 71, 8, 181, 112, 28, 4, 176, 132, 32, 1, 28, 14, 240, 2, 253, 34, 30, 5, 208, 18, + 35, 0, 147, 0, 35, 1, 147, 1, 28, 32, 33, 128, 34, 8, 35, 0, 149, 2, 247, 255, 255, 97, 30, 4, 219, 1, 120, 43, 112, + 51, 28, 40, 240, 2, 253, 44, 224, 1, 36, 4, 66, 100, 176, 4, 28, 32, 188, 112, 188, 2, 71, 8, 181, 16, 176, 132, 70, + 108, 2, 18, 52, 27, 120, 36, 67, 19, 34, 0, 146, 0, 145, 2, 34, 6, 33, 128, 148, 1, 247, 255, 255, 67, 176, 4, 188, + 16, 188, 2, 71, 8, 0, 0, 181, 240, 176, 137, 75, 65, 28, 2, 104, 30, 0, 129, 104, 179, 24, 9, 50, 16, 0, 201, 0, 146, + 24, 154, 28, 11, 51, 88, 28, 7, 24, 240, 144, 5, 24, 113, 32, 0, 103, 8, 104, 81, 29, 21, 74, 56, 64, 10, 42, 1, 209, + 99, 34, 4, 146, 4, 147, 7, 75, 54, 32, 10, 96, 43, 247, 254, 255, 134, 75, 52, 32, 100, 96, 43, 247, 254, 255, 129, + 75, 51, 34, 128, 96, 43, 35, 250, 0, 219, 147, 0, 0, 82, 35, 0, 28, 40, 28, 41, 247, 254, 255, 249, 104, 42, 75, 45, + 28, 4, 64, 19, 43, 5, 209, 50, 35, 192, 1, 27, 64, 26, 35, 128, 0, 219, 66, 154, 208, 43, 40, 0, 209, 43, 32, 100, 247, + 254, 255, 99, 75, 38, 74, 38, 104, 24, 96, 26, 154, 7, 35, 22, 147, 1, 24, 179, 147, 2, 35, 128, 144, 6, 33, 128, 152, + 5, 34, 6, 0, 91, 148, 0, 247, 255, 254, 231, 30, 4, 219, 12, 28, 123, 32, 0, 4, 27, 144, 0, 144, 1, 144, 2, 12, 27, + 152, 5, 33, 0, 34, 5, 247, 255, 254, 217, 28, 4, 75, 21, 154, 6, 96, 26, 44, 0, 219, 2, 224, 8, 36, 1, 66, 100, 155, + 4, 59, 1, 147, 4, 43, 0, 209, 169, 44, 0, 219, 10, 0, 187, 25, 219, 0, 219, 24, 246, 55, 1, 35, 0, 103, 243, 103, 55, + 224, 1, 36, 1, 66, 100, 176, 9, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 60, 0, 0, 32, 1, 0, 0, 24, 3, + 0, 0, 25, 3, 0, 0, 16, 1, 0, 0, 32, 5, 19, 112, 96, 56, 0, 6, 26, 128, 181, 248, 76, 25, 28, 6, 104, 35, 104, 157, 35, + 1, 96, 171, 247, 255, 255, 97, 30, 7, 218, 24, 104, 35, 33, 55, 104, 155, 28, 52, 104, 90, 32, 10, 64, 10, 96, 90, 52, + 16, 247, 254, 254, 252, 0, 164, 75, 15, 25, 44, 96, 99, 32, 50, 247, 254, 254, 245, 75, 13, 32, 100, 96, 99, 247, 254, + 254, 240, 75, 11, 96, 99, 75, 7, 32, 55, 104, 26, 104, 146, 104, 81, 64, 1, 96, 81, 104, 27, 34, 4, 104, 155, 28, 56, + 96, 154, 188, 248, 188, 2, 71, 8, 70, 192, 19, 112, 96, 60, 0, 0, 24, 3, 0, 0, 25, 3, 0, 0, 16, 1, 181, 240, 176, 137, + 75, 126, 34, 0, 104, 31, 0, 131, 24, 27, 0, 219, 28, 30, 147, 5, 54, 88, 24, 251, 28, 4, 25, 190, 103, 26, 32, 50, 247, + 254, 254, 196, 37, 3, 150, 6, 72, 118, 247, 255, 251, 213, 32, 50, 247, 254, 254, 188, 35, 22, 147, 1, 155, 6, 34, 0, + 147, 2, 35, 128, 146, 0, 28, 48, 33, 128, 34, 6, 0, 91, 247, 255, 254, 69, 40, 0, 218, 12, 72, 109, 247, 255, 251, 192, + 32, 50, 247, 254, 254, 167, 61, 1, 32, 100, 247, 254, 254, 163, 45, 0, 209, 222, 224, 2, 154, 5, 24, 187, 224, 38, 28, + 32, 247, 255, 249, 84, 75, 100, 66, 152, 209, 0, 224, 184, 32, 100, 247, 254, 254, 147, 72, 98, 247, 255, 251, 166, + 35, 22, 147, 1, 155, 6, 34, 0, 147, 2, 35, 128, 146, 0, 28, 48, 33, 128, 34, 6, 0, 91, 247, 255, 254, 25, 40, 0, 218, + 6, 72, 90, 53, 1, 247, 255, 251, 147, 45, 3, 209, 221, 224, 12, 0, 163, 25, 27, 0, 219, 24, 251, 110, 26, 75, 85, 66, + 154, 209, 46, 28, 32, 247, 255, 249, 175, 76, 83, 224, 144, 28, 32, 247, 255, 248, 248, 32, 100, 247, 254, 254, 103, + 28, 32, 247, 255, 249, 30, 74, 73, 66, 144, 209, 0, 224, 130, 32, 100, 247, 254, 254, 93, 72, 75, 247, 255, 251, 112, + 35, 0, 154, 6, 147, 0, 35, 22, 147, 1, 35, 128, 146, 2, 28, 48, 33, 128, 34, 6, 0, 91, 247, 255, 253, 227, 40, 0, 218, + 7, 72, 63, 61, 1, 247, 255, 251, 93, 45, 0, 209, 215, 76, 64, 224, 102, 72, 64, 247, 255, 251, 86, 28, 99, 147, 5, 4, + 27, 12, 27, 147, 7, 0, 163, 25, 28, 0, 228, 37, 0, 25, 63, 32, 50, 247, 254, 254, 51, 72, 57, 28, 41, 247, 255, 251, + 69, 35, 0, 147, 0, 147, 1, 147, 2, 28, 48, 33, 0, 34, 5, 155, 7, 247, 255, 253, 187, 30, 4, 218, 7, 76, 50, 28, 41, + 72, 50, 27, 100, 247, 255, 251, 51, 53, 1, 224, 3, 72, 48, 28, 41, 247, 255, 251, 45, 155, 5, 34, 0, 103, 250, 103, + 59, 44, 0, 218, 47, 33, 0, 28, 48, 247, 255, 254, 5, 32, 50, 247, 254, 254, 10, 72, 40, 247, 255, 251, 29, 35, 22, 147, + 1, 155, 6, 34, 0, 147, 2, 35, 128, 146, 0, 28, 48, 33, 128, 34, 6, 0, 91, 247, 255, 253, 144, 30, 4, 218, 6, 72, 32, + 247, 255, 251, 11, 34, 0, 103, 58, 76, 31, 224, 2, 72, 31, 247, 255, 251, 4, 53, 1, 44, 0, 218, 7, 15, 233, 34, 0, 35, + 4, 66, 171, 65, 74, 6, 18, 42, 0, 209, 172, 44, 0, 219, 4, 72, 24, 247, 255, 250, 244, 224, 0, 76, 6, 176, 9, 28, 32, + 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 60, 19, 112, 97, 75, 19, 112, 97, 0, 255, 255, 251, 161, 19, 112, 97, + 30, 19, 112, 97, 69, 149, 11, 32, 119, 255, 255, 251, 160, 19, 112, 97, 106, 255, 255, 247, 103, 19, 112, 97, 155, 19, + 112, 97, 181, 255, 255, 224, 192, 19, 112, 97, 212, 19, 112, 97, 243, 19, 112, 98, 55, 19, 112, 98, 14, 255, 255, 247, + 62, 19, 112, 98, 52, 19, 112, 98, 87, 181, 16, 75, 10, 34, 1, 104, 27, 28, 4, 104, 155, 96, 154, 247, 255, 248, 95, + 40, 0, 219, 2, 28, 32, 247, 255, 254, 202, 75, 3, 34, 4, 104, 27, 104, 155, 96, 154, 188, 16, 188, 2, 71, 8, 19, 112, + 96, 60, 181, 240, 176, 141, 144, 6, 32, 22, 28, 13, 240, 2, 250, 217, 30, 7, 209, 0, 224, 214, 32, 10, 247, 254, 253, + 137, 35, 18, 147, 0, 152, 6, 28, 57, 34, 1, 35, 0, 247, 255, 253, 197, 40, 0, 218, 54, 72, 180, 247, 255, 250, 146, + 76, 179, 104, 32, 247, 255, 255, 202, 28, 6, 32, 20, 247, 254, 253, 116, 75, 176, 104, 32, 104, 27, 0, 130, 104, 155, + 51, 68, 24, 155, 104, 26, 46, 0, 219, 4, 75, 172, 64, 26, 75, 172, 66, 154, 208, 13, 247, 255, 254, 81, 32, 20, 247, + 254, 253, 96, 75, 166, 104, 27, 104, 154, 75, 164, 50, 68, 104, 27, 0, 155, 24, 211, 104, 27, 32, 30, 247, 254, 253, + 84, 35, 18, 147, 0, 152, 6, 28, 57, 34, 1, 35, 0, 247, 255, 253, 144, 30, 4, 218, 1, 72, 158, 224, 165, 28, 57, 34, + 18, 28, 40, 240, 3, 250, 83, 28, 56, 240, 2, 250, 168, 120, 170, 120, 235, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, + 19, 4, 27, 12, 26, 112, 234, 14, 27, 122, 42, 112, 171, 122, 107, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, 19, 4, 27, + 12, 26, 114, 106, 14, 27, 122, 170, 114, 43, 122, 235, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, 19, 4, 27, 12, 26, + 114, 234, 14, 27, 123, 42, 114, 171, 123, 107, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, 19, 4, 27, 12, 26, 14, 27, + 115, 43, 124, 107, 115, 106, 0, 88, 24, 192, 0, 128, 24, 192, 240, 2, 250, 79, 14, 3, 116, 171, 12, 3, 116, 235, 10, + 3, 117, 43, 117, 104, 40, 0, 209, 1, 72, 120, 224, 204, 124, 107, 33, 0, 0, 90, 24, 210, 0, 146, 24, 210, 240, 3, 250, + 68, 33, 0, 145, 10, 145, 5, 28, 46, 225, 51, 32, 9, 240, 2, 250, 52, 30, 4, 208, 49, 154, 5, 28, 33, 6, 19, 14, 27, + 147, 3, 35, 9, 147, 0, 34, 2, 155, 3, 152, 6, 247, 255, 253, 32, 124, 179, 124, 245, 6, 27, 4, 45, 67, 29, 125, 51, + 28, 33, 2, 27, 67, 29, 125, 115, 34, 9, 67, 29, 155, 10, 24, 237, 28, 40, 240, 3, 249, 219, 28, 32, 240, 2, 250, 48, + 120, 170, 120, 235, 2, 18, 67, 26, 4, 18, 10, 19, 14, 18, 67, 19, 4, 27, 12, 28, 14, 27, 112, 171, 112, 236, 28, 32, + 240, 2, 250, 2, 30, 7, 209, 6, 28, 53, 72, 86, 36, 4, 247, 255, 249, 199, 66, 100, 225, 3, 120, 235, 152, 6, 147, 0, + 28, 57, 34, 2, 155, 3, 247, 255, 252, 235, 40, 0, 218, 5, 28, 4, 72, 78, 28, 53, 247, 255, 249, 182, 224, 238, 120, + 41, 145, 2, 121, 43, 0, 216, 24, 192, 0, 64, 240, 2, 249, 225, 14, 3, 114, 107, 12, 3, 114, 171, 10, 3, 114, 235, 115, + 40, 40, 0, 209, 2, 28, 53, 72, 68, 224, 93, 155, 2, 154, 2, 26, 228, 25, 210, 146, 3, 148, 4, 121, 43, 33, 0, 0, 218, + 24, 210, 0, 82, 240, 3, 249, 208, 33, 0, 145, 11, 145, 8, 224, 177, 122, 107, 122, 172, 6, 27, 4, 36, 67, 28, 122, 235, + 154, 11, 2, 27, 67, 28, 123, 43, 153, 3, 67, 28, 24, 164, 28, 32, 34, 9, 240, 3, 249, 121, 120, 35, 147, 2, 121, 35, + 0, 216, 26, 192, 240, 2, 249, 172, 14, 3, 115, 163, 12, 3, 115, 227, 10, 3, 116, 35, 116, 96, 40, 0, 209, 2, 28, 53, + 72, 42, 224, 40, 153, 3, 155, 4, 28, 10, 153, 2, 24, 82, 153, 2, 146, 3, 26, 91, 147, 4, 121, 35, 33, 0, 0, 218, 26, + 210, 240, 3, 249, 153, 152, 3, 153, 4, 247, 255, 248, 27, 35, 11, 144, 2, 70, 106, 92, 154, 115, 98, 155, 2, 43, 0, + 208, 31, 28, 24, 240, 2, 249, 130, 14, 3, 114, 99, 12, 3, 114, 163, 10, 3, 114, 227, 115, 32, 40, 0, 209, 6, 72, 23, + 28, 53, 36, 4, 247, 255, 249, 64, 66, 100, 224, 119, 153, 3, 154, 2, 240, 3, 249, 52, 153, 3, 155, 4, 28, 10, 153, 2, + 24, 82, 153, 2, 146, 3, 26, 91, 147, 4, 34, 0, 146, 7, 146, 9, 224, 68, 70, 192, 19, 112, 98, 121, 19, 112, 107, 104, + 19, 112, 96, 60, 0, 0, 49, 5, 0, 0, 16, 5, 19, 112, 98, 163, 19, 112, 98, 198, 19, 112, 98, 96, 19, 112, 98, 236, 19, + 112, 99, 22, 19, 112, 99, 56, 19, 112, 99, 89, 123, 162, 123, 227, 6, 18, 4, 27, 67, 19, 124, 34, 153, 7, 2, 18, 67, + 19, 124, 98, 67, 19, 24, 201, 145, 2, 28, 8, 34, 7, 153, 3, 240, 3, 248, 252, 154, 2, 153, 3, 120, 19, 24, 201, 145, + 3, 153, 2, 121, 18, 121, 75, 2, 18, 67, 26, 4, 18, 10, 17, 14, 19, 67, 11, 4, 27, 153, 2, 12, 26, 14, 27, 113, 11, 113, + 74, 154, 9, 155, 7, 50, 1, 51, 7, 146, 9, 147, 7, 121, 35, 153, 9, 66, 153, 211, 207, 154, 8, 155, 11, 50, 1, 51, 18, + 146, 8, 147, 11, 121, 43, 153, 8, 66, 153, 210, 0, 231, 72, 28, 56, 240, 2, 249, 40, 154, 5, 155, 10, 50, 1, 51, 13, + 146, 5, 147, 10, 39, 0, 124, 115, 153, 5, 66, 153, 210, 0, 230, 198, 28, 53, 36, 0, 47, 0, 208, 7, 28, 56, 240, 2, 249, + 21, 44, 0, 208, 2, 28, 40, 247, 254, 255, 145, 176, 13, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 181, 248, 28, 4, 38, + 3, 39, 64, 224, 79, 104, 160, 240, 3, 248, 112, 10, 3, 64, 51, 28, 5, 43, 2, 208, 7, 107, 227, 108, 34, 105, 89, 24, + 138, 0, 65, 12, 73, 26, 82, 97, 90, 108, 35, 43, 0, 208, 59, 66, 47, 208, 57, 75, 36, 120, 27, 43, 0, 208, 2, 72, 35, + 247, 255, 248, 151, 6, 235, 213, 6, 75, 32, 120, 27, 43, 0, 208, 2, 72, 32, 247, 255, 248, 142, 7, 107, 213, 6, 75, + 27, 120, 27, 43, 0, 208, 2, 72, 28, 247, 255, 248, 133, 6, 171, 213, 6, 75, 23, 120, 27, 43, 0, 208, 2, 72, 25, 247, + 255, 248, 124, 7, 43, 213, 6, 75, 18, 120, 27, 43, 0, 208, 2, 72, 21, 247, 255, 248, 115, 75, 15, 120, 27, 43, 0, 208, + 2, 72, 19, 247, 255, 248, 108, 75, 11, 120, 27, 43, 0, 208, 8, 72, 16, 247, 255, 248, 101, 224, 4, 107, 164, 44, 0, + 209, 173, 32, 0, 224, 3, 107, 227, 32, 1, 66, 64, 97, 88, 75, 11, 34, 0, 104, 27, 100, 26, 188, 248, 188, 2, 71, 8, + 19, 112, 107, 20, 19, 112, 99, 118, 19, 112, 99, 131, 19, 112, 99, 139, 19, 112, 99, 160, 19, 112, 99, 179, 19, 112, + 99, 190, 19, 112, 105, 116, 19, 112, 96, 60, 181, 240, 176, 131, 144, 1, 35, 9, 7, 192, 213, 60, 75, 59, 120, 27, 43, + 0, 208, 2, 72, 58, 247, 255, 248, 53, 75, 58, 104, 28, 44, 0, 208, 9, 107, 96, 33, 96, 240, 3, 249, 67, 107, 96, 33, + 96, 240, 2, 248, 49, 107, 164, 231, 243, 76, 52, 104, 35, 43, 0, 208, 24, 108, 88, 33, 32, 240, 3, 249, 53, 104, 35, + 33, 32, 108, 88, 240, 2, 248, 34, 104, 35, 34, 128, 4, 82, 97, 26, 74, 44, 108, 88, 104, 18, 33, 32, 107, 82, 97, 90, + 240, 3, 249, 58, 104, 35, 105, 154, 75, 40, 96, 26, 75, 36, 104, 24, 247, 255, 255, 78, 35, 0, 40, 0, 208, 4, 74, 37, + 104, 18, 100, 19, 35, 5, 66, 91, 152, 1, 7, 64, 213, 40, 75, 27, 120, 27, 43, 0, 208, 2, 72, 32, 247, 254, 255, 244, + 32, 128, 1, 128, 79, 28, 78, 30, 33, 0, 35, 1, 34, 0, 70, 132, 104, 56, 104, 132, 104, 48, 52, 68, 24, 100, 104, 37, + 66, 130, 209, 7, 32, 1, 66, 40, 209, 10, 75, 23, 80, 88, 35, 1, 66, 91, 224, 5, 72, 21, 64, 5, 45, 3, 209, 1, 70, 96, + 96, 32, 50, 1, 49, 4, 42, 4, 209, 229, 43, 0, 208, 5, 153, 1, 34, 18, 66, 10, 208, 1, 35, 6, 66, 91, 176, 3, 28, 24, + 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 20, 19, 112, 99, 207, 19, 112, 107, 56, 19, 112, 107, 28, 19, 112, 107, + 40, 19, 112, 107, 36, 19, 112, 96, 60, 19, 112, 99, 240, 19, 112, 107, 104, 19, 112, 107, 108, 0, 0, 32, 3, 181, 240, + 176, 131, 147, 1, 70, 107, 51, 39, 120, 30, 28, 5, 30, 115, 6, 27, 14, 27, 145, 0, 28, 23, 43, 15, 216, 61, 28, 4, 52, + 160, 104, 32, 40, 0, 208, 56, 33, 0, 34, 31, 240, 2, 255, 211, 104, 35, 74, 29, 96, 26, 28, 43, 51, 152, 104, 24, 240, + 2, 255, 79, 104, 35, 96, 88, 28, 56, 240, 2, 255, 74, 104, 35, 70, 105, 96, 152, 29, 202, 120, 17, 104, 35, 70, 106, + 115, 25, 28, 209, 104, 35, 120, 10, 115, 90, 104, 34, 35, 6, 46, 6, 217, 0, 35, 10, 28, 44, 115, 147, 52, 160, 104, + 32, 153, 8, 28, 50, 48, 15, 240, 2, 255, 106, 28, 43, 51, 144, 104, 24, 123, 105, 104, 35, 34, 31, 247, 255, 249, 212, + 40, 31, 208, 6, 40, 0, 219, 5, 72, 5, 224, 3, 32, 3, 66, 64, 224, 0, 32, 0, 176, 3, 188, 240, 188, 2, 71, 8, 85, 83, + 66, 67, 255, 255, 216, 238, 181, 240, 28, 4, 176, 131, 52, 160, 28, 5, 145, 0, 28, 23, 33, 255, 34, 13, 104, 32, 240, + 2, 255, 134, 28, 43, 51, 144, 104, 24, 123, 41, 104, 35, 34, 13, 247, 255, 249, 174, 40, 13, 208, 1, 40, 0, 218, 40, + 40, 0, 219, 43, 28, 43, 51, 160, 104, 28, 104, 32, 240, 2, 254, 246, 144, 1, 104, 96, 240, 2, 254, 242, 28, 6, 104, + 160, 240, 2, 254, 238, 74, 16, 153, 1, 123, 35, 66, 145, 209, 21, 47, 0, 208, 0, 96, 56, 154, 0, 42, 0, 208, 0, 112, + 19, 53, 152, 104, 43, 66, 158, 209, 12, 28, 179, 4, 27, 12, 54, 12, 27, 4, 54, 67, 30, 96, 46, 32, 0, 224, 4, 72, 5, + 224, 2, 72, 5, 224, 0, 72, 5, 176, 3, 188, 240, 188, 2, 71, 8, 83, 66, 83, 85, 255, 255, 216, 237, 255, 255, 216, 236, + 255, 255, 216, 235, 181, 240, 176, 135, 74, 78, 75, 79, 120, 18, 28, 6, 104, 31, 42, 0, 208, 1, 73, 77, 224, 0, 73, + 77, 96, 25, 28, 51, 51, 144, 104, 24, 75, 75, 104, 27, 104, 153, 105, 195, 49, 68, 0, 155, 24, 203, 104, 25, 35, 1, + 66, 11, 209, 9, 74, 71, 36, 1, 104, 17, 74, 70, 0, 137, 80, 139, 75, 64, 66, 100, 96, 31, 224, 116, 75, 62, 42, 0, 208, + 1, 74, 61, 224, 0, 74, 61, 96, 26, 75, 64, 120, 27, 43, 0, 208, 17, 136, 243, 34, 255, 147, 0, 33, 33, 35, 0, 147, 1, + 147, 2, 247, 255, 249, 75, 28, 4, 28, 33, 72, 58, 247, 254, 254, 198, 32, 60, 247, 254, 249, 173, 224, 6, 33, 0, 247, + 255, 249, 161, 32, 5, 247, 254, 249, 166, 36, 0, 75, 43, 120, 26, 75, 43, 42, 0, 208, 1, 74, 42, 224, 0, 74, 42, 28, + 53, 96, 26, 53, 144, 44, 0, 218, 10, 32, 50, 247, 254, 249, 149, 104, 40, 33, 0, 247, 255, 249, 137, 32, 5, 247, 254, + 249, 142, 224, 48, 123, 49, 104, 40, 247, 255, 249, 129, 28, 4, 32, 5, 247, 254, 249, 133, 72, 36, 28, 33, 247, 254, + 254, 151, 44, 0, 219, 34, 123, 113, 104, 40, 247, 255, 249, 115, 28, 4, 32, 5, 247, 254, 249, 119, 72, 30, 28, 33, 247, + 254, 254, 137, 44, 0, 219, 20, 32, 10, 247, 254, 249, 110, 70, 105, 49, 23, 104, 40, 247, 255, 249, 138, 28, 4, 28, + 33, 72, 23, 247, 254, 254, 122, 44, 0, 219, 5, 72, 21, 247, 254, 254, 117, 75, 9, 96, 31, 224, 7, 75, 7, 33, 1, 96, + 31, 75, 10, 104, 26, 75, 10, 0, 146, 80, 209, 176, 7, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 1, 15, 19, + 112, 96, 56, 0, 15, 66, 64, 0, 3, 13, 64, 19, 112, 96, 60, 19, 112, 107, 104, 19, 112, 107, 108, 19, 112, 1, 13, 19, + 112, 100, 17, 19, 112, 100, 50, 19, 112, 100, 89, 19, 112, 100, 129, 19, 112, 100, 176, 181, 240, 176, 141, 146, 6, + 70, 106, 50, 79, 120, 18, 28, 31, 70, 107, 51, 95, 146, 7, 70, 106, 120, 27, 50, 83, 120, 18, 6, 27, 22, 27, 146, 8, + 70, 106, 50, 47, 147, 4, 35, 0, 112, 19, 74, 156, 145, 5, 104, 17, 74, 156, 0, 137, 77, 156, 28, 6, 147, 10, 80, 139, + 147, 3, 36, 0, 28, 163, 43, 1, 216, 5, 75, 149, 33, 1, 104, 26, 75, 149, 0, 146, 224, 30, 44, 0, 208, 36, 28, 48, 33, + 0, 247, 255, 255, 22, 30, 4, 219, 1, 32, 5, 224, 0, 32, 60, 247, 254, 248, 253, 74, 143, 75, 139, 104, 18, 104, 27, + 104, 146, 0, 155, 50, 68, 24, 210, 104, 18, 44, 0, 218, 12, 154, 3, 50, 1, 146, 3, 42, 2, 221, 3, 74, 133, 33, 1, 80, + 153, 224, 231, 32, 10, 247, 254, 248, 230, 224, 219, 35, 0, 147, 3, 155, 4, 154, 8, 59, 1, 6, 27, 22, 27, 147, 4, 42, + 0, 208, 59, 104, 43, 74, 126, 147, 2, 120, 19, 43, 0, 208, 1, 75, 125, 224, 0, 75, 125, 96, 43, 154, 7, 155, 18, 146, + 1, 147, 0, 28, 58, 35, 0, 28, 48, 153, 5, 247, 255, 254, 47, 75, 115, 154, 2, 28, 4, 96, 26, 28, 131, 43, 1, 216, 0, + 224, 189, 40, 0, 218, 0, 224, 178, 155, 6, 34, 144, 25, 146, 28, 60, 147, 2, 146, 9, 224, 18, 155, 9, 123, 113, 104, + 24, 28, 34, 155, 2, 247, 255, 248, 48, 28, 131, 43, 1, 216, 0, 224, 159, 40, 0, 219, 69, 66, 160, 209, 67, 154, 2, 26, + 36, 24, 18, 146, 2, 44, 0, 209, 234, 224, 62, 104, 43, 74, 96, 147, 2, 120, 19, 43, 0, 208, 1, 75, 95, 224, 0, 75, 95, + 96, 43, 154, 7, 155, 18, 146, 1, 147, 0, 28, 58, 35, 128, 28, 48, 153, 5, 247, 255, 253, 243, 75, 85, 154, 2, 28, 4, + 96, 26, 40, 0, 218, 3, 72, 87, 28, 33, 247, 254, 253, 151, 28, 163, 43, 1, 217, 124, 44, 0, 218, 22, 224, 113, 28, 51, + 51, 144, 104, 24, 123, 49, 28, 58, 155, 6, 247, 254, 255, 245, 30, 4, 218, 3, 72, 78, 28, 33, 247, 254, 253, 131, 28, + 163, 43, 1, 217, 96, 44, 0, 219, 5, 66, 188, 208, 5, 224, 2, 47, 0, 209, 231, 224, 1, 76, 71, 224, 86, 75, 63, 74, 65, + 104, 27, 147, 2, 120, 19, 43, 0, 208, 1, 75, 63, 224, 0, 75, 63, 74, 58, 70, 105, 96, 19, 28, 48, 49, 47, 170, 10, 247, + 255, 254, 14, 30, 4, 218, 34, 72, 61, 28, 33, 247, 254, 253, 94, 28, 99, 209, 3, 75, 50, 154, 2, 96, 26, 224, 64, 28, + 52, 52, 144, 104, 32, 123, 49, 247, 255, 248, 52, 123, 51, 32, 15, 104, 34, 64, 24, 9, 219, 1, 27, 106, 81, 24, 27, + 32, 1, 64, 152, 67, 129, 98, 81, 70, 105, 28, 48, 49, 47, 170, 10, 247, 255, 253, 234, 28, 4, 75, 36, 154, 2, 96, 26, + 28, 163, 43, 1, 217, 33, 155, 10, 43, 0, 208, 7, 75, 40, 104, 27, 43, 0, 208, 3, 70, 107, 34, 1, 51, 47, 112, 26, 44, + 0, 219, 12, 75, 35, 36, 0, 104, 27, 43, 0, 208, 7, 70, 107, 51, 47, 120, 27, 43, 0, 208, 2, 76, 31, 224, 0, 28, 4, 155, + 4, 43, 0, 221, 2, 44, 0, 218, 0, 230, 239, 44, 0, 218, 4, 75, 14, 33, 1, 104, 26, 0, 146, 224, 3, 75, 11, 33, 0, 104, + 26, 0, 146, 75, 10, 80, 209, 154, 21, 42, 0, 208, 3, 70, 107, 51, 47, 120, 27, 112, 19, 155, 22, 43, 0, 208, 2, 155, + 10, 154, 22, 96, 19, 176, 13, 28, 32, 188, 240, 188, 2, 71, 8, 19, 112, 107, 104, 19, 112, 107, 108, 19, 112, 96, 56, + 19, 112, 96, 60, 19, 112, 1, 15, 0, 15, 66, 64, 0, 3, 13, 64, 19, 112, 100, 186, 19, 112, 100, 205, 255, 255, 216, 233, + 19, 112, 100, 229, 19, 112, 107, 64, 255, 255, 216, 234, 181, 240, 28, 3, 176, 143, 51, 160, 145, 7, 104, 30, 33, 128, + 70, 111, 1, 9, 55, 55, 37, 0, 172, 9, 24, 118, 144, 6, 112, 61, 28, 32, 33, 0, 34, 16, 240, 2, 253, 8, 46, 0, 208, 87, + 35, 6, 147, 1, 35, 1, 147, 2, 35, 10, 147, 5, 153, 7, 35, 0, 152, 6, 34, 0, 149, 4, 148, 0, 151, 3, 247, 255, 254, 113, + 28, 5, 28, 41, 72, 40, 247, 254, 252, 182, 28, 107, 208, 69, 45, 0, 219, 2, 120, 59, 43, 0, 208, 64, 153, 7, 171, 9, + 34, 3, 112, 26, 1, 74, 112, 90, 34, 18, 113, 26, 34, 0, 113, 90, 28, 48, 33, 0, 34, 18, 240, 2, 252, 220, 75, 28, 53, + 2, 209, 1, 74, 28, 224, 0, 74, 28, 96, 26, 171, 9, 147, 0, 35, 6, 147, 1, 35, 0, 147, 2, 147, 3, 147, 4, 35, 10, 147, + 5, 153, 7, 152, 6, 28, 50, 35, 18, 247, 255, 254, 63, 28, 5, 28, 41, 72, 19, 247, 254, 252, 132, 45, 0, 219, 19, 120, + 179, 33, 15, 70, 108, 64, 25, 52, 55, 72, 15, 112, 33, 247, 254, 252, 121, 120, 35, 59, 2, 6, 27, 14, 27, 43, 2, 217, + 3, 224, 3, 37, 4, 66, 109, 224, 0, 77, 9, 176, 15, 28, 40, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 100, 244, 19, + 112, 96, 56, 0, 38, 37, 160, 0, 152, 150, 128, 19, 112, 101, 21, 19, 112, 101, 52, 255, 255, 216, 239, 181, 240, 176, + 147, 70, 106, 79, 212, 35, 0, 50, 71, 112, 19, 28, 4, 104, 58, 72, 210, 37, 16, 84, 133, 72, 209, 28, 34, 50, 152, 96, + 16, 174, 12, 58, 8, 96, 17, 28, 8, 130, 115, 28, 49, 130, 179, 247, 255, 249, 149, 28, 5, 28, 41, 72, 203, 247, 254, + 252, 60, 45, 0, 218, 0, 225, 230, 104, 59, 74, 200, 0, 153, 88, 138, 42, 0, 208, 42, 74, 199, 24, 201, 0, 137, 24, 81, + 121, 136, 121, 50, 66, 144, 209, 25, 121, 200, 121, 114, 66, 144, 209, 21, 28, 10, 137, 8, 137, 49, 50, 8, 66, 136, + 209, 15, 136, 80, 137, 113, 66, 136, 209, 11, 121, 16, 123, 177, 66, 136, 209, 7, 121, 80, 123, 241, 66, 136, 209, 3, + 121, 145, 124, 50, 66, 145, 208, 8, 168, 12, 247, 254, 250, 225, 37, 1, 72, 181, 247, 254, 252, 10, 66, 109, 225, 195, + 0, 153, 24, 201, 72, 176, 171, 12, 121, 26, 0, 137, 24, 65, 113, 138, 121, 90, 38, 0, 113, 202, 137, 24, 28, 10, 129, + 8, 137, 89, 50, 8, 128, 81, 123, 153, 113, 17, 123, 217, 113, 81, 124, 27, 113, 147, 74, 168, 35, 128, 66, 91, 96, 19, + 35, 0, 147, 8, 147, 5, 224, 232, 138, 111, 138, 171, 154, 8, 4, 63, 67, 31, 24, 191, 122, 58, 72, 161, 0, 82, 153, 5, + 247, 254, 251, 221, 34, 156, 35, 0, 25, 18, 147, 10, 147, 6, 146, 11, 224, 202, 122, 123, 122, 189, 6, 27, 4, 45, 67, + 29, 122, 251, 2, 27, 67, 29, 123, 59, 67, 29, 155, 10, 24, 237, 121, 107, 43, 8, 209, 126, 121, 235, 43, 80, 209, 123, + 121, 43, 43, 1, 217, 120, 154, 11, 35, 1, 112, 19, 121, 169, 72, 144, 34, 1, 247, 254, 251, 185, 35, 0, 115, 102, 115, + 38, 147, 9, 147, 7, 224, 49, 123, 170, 123, 235, 6, 18, 4, 27, 67, 19, 124, 42, 2, 18, 67, 19, 124, 106, 67, 19, 154, + 9, 24, 155, 120, 218, 42, 2, 209, 28, 120, 154, 6, 17, 41, 0, 218, 11, 123, 33, 41, 0, 209, 8, 42, 0, 208, 6, 115, 34, + 121, 25, 121, 91, 2, 9, 67, 25, 72, 125, 224, 10, 123, 97, 41, 0, 209, 9, 42, 0, 208, 7, 115, 98, 121, 25, 121, 91, + 2, 9, 72, 121, 67, 25, 247, 254, 251, 135, 155, 7, 154, 9, 51, 1, 50, 7, 147, 7, 146, 9, 121, 43, 154, 7, 66, 154, 211, + 201, 123, 33, 41, 0, 208, 104, 123, 98, 42, 0, 208, 101, 72, 112, 247, 254, 251, 116, 73, 98, 74, 103, 104, 11, 0, 152, + 24, 195, 0, 155, 123, 32, 24, 211, 116, 24, 123, 96, 116, 88, 121, 123, 112, 35, 104, 9, 120, 171, 0, 136, 24, 65, 0, + 137, 24, 82, 96, 99, 115, 211, 120, 235, 168, 12, 96, 163, 247, 254, 250, 45, 75, 92, 74, 98, 72, 98, 96, 26, 120, 33, + 104, 162, 247, 254, 251, 82, 28, 35, 51, 144, 70, 105, 104, 24, 49, 71, 247, 254, 254, 86, 40, 0, 218, 89, 224, 85, + 123, 170, 123, 235, 6, 18, 4, 27, 67, 19, 124, 42, 124, 104, 2, 18, 67, 19, 67, 24, 208, 1, 240, 1, 251, 141, 122, 106, + 122, 171, 6, 18, 4, 27, 67, 19, 122, 234, 123, 40, 2, 18, 67, 19, 115, 174, 115, 238, 116, 46, 116, 110, 67, 24, 208, + 1, 240, 1, 251, 124, 121, 107, 114, 110, 114, 174, 114, 238, 115, 46, 43, 9, 209, 4, 74, 73, 75, 64, 168, 12, 96, 26, + 224, 39, 43, 8, 209, 10, 121, 235, 43, 80, 209, 7, 121, 43, 43, 1, 217, 4, 121, 171, 74, 67, 26, 211, 74, 57, 96, 19, + 155, 6, 154, 10, 51, 1, 50, 18, 147, 6, 146, 10, 121, 59, 154, 6, 66, 154, 210, 0, 231, 47, 155, 5, 154, 8, 51, 1, 50, + 13, 147, 5, 146, 8, 173, 12, 124, 107, 154, 5, 66, 154, 210, 0, 231, 16, 72, 54, 247, 254, 250, 247, 28, 40, 247, 254, + 249, 199, 77, 50, 224, 159, 72, 51, 247, 254, 250, 239, 70, 107, 51, 71, 120, 25, 120, 34, 72, 49, 247, 254, 250, 232, + 75, 35, 74, 48, 96, 26, 28, 35, 51, 144, 104, 24, 120, 33, 247, 254, 253, 220, 40, 0, 218, 2, 72, 44, 247, 254, 250, + 218, 74, 43, 75, 28, 121, 225, 96, 26, 28, 35, 51, 144, 104, 24, 122, 226, 247, 254, 253, 191, 40, 0, 218, 2, 72, 38, + 247, 254, 250, 203, 72, 38, 70, 109, 247, 254, 250, 199, 53, 70, 35, 0, 112, 43, 28, 35, 51, 144, 104, 24, 136, 227, + 33, 161, 147, 0, 35, 1, 147, 1, 34, 254, 35, 0, 149, 2, 247, 254, 253, 55, 40, 0, 218, 58, 72, 28, 247, 254, 250, 178, + 35, 8, 115, 163, 224, 60, 70, 192, 19, 112, 107, 104, 19, 112, 96, 64, 44, 13, 224, 1, 19, 112, 101, 86, 19, 112, 107, + 76, 19, 114, 198, 232, 19, 112, 101, 128, 19, 112, 107, 32, 19, 112, 101, 166, 19, 112, 101, 205, 19, 112, 102, 4, 19, + 112, 102, 18, 19, 112, 102, 33, 255, 255, 251, 79, 19, 112, 102, 102, 255, 255, 177, 224, 255, 255, 216, 240, 19, 112, + 102, 53, 19, 112, 102, 148, 19, 112, 102, 189, 255, 255, 251, 78, 19, 112, 102, 222, 255, 255, 251, 77, 19, 112, 102, + 252, 19, 112, 103, 43, 19, 112, 103, 102, 120, 41, 72, 29, 49, 1, 6, 9, 14, 9, 115, 161, 247, 254, 250, 114, 28, 35, + 51, 160, 104, 27, 43, 0, 209, 16, 77, 24, 104, 43, 43, 0, 209, 7, 32, 129, 1, 64, 240, 1, 250, 153, 35, 31, 48, 31, + 67, 152, 96, 40, 74, 18, 28, 35, 104, 18, 51, 160, 96, 26, 52, 160, 104, 35, 37, 0, 43, 0, 209, 4, 74, 14, 75, 14, 37, + 4, 96, 26, 66, 109, 74, 13, 75, 14, 96, 26, 45, 0, 208, 5, 75, 10, 72, 12, 104, 25, 247, 254, 250, 72, 224, 2, 72, 11, + 247, 254, 250, 68, 176, 19, 28, 40, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 103, 141, 19, 112, 107, 24, 255, 255, + 251, 75, 19, 112, 107, 32, 0, 15, 66, 64, 19, 112, 96, 56, 19, 112, 103, 164, 19, 112, 103, 198, 181, 8, 33, 0, 247, + 255, 251, 38, 188, 8, 188, 2, 71, 8, 0, 0, 181, 240, 176, 139, 171, 8, 34, 18, 112, 26, 1, 74, 112, 90, 34, 0, 112, + 154, 112, 218, 113, 90, 28, 15, 28, 2, 33, 36, 50, 160, 113, 25, 104, 20, 34, 128, 1, 18, 24, 164, 144, 7, 44, 0, 208, + 48, 38, 0, 33, 0, 34, 36, 28, 32, 240, 2, 250, 69, 171, 8, 34, 6, 147, 0, 35, 10, 37, 0, 146, 1, 147, 5, 152, 7, 28, + 57, 28, 34, 35, 36, 149, 2, 149, 3, 149, 4, 247, 255, 251, 175, 40, 0, 219, 7, 120, 35, 34, 31, 64, 19, 43, 5, 208, + 6, 43, 7, 209, 11, 224, 3, 54, 1, 46, 2, 208, 15, 231, 220, 75, 9, 33, 1, 104, 26, 75, 8, 0, 146, 80, 209, 224, 7, 75, + 5, 104, 26, 75, 5, 0, 146, 80, 213, 224, 1, 32, 4, 66, 64, 176, 11, 188, 240, 188, 2, 71, 8, 19, 112, 107, 104, 19, + 112, 107, 44, 181, 112, 176, 136, 28, 22, 28, 29, 34, 37, 171, 7, 112, 26, 1, 74, 112, 90, 28, 2, 50, 160, 104, 20, + 34, 128, 1, 18, 24, 164, 44, 0, 208, 34, 147, 0, 35, 2, 147, 1, 35, 0, 147, 2, 147, 3, 147, 4, 35, 10, 147, 5, 28, 34, + 35, 8, 247, 255, 251, 108, 40, 0, 219, 21, 168, 6, 28, 33, 34, 4, 240, 2, 249, 169, 45, 0, 208, 1, 155, 6, 96, 43, 29, + 33, 168, 6, 34, 4, 240, 2, 249, 160, 32, 0, 46, 0, 208, 4, 155, 6, 96, 51, 224, 1, 32, 4, 66, 64, 176, 8, 188, 112, + 188, 2, 71, 8, 181, 240, 176, 131, 123, 131, 28, 15, 28, 5, 66, 187, 217, 72, 78, 40, 73, 40, 72, 41, 96, 49, 33, 0, + 247, 254, 249, 140, 75, 39, 28, 40, 96, 51, 28, 57, 247, 255, 252, 166, 30, 4, 219, 53, 74, 33, 28, 57, 96, 50, 28, + 40, 247, 255, 255, 88, 28, 4, 28, 33, 72, 32, 247, 254, 249, 121, 44, 0, 219, 40, 28, 43, 51, 160, 104, 26, 35, 128, + 1, 27, 92, 211, 33, 31, 64, 25, 72, 27, 247, 254, 249, 108, 28, 59, 29, 62, 51, 20, 0, 182, 0, 155, 25, 170, 147, 1, + 28, 57, 24, 235, 28, 40, 247, 255, 255, 140, 153, 1, 28, 4, 89, 75, 89, 114, 72, 18, 28, 33, 247, 254, 249, 88, 89, + 114, 75, 17, 66, 154, 217, 3, 154, 1, 89, 83, 43, 9, 216, 1, 36, 33, 66, 100, 74, 6, 75, 5, 96, 26, 224, 1, 36, 3, 66, + 100, 176, 3, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 56, 0, 15, 66, 64, 19, 112, 103, 231, 1, 49, 45, + 0, 19, 112, 104, 2, 19, 112, 104, 22, 19, 112, 104, 43, 0, 0, 1, 255, 181, 240, 176, 141, 175, 9, 37, 40, 112, 61, 1, + 77, 112, 125, 14, 21, 112, 189, 70, 110, 12, 21, 36, 0, 112, 253, 113, 122, 10, 21, 54, 47, 10, 26, 112, 52, 113, 61, + 113, 188, 113, 250, 114, 59, 114, 124, 123, 130, 66, 138, 217, 28, 29, 10, 0, 146, 88, 18, 146, 7, 42, 0, 208, 22, 77, + 15, 34, 1, 96, 42, 154, 7, 151, 0, 67, 83, 34, 10, 146, 1, 34, 6, 146, 5, 154, 18, 148, 2, 150, 3, 148, 4, 247, 255, + 250, 184, 96, 44, 40, 0, 221, 7, 120, 51, 43, 0, 209, 3, 224, 3, 32, 3, 66, 64, 224, 0, 72, 3, 176, 13, 188, 240, 188, + 2, 71, 8, 70, 192, 19, 112, 107, 64, 255, 255, 216, 234, 181, 240, 176, 141, 147, 7, 1, 75, 175, 9, 37, 42, 70, 156, + 112, 61, 35, 8, 70, 101, 67, 43, 112, 123, 14, 21, 155, 7, 112, 189, 70, 110, 12, 21, 36, 0, 112, 253, 113, 122, 10, + 21, 54, 47, 10, 26, 112, 52, 113, 61, 113, 188, 113, 250, 114, 59, 114, 124, 123, 130, 66, 138, 217, 32, 29, 10, 0, + 146, 88, 18, 70, 148, 69, 164, 208, 26, 77, 17, 35, 1, 96, 43, 155, 7, 151, 0, 70, 98, 67, 90, 70, 148, 34, 1, 146, + 2, 34, 6, 146, 5, 39, 10, 154, 18, 70, 99, 151, 1, 150, 3, 148, 4, 247, 255, 250, 106, 96, 44, 40, 0, 221, 7, 120, 51, + 43, 0, 209, 3, 224, 3, 32, 3, 66, 64, 224, 0, 72, 3, 176, 13, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 64, 255, + 255, 216, 234, 181, 240, 176, 135, 28, 28, 147, 2, 28, 11, 51, 4, 0, 155, 145, 3, 88, 25, 28, 6, 28, 11, 67, 99, 32, + 128, 2, 64, 28, 23, 66, 131, 217, 2, 240, 2, 249, 135, 144, 2, 155, 3, 32, 1, 51, 4, 0, 155, 66, 64, 147, 5, 224, 28, + 154, 2, 28, 37, 66, 148, 217, 0, 28, 21, 4, 43, 12, 27, 147, 4, 155, 12, 28, 48, 147, 0, 153, 3, 28, 58, 155, 4, 247, + 255, 255, 67, 40, 0, 219, 12, 154, 4, 25, 127, 26, 164, 154, 5, 4, 36, 89, 147, 12, 36, 67, 93, 155, 12, 25, 91, 147, + 12, 44, 0, 209, 224, 176, 7, 188, 240, 188, 2, 71, 8, 181, 240, 176, 135, 28, 28, 147, 2, 28, 11, 51, 4, 0, 155, 145, + 3, 88, 25, 28, 6, 28, 11, 67, 99, 32, 128, 2, 64, 28, 23, 66, 131, 217, 2, 240, 2, 249, 73, 144, 2, 155, 3, 32, 1, 51, + 4, 0, 155, 66, 64, 147, 5, 224, 28, 154, 2, 28, 37, 66, 148, 217, 0, 28, 21, 4, 43, 12, 27, 147, 4, 155, 12, 28, 48, + 147, 0, 153, 3, 28, 58, 155, 4, 247, 255, 255, 73, 40, 0, 219, 12, 154, 4, 25, 127, 26, 164, 154, 5, 4, 36, 89, 147, + 12, 36, 67, 93, 155, 12, 25, 91, 147, 12, 44, 0, 209, 224, 176, 7, 188, 240, 188, 2, 71, 8, 181, 240, 176, 139, 76, + 181, 144, 8, 104, 32, 73, 181, 0, 67, 34, 0, 82, 90, 73, 180, 37, 0, 82, 90, 75, 179, 38, 164, 84, 29, 75, 179, 73, + 179, 84, 29, 67, 112, 35, 120, 74, 178, 66, 91, 24, 64, 96, 19, 153, 8, 247, 255, 251, 175, 40, 0, 218, 0, 225, 73, + 104, 35, 74, 172, 67, 94, 33, 1, 24, 182, 0, 154, 115, 177, 24, 210, 78, 170, 0, 146, 89, 144, 24, 178, 40, 0, 208, + 101, 121, 82, 42, 0, 209, 3, 74, 167, 120, 18, 66, 17, 208, 42, 38, 164, 67, 115, 77, 161, 32, 0, 24, 234, 28, 17, 49, + 8, 113, 136, 76, 162, 73, 162, 24, 235, 96, 33, 28, 17, 136, 210, 49, 144, 51, 14, 104, 8, 39, 1, 146, 0, 147, 2, 33, + 161, 35, 0, 34, 254, 151, 1, 247, 254, 250, 87, 75, 155, 96, 35, 75, 143, 40, 0, 218, 4, 104, 27, 67, 94, 25, 173, 115, + 175, 224, 253, 104, 27, 67, 94, 25, 173, 53, 8, 121, 171, 51, 1, 113, 171, 76, 135, 72, 147, 104, 35, 39, 164, 0, 154, + 24, 211, 74, 139, 0, 155, 24, 211, 121, 30, 28, 49, 247, 253, 255, 185, 104, 32, 75, 133, 67, 120, 28, 49, 24, 192, + 247, 255, 254, 24, 28, 5, 28, 41, 72, 137, 247, 253, 255, 173, 28, 171, 43, 1, 216, 13, 104, 32, 73, 126, 67, 71, 24, + 120, 247, 255, 253, 118, 74, 124, 35, 121, 66, 91, 96, 19, 33, 0, 104, 35, 74, 118, 224, 71, 45, 0, 218, 0, 224, 201, + 104, 35, 74, 115, 33, 1, 84, 209, 74, 115, 84, 214, 224, 186, 39, 1, 113, 85, 38, 0, 151, 6, 28, 37, 28, 50, 30, 83, + 65, 154, 146, 9, 42, 0, 209, 5, 75, 112, 120, 27, 7, 220, 213, 1, 47, 0, 209, 51, 72, 115, 28, 49, 247, 253, 255, 125, + 76, 99, 33, 164, 104, 32, 74, 102, 67, 72, 6, 51, 14, 27, 28, 25, 24, 128, 147, 5, 247, 255, 253, 215, 144, 7, 153, + 7, 72, 105, 247, 253, 255, 108, 155, 7, 51, 2, 43, 1, 216, 21, 155, 9, 43, 0, 208, 18, 104, 32, 33, 164, 75, 95, 67, + 72, 74, 96, 96, 26, 74, 89, 24, 128, 247, 255, 253, 45, 74, 88, 35, 121, 66, 91, 96, 19, 104, 35, 74, 82, 33, 0, 84, + 209, 224, 149, 155, 7, 43, 0, 218, 90, 47, 0, 208, 82, 104, 42, 39, 164, 0, 147, 67, 122, 76, 88, 73, 77, 32, 0, 81, + 24, 24, 139, 28, 25, 49, 8, 113, 136, 76, 78, 73, 78, 96, 33, 28, 25, 49, 144, 104, 8, 73, 71, 136, 219, 24, 82, 147, + 0, 50, 14, 35, 1, 147, 1, 146, 2, 35, 0, 33, 161, 34, 254, 247, 254, 249, 174, 75, 70, 144, 5, 96, 35, 40, 0, 218, 10, + 104, 43, 74, 61, 67, 95, 72, 70, 24, 191, 36, 1, 0, 155, 33, 0, 115, 188, 80, 25, 224, 14, 104, 43, 74, 56, 67, 95, + 24, 191, 55, 8, 121, 186, 36, 1, 50, 1, 113, 186, 0, 154, 24, 211, 74, 53, 0, 155, 24, 211, 113, 92, 75, 44, 34, 164, + 104, 27, 72, 58, 67, 83, 74, 46, 153, 5, 24, 211, 123, 155, 39, 0, 28, 26, 147, 6, 247, 253, 255, 1, 75, 45, 120, 27, + 7, 216, 213, 3, 153, 5, 15, 207, 224, 0, 54, 1, 154, 6, 66, 150, 218, 0, 231, 103, 224, 36, 75, 30, 72, 37, 104, 27, + 34, 1, 0, 153, 24, 201, 0, 137, 80, 10, 70, 108, 24, 65, 32, 23, 93, 4, 113, 12, 152, 8, 0, 89, 137, 4, 72, 23, 82, + 12, 152, 8, 137, 68, 72, 22, 82, 12, 73, 22, 36, 23, 84, 202, 70, 105, 74, 21, 92, 97, 84, 209, 74, 27, 75, 24, 32, + 0, 96, 26, 75, 20, 34, 0, 96, 26, 224, 20, 76, 12, 74, 17, 35, 122, 66, 91, 104, 32, 96, 19, 35, 164, 67, 88, 75, 13, + 24, 192, 247, 255, 252, 149, 104, 35, 74, 8, 33, 0, 84, 209, 72, 21, 247, 253, 254, 188, 32, 3, 66, 64, 176, 11, 188, + 240, 188, 2, 71, 8, 19, 112, 107, 104, 19, 112, 107, 60, 19, 112, 107, 68, 19, 112, 107, 84, 19, 112, 96, 64, 19, 114, + 197, 160, 19, 112, 107, 32, 19, 114, 198, 232, 19, 112, 1, 14, 19, 112, 96, 56, 0, 152, 150, 128, 0, 15, 66, 64, 19, + 112, 104, 98, 19, 112, 104, 128, 19, 112, 104, 103, 19, 112, 107, 108, 19, 112, 104, 157, 19, 112, 104, 198, 181, 16, + 75, 16, 104, 28, 75, 16, 0, 160, 88, 195, 43, 0, 208, 22, 74, 14, 93, 19, 43, 0, 208, 14, 75, 13, 33, 16, 85, 25, 35, + 0, 85, 19, 74, 12, 33, 0, 80, 131, 25, 0, 75, 11, 0, 128, 24, 192, 34, 20, 240, 1, 254, 179, 75, 4, 0, 164, 34, 0, 80, + 226, 188, 16, 188, 1, 71, 0, 19, 112, 107, 104, 19, 112, 107, 76, 19, 112, 107, 84, 19, 112, 96, 64, 19, 112, 107, 108, + 19, 114, 198, 232, 181, 240, 176, 135, 40, 1, 208, 4, 40, 2, 208, 6, 32, 0, 144, 2, 224, 5, 33, 1, 145, 2, 37, 1, 224, + 2, 34, 1, 146, 2, 37, 0, 78, 114, 34, 0, 104, 51, 32, 0, 104, 155, 0, 175, 96, 154, 247, 252, 255, 149, 74, 111, 35, + 100, 66, 91, 96, 19, 72, 110, 172, 4, 247, 253, 254, 67, 25, 228, 224, 177, 75, 105, 74, 107, 104, 30, 35, 1, 81, 211, + 72, 106, 75, 107, 33, 0, 81, 193, 85, 89, 0, 169, 25, 73, 74, 105, 0, 136, 35, 0, 80, 131, 35, 1, 66, 91, 96, 35, 0, + 203, 24, 243, 103, 93, 73, 101, 111, 27, 96, 13, 43, 0, 209, 57, 104, 179, 32, 1, 51, 68, 25, 219, 104, 27, 66, 24, + 209, 2, 28, 40, 247, 253, 252, 24, 73, 86, 104, 11, 104, 155, 51, 68, 25, 219, 104, 27, 7, 218, 213, 39, 28, 40, 247, + 254, 251, 77, 35, 140, 0, 219, 96, 32, 24, 192, 40, 1, 216, 4, 35, 100, 74, 78, 66, 91, 96, 19, 224, 115, 28, 40, 247, + 254, 249, 219, 144, 3, 96, 32, 32, 20, 247, 253, 248, 232, 72, 71, 153, 3, 104, 3, 104, 155, 51, 68, 25, 219, 104, 26, + 41, 0, 219, 4, 75, 75, 64, 26, 75, 75, 66, 154, 208, 3, 28, 40, 247, 254, 251, 41, 96, 32, 0, 168, 25, 64, 0, 192, 24, + 51, 111, 27, 43, 0, 208, 82, 74, 66, 73, 61, 104, 19, 48, 88, 0, 155, 34, 1, 80, 90, 24, 48, 247, 255, 253, 183, 40, + 0, 209, 67, 73, 60, 74, 56, 104, 11, 33, 1, 0, 155, 80, 153, 74, 53, 80, 152, 74, 59, 88, 155, 43, 0, 208, 1, 72, 58, + 224, 0, 72, 58, 247, 253, 253, 200, 72, 57, 247, 253, 253, 197, 32, 100, 247, 253, 248, 172, 78, 55, 75, 56, 32, 1, + 96, 51, 75, 55, 112, 24, 72, 55, 247, 253, 253, 185, 75, 44, 32, 164, 104, 27, 74, 53, 67, 88, 24, 128, 74, 53, 92, + 209, 75, 53, 34, 0, 147, 0, 35, 1, 247, 255, 253, 11, 73, 46, 75, 50, 34, 0, 96, 32, 112, 10, 96, 51, 40, 0, 218, 7, + 72, 48, 247, 253, 253, 160, 28, 40, 247, 253, 251, 199, 75, 46, 224, 3, 72, 46, 247, 253, 253, 152, 35, 1, 96, 35, 224, + 2, 28, 40, 247, 253, 251, 188, 53, 1, 52, 4, 55, 4, 152, 2, 66, 133, 220, 0, 231, 73, 72, 39, 247, 252, 254, 210, 73, + 12, 34, 4, 104, 11, 104, 155, 96, 154, 154, 4, 155, 5, 42, 1, 209, 6, 74, 9, 33, 0, 96, 17, 43, 1, 209, 5, 35, 2, 224, + 2, 43, 1, 209, 1, 74, 4, 96, 19, 75, 3, 104, 24, 176, 7, 188, 240, 188, 2, 71, 8, 19, 112, 96, 60, 19, 112, 107, 32, + 19, 112, 104, 227, 19, 112, 107, 108, 19, 112, 107, 76, 19, 112, 107, 84, 19, 114, 198, 232, 19, 112, 107, 104, 0, 0, + 57, 5, 0, 0, 16, 5, 19, 112, 107, 44, 19, 112, 105, 118, 19, 112, 105, 135, 19, 112, 105, 154, 19, 112, 96, 56, 0, 152, + 150, 128, 19, 112, 107, 20, 19, 112, 105, 203, 19, 114, 197, 160, 19, 112, 96, 64, 19, 112, 107, 160, 0, 7, 161, 32, + 19, 112, 105, 176, 255, 255, 251, 162, 19, 112, 105, 200, 19, 112, 17, 185, 181, 0, 30, 2, 208, 1, 35, 0, 96, 19, 75, + 19, 73, 19, 104, 27, 32, 0, 92, 201, 41, 1, 209, 28, 73, 17, 92, 201, 41, 16, 208, 24, 42, 0, 208, 9, 0, 152, 24, 192, + 0, 192, 24, 195, 24, 89, 49, 4, 72, 12, 0, 137, 88, 11, 96, 19, 75, 7, 74, 9, 104, 27, 0, 153, 24, 201, 92, 210, 0, + 201, 24, 203, 24, 155, 51, 20, 74, 5, 0, 155, 88, 152, 188, 2, 71, 8, 70, 192, 19, 112, 107, 104, 19, 112, 107, 84, + 19, 112, 96, 64, 19, 114, 197, 160, 181, 240, 176, 139, 75, 104, 104, 26, 75, 104, 0, 145, 88, 203, 43, 0, 209, 0, 224, + 188, 75, 102, 33, 16, 84, 153, 75, 102, 120, 25, 75, 102, 41, 0, 208, 1, 73, 101, 224, 0, 73, 101, 39, 164, 67, 122, + 78, 101, 96, 25, 172, 4, 35, 0, 24, 178, 130, 99, 130, 163, 50, 144, 104, 16, 28, 33, 247, 254, 250, 55, 77, 88, 104, + 43, 0, 154, 24, 211, 74, 94, 0, 155, 24, 210, 121, 145, 121, 35, 66, 153, 209, 25, 121, 209, 121, 99, 66, 153, 209, + 21, 28, 19, 137, 17, 137, 34, 51, 8, 66, 145, 209, 15, 136, 89, 137, 98, 66, 145, 209, 11, 121, 25, 123, 162, 66, 145, + 209, 7, 121, 89, 123, 226, 66, 145, 209, 3, 121, 154, 124, 35, 66, 154, 208, 6, 168, 4, 247, 253, 251, 142, 72, 77, + 247, 253, 252, 184, 224, 119, 28, 32, 247, 253, 251, 135, 74, 74, 75, 68, 96, 26, 104, 43, 67, 123, 24, 242, 50, 144, + 104, 16, 92, 241, 247, 253, 255, 166, 40, 0, 219, 105, 104, 43, 67, 95, 25, 246, 104, 178, 42, 0, 208, 9, 28, 51, 51, + 144, 6, 18, 104, 24, 121, 241, 14, 18, 247, 253, 255, 136, 40, 0, 219, 91, 75, 53, 120, 26, 75, 53, 42, 0, 208, 1, 74, + 52, 224, 0, 74, 52, 76, 46, 96, 26, 104, 34, 38, 164, 67, 114, 77, 50, 32, 0, 24, 171, 28, 25, 49, 8, 113, 136, 136, + 219, 49, 136, 24, 170, 104, 8, 50, 14, 33, 1, 147, 0, 145, 1, 146, 2, 33, 161, 34, 254, 35, 0, 247, 253, 254, 246, 40, + 0, 218, 9, 104, 32, 35, 1, 67, 70, 25, 168, 115, 131, 33, 0, 247, 254, 253, 104, 72, 39, 224, 51, 104, 35, 79, 35, 28, + 48, 67, 88, 24, 40, 28, 2, 50, 8, 121, 145, 49, 1, 113, 145, 0, 154, 24, 211, 0, 155, 24, 251, 121, 25, 247, 254, 255, + 121, 40, 0, 219, 29, 104, 35, 67, 94, 0, 154, 24, 211, 0, 155, 24, 251, 25, 112, 121, 25, 247, 255, 250, 39, 40, 0, + 219, 19, 104, 35, 0, 154, 24, 210, 0, 146, 24, 191, 121, 57, 74, 11, 84, 209, 74, 20, 33, 1, 84, 209, 224, 8, 72, 19, + 224, 6, 72, 19, 224, 4, 72, 19, 224, 2, 72, 19, 224, 0, 72, 19, 176, 11, 188, 240, 188, 2, 71, 8, 19, 112, 107, 104, + 19, 112, 107, 76, 19, 112, 96, 64, 19, 112, 1, 15, 19, 112, 96, 56, 0, 15, 66, 64, 0, 3, 13, 64, 19, 114, 197, 160, + 19, 114, 198, 232, 19, 112, 101, 128, 0, 12, 53, 0, 255, 255, 252, 20, 19, 112, 107, 84, 255, 255, 252, 15, 255, 255, + 252, 23, 255, 255, 252, 22, 255, 255, 252, 19, 255, 255, 252, 18, 181, 240, 176, 131, 76, 59, 75, 60, 104, 34, 38, 1, + 0, 146, 88, 211, 43, 0, 208, 105, 75, 57, 104, 27, 104, 155, 28, 25, 49, 68, 24, 138, 104, 18, 66, 22, 208, 96, 34, + 0, 32, 0, 96, 154, 247, 252, 253, 64, 104, 32, 247, 253, 255, 203, 30, 7, 218, 3, 104, 32, 247, 253, 255, 198, 28, 7, + 32, 60, 247, 252, 254, 212, 77, 44, 76, 42, 104, 43, 72, 44, 104, 154, 104, 35, 50, 68, 0, 155, 24, 211, 104, 27, 28, + 57, 28, 26, 147, 1, 247, 253, 251, 219, 104, 34, 72, 35, 0, 147, 33, 1, 80, 25, 38, 1, 47, 0, 219, 53, 153, 1, 72, 34, + 64, 8, 73, 34, 66, 136, 209, 47, 72, 34, 33, 0, 84, 129, 74, 27, 32, 0, 80, 152, 247, 255, 254, 191, 40, 0, 219, 15, + 104, 35, 73, 23, 0, 154, 32, 0, 80, 80, 73, 26, 72, 27, 84, 206, 247, 252, 253, 3, 104, 43, 34, 4, 104, 155, 38, 0, + 96, 154, 224, 21, 104, 35, 74, 15, 0, 155, 80, 158, 72, 21, 28, 57, 247, 253, 251, 171, 104, 35, 72, 16, 33, 0, 84, + 193, 32, 100, 247, 252, 254, 142, 72, 14, 247, 252, 252, 235, 104, 43, 34, 4, 104, 155, 96, 154, 32, 100, 247, 252, + 254, 132, 176, 3, 28, 48, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 104, 19, 112, 107, 108, 19, 112, 96, 60, 19, + 112, 105, 221, 0, 0, 49, 5, 0, 0, 16, 5, 19, 112, 107, 84, 19, 112, 17, 185, 19, 112, 105, 249, 181, 240, 176, 131, + 76, 42, 75, 43, 104, 34, 0, 146, 88, 211, 34, 0, 146, 1, 43, 0, 208, 70, 247, 255, 255, 103, 104, 35, 73, 38, 0, 154, + 88, 81, 41, 0, 209, 62, 73, 37, 92, 201, 41, 0, 208, 58, 39, 164, 28, 56, 67, 88, 78, 34, 24, 211, 77, 34, 0, 155, 24, + 243, 25, 64, 121, 25, 247, 255, 249, 57, 40, 0, 219, 44, 104, 35, 0, 154, 24, 210, 0, 145, 24, 113, 0, 210, 121, 9, + 67, 95, 24, 211, 24, 203, 29, 26, 51, 20, 0, 146, 0, 155, 25, 120, 25, 82, 25, 91, 247, 255, 249, 118, 40, 0, 219, 23, + 104, 35, 74, 20, 0, 153, 88, 138, 42, 0, 208, 13, 24, 201, 0, 136, 24, 54, 121, 48, 0, 201, 24, 201, 24, 9, 49, 4, 0, + 137, 35, 128, 89, 73, 1, 27, 66, 153, 209, 3, 28, 19, 30, 90, 65, 147, 147, 1, 152, 1, 176, 3, 188, 240, 188, 2, 71, + 8, 70, 192, 19, 112, 107, 104, 19, 112, 107, 76, 19, 112, 107, 108, 19, 112, 107, 84, 19, 114, 198, 232, 19, 114, 197, + 160, 19, 112, 107, 44, 181, 240, 176, 135, 75, 81, 144, 3, 104, 27, 146, 4, 43, 0, 209, 8, 75, 79, 104, 26, 75, 79, + 0, 146, 88, 211, 43, 0, 208, 1, 75, 78, 96, 24, 75, 78, 104, 27, 43, 0, 208, 9, 75, 73, 104, 26, 75, 76, 0, 146, 88, + 208, 40, 1, 221, 2, 33, 1, 80, 209, 224, 126, 4, 9, 12, 9, 79, 70, 38, 4, 36, 0, 145, 5, 247, 255, 254, 231, 30, 67, + 65, 152, 104, 59, 66, 64, 64, 4, 43, 0, 208, 6, 75, 60, 74, 64, 104, 27, 0, 155, 88, 155, 43, 3, 208, 101, 44, 0, 219, + 5, 75, 56, 74, 60, 104, 27, 92, 211, 43, 1, 208, 7, 75, 53, 33, 1, 104, 26, 75, 55, 0, 146, 36, 1, 80, 209, 66, 100, + 75, 49, 74, 52, 104, 27, 0, 155, 88, 154, 42, 0, 209, 75, 74, 46, 88, 154, 75, 50, 42, 0, 208, 1, 74, 50, 224, 0, 74, + 50, 96, 26, 28, 99, 208, 29, 32, 0, 247, 252, 252, 12, 77, 47, 34, 0, 104, 43, 32, 164, 104, 155, 96, 154, 75, 35, 74, + 45, 104, 27, 67, 88, 24, 128, 74, 44, 92, 209, 154, 4, 155, 5, 146, 0, 154, 3, 247, 255, 250, 15, 28, 4, 72, 40, 247, + 252, 251, 245, 104, 43, 34, 4, 104, 155, 96, 154, 74, 38, 75, 30, 96, 26, 44, 0, 218, 5, 75, 22, 33, 1, 104, 26, 75, + 25, 0, 146, 80, 209, 75, 22, 104, 27, 43, 0, 208, 6, 75, 17, 104, 26, 75, 20, 0, 146, 88, 211, 43, 0, 209, 18, 75, 14, + 74, 17, 104, 27, 0, 153, 88, 138, 42, 0, 209, 5, 74, 15, 92, 211, 43, 1, 209, 1, 44, 0, 218, 7, 62, 1, 46, 0, 209, 137, + 67, 228, 15, 224, 224, 2, 32, 0, 224, 0, 32, 1, 176, 7, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 12, 19, 112, + 107, 104, 19, 112, 107, 44, 19, 112, 106, 224, 19, 112, 106, 228, 19, 112, 107, 108, 19, 112, 107, 84, 19, 112, 96, + 56, 0, 152, 150, 128, 0, 12, 53, 0, 19, 112, 96, 60, 19, 114, 197, 160, 19, 112, 96, 64, 19, 112, 17, 185, 0, 7, 161, + 32, 181, 240, 176, 133, 75, 49, 28, 23, 104, 26, 75, 49, 0, 146, 88, 211, 144, 2, 32, 0, 43, 0, 209, 85, 4, 9, 12, 9, + 36, 0, 145, 3, 247, 255, 254, 56, 40, 0, 208, 2, 44, 0, 219, 7, 224, 0, 36, 0, 75, 38, 74, 40, 104, 27, 92, 211, 43, + 1, 208, 7, 75, 35, 33, 1, 104, 26, 75, 37, 0, 146, 36, 1, 80, 209, 66, 100, 77, 31, 75, 34, 104, 42, 0, 146, 88, 211, + 43, 0, 209, 226, 73, 32, 74, 32, 96, 17, 28, 98, 208, 26, 78, 31, 32, 0, 104, 50, 104, 146, 96, 147, 247, 252, 251, + 105, 104, 43, 32, 164, 67, 88, 74, 27, 24, 128, 74, 27, 92, 209, 154, 2, 155, 3, 151, 0, 247, 255, 249, 177, 28, 4, + 72, 24, 247, 252, 251, 89, 104, 51, 34, 4, 104, 155, 96, 154, 74, 22, 75, 16, 96, 26, 44, 0, 218, 5, 75, 9, 33, 1, 104, + 26, 75, 11, 0, 146, 80, 209, 75, 6, 104, 26, 75, 8, 0, 146, 88, 211, 43, 0, 209, 176, 44, 0, 219, 174, 32, 1, 176, 5, + 188, 240, 188, 2, 71, 8, 19, 112, 107, 104, 19, 112, 107, 44, 19, 112, 107, 84, 19, 112, 107, 108, 0, 15, 66, 64, 19, + 112, 96, 56, 19, 112, 96, 60, 19, 114, 197, 160, 19, 112, 96, 64, 19, 112, 17, 185, 0, 7, 161, 32, 181, 240, 176, 137, + 74, 95, 104, 19, 43, 0, 209, 6, 75, 94, 104, 27, 105, 153, 96, 17, 106, 26, 75, 93, 96, 26, 75, 92, 77, 90, 104, 28, + 38, 0, 28, 55, 104, 43, 55, 8, 0, 191, 80, 252, 28, 32, 33, 0, 34, 96, 240, 1, 249, 253, 104, 43, 33, 96, 88, 248, 240, + 0, 249, 214, 52, 127, 33, 31, 54, 1, 67, 140, 46, 8, 209, 234, 37, 8, 38, 31, 28, 32, 33, 0, 34, 96, 240, 1, 249, 235, + 61, 1, 28, 32, 33, 96, 52, 127, 240, 0, 249, 195, 67, 180, 45, 0, 209, 241, 75, 72, 34, 255, 96, 28, 75, 68, 39, 128, + 104, 29, 35, 255, 2, 18, 4, 27, 76, 69, 38, 0, 5, 255, 146, 2, 147, 3, 33, 0, 96, 37, 34, 96, 28, 40, 240, 1, 249, 206, + 28, 40, 240, 0, 249, 152, 28, 50, 100, 104, 104, 35, 30, 81, 65, 138, 5, 210, 96, 90, 104, 35, 34, 0, 96, 154, 104, + 35, 33, 31, 97, 159, 53, 127, 67, 141, 104, 34, 28, 40, 146, 5, 240, 0, 249, 132, 144, 6, 28, 40, 240, 0, 249, 128, + 144, 1, 28, 40, 240, 0, 249, 124, 144, 7, 28, 40, 240, 0, 249, 120, 35, 255, 2, 27, 153, 2, 147, 4, 34, 255, 155, 1, + 4, 18, 64, 11, 146, 1, 153, 3, 154, 7, 2, 27, 64, 10, 10, 18, 67, 19, 14, 0, 154, 6, 67, 3, 32, 224, 64, 16, 33, 2, + 67, 8, 154, 5, 6, 0, 67, 3, 96, 19, 104, 34, 35, 128, 4, 91, 97, 19, 104, 34, 33, 96, 97, 83, 204, 1, 54, 1, 240, 0, + 249, 100, 46, 6, 209, 175, 76, 22, 77, 26, 104, 32, 105, 110, 240, 0, 249, 75, 144, 2, 104, 32, 240, 0, 249, 71, 28, + 7, 104, 32, 240, 0, 249, 67, 144, 3, 104, 32, 240, 0, 249, 63, 155, 4, 153, 1, 64, 31, 155, 3, 2, 63, 64, 11, 10, 27, + 67, 31, 154, 2, 14, 3, 28, 56, 67, 24, 35, 224, 64, 19, 33, 2, 67, 11, 6, 27, 67, 24, 96, 48, 105, 104, 33, 96, 240, + 0, 249, 57, 176, 9, 188, 240, 188, 1, 71, 0, 70, 192, 19, 112, 107, 100, 19, 112, 96, 60, 19, 112, 107, 72, 19, 112, + 107, 40, 19, 114, 195, 192, 181, 248, 75, 16, 39, 128, 104, 28, 38, 0, 4, 127, 28, 32, 247, 252, 252, 153, 46, 3, 208, + 15, 28, 37, 35, 31, 53, 127, 67, 157, 107, 104, 240, 1, 248, 190, 96, 103, 96, 32, 33, 96, 28, 32, 240, 0, 249, 18, + 54, 1, 28, 44, 231, 234, 28, 32, 33, 96, 240, 0, 249, 11, 188, 248, 188, 1, 71, 0, 19, 112, 107, 40, 181, 240, 176, + 131, 247, 255, 255, 12, 247, 255, 255, 214, 76, 65, 75, 66, 104, 34, 104, 24, 77, 65, 97, 144, 104, 89, 78, 65, 97, + 209, 104, 154, 79, 64, 96, 42, 104, 218, 105, 27, 96, 50, 96, 59, 33, 96, 240, 0, 249, 1, 104, 35, 33, 0, 105, 154, + 100, 211, 105, 154, 100, 145, 105, 155, 28, 24, 147, 1, 240, 0, 248, 209, 155, 1, 104, 34, 100, 88, 104, 59, 105, 145, + 108, 91, 34, 255, 2, 18, 64, 26, 2, 16, 34, 255, 4, 18, 64, 26, 10, 18, 67, 2, 14, 24, 67, 2, 32, 224, 64, 3, 39, 2, + 67, 59, 6, 24, 28, 19, 67, 3, 96, 11, 104, 35, 34, 128, 105, 153, 4, 18, 96, 74, 105, 154, 33, 0, 96, 145, 105, 153, + 34, 128, 5, 210, 97, 138, 105, 153, 34, 128, 4, 82, 97, 10, 105, 153, 97, 74, 105, 152, 33, 96, 240, 0, 248, 179, 104, + 35, 33, 96, 105, 216, 240, 0, 248, 194, 104, 35, 33, 0, 105, 218, 100, 211, 105, 218, 100, 145, 105, 223, 28, 56, 240, + 0, 248, 147, 33, 96, 100, 120, 104, 40, 240, 0, 248, 179, 104, 47, 104, 35, 28, 56, 100, 251, 35, 0, 100, 187, 240, + 0, 248, 134, 33, 96, 100, 120, 104, 40, 240, 0, 248, 146, 104, 48, 33, 96, 240, 0, 248, 162, 104, 53, 104, 35, 33, 0, + 100, 235, 100, 169, 28, 40, 240, 0, 248, 117, 33, 96, 100, 104, 104, 48, 240, 0, 248, 129, 176, 3, 188, 240, 188, 1, + 71, 0, 70, 192, 19, 112, 96, 60, 19, 114, 195, 192, 19, 112, 107, 16, 19, 112, 107, 96, 19, 112, 107, 92, 181, 248, + 76, 35, 75, 35, 96, 35, 240, 0, 248, 74, 40, 0, 219, 58, 240, 0, 249, 121, 74, 32, 104, 131, 96, 32, 96, 26, 104, 32, + 35, 1, 104, 130, 104, 17, 66, 11, 209, 252, 33, 252, 240, 0, 248, 91, 38, 128, 39, 128, 76, 23, 37, 0, 4, 118, 1, 127, + 104, 35, 33, 4, 108, 219, 81, 94, 104, 35, 108, 219, 89, 88, 53, 4, 240, 0, 248, 75, 66, 189, 209, 243, 247, 255, 255, + 65, 104, 35, 34, 2, 51, 84, 112, 26, 247, 252, 254, 95, 104, 35, 32, 0, 104, 154, 105, 155, 108, 91, 97, 147, 104, 35, + 34, 4, 104, 155, 96, 154, 104, 35, 74, 8, 104, 155, 96, 26, 104, 35, 104, 155, 104, 27, 224, 1, 32, 1, 66, 64, 188, + 248, 188, 2, 71, 8, 19, 112, 96, 60, 19, 114, 199, 16, 0, 1, 0, 32, 0, 1, 0, 33, 181, 8, 33, 224, 72, 4, 2, 9, 240, + 1, 249, 94, 75, 3, 96, 24, 23, 192, 188, 8, 188, 2, 71, 8, 19, 112, 163, 160, 19, 112, 96, 68, 71, 112, 71, 112, 181, + 16, 28, 4, 240, 1, 249, 42, 28, 32, 188, 16, 188, 2, 71, 8, 181, 16, 28, 4, 240, 1, 249, 34, 28, 32, 188, 16, 188, 2, + 71, 8, 181, 16, 28, 4, 240, 1, 249, 26, 28, 32, 188, 16, 188, 2, 71, 8, 181, 8, 240, 1, 249, 27, 188, 8, 188, 1, 71, + 0, 181, 8, 240, 1, 249, 21, 188, 8, 188, 1, 71, 0, 181, 8, 240, 1, 249, 15, 188, 8, 188, 1, 71, 0, 181, 8, 75, 12, 28, + 1, 34, 32, 104, 24, 240, 1, 249, 45, 40, 0, 209, 13, 72, 9, 240, 1, 249, 56, 240, 0, 249, 67, 32, 200, 247, 252, 250, + 165, 240, 0, 249, 72, 32, 200, 247, 252, 250, 160, 231, 244, 188, 8, 188, 2, 71, 8, 19, 112, 96, 68, 19, 112, 106, 17, + 181, 8, 75, 4, 28, 1, 104, 24, 240, 1, 248, 170, 188, 8, 188, 1, 71, 0, 70, 192, 19, 112, 96, 68, 181, 248, 76, 13, + 28, 15, 104, 33, 28, 30, 28, 24, 28, 21, 67, 81, 240, 1, 248, 208, 28, 56, 28, 41, 28, 50, 247, 255, 252, 119, 35, 1, + 40, 0, 208, 5, 104, 33, 28, 48, 67, 105, 240, 1, 248, 203, 35, 0, 28, 24, 188, 248, 188, 2, 71, 8, 19, 114, 200, 12, + 181, 16, 76, 11, 104, 32, 40, 0, 208, 3, 240, 0, 252, 43, 35, 0, 96, 35, 76, 8, 104, 35, 43, 0, 209, 3, 72, 7, 247, + 251, 250, 220, 96, 32, 74, 6, 35, 1, 66, 91, 96, 19, 188, 16, 188, 1, 71, 0, 19, 112, 107, 124, 19, 112, 107, 116, 0, + 0, 128, 32, 19, 112, 96, 24, 181, 56, 75, 14, 104, 24, 40, 0, 208, 1, 247, 251, 250, 227, 76, 12, 75, 10, 104, 32, 37, + 0, 96, 29, 40, 0, 208, 2, 240, 0, 252, 2, 96, 37, 75, 8, 104, 24, 40, 0, 208, 1, 240, 0, 251, 95, 75, 5, 34, 0, 96, + 26, 188, 56, 188, 1, 71, 0, 70, 192, 19, 112, 107, 116, 19, 112, 107, 124, 19, 112, 107, 120, 181, 240, 176, 135, 75, + 38, 28, 7, 104, 24, 145, 5, 40, 0, 208, 1, 247, 251, 250, 188, 75, 34, 77, 35, 34, 0, 96, 26, 104, 43, 43, 0, 209, 25, + 75, 33, 36, 0, 104, 24, 247, 255, 248, 198, 78, 31, 28, 48, 247, 255, 249, 236, 75, 30, 96, 24, 40, 0, 208, 43, 154, + 5, 104, 51, 33, 0, 144, 0, 146, 1, 72, 27, 34, 0, 148, 2, 240, 0, 249, 240, 96, 40, 40, 0, 208, 30, 77, 24, 104, 44, + 44, 0, 208, 11, 72, 23, 28, 57, 34, 6, 240, 0, 254, 216, 40, 0, 208, 19, 28, 32, 240, 0, 251, 181, 35, 0, 96, 43, 75, + 11, 28, 57, 104, 24, 240, 0, 251, 56, 75, 13, 28, 4, 96, 24, 40, 0, 208, 4, 72, 12, 28, 57, 34, 6, 240, 0, 254, 240, + 176, 7, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 107, 116, 19, 112, 107, 120, 19, 112, 107, 104, 19, 114, + 200, 12, 19, 114, 200, 16, 19, 112, 75, 241, 19, 112, 107, 124, 19, 112, 96, 72, 181, 8, 28, 3, 28, 10, 32, 0, 28, 25, + 35, 0, 240, 0, 255, 214, 188, 8, 188, 1, 71, 0, 181, 8, 32, 1, 33, 0, 34, 0, 35, 0, 240, 0, 255, 204, 188, 8, 188, 2, + 71, 8, 181, 8, 32, 17, 33, 0, 34, 0, 35, 0, 240, 0, 255, 194, 188, 8, 188, 2, 71, 8, 181, 8, 32, 18, 33, 0, 34, 0, 35, + 0, 240, 0, 255, 184, 188, 8, 188, 2, 71, 8, 181, 56, 28, 5, 28, 12, 30, 19, 221, 4, 32, 2, 28, 41, 28, 34, 240, 0, 255, + 171, 188, 56, 188, 1, 71, 0, 181, 56, 28, 5, 28, 12, 30, 19, 221, 4, 32, 9, 28, 41, 28, 34, 240, 0, 255, 158, 188, 56, + 188, 1, 71, 0, 181, 8, 28, 3, 28, 10, 32, 5, 28, 25, 35, 0, 240, 0, 255, 147, 188, 8, 188, 1, 71, 0, 181, 8, 28, 3, + 28, 10, 32, 6, 28, 25, 35, 0, 240, 0, 255, 136, 188, 8, 188, 1, 71, 0, 181, 56, 28, 5, 28, 12, 28, 19, 28, 41, 28, 34, + 32, 16, 240, 0, 255, 124, 188, 56, 188, 2, 71, 8, 181, 8, 32, 128, 33, 0, 34, 0, 35, 0, 240, 0, 255, 114, 188, 8, 188, + 1, 71, 0, 181, 8, 32, 129, 33, 0, 34, 0, 35, 0, 240, 0, 255, 104, 188, 8, 188, 1, 71, 0, 181, 0, 35, 0, 224, 3, 51, + 1, 6, 27, 14, 27, 8, 64, 40, 0, 209, 249, 30, 88, 6, 0, 14, 0, 188, 2, 71, 8, 0, 0, 181, 240, 176, 133, 144, 1, 32, + 84, 145, 3, 146, 2, 28, 30, 159, 11, 247, 251, 249, 182, 28, 4, 30, 48, 209, 1, 32, 128, 0, 128, 247, 251, 249, 175, + 28, 5, 32, 128, 2, 0, 97, 32, 247, 255, 255, 218, 75, 125, 117, 32, 97, 227, 154, 10, 75, 124, 96, 37, 96, 30, 11, 211, + 67, 115, 97, 163, 155, 12, 98, 231, 43, 0, 208, 50, 33, 0, 28, 50, 28, 40, 240, 0, 254, 106, 35, 87, 112, 43, 35, 66, + 112, 107, 35, 70, 112, 171, 35, 83, 112, 235, 28, 48, 247, 255, 255, 188, 114, 40, 159, 10, 73, 111, 14, 59, 113, 43, + 12, 59, 113, 107, 10, 59, 113, 171, 113, 239, 105, 162, 35, 6, 66, 138, 217, 16, 73, 106, 35, 7, 66, 138, 217, 12, 73, + 105, 35, 8, 66, 138, 217, 8, 73, 104, 35, 9, 66, 138, 217, 4, 75, 103, 66, 147, 65, 155, 66, 91, 51, 10, 125, 34, 24, + 155, 114, 107, 224, 6, 28, 57, 152, 2, 34, 1, 28, 43, 159, 1, 240, 0, 248, 207, 120, 43, 120, 106, 6, 27, 4, 18, 67, + 26, 120, 171, 2, 27, 67, 26, 120, 235, 67, 26, 75, 91, 66, 154, 208, 4, 72, 90, 240, 0, 255, 97, 72, 90, 224, 40, 75, + 90, 104, 27, 66, 95, 65, 95, 46, 0, 208, 12, 47, 0, 208, 10, 28, 48, 247, 255, 255, 117, 122, 43, 66, 131, 208, 4, 72, + 81, 240, 0, 255, 78, 72, 82, 224, 21, 154, 10, 42, 0, 208, 31, 47, 0, 208, 29, 121, 42, 121, 107, 6, 18, 4, 27, 67, + 19, 121, 170, 159, 10, 2, 18, 67, 19, 121, 234, 67, 19, 66, 187, 208, 16, 72, 69, 240, 0, 255, 55, 72, 72, 240, 0, 255, + 52, 72, 71, 240, 0, 255, 49, 28, 32, 247, 251, 249, 60, 28, 40, 247, 251, 249, 57, 36, 0, 224, 100, 122, 46, 39, 1, + 28, 58, 64, 178, 96, 98, 122, 43, 28, 22, 147, 0, 114, 35, 121, 42, 121, 107, 6, 18, 4, 27, 67, 19, 121, 170, 121, 232, + 2, 18, 67, 19, 67, 24, 105, 33, 96, 224, 240, 0, 254, 148, 67, 112, 97, 160, 122, 107, 125, 34, 64, 159, 98, 99, 26, + 155, 105, 226, 64, 216, 64, 218, 4, 18, 12, 18, 4, 51, 12, 27, 133, 98, 50, 128, 4, 1, 133, 32, 0, 82, 30, 88, 24, 130, + 66, 91, 64, 26, 155, 3, 28, 37, 53, 72, 99, 99, 12, 201, 155, 0, 98, 39, 128, 42, 26, 127, 154, 1, 64, 223, 99, 34, + 100, 39, 154, 2, 159, 12, 99, 162, 47, 0, 209, 1, 100, 103, 224, 11, 30, 112, 24, 65, 66, 112, 64, 8, 247, 251, 248, + 209, 141, 34, 100, 96, 8, 210, 33, 255, 240, 0, 253, 159, 28, 35, 51, 72, 136, 25, 108, 32, 122, 35, 56, 1, 65, 25, + 240, 0, 254, 84, 4, 2, 104, 96, 12, 18, 28, 3, 59, 12, 135, 162, 66, 154, 217, 0, 135, 163, 247, 251, 248, 183, 35, + 0, 100, 224, 101, 35, 176, 5, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 0, 4, 96, 144, 19, 112, 107, 136, 0, 63, 255, + 255, 0, 127, 255, 255, 0, 255, 255, 255, 1, 255, 255, 255, 3, 255, 255, 255, 87, 66, 70, 83, 19, 112, 106, 48, 19, 112, + 106, 60, 19, 112, 107, 132, 19, 112, 106, 70, 19, 112, 106, 99, 19, 112, 105, 116, 71, 56, 70, 192, 181, 240, 176, 165, + 144, 9, 28, 24, 146, 10, 147, 11, 145, 15, 247, 251, 248, 135, 35, 1, 74, 140, 77, 141, 66, 91, 96, 19, 104, 43, 28, + 4, 43, 0, 209, 3, 152, 11, 247, 251, 248, 123, 96, 40, 75, 136, 152, 11, 33, 0, 96, 24, 34, 1, 152, 10, 28, 35, 157, + 9, 240, 0, 249, 24, 40, 0, 208, 0, 224, 246, 35, 255, 0, 91, 92, 227, 43, 85, 209, 28, 51, 171, 51, 255, 92, 227, 43, + 170, 209, 23, 28, 224, 73, 125, 34, 4, 240, 0, 253, 142, 40, 0, 208, 16, 77, 123, 28, 32, 48, 54, 28, 41, 34, 3, 240, + 0, 253, 133, 40, 0, 208, 7, 28, 32, 48, 82, 28, 41, 34, 3, 240, 0, 253, 125, 40, 0, 209, 5, 168, 20, 33, 0, 34, 64, + 240, 0, 253, 26, 224, 6, 28, 33, 49, 191, 49, 255, 168, 20, 34, 64, 240, 0, 252, 208, 175, 32, 151, 5, 32, 228, 39, + 1, 66, 127, 0, 64, 38, 0, 173, 20, 144, 14, 151, 8, 122, 235, 122, 47, 122, 105, 122, 170, 147, 7, 120, 32, 120, 99, + 6, 0, 4, 27, 67, 24, 120, 163, 2, 27, 67, 24, 120, 227, 67, 24, 75, 97, 66, 152, 208, 3, 121, 40, 40, 0, 209, 0, 224, + 162, 2, 9, 4, 18, 67, 17, 67, 57, 159, 7, 6, 59, 67, 25, 145, 7, 121, 43, 43, 15, 209, 101, 33, 227, 32, 0, 0, 73, 159, + 8, 144, 12, 35, 0, 145, 13, 149, 4, 154, 7, 152, 10, 24, 210, 146, 8, 28, 17, 28, 35, 34, 1, 157, 9, 240, 0, 248, 169, + 40, 0, 208, 0, 224, 135, 72, 78, 153, 14, 92, 34, 92, 99, 2, 18, 4, 27, 67, 26, 77, 76, 155, 13, 152, 8, 92, 225, 93, + 99, 67, 10, 6, 27, 67, 19, 24, 192, 35, 235, 144, 8, 0, 91, 92, 227, 152, 10, 147, 16, 75, 69, 153, 8, 92, 227, 34, + 1, 147, 17, 35, 236, 0, 91, 92, 227, 157, 9, 147, 18, 75, 65, 92, 227, 147, 19, 28, 35, 240, 0, 248, 128, 40, 0, 209, + 95, 120, 34, 120, 99, 6, 18, 4, 27, 67, 19, 120, 162, 73, 54, 2, 18, 67, 19, 120, 226, 67, 19, 66, 139, 209, 10, 154, + 43, 66, 150, 209, 2, 144, 0, 155, 8, 224, 48, 152, 8, 66, 184, 208, 1, 54, 1, 28, 7, 153, 17, 157, 18, 2, 11, 4, 42, + 152, 16, 153, 19, 67, 19, 67, 3, 6, 10, 67, 19, 208, 53, 154, 12, 50, 1, 146, 12, 42, 8, 209, 162, 224, 47, 152, 10, + 153, 7, 34, 1, 28, 35, 159, 9, 240, 0, 248, 79, 40, 0, 209, 45, 120, 34, 120, 99, 6, 18, 4, 27, 67, 19, 120, 162, 73, + 29, 2, 18, 67, 19, 120, 226, 67, 19, 66, 139, 209, 27, 154, 43, 66, 150, 209, 15, 155, 7, 144, 0, 157, 44, 147, 1, 152, + 9, 153, 15, 154, 10, 155, 11, 149, 2, 247, 255, 253, 201, 28, 5, 28, 32, 247, 250, 255, 163, 224, 17, 159, 7, 152, 8, + 66, 135, 208, 4, 54, 1, 151, 8, 224, 1, 157, 4, 151, 8, 153, 5, 66, 141, 208, 1, 53, 16, 231, 65, 28, 32, 247, 250, + 255, 145, 37, 0, 176, 37, 28, 40, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 80, 19, 112, 107, 128, 19, 112, 107, + 136, 19, 112, 106, 127, 19, 112, 106, 132, 87, 66, 70, 83, 0, 0, 1, 199, 0, 0, 1, 201, 0, 0, 1, 215, 0, 0, 1, 217, 71, + 40, 71, 56, 181, 16, 109, 3, 28, 4, 43, 0, 208, 9, 72, 13, 240, 0, 253, 94, 72, 12, 240, 0, 253, 91, 72, 12, 240, 0, + 253, 88, 224, 13, 104, 0, 247, 250, 255, 98, 108, 224, 247, 250, 255, 95, 108, 96, 40, 0, 208, 1, 247, 250, 255, 90, + 28, 32, 247, 250, 255, 87, 188, 16, 188, 1, 71, 0, 19, 112, 106, 48, 19, 112, 106, 136, 19, 112, 105, 116, 181, 240, + 28, 3, 51, 72, 176, 131, 136, 26, 122, 3, 28, 5, 65, 26, 146, 1, 35, 1, 74, 48, 66, 91, 96, 19, 34, 0, 28, 14, 146, + 0, 39, 0, 224, 79, 104, 43, 25, 219, 123, 27, 43, 0, 208, 68, 106, 233, 155, 0, 49, 1, 24, 201, 34, 1, 107, 168, 108, + 235, 107, 44, 240, 0, 248, 83, 108, 233, 28, 48, 34, 6, 240, 0, 251, 110, 40, 0, 209, 51, 32, 12, 247, 250, 255, 5, + 30, 6, 209, 9, 72, 32, 240, 0, 253, 16, 72, 31, 240, 0, 253, 13, 72, 31, 240, 0, 253, 10, 224, 46, 28, 43, 51, 72, 96, + 53, 96, 183, 136, 24, 247, 250, 254, 242, 28, 3, 96, 112, 40, 0, 209, 12, 72, 21, 240, 0, 252, 251, 72, 21, 240, 0, + 252, 248, 72, 20, 240, 0, 252, 245, 28, 48, 247, 250, 255, 0, 224, 21, 106, 233, 154, 0, 49, 1, 24, 137, 107, 168, 107, + 44, 154, 1, 240, 0, 248, 28, 109, 43, 51, 1, 101, 43, 224, 9, 155, 0, 55, 1, 28, 26, 155, 1, 24, 210, 146, 0, 143, 171, + 66, 159, 211, 172, 38, 0, 176, 3, 28, 48, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 80, 19, 112, 106, 48, 19, 112, + 106, 180, 19, 112, 105, 116, 71, 32, 70, 192, 181, 16, 104, 3, 28, 4, 109, 26, 58, 1, 101, 26, 104, 64, 247, 250, 254, + 207, 28, 32, 247, 250, 254, 204, 188, 16, 188, 1, 71, 0, 0, 0, 181, 240, 176, 137, 104, 5, 147, 0, 106, 107, 146, 1, + 28, 6, 30, 154, 28, 8, 64, 208, 4, 2, 12, 18, 104, 104, 146, 4, 70, 132, 152, 4, 104, 116, 48, 128, 0, 64, 90, 32, 122, + 47, 106, 42, 144, 2, 36, 1, 40, 0, 209, 0, 224, 194, 58, 1, 64, 250, 27, 219, 146, 6, 63, 2, 28, 10, 64, 250, 147, 5, + 155, 6, 28, 23, 70, 96, 64, 31, 8, 131, 59, 1, 28, 26, 64, 10, 146, 7, 209, 0, 224, 133, 155, 2, 152, 5, 106, 233, 64, + 131, 24, 121, 147, 3, 24, 201, 75, 87, 104, 26, 108, 235, 66, 138, 209, 5, 74, 85, 28, 24, 104, 17, 74, 85, 104, 18, + 224, 18, 107, 44, 107, 168, 34, 1, 240, 0, 248, 165, 30, 4, 208, 0, 224, 149, 106, 235, 152, 3, 73, 76, 24, 251, 24, + 27, 96, 11, 75, 75, 108, 233, 104, 24, 75, 75, 104, 26, 240, 0, 250, 231, 154, 7, 104, 108, 0, 145, 155, 0, 26, 100, + 66, 156, 217, 0, 28, 28, 108, 235, 152, 1, 24, 89, 28, 34, 240, 0, 250, 217, 152, 0, 153, 1, 27, 0, 25, 9, 144, 0, 145, + 1, 55, 1, 40, 0, 208, 71, 154, 6, 66, 186, 210, 68, 155, 4, 104, 114, 51, 1, 4, 27, 12, 27, 147, 4, 51, 128, 0, 91, + 90, 211, 36, 1, 147, 2, 39, 0, 43, 0, 209, 54, 224, 95, 122, 43, 152, 0, 64, 216, 106, 43, 25, 194, 144, 3, 66, 154, + 217, 1, 27, 219, 147, 3, 154, 5, 106, 233, 155, 2, 24, 121, 64, 147, 24, 201, 107, 168, 107, 44, 154, 3, 155, 1, 240, + 0, 248, 86, 40, 0, 209, 70, 122, 43, 152, 3, 153, 0, 64, 152, 154, 3, 26, 9, 28, 3, 145, 0, 24, 191, 41, 0, 208, 15, + 152, 6, 66, 184, 210, 12, 154, 4, 104, 113, 50, 1, 4, 18, 12, 18, 146, 4, 50, 128, 0, 82, 90, 138, 146, 2, 42, 0, 208, + 42, 39, 0, 153, 1, 24, 201, 145, 1, 104, 107, 154, 0, 66, 154, 210, 197, 36, 0, 42, 0, 208, 34, 155, 5, 158, 2, 106, + 233, 64, 158, 24, 121, 107, 44, 107, 168, 25, 137, 108, 235, 34, 1, 240, 0, 248, 35, 30, 4, 209, 20, 106, 235, 108, + 233, 24, 255, 75, 11, 25, 190, 96, 30, 75, 11, 104, 24, 75, 11, 104, 26, 240, 0, 250, 103, 108, 233, 152, 1, 154, 0, + 240, 0, 250, 98, 224, 2, 36, 1, 224, 0, 28, 4, 176, 9, 28, 32, 188, 240, 188, 2, 71, 8, 70, 192, 19, 112, 96, 80, 19, + 112, 107, 128, 19, 112, 107, 136, 71, 32, 70, 192, 0, 0, 0, 0, 0, 0, 0, 0, 239, 0, 0, 204, 225, 47, 255, 30, 225, 160, + 0, 0, 225, 160, 0, 0, 230, 0, 0, 16, 225, 47, 255, 30, 230, 0, 0, 48, 225, 47, 255, 30, 230, 0, 0, 80, 225, 47, 255, + 30, 230, 0, 0, 112, 225, 47, 255, 30, 230, 0, 0, 144, 225, 47, 255, 30, 230, 0, 0, 176, 225, 47, 255, 30, 230, 0, 0, + 208, 225, 47, 255, 30, 230, 0, 0, 240, 225, 47, 255, 30, 230, 0, 1, 16, 225, 47, 255, 30, 230, 0, 1, 48, 225, 47, 255, + 30, 230, 0, 1, 80, 225, 47, 255, 30, 230, 0, 1, 112, 225, 47, 255, 30, 230, 0, 1, 144, 225, 47, 255, 30, 230, 0, 1, + 176, 225, 47, 255, 30, 230, 0, 1, 208, 225, 47, 255, 30, 230, 0, 1, 240, 225, 47, 255, 30, 230, 0, 2, 16, 225, 47, 255, + 30, 230, 0, 2, 48, 225, 47, 255, 30, 230, 0, 2, 80, 225, 47, 255, 30, 230, 0, 2, 112, 225, 47, 255, 30, 230, 0, 2, 144, + 225, 47, 255, 30, 230, 0, 2, 176, 225, 47, 255, 30, 230, 0, 2, 208, 225, 47, 255, 30, 230, 0, 2, 240, 225, 47, 255, + 30, 230, 0, 3, 16, 225, 47, 255, 30, 230, 0, 3, 48, 225, 47, 255, 30, 230, 0, 3, 80, 225, 47, 255, 30, 230, 0, 3, 112, + 225, 47, 255, 30, 230, 0, 3, 144, 225, 47, 255, 30, 230, 0, 3, 176, 225, 47, 255, 30, 230, 0, 3, 208, 225, 47, 255, + 30, 230, 0, 3, 240, 225, 47, 255, 30, 230, 0, 4, 16, 225, 47, 255, 30, 230, 0, 4, 48, 225, 47, 255, 30, 230, 0, 4, 80, + 225, 47, 255, 30, 230, 0, 4, 112, 225, 47, 255, 30, 230, 0, 4, 144, 225, 47, 255, 30, 230, 0, 4, 176, 225, 47, 255, + 30, 230, 0, 4, 208, 225, 47, 255, 30, 230, 0, 4, 240, 225, 47, 255, 30, 230, 0, 5, 16, 225, 47, 255, 30, 230, 0, 5, + 48, 225, 47, 255, 30, 230, 0, 5, 80, 225, 47, 255, 30, 230, 0, 6, 144, 225, 47, 255, 30, 230, 0, 7, 240, 225, 47, 255, + 30, 230, 0, 8, 16, 225, 47, 255, 30, 230, 0, 10, 16, 225, 47, 255, 30, 226, 144, 16, 0, 227, 176, 0, 4, 239, 0, 0, 171, + 225, 47, 255, 30, 225, 160, 0, 0, 225, 160, 0, 0, 180, 3, 70, 113, 8, 73, 0, 64, 0, 73, 90, 9, 0, 73, 68, 142, 188, + 3, 71, 112, 226, 81, 32, 1, 1, 47, 255, 30, 58, 0, 0, 54, 225, 80, 0, 1, 154, 0, 0, 34, 225, 17, 0, 2, 10, 0, 0, 35, + 227, 17, 2, 14, 1, 160, 17, 129, 3, 160, 48, 8, 19, 160, 48, 1, 227, 81, 2, 1, 49, 81, 0, 0, 49, 160, 18, 1, 49, 160, + 50, 3, 58, 255, 255, 250, 227, 81, 1, 2, 49, 81, 0, 0, 49, 160, 16, 129, 49, 160, 48, 131, 58, 255, 255, 250, 227, 160, + 32, 0, 225, 80, 0, 1, 32, 64, 0, 1, 33, 130, 32, 3, 225, 80, 0, 161, 32, 64, 0, 161, 33, 130, 32, 163, 225, 80, 1, 33, + 32, 64, 1, 33, 33, 130, 33, 35, 225, 80, 1, 161, 32, 64, 1, 161, 33, 130, 33, 163, 227, 80, 0, 0, 17, 176, 50, 35, 17, + 160, 18, 33, 26, 255, 255, 239, 225, 160, 0, 2, 225, 47, 255, 30, 3, 160, 0, 1, 19, 160, 0, 0, 225, 47, 255, 30, 227, + 81, 8, 1, 33, 160, 24, 33, 35, 160, 32, 16, 51, 160, 32, 0, 227, 81, 12, 1, 33, 160, 20, 33, 34, 130, 32, 8, 227, 81, + 0, 16, 33, 160, 18, 33, 34, 130, 32, 4, 227, 81, 0, 4, 130, 130, 32, 3, 144, 130, 32, 161, 225, 160, 2, 48, 225, 47, + 255, 30, 225, 47, 255, 31, 225, 160, 0, 0, 227, 80, 0, 0, 19, 224, 0, 0, 234, 0, 1, 9, 227, 81, 0, 0, 10, 255, 255, + 248, 233, 45, 64, 3, 235, 255, 255, 188, 232, 189, 64, 6, 224, 3, 0, 146, 224, 65, 16, 3, 225, 47, 255, 30, 227, 81, + 0, 0, 10, 0, 0, 67, 224, 32, 192, 1, 66, 97, 16, 0, 226, 81, 32, 1, 10, 0, 0, 39, 225, 176, 48, 0, 66, 96, 48, 0, 225, + 83, 0, 1, 154, 0, 0, 38, 225, 17, 0, 2, 10, 0, 0, 40, 227, 17, 2, 14, 1, 160, 17, 129, 3, 160, 32, 8, 19, 160, 32, 1, + 227, 81, 2, 1, 49, 81, 0, 3, 49, 160, 18, 1, 49, 160, 34, 2, 58, 255, 255, 250, 227, 81, 1, 2, 49, 81, 0, 3, 49, 160, + 16, 129, 49, 160, 32, 130, 58, 255, 255, 250, 227, 160, 0, 0, 225, 83, 0, 1, 32, 67, 48, 1, 33, 128, 0, 2, 225, 83, + 0, 161, 32, 67, 48, 161, 33, 128, 0, 162, 225, 83, 1, 33, 32, 67, 49, 33, 33, 128, 1, 34, 225, 83, 1, 161, 32, 67, 49, + 161, 33, 128, 1, 162, 227, 83, 0, 0, 17, 176, 34, 34, 17, 160, 18, 33, 26, 255, 255, 239, 227, 92, 0, 0, 66, 96, 0, + 0, 225, 47, 255, 30, 225, 60, 0, 0, 66, 96, 0, 0, 225, 47, 255, 30, 51, 160, 0, 0, 1, 160, 15, 204, 3, 128, 0, 1, 225, + 47, 255, 30, 227, 81, 8, 1, 33, 160, 24, 33, 35, 160, 32, 16, 51, 160, 32, 0, 227, 81, 12, 1, 33, 160, 20, 33, 34, 130, + 32, 8, 227, 81, 0, 16, 33, 160, 18, 33, 34, 130, 32, 4, 227, 81, 0, 4, 130, 130, 32, 3, 144, 130, 32, 161, 227, 92, + 0, 0, 225, 160, 2, 51, 66, 96, 0, 0, 225, 47, 255, 30, 225, 47, 255, 31, 225, 160, 0, 0, 227, 80, 0, 0, 195, 224, 1, + 2, 179, 160, 1, 2, 234, 0, 0, 181, 227, 81, 0, 0, 10, 255, 255, 247, 233, 45, 64, 3, 235, 255, 255, 177, 232, 189, 64, + 6, 224, 3, 0, 146, 224, 65, 16, 3, 225, 47, 255, 30, 71, 112, 70, 192, 33, 16, 6, 2, 14, 3, 65, 200, 67, 19, 6, 2, 14, + 0, 67, 16, 65, 200, 67, 24, 71, 112, 70, 192, 181, 112, 28, 4, 28, 13, 42, 3, 217, 33, 28, 11, 67, 3, 7, 158, 208, 18, + 120, 32, 120, 41, 66, 136, 209, 29, 58, 1, 35, 0, 224, 5, 52, 1, 51, 1, 120, 32, 92, 233, 66, 136, 209, 20, 66, 154, + 209, 247, 32, 0, 188, 112, 188, 2, 71, 8, 28, 13, 28, 4, 201, 8, 200, 64, 66, 158, 209, 4, 58, 4, 28, 4, 28, 13, 42, + 3, 216, 244, 32, 0, 42, 0, 209, 222, 231, 237, 26, 64, 231, 235, 70, 192, 181, 240, 28, 5, 28, 14, 28, 20, 42, 15, 217, + 3, 28, 11, 67, 3, 7, 159, 208, 10, 44, 0, 208, 5, 35, 0, 92, 242, 84, 234, 51, 1, 66, 163, 209, 250, 188, 240, 188, + 2, 71, 8, 28, 21, 28, 12, 28, 3, 104, 38, 96, 30, 104, 102, 96, 94, 104, 166, 96, 158, 104, 230, 61, 16, 96, 222, 52, + 16, 51, 16, 45, 15, 216, 242, 58, 16, 9, 23, 28, 126, 1, 63, 1, 54, 27, 215, 25, 133, 28, 60, 25, 142, 47, 3, 217, 217, + 28, 52, 28, 59, 28, 42, 204, 2, 59, 4, 194, 2, 43, 3, 216, 250, 63, 4, 8, 188, 28, 99, 0, 155, 0, 164, 24, 237, 24, + 246, 27, 60, 231, 200, 70, 192, 181, 112, 28, 3, 7, 132, 208, 13, 42, 0, 208, 64, 6, 13, 58, 1, 14, 45, 36, 3, 224, + 2, 42, 0, 208, 57, 58, 1, 112, 29, 51, 1, 66, 35, 209, 248, 42, 3, 217, 41, 37, 255, 64, 13, 2, 44, 67, 37, 4, 44, 28, + 30, 67, 37, 42, 15, 217, 18, 28, 28, 28, 22, 62, 16, 96, 37, 96, 101, 96, 165, 96, 229, 52, 16, 46, 15, 216, 247, 58, + 16, 9, 22, 54, 1, 1, 54, 25, 158, 35, 15, 64, 26, 42, 3, 217, 12, 28, 52, 28, 19, 59, 4, 196, 32, 43, 3, 216, 251, 58, + 4, 8, 147, 51, 1, 0, 155, 24, 246, 35, 3, 64, 26, 28, 51, 42, 0, 208, 6, 6, 9, 14, 12, 33, 0, 84, 92, 49, 1, 66, 138, + 209, 251, 188, 112, 188, 2, 71, 8, 120, 2, 120, 11, 48, 1, 49, 1, 42, 0, 208, 1, 66, 154, 208, 247, 26, 208, 71, 112, + 35, 0, 92, 194, 51, 1, 42, 0, 209, 251, 30, 88, 71, 112, 70, 192, 181, 240, 28, 3, 32, 0, 42, 0, 208, 72, 28, 8, 67, + 24, 36, 3, 30, 85, 64, 4, 209, 42, 28, 30, 28, 13, 42, 3, 217, 67, 104, 31, 104, 8, 66, 135, 209, 63, 58, 4, 28, 32, + 42, 0, 208, 54, 72, 34, 24, 61, 67, 189, 79, 33, 28, 32, 66, 61, 209, 47, 29, 28, 29, 8, 224, 11, 204, 8, 200, 2, 66, + 139, 209, 45, 58, 4, 42, 0, 208, 40, 77, 25, 25, 89, 67, 153, 66, 57, 209, 35, 28, 38, 28, 5, 42, 3, 216, 239, 28, 35, + 28, 1, 42, 0, 208, 33, 30, 85, 120, 28, 120, 8, 66, 132, 209, 18, 32, 0, 45, 0, 208, 16, 44, 0, 208, 14, 61, 1, 34, + 0, 224, 4, 66, 170, 208, 12, 50, 1, 44, 0, 208, 9, 24, 152, 120, 68, 24, 136, 120, 64, 66, 132, 208, 244, 26, 32, 188, + 240, 188, 2, 71, 8, 32, 0, 231, 250, 28, 41, 28, 51, 30, 85, 231, 222, 120, 36, 120, 0, 26, 32, 231, 242, 70, 192, 254, + 254, 254, 255, 128, 128, 128, 128, 0, 0, 0, 0, 71, 120, 70, 192, 234, 255, 254, 196, 71, 120, 70, 192, 234, 255, 254, + 85, 71, 120, 70, 192, 234, 255, 254, 139, 71, 120, 70, 192, 234, 255, 254, 139, 71, 120, 70, 192, 234, 255, 232, 227, + 71, 120, 70, 192, 234, 255, 232, 229, 71, 120, 70, 192, 234, 255, 254, 85, 71, 120, 70, 192, 234, 255, 232, 177, 229, + 159, 192, 0, 225, 47, 255, 28, 19, 112, 90, 177, 71, 120, 70, 192, 234, 255, 254, 240, 71, 120, 70, 192, 234, 255, 254, + 90, 71, 120, 70, 192, 234, 255, 254, 98, 71, 120, 70, 192, 234, 255, 232, 170, 71, 120, 70, 192, 234, 255, 254, 96, + 71, 120, 70, 192, 234, 255, 254, 102, 71, 120, 70, 192, 234, 255, 254, 150, 71, 120, 70, 192, 234, 255, 254, 106, 71, + 120, 70, 192, 234, 255, 254, 144, 71, 120, 70, 192, 234, 255, 254, 72, 71, 120, 70, 192, 234, 255, 255, 48, 71, 120, + 70, 192, 234, 255, 254, 136, 71, 120, 70, 192, 234, 255, 232, 187, 71, 120, 70, 192, 234, 255, 254, 130, 71, 120, 70, + 192, 234, 255, 254, 72, 71, 120, 70, 192, 234, 255, 254, 86, 71, 120, 70, 192, 234, 255, 254, 216, 71, 120, 70, 192, + 234, 255, 254, 72, 71, 120, 70, 192, 234, 255, 254, 86, 71, 120, 70, 192, 234, 255, 254, 72, 71, 120, 70, 192, 234, + 255, 254, 68, 71, 120, 70, 192, 234, 255, 232, 136, 71, 120, 70, 192, 234, 255, 254, 122, 0, 0, 0, 0, 73, 79, 83, 32, + 109, 111, 100, 117, 108, 101, 0, 0, 255, 255, 255, 255, 1, 0, 0, 0, 0, 0, 0, 1, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 229, 31, 240, 4, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 7, 161, + 32, 19, 114, 199, 16, 16, 16, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 87, 66, 70, 83, + 32, 110, 111, 116, 32, 101, 110, 111, 117, 103, 104, 32, 109, 101, 109, 111, 114, 121, 33, 10, 0, 47, 100, 101, 118, + 47, 117, 115, 98, 49, 50, 51, 0, 47, 100, 101, 118, 47, 117, 115, 98, 49, 50, 51, 47, 79, 70, 70, 0, 102, 105, 114, + 115, 116, 32, 114, 101, 97, 100, 32, 115, 101, 99, 116, 111, 114, 32, 40, 37, 105, 41, 32, 79, 75, 10, 0, 102, 105, + 114, 115, 116, 32, 114, 101, 97, 100, 32, 115, 101, 99, 116, 111, 114, 32, 40, 37, 105, 41, 32, 69, 82, 82, 79, 82, + 10, 0, 101, 104, 99, 105, 95, 105, 110, 116, 95, 119, 111, 114, 107, 105, 110, 103, 95, 99, 97, 108, 108, 98, 97, 99, + 107, 95, 112, 97, 114, 116, 49, 44, 32, 116, 105, 109, 101, 111, 117, 116, 58, 32, 37, 117, 10, 0, 117, 114, 98, 32, + 114, 101, 116, 118, 97, 108, 58, 32, 37, 105, 10, 0, 117, 110, 97, 98, 108, 101, 32, 116, 111, 32, 103, 101, 116, 32, + 100, 101, 118, 105, 99, 101, 32, 100, 101, 115, 99, 46, 46, 46, 10, 0, 103, 101, 116, 116, 105, 110, 103, 32, 85, 83, + 66, 95, 82, 69, 81, 95, 71, 69, 84, 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, 32, 45, 32, 114, 101, 115, 101, 116, 10, + 0, 101, 114, 114, 111, 114, 32, 103, 101, 116, 116, 105, 110, 103, 32, 85, 83, 66, 95, 82, 69, 81, 95, 71, 69, 84, 68, + 69, 83, 67, 82, 73, 80, 84, 79, 82, 10, 0, 103, 101, 116, 116, 105, 110, 103, 32, 85, 83, 66, 95, 82, 69, 81, 95, 71, + 69, 84, 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, 32, 45, 32, 97, 100, 113, 117, 105, 114, 101, 32, 45, 32, 114, 101, + 115, 101, 116, 10, 0, 85, 83, 66, 95, 82, 69, 81, 95, 71, 69, 84, 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, 32, 111, 107, + 10, 0, 116, 114, 121, 105, 110, 103, 32, 85, 83, 66, 95, 82, 69, 81, 95, 83, 69, 84, 65, 68, 68, 82, 69, 83, 83, 58, + 32, 37, 100, 10, 0, 117, 110, 97, 98, 108, 101, 32, 116, 111, 32, 115, 101, 116, 32, 100, 101, 118, 105, 99, 101, 32, + 97, 100, 100, 114, 58, 32, 37, 100, 10, 0, 85, 83, 66, 95, 82, 69, 81, 95, 83, 69, 84, 65, 68, 68, 82, 69, 83, 83, 32, + 111, 107, 58, 32, 37, 100, 10, 0, 101, 114, 114, 111, 114, 32, 99, 104, 101, 99, 107, 105, 110, 103, 32, 85, 83, 66, + 95, 82, 69, 81, 95, 71, 69, 84, 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, 10, 0, 111, 107, 32, 99, 104, 101, 99, 107, + 105, 110, 103, 32, 85, 83, 66, 95, 82, 69, 81, 95, 71, 69, 84, 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, 10, 0, 105, 110, + 105, 116, 32, 111, 107, 10, 0, 98, 117, 102, 102, 101, 114, 32, 61, 61, 32, 78, 85, 76, 76, 32, 40, 110, 111, 32, 109, + 101, 109, 41, 10, 0, 95, 95, 117, 115, 98, 95, 103, 101, 116, 100, 101, 115, 99, 32, 101, 114, 114, 111, 114, 32, 85, + 83, 66, 95, 68, 84, 95, 68, 69, 86, 73, 67, 69, 58, 32, 114, 101, 116, 114, 121, 10, 0, 95, 95, 117, 115, 98, 95, 103, + 101, 116, 100, 101, 115, 99, 32, 101, 114, 114, 111, 114, 32, 85, 83, 66, 95, 68, 84, 95, 68, 69, 86, 73, 67, 69, 10, + 0, 117, 100, 100, 45, 62, 99, 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 115, 32, 61, 61, 32, 78, 85, + 76, 76, 32, 40, 110, 111, 32, 109, 101, 109, 41, 10, 0, 95, 95, 117, 115, 98, 95, 103, 101, 116, 100, 101, 115, 99, + 32, 101, 114, 114, 111, 114, 32, 85, 83, 66, 95, 68, 84, 95, 67, 79, 78, 70, 73, 71, 58, 32, 114, 101, 116, 114, 121, + 10, 0, 117, 99, 100, 45, 62, 105, 110, 116, 101, 114, 102, 97, 99, 101, 115, 32, 61, 61, 32, 78, 85, 76, 76, 32, 40, + 110, 111, 32, 109, 101, 109, 41, 10, 0, 117, 105, 100, 45, 62, 101, 110, 100, 112, 111, 105, 110, 116, 115, 32, 61, + 61, 32, 78, 85, 76, 76, 32, 40, 110, 111, 32, 109, 101, 109, 41, 10, 0, 117, 105, 100, 45, 62, 101, 120, 116, 114, 97, + 32, 61, 61, 32, 78, 85, 76, 76, 32, 40, 110, 111, 32, 109, 101, 109, 41, 10, 0, 10, 113, 116, 100, 32, 101, 114, 114, + 111, 114, 33, 58, 0, 32, 66, 65, 66, 66, 76, 69, 0, 32, 32, 109, 105, 115, 115, 101, 100, 32, 109, 105, 99, 114, 111, + 32, 102, 114, 97, 109, 101, 0, 32, 32, 100, 97, 116, 97, 98, 117, 102, 102, 101, 114, 32, 101, 114, 114, 111, 114, 0, + 32, 119, 114, 111, 110, 103, 32, 97, 99, 107, 0, 32, 116, 111, 111, 32, 109, 97, 110, 121, 32, 101, 114, 114, 111, 114, + 115, 0, 105, 110, 116, 101, 114, 114, 117, 112, 116, 95, 99, 97, 108, 108, 98, 97, 99, 107, 95, 104, 97, 110, 100, 32, + 83, 84, 83, 95, 73, 78, 84, 10, 0, 105, 110, 116, 101, 114, 114, 117, 112, 116, 95, 99, 97, 108, 108, 98, 97, 99, 107, + 95, 104, 97, 110, 100, 32, 83, 84, 83, 95, 80, 67, 68, 10, 0, 117, 115, 98, 115, 116, 111, 114, 97, 103, 101, 32, 114, + 101, 115, 101, 116, 58, 32, 66, 85, 76, 75, 32, 82, 69, 83, 69, 84, 32, 37, 105, 10, 0, 117, 115, 98, 115, 116, 111, + 114, 97, 103, 101, 32, 114, 101, 115, 101, 116, 58, 32, 99, 108, 101, 97, 114, 104, 97, 108, 116, 32, 105, 110, 32, + 114, 101, 116, 32, 37, 105, 10, 0, 117, 115, 98, 115, 116, 111, 114, 97, 103, 101, 32, 114, 101, 115, 101, 116, 58, + 32, 99, 108, 101, 97, 114, 104, 97, 108, 116, 32, 111, 117, 116, 32, 114, 101, 116, 32, 37, 105, 10, 0, 117, 115, 98, + 115, 116, 111, 114, 97, 103, 101, 32, 114, 101, 115, 101, 116, 58, 32, 85, 83, 66, 95, 71, 101, 116, 67, 111, 110, 102, + 105, 103, 117, 114, 97, 116, 105, 111, 110, 32, 114, 101, 116, 32, 37, 105, 10, 0, 114, 101, 115, 101, 116, 32, 111, + 107, 10, 0, 95, 95, 115, 101, 110, 100, 95, 99, 98, 119, 32, 114, 101, 116, 32, 37, 105, 10, 0, 95, 95, 85, 83, 66, + 95, 66, 108, 107, 77, 115, 103, 84, 105, 109, 101, 111, 117, 116, 32, 37, 105, 10, 0, 95, 95, 114, 101, 97, 100, 95, + 99, 115, 119, 32, 37, 105, 10, 0, 32, 32, 32, 32, 83, 67, 83, 73, 95, 84, 69, 83, 84, 95, 85, 78, 73, 84, 95, 82, 69, + 65, 68, 89, 32, 114, 101, 116, 32, 37, 105, 10, 0, 32, 32, 32, 32, 83, 67, 83, 73, 95, 82, 69, 81, 85, 69, 83, 84, 95, + 83, 69, 78, 83, 69, 32, 114, 101, 116, 32, 37, 105, 10, 0, 32, 32, 32, 32, 83, 67, 83, 73, 95, 82, 69, 81, 85, 69, 83, + 84, 95, 83, 69, 78, 83, 69, 32, 115, 116, 97, 116, 117, 115, 32, 37, 120, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, + 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 85, 83, 66, 95, 71, 101, 116, 68, 101, 115, 99, 114, 105, 112, 116, + 111, 114, 115, 32, 37, 105, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, + 100, 101, 118, 105, 99, 101, 32, 99, 104, 97, 110, 103, 101, 100, 33, 33, 33, 10, 0, 85, 83, 66, 83, 116, 111, 114, + 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 117, 99, 100, 32, 37, 105, 32, 80, 111, 119, 101, 114, 32, 37, + 105, 32, 109, 65, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 105, 110, + 116, 101, 114, 102, 97, 99, 101, 32, 115, 117, 98, 99, 108, 97, 115, 115, 32, 37, 105, 32, 97, 116, 97, 95, 112, 114, + 111, 116, 32, 37, 105, 32, 10, 0, 73, 110, 32, 80, 111, 105, 110, 116, 58, 32, 37, 105, 10, 0, 79, 117, 116, 32, 80, + 111, 105, 110, 116, 58, 32, 37, 105, 10, 0, 101, 112, 95, 105, 110, 32, 37, 120, 32, 101, 112, 95, 111, 117, 116, 32, + 37, 120, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 99, 97, 110, 110, + 111, 116, 32, 102, 105, 110, 100, 32, 97, 110, 121, 32, 105, 110, 116, 101, 114, 102, 97, 99, 101, 33, 33, 33, 10, 0, + 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 99, 111, 110, 102, 58, 32, 37, 120, + 32, 97, 108, 116, 73, 110, 116, 101, 114, 102, 97, 99, 101, 58, 32, 37, 120, 10, 0, 85, 83, 66, 95, 71, 101, 116, 67, + 111, 110, 102, 105, 103, 117, 114, 97, 116, 105, 111, 110, 40, 41, 32, 69, 114, 114, 111, 114, 46, 32, 67, 111, 110, + 116, 105, 110, 117, 101, 46, 10, 0, 65, 99, 116, 117, 97, 108, 32, 99, 111, 110, 102, 58, 32, 37, 120, 32, 32, 32, 110, + 101, 120, 116, 32, 99, 111, 110, 102, 58, 32, 37, 120, 10, 0, 85, 83, 66, 95, 83, 101, 116, 67, 111, 110, 102, 105, + 103, 117, 114, 97, 116, 105, 111, 110, 40, 41, 32, 69, 114, 114, 111, 114, 10, 0, 85, 83, 66, 95, 83, 101, 116, 65, + 108, 116, 101, 114, 110, 97, 116, 105, 118, 101, 73, 110, 116, 101, 114, 102, 97, 99, 101, 40, 41, 32, 69, 114, 114, + 111, 114, 46, 32, 67, 111, 110, 116, 105, 110, 117, 101, 10, 0, 85, 83, 66, 95, 83, 101, 116, 67, 111, 110, 102, 105, + 103, 117, 114, 97, 116, 105, 111, 110, 40, 41, 32, 38, 32, 85, 83, 66, 95, 83, 101, 116, 65, 108, 116, 101, 114, 110, + 97, 116, 105, 118, 101, 73, 110, 116, 101, 114, 102, 97, 99, 101, 40, 41, 32, 79, 75, 10, 0, 71, 101, 116, 95, 77, 97, + 120, 95, 76, 117, 110, 40, 41, 58, 32, 101, 114, 114, 44, 32, 100, 101, 102, 97, 117, 108, 116, 32, 109, 97, 120, 95, + 108, 117, 110, 61, 56, 10, 0, 71, 101, 116, 95, 77, 97, 120, 95, 76, 117, 110, 40, 41, 58, 32, 79, 75, 58, 32, 37, 105, + 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, 32, 116, 114, 121, 95, 115, 116, + 97, 116, 117, 115, 32, 37, 105, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 79, 112, 101, 110, 40, 41, 58, + 32, 79, 75, 44, 32, 114, 101, 116, 117, 114, 110, 32, 48, 10, 0, 32, 32, 32, 32, 115, 116, 97, 114, 116, 95, 115, 116, + 111, 112, 32, 99, 109, 100, 32, 114, 101, 116, 32, 37, 105, 10, 0, 32, 32, 32, 32, 73, 110, 113, 117, 105, 114, 121, + 32, 114, 101, 116, 32, 37, 105, 10, 0, 32, 32, 32, 32, 68, 101, 118, 105, 99, 101, 32, 84, 121, 112, 101, 58, 32, 37, + 120, 10, 0, 32, 32, 32, 32, 82, 101, 97, 100, 67, 97, 112, 97, 99, 105, 116, 121, 32, 114, 101, 116, 32, 37, 105, 32, + 32, 115, 101, 99, 116, 111, 114, 95, 115, 105, 122, 101, 58, 32, 37, 117, 32, 32, 115, 101, 99, 116, 111, 114, 115, + 58, 32, 37, 117, 10, 0, 70, 97, 115, 116, 32, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 77, 111, 117, 110, 116, + 76, 85, 78, 32, 37, 105, 35, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 77, 111, 117, 110, 116, 76, 85, + 78, 58, 32, 114, 101, 116, 32, 37, 105, 10, 0, 85, 83, 66, 83, 84, 79, 82, 65, 71, 69, 95, 71, 69, 84, 95, 77, 65, 88, + 95, 76, 85, 78, 32, 114, 101, 116, 32, 37, 105, 32, 109, 97, 120, 108, 117, 110, 32, 37, 105, 10, 0, 85, 83, 66, 83, + 116, 111, 114, 97, 103, 101, 95, 77, 111, 117, 110, 116, 76, 85, 78, 32, 102, 97, 105, 108, 33, 33, 33, 10, 0, 10, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 10, 82, 111, 100, 114, 105, 101, + 115, 32, 101, 104, 99, 109, 111, 100, 117, 108, 101, 32, 49, 46, 48, 10, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, + 95, 73, 110, 105, 116, 40, 41, 10, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 10, 10, 0, 68, 101, 118, 105, 99, 101, 32, 105, 115, 32, 97, 32, 100, 118, 100, 10, 0, 68, 101, 118, 105, 99, 101, + 32, 105, 115, 32, 97, 32, 117, 115, 98, 104, 100, 10, 0, 85, 83, 66, 83, 116, 111, 114, 97, 103, 101, 95, 73, 110, 105, + 116, 40, 41, 32, 79, 107, 10, 0, 69, 114, 114, 111, 114, 32, 82, 101, 97, 100, 105, 110, 103, 32, 115, 101, 99, 116, + 111, 114, 32, 48, 10, 0, 79, 75, 32, 82, 101, 97, 100, 105, 110, 103, 32, 115, 101, 99, 116, 111, 114, 32, 48, 10, 0, + 85, 110, 112, 108, 117, 103, 58, 32, 114, 101, 115, 101, 116, 32, 37, 105, 32, 115, 116, 97, 116, 117, 115, 32, 37, + 120, 10, 0, 102, 97, 115, 116, 95, 114, 101, 109, 111, 117, 110, 116, 32, 75, 79, 32, 114, 101, 116, 32, 37, 105, 10, + 0, 85, 83, 66, 32, 65, 108, 108, 111, 99, 58, 32, 110, 111, 116, 32, 101, 110, 111, 117, 103, 104, 32, 109, 101, 109, + 111, 114, 121, 33, 10, 0, 119, 98, 102, 115, 32, 101, 114, 114, 111, 114, 32, 0, 98, 97, 100, 32, 109, 97, 103, 105, + 99, 0, 104, 100, 32, 115, 101, 99, 116, 111, 114, 32, 115, 105, 122, 101, 32, 100, 111, 101, 115, 110, 39, 116, 32, + 109, 97, 116, 99, 104, 0, 104, 100, 32, 110, 117, 109, 32, 115, 101, 99, 116, 111, 114, 32, 100, 111, 101, 115, 110, + 39, 116, 32, 109, 97, 116, 99, 104, 0, 78, 84, 70, 83, 0, 70, 65, 84, 0, 116, 114, 121, 105, 110, 103, 32, 116, 111, + 32, 99, 108, 111, 115, 101, 32, 119, 98, 102, 115, 32, 119, 104, 105, 108, 101, 32, 100, 105, 115, 99, 115, 32, 115, + 116, 105, 108, 108, 32, 111, 112, 101, 110, 0, 97, 108, 108, 111, 99, 97, 116, 105, 110, 103, 32, 109, 101, 109, 111, + 114, 121, 0 }; diff --git a/source/mload/modules/ehcmodule_5.h b/source/mload/modules/ehcmodule_5.h index cefdbf57..85b0d531 100644 --- a/source/mload/modules/ehcmodule_5.h +++ b/source/mload/modules/ehcmodule_5.h @@ -1,3 +1,3 @@ -#define size_ehcmodule_5 26234 +#define size_ehcmodule_5 27134 -extern unsigned char ehcmodule_5[26234]; +extern unsigned char ehcmodule_5[27134]; diff --git a/source/network/FileDownloader.cpp b/source/network/FileDownloader.cpp index bc3b6732..4c679abd 100644 --- a/source/network/FileDownloader.cpp +++ b/source/network/FileDownloader.cpp @@ -94,7 +94,7 @@ int DownloadFileToMem(const char *url, u8 **inbuffer, u32 *size) return -5; } - int blocksize = 10*1024; + int blocksize = 4*1024; u8 * buffer = (u8 *) malloc(filesize); if(!buffer) @@ -104,11 +104,20 @@ int DownloadFileToMem(const char *url, u8 **inbuffer, u32 *size) return -6; } + ProgressCancelEnable(true); + StartProgress(tr("Downloading file..."), 0, filename, true, true); + int done = 0; while(done < filesize) { - ShowProgress(tr("Downloading file..."), 0, filename, (f32) done, (f32) filesize, true, true); + if(ProgressCanceled()) + { + done = PROGRESS_CANCELED; + break; + } + + ShowProgress(done, filesize); if(blocksize > filesize - done) blocksize = filesize - done; @@ -118,11 +127,9 @@ int DownloadFileToMem(const char *url, u8 **inbuffer, u32 *size) if(read < 0) { - free(buffer); - ProgressStop(); - net_close(connection); + done = -8; ShowError(tr("Transfer failed")); - return -8; + break; } else if(!read) break; @@ -131,12 +138,19 @@ int DownloadFileToMem(const char *url, u8 **inbuffer, u32 *size) } ProgressStop(); + ProgressCancelEnable(false); net_close(connection); + if(done < 0) + { + free(buffer); + return done; + } + *inbuffer = buffer; *size = filesize; - return 1; + return done; } /**************************************************************************** @@ -214,7 +228,7 @@ int DownloadFileToPath(const char *orig_url, const char *dest, bool UseFilename) return -5; } - int blocksize = 10*1024; + int blocksize = 4*1024; u8 *buffer = (u8 *) malloc(blocksize); if(!buffer) @@ -252,11 +266,20 @@ int DownloadFileToPath(const char *orig_url, const char *dest, bool UseFilename) return -7; } + ProgressCancelEnable(true); + StartProgress(tr("Downloading file..."), 0, filename, true, true); + int done = 0; while(done < filesize) { - ShowProgress(tr("Downloading file..."), 0, filename, (f32) done, (f32) filesize, true, true); + if(ProgressCanceled()) + { + done = PROGRESS_CANCELED; + break; + } + + ShowProgress(done, filesize); if(blocksize > filesize - done) blocksize = filesize - done; @@ -265,12 +288,9 @@ int DownloadFileToPath(const char *orig_url, const char *dest, bool UseFilename) if(read < 0) { - free(buffer); - ProgressStop(); - net_close(connection); - fclose(file); + done = -8; ShowError(tr("Transfer failed")); - return -8; + break; } else if(!read) break; @@ -284,6 +304,8 @@ int DownloadFileToPath(const char *orig_url, const char *dest, bool UseFilename) ProgressStop(); net_close(connection); fclose(file); + ProgressStop(); + ProgressCancelEnable(false); return done; } diff --git a/source/network/networkops.cpp b/source/network/networkops.cpp index 1cedd0b2..7a293fb9 100644 --- a/source/network/networkops.cpp +++ b/source/network/networkops.cpp @@ -566,8 +566,8 @@ static void * networkinitcallback(void *arg) { while (1) { - - if (!checkincomming && networkHalt) LWP_SuspendThread(networkthread); + if (!checkincomming && networkHalt) + LWP_SuspendThread(networkthread); Initialize_Network(); @@ -582,6 +582,8 @@ static void * networkinitcallback(void *arg) } if (checkincomming) NetworkWait(); + + usleep(100000); } return NULL; } diff --git a/source/patches/bca.c b/source/patches/bca.c index a061b23f..fbe87ec4 100644 --- a/source/patches/bca.c +++ b/source/patches/bca.c @@ -12,7 +12,7 @@ u32 do_bca_code(const char *BCAFilepath, u8 *gameid) { - if (!BCAFilepath) return 0; + if (!BCAFilepath || !gameid) return 0; if (IOS_GetVersion() == 222 || IOS_GetVersion() == 223) { @@ -22,26 +22,19 @@ u32 do_bca_code(const char *BCAFilepath, u8 *gameid) memset(filepath, 0, 150); u8 bcaCode[64] ATTRIBUTE_ALIGN( 32 ); - sprintf(filepath, "%s%6s", BCAFilepath, gameid); - filepath[strlen(BCAFilepath) + 6] = '.'; - filepath[strlen(BCAFilepath) + 7] = 'b'; - filepath[strlen(BCAFilepath) + 8] = 'c'; - filepath[strlen(BCAFilepath) + 9] = 'a'; + // Attempt to open the BCA file using the full game ID. + snprintf(filepath, sizeof(filepath), "%s%.6s.bca", BCAFilepath, gameid); fp = fopen(filepath, "rb"); if (!fp) { - memset(filepath, 0, 150); - sprintf(filepath, "%s%3s", BCAFilepath, gameid + 1); - filepath[strlen(BCAFilepath) + 3] = '.'; - filepath[strlen(BCAFilepath) + 4] = 'b'; - filepath[strlen(BCAFilepath) + 5] = 'c'; - filepath[strlen(BCAFilepath) + 6] = 'a'; - fp = fopen(filepath, "rb"); + // Not found. Try again without the system code or company code. + snprintf(filepath, sizeof(filepath), "%s%.3s.bca", BCAFilepath, gameid+1); + fp = fopen(filepath, "rb"); if (!fp) { - // Set default bcaCode + // Not found. Use the default BCA. memset(bcaCode, 0, 64); bcaCode[0x33] = 1; } @@ -49,6 +42,7 @@ u32 do_bca_code(const char *BCAFilepath, u8 *gameid) if (fp) { + // BCA file opened. u32 ret = 0; fseek(fp, 0, SEEK_END); @@ -63,7 +57,8 @@ u32 do_bca_code(const char *BCAFilepath, u8 *gameid) if (ret != 64) { - // Set default bcaCode + // Error reading the BCA file. + // Use the default BCA. memset(bcaCode, 0, 64); bcaCode[0x33] = 1; } diff --git a/source/patches/gamepatches.c b/source/patches/gamepatches.c index 94be989a..7a3c16b2 100644 --- a/source/patches/gamepatches.c +++ b/source/patches/gamepatches.c @@ -248,7 +248,7 @@ static GXRModeObj* vmodes[] = static GXRModeObj* PAL2NTSC[] = { &TVMpal480IntDf, &TVNtsc480IntDf, &TVPal264Ds, &TVNtsc240Ds, &TVPal264DsAa, &TVNtsc240DsAa, &TVPal264Int, &TVNtsc240Int, &TVPal264IntAa, &TVNtsc240IntAa, &TVPal524IntAa, &TVNtsc480IntAa, - &TVPal528Int, &TVNtsc480IntAa, &TVPal528IntDf, &TVNtsc480IntDf, &TVPal574IntDfScale, &TVNtsc480IntDf, + &TVPal528Int, &TVNtsc480IntAa, &TVPal528IntDf, &TVNtsc480IntDf, &TVPal576IntDfScale, &TVNtsc480IntDf, &TVEurgb60Hz240Ds, &TVNtsc240Ds, &TVEurgb60Hz240DsAa, &TVNtsc240DsAa, &TVEurgb60Hz240Int, &TVNtsc240Int, &TVEurgb60Hz240IntAa, &TVNtsc240IntAa, &TVEurgb60Hz480Int, &TVNtsc480IntAa, &TVEurgb60Hz480IntDf, &TVNtsc480IntDf, &TVEurgb60Hz480IntAa, &TVNtsc480IntAa, &TVEurgb60Hz480Prog, &TVNtsc480Prog, diff --git a/source/prompts/BannerWindow.cpp b/source/prompts/BannerWindow.cpp new file mode 100644 index 00000000..216a2a52 --- /dev/null +++ b/source/prompts/BannerWindow.cpp @@ -0,0 +1,592 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include +#include "BannerWindow.hpp" +#include "GUI/GuiBannerGrid.h" +#include "banner/BannerAsync.h" +#include "banner/CustomBanner.h" +#include "banner/OpeningBNR.hpp" +#include "settings/CSettings.h" +#include "settings/CGameStatistics.h" +#include "settings/menus/GameSettingsMenu.hpp" +#include "SystemMenu/SystemMenuResources.h" +#include "prompts/GameWindow.hpp" +#include "themes/CTheme.h" +#include "language/gettext.h" +#include "menu/menus.h" +#include "utils/tools.h" + +// Load only once +BannerFrame BannerWindow::bannerFrame; + +BannerWindow::BannerWindow(GameBrowseMenu *m, struct discHdr *header) + : GuiWindow(screenwidth, screenheight) + , browserMenu(m) + , MaxAnimSteps(Settings.BannerZoomDuration) +{ + ScreenProps.x = screenwidth; + ScreenProps.y = screenheight; + + f32 xOffset = Settings.BannerProjectionOffsetX; + f32 yOffset = Settings.BannerProjectionOffsetY; + + guMtxIdentity(modelview); + guMtxTransApply (modelview, modelview, xOffset, yOffset, 0.0F); + + memcpy(&originalProjection, &FSProjection2D, sizeof(Mtx44)); + + returnVal = -1; + gameSelected = 0; + gameSound = NULL; + dvdheader = NULL; + reducedVol = false; + + if(!bannerFrame.IsLoaded()) + bannerFrame.Load(U8Archive(SystemMenuResources::Instance()->GetChanTtlAsh(), + SystemMenuResources::Instance()->GetChanTtlAshSize())); + + AnimStep = 0; + AnimPosX = 0.5f * (ScreenProps.x - fIconWidth); + AnimPosY = 0.5f * (ScreenProps.y - fIconHeight); + AnimZoomIn = true; + AnimationRunning = false; + + int gameIdx; + + //! get the game index to this header + for(gameIdx = 0; gameIdx < gameList.size(); ++gameIdx) + { + if(gameList[gameIdx] == header) + { + gameSelected = gameIdx; + break; + } + } + + //! Set dvd header if the header does not match any of the list games + if(gameIdx == gameList.size()) + dvdheader = header; + + GuiBannerGrid *bannerBrowser = dynamic_cast(browserMenu->GetGameBrowser()); + if(bannerBrowser) + bannerBrowser->GetIconCoordinates(gameSelected, &AnimPosX, &AnimPosY); + + gameBanner = new Banner; + + imgLeft = Resources::GetImageData("startgame_arrow_left.png"); + imgRight = Resources::GetImageData("startgame_arrow_right.png"); + + trigA = new GuiTrigger; + trigA->SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); + trigB = new GuiTrigger; + trigB->SetButtonOnlyTrigger(-1, WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B, PAD_BUTTON_B); + trigL = new GuiTrigger; + trigL->SetButtonOnlyTrigger(-1, WPAD_BUTTON_LEFT | WPAD_CLASSIC_BUTTON_LEFT, PAD_BUTTON_LEFT); + trigR = new GuiTrigger; + trigR->SetButtonOnlyTrigger(-1, WPAD_BUTTON_RIGHT | WPAD_CLASSIC_BUTTON_RIGHT, PAD_BUTTON_RIGHT); + trigPlus = new GuiTrigger; + trigPlus->SetButtonOnlyTrigger(-1, WPAD_BUTTON_PLUS | WPAD_CLASSIC_BUTTON_PLUS, PAD_TRIGGER_R); + trigMinus = new GuiTrigger; + trigMinus->SetButtonOnlyTrigger(-1, WPAD_BUTTON_MINUS | WPAD_CLASSIC_BUTTON_MINUS, PAD_TRIGGER_L); + + playcntTxt = new GuiText((char*) NULL, 18, thColor("r=0 g=0 b=0 a=255 - banner window playcount text color")); + playcntTxt->SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); + playcntTxt->SetPosition(thInt("0 - banner window play count pos x"), + thInt("215 - banner window play count pos y") - Settings.AdjustOverscanY / 2); + + settingsBtn = new GuiButton(215, 75); + settingsBtn->SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); + settingsBtn->SetSoundOver(btnSoundOver); + settingsBtn->SetSoundClick(btnSoundClick2); + settingsBtn->SetPosition(-120, 175); + settingsBtn->SetTrigger(trigA); + + startBtn = new GuiButton(215, 75); + startBtn->SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); + startBtn->SetSoundOver(btnSoundOver); + startBtn->SetSoundClick(btnSoundClick2); + startBtn->SetPosition(110, 175); + startBtn->SetTrigger(trigA); + + backBtn = new GuiButton(215, 75); + backBtn->SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); + backBtn->SetSoundOver(btnSoundOver); + backBtn->SetSoundClick(btnSoundClick2); + backBtn->SetPosition(-screenwidth, -screenheight); // set out of screen + backBtn->SetTrigger(0, trigA); + backBtn->SetTrigger(1, trigB); + + btnLeftImg = new GuiImage(imgLeft); + if (Settings.wsprompt) btnLeftImg->SetWidescreen(Settings.widescreen); + btnLeft = new GuiButton(btnLeftImg, btnLeftImg, ALIGN_LEFT, ALIGN_MIDDLE, 20, -50, trigA, btnSoundOver, btnSoundClick2, 1); + btnLeft->SetTrigger(trigL); + btnLeft->SetTrigger(trigMinus); + + btnRightImg = new GuiImage(imgRight); + if (Settings.wsprompt) btnRightImg->SetWidescreen(Settings.widescreen); + btnRight = new GuiButton(btnRightImg, btnRightImg, ALIGN_RIGHT, ALIGN_MIDDLE, -20, -50, trigA, btnSoundOver, btnSoundClick2, 1); + btnRight->SetTrigger(trigR); + btnRight->SetTrigger(trigPlus); + + if (Settings.ShowPlayCount) + Append(playcntTxt); + Append(backBtn); + + if (!dvdheader) //stuff we don't show if it is a DVD mounted + { + Append(btnLeft); + Append(btnRight); + } + + bannerFrame.SetButtonBText(tr("Start")); + + //check if unlocked + if (Settings.godmode || !(Settings.ParentalBlocks & BLOCK_GAME_SETTINGS)) + { + bannerFrame.SetButtonAText(tr("Settings")); + Append(settingsBtn); + } + else + { + bannerFrame.SetButtonAText(tr("Back")); + backBtn->SetPosition(-120, 175); + } + + Append(startBtn); //! Appending the disc on top of all + + ChangeGame(false); +} + +BannerWindow::~BannerWindow() +{ + if(parentElement) + ((GuiWindow * ) parentElement)->Remove(this); + + RemoveAll(); + + delete trigA; + delete trigB; + delete trigL; + delete trigR; + delete trigPlus; + delete trigMinus; + + delete imgLeft; + delete imgRight; + + delete btnLeftImg; + delete btnRightImg; + + delete playcntTxt; + + delete startBtn; + delete backBtn; + delete settingsBtn; + delete btnLeft; + delete btnRight; + + if(gameSound) gameSound->Stop(); + delete gameSound; + bgMusic->SetVolume(Settings.volume); + + delete gameBanner; + + memcpy(&FSProjection2D, &originalProjection, sizeof(Mtx44)); + ResumeGui(); +} + +void BannerWindow::ChangeGame(bool playsound) +{ + struct discHdr * header = (dvdheader ? dvdheader : gameList[gameSelected]); + + //! Stop thread because all the extract functions are not thread safe + //! Let it finish the current loading though + BannerAsync::HaltThread(); + + Banner *newBanner = NULL; + // continue playing sound during loading process + if((header->type == TYPE_GAME_GC_IMG) || (header->type == TYPE_GAME_GC_DISC) || (header->type == TYPE_GAME_GC_EXTRACTED)) + { + //! try cache file first and if that fails create the default one + if(BNRInstance::Instance()->Load(header) && BNRInstance::Instance()->Get() != NULL) + newBanner = new Banner; + else + newBanner = BNRInstance::Instance()->CreateGCBanner(header); + } + else { + BNRInstance::Instance()->Load(header); + newBanner = new Banner; + } + + //! remove game sound + if (gameSound) + { + gameSound->Stop(); + delete gameSound; + gameSound = NULL; + } + + playcntTxt->SetTextf("%s: %i", tr( "Play Count" ), GameStatistics.GetPlayCount(header)); + + HaltGui(); + + // set the new banner + delete gameBanner; + gameBanner = newBanner; + + // Do not load stuff on game cube games + if(BNRInstance::Instance()->Get() != NULL) + gameBanner->LoadBanner(BNRInstance::Instance()->Get(), BNRInstance::Instance()->GetSize()); + + if (Settings.gamesoundvolume != 0) + { + if( (BNRInstance::Instance()->Get() != NULL) + && gameBanner->LoadSound(BNRInstance::Instance()->Get(), BNRInstance::Instance()->GetSize()) + && gameBanner->getSound()) + { + gameSound = new GuiSound(gameBanner->getSound(), gameBanner->getSoundSize(), Settings.gamesoundvolume); + } + else if((header->type == TYPE_GAME_GC_IMG) || (header->type == TYPE_GAME_GC_DISC) || (header->type == TYPE_GAME_GC_EXTRACTED)) + { + //! on game cube load the default sound + gameSound = new GuiSound(Resources::GetFile("gc_banner.ogg"), Resources::GetFileSize("gc_banner.ogg"), Settings.gamesoundvolume); + } + if(gameSound) + { + bgMusic->SetVolume(0); + if (Settings.gamesound == 2) + gameSound->SetLoop(1); + // If the game is changed within window play sound here directly + if(playsound) + gameSound->Play(); + } + } + + //! Resume all threads + BannerAsync::ResumeThread(); + ResumeGui(); +} + +int BannerWindow::Run() +{ + int choice = -1; + + while(choice == -1) + { + usleep(50000); + + if (shutdown) //for power button + Sys_Shutdown(); + else if (reset) //for reset button + Sys_Reboot(); + + choice = MainLoop(); + } + + return choice; +} + +int BannerWindow::MainLoop() +{ + if (startBtn->GetState() == STATE_CLICKED) + { + // If this function was left then the game start was canceled + GameWindow::BootGame(dvdheader ? dvdheader : gameList[gameSelected]); + // If it returns from that function reload the list + gameList.FilterList(); + startBtn->ResetState(); + } + + else if (backBtn->GetState() == STATE_CLICKED) //back + { + GuiBannerGrid *bannerBrowser = dynamic_cast(browserMenu->GetGameBrowser()); + if(bannerBrowser) + { + bannerBrowser->GetIconCoordinates(gameSelected, &AnimPosX, &AnimPosY); + bannerBrowser->SetPage(gameSelected / 12); + } + // activate rendering again + browserMenu->GetGameBrowser()->SetVisible(true); + + // finish on going animations first + while(AnimStep < MaxAnimSteps) + usleep(1000); + + // set new animation for zoom out + AnimZoomIn = false; + AnimStep = 0; + + // finish animation + while(AnimStep < MaxAnimSteps) + usleep(1000); + + mainWindow->SetState(STATE_DEFAULT); + returnVal = 0; + } + + else if(settingsBtn->GetState() == STATE_CLICKED) //settings + { + this->SetState(STATE_DISABLED); + + wiilight(0); + int settret = GameSettingsMenu::Execute(browserMenu, dvdheader ? dvdheader : gameList[gameSelected]); + + this->SetState(STATE_DEFAULT); + settingsBtn->ResetState(); + + // Show the window again or return to browser on uninstall + if (settret == MENU_DISCLIST) + returnVal = 1; + } + + else if (btnRight->GetState() == STATE_CLICKED) //next game + { + if(Settings.xflip == XFLIP_YES) + { + gameSelected = (gameSelected - 1 + gameList.size()) % gameList.size(); + ChangeGame(true); + } + else if(Settings.xflip == XFLIP_SYSMENU) + { + gameSelected = (gameSelected + 1) % gameList.size(); + ChangeGame(true); + } + else if(Settings.xflip == XFLIP_WTF) + { + gameSelected = (gameSelected - 1 + gameList.size()) % gameList.size(); + ChangeGame(true); + } + else + { + gameSelected = (gameSelected + 1) % gameList.size(); + ChangeGame(true); + } + + btnRight->ResetState(); + } + + else if (btnLeft->GetState() == STATE_CLICKED) //previous game + { + if(Settings.xflip == XFLIP_YES) + { + gameSelected = (gameSelected + 1) % gameList.size(); + ChangeGame(true); + } + else if(Settings.xflip == XFLIP_SYSMENU) + { + gameSelected = (gameSelected - 1 + gameList.size()) % gameList.size(); + ChangeGame(true); + } + else if(Settings.xflip == XFLIP_WTF) + { + gameSelected = (gameSelected + 1) % gameList.size(); + ChangeGame(true); + } + else + { + gameSelected = (gameSelected - 1 + gameList.size()) % gameList.size(); + ChangeGame(true); + } + + btnLeft->ResetState(); + } + + if (reducedVol) + { + if (gameSound) + { + if (Settings.gamesound == 1 && !gameSound->IsPlaying()) + { + bgMusic->SetVolume(Settings.volume); + reducedVol = false; + } + } + else + { + bgMusic->SetVolume(Settings.volume); + reducedVol = false; + } + } + + return returnVal; +} + +void BannerWindow::Animate(void) +{ + // animation is on going + if(AnimStep < MaxAnimSteps) + { + AnimationRunning = true; + AnimStep++; + + // zoom in animation + if(AnimZoomIn) { + BGAlpha = std::min(255.f * AnimStep * 2.f / MaxAnimSteps, 255.f); + if(AnimStep < 0.4f * MaxAnimSteps) + BannerAlpha = 0; + else + BannerAlpha = std::min(255.f * (AnimStep - 0.4f * MaxAnimSteps) / (0.6f * MaxAnimSteps), 255.f); + } + // zoom out animation + else { + BGAlpha = std::min(255.f * (MaxAnimSteps-AnimStep) * 2.f / MaxAnimSteps, 255.f); + if((MaxAnimSteps - AnimStep) < 0.4f * MaxAnimSteps) + BannerAlpha = 0; + else + BannerAlpha = std::min(255.f * ((MaxAnimSteps - AnimStep) - 0.4f * MaxAnimSteps) / (0.6f * MaxAnimSteps), 255.f); + } + + float curAnimStep = AnimZoomIn ? ((float)(MaxAnimSteps - AnimStep)/(float)MaxAnimSteps) : ((float)AnimStep/(float)MaxAnimSteps); + + float stepx1 = Settings.AdjustOverscanX - AnimPosX; + float stepy1 = Settings.AdjustOverscanY - AnimPosY; + float stepx2 = (screenwidth - 1 - Settings.AdjustOverscanX) - (AnimPosX + fIconWidth); + float stepy2 = (screenheight - 1 - Settings.AdjustOverscanY) - (AnimPosY + fIconHeight); + + float top = AnimPosY + stepy1 * curAnimStep; + float bottom = AnimPosY + fIconHeight + stepy2 * curAnimStep; + float left = AnimPosX + stepx1 * curAnimStep; + float right = AnimPosX + fIconWidth + stepx2 * curAnimStep; + + // set main projection of all GUI stuff if we are using the banner browser + if(dynamic_cast(browserMenu->GetGameBrowser()) != NULL) + guOrtho(FSProjection2D, top, bottom, left, right, 0, 10000); + + float xDiff = 0.5f * Settings.BannerProjectionWidth; + float yDiff = 0.5f * Settings.BannerProjectionHeight; + + // this just looks better for banner/icon ratio + float iconWidth = fIconWidth - 20; + float iconHeight = fIconHeight - 20; + + f32 ratioX = xDiff * 2.f / iconWidth; + f32 ratioY = yDiff * 2.f / iconHeight; + stepx1 = ((ScreenProps.x * 0.5f - xDiff) - (AnimPosX + 0.5f * fIconWidth - 0.5f * iconWidth)) * ratioX; + stepx2 = ((ScreenProps.x * 0.5f + xDiff) - (AnimPosX + 0.5f * fIconWidth + 0.5f * iconWidth)) * ratioX; + stepy1 = ((ScreenProps.y * 0.5f - yDiff) - (AnimPosY + 0.5f * fIconHeight - 0.5f * iconHeight)) * ratioY; + stepy2 = ((ScreenProps.y * 0.5f + yDiff) - (AnimPosY + 0.5f * fIconHeight + 0.5f * iconHeight)) * ratioY; + + //! This works good for banners + top = (ScreenProps.y * 0.5f - yDiff) + stepy1 * curAnimStep; + bottom = (ScreenProps.y * 0.5f + yDiff) + stepy2 * curAnimStep; + left = (ScreenProps.x * 0.5f - xDiff) + stepx1 * curAnimStep; + right = (ScreenProps.x * 0.5f + xDiff) + stepx2 * curAnimStep; + + // set banner projection + guOrtho(projection,top, bottom, left, right,-100,10000); + } + // last animation step + else if(AnimationRunning) + { + // set back original projection and stop animation/render of the browser (save some CPU ;P) + memcpy(&FSProjection2D, &originalProjection, sizeof(Mtx44)); + browserMenu->GetGameBrowser()->SetVisible(false); + AnimationRunning = false; + } +} + +void BannerWindow::Draw(void) +{ + bool btnAGrow = (settingsBtn->GetState() == STATE_SELECTED || backBtn->GetState() == STATE_SELECTED); + bannerFrame.SetButtonAGrow(btnAGrow); + bannerFrame.SetButtonBGrow(startBtn->GetState() == STATE_SELECTED); + + //! Start playing banner sound after last animation frame if animation after zoom is enabled + //! or on first frame if during zoom is enable + if( AnimZoomIn && gameSound && (((Settings.BannerAnimStart == BANNER_START_ON_ZOOM) && AnimStep == 0) + || ((Settings.BannerAnimStart == BANNER_START_AFTER_ZOOM) && ((AnimStep + 1) == MaxAnimSteps)))) + { + reducedVol = true; + gameSound->Play(); + } + + // Run window animation + Animate(); + + // draw a black background image first + Menu_DrawRectangle(0.0f, 0.0f, ScreenProps.x, ScreenProps.y, (GXColor) {0, 0, 0, BGAlpha}, true); + + // no banner alpha means its the start of the animation + if(BannerAlpha == 0) + return; + + // cut the unneeded crap + Mtx mv1, mv2, mv3; + guMtxIdentity (mv2); + guMtxIdentity (mv3); + guMtxScaleApply(modelview,mv1, 1.f, -1.f, 1.f); + guMtxTransApply(mv1,mv1, 0.5f * ScreenProps.x, 0.5f * ScreenProps.y, 0.f); + guMtxTransApply(mv2,mv2, -0.5f * fBannerWidth, 0.5f * fBannerHeight, 0.f); + guMtxTransApply(mv3,mv3, 0.5f * fBannerWidth, -0.5f * fBannerHeight, 0.f); + guMtxConcat (mv1, mv2, mv2); + guMtxConcat (mv1, mv3, mv3); + + f32 viewportv[6]; + f32 projectionv[7]; + + GX_GetViewportv(viewportv, vmode); + GX_GetProjectionv(projectionv, projection, GX_ORTHOGRAPHIC); + + guVector vecTL; + guVector vecBR; + GX_Project(0.0f, 0.0f, 0.0f, mv2, projectionv, viewportv, &vecTL.x, &vecTL.y, &vecTL.z); + GX_Project(0.0f, 0.0f, 0.0f, mv3, projectionv, viewportv, &vecBR.x, &vecBR.y, &vecBR.z); + + // round up scissor box offset and round down the size + u32 scissorX = (u32)(0.5f + std::max(vecTL.x, 0.0f)); + u32 scissorY = (u32)(0.5f + std::max(vecTL.y, 0.0f)); + u32 scissorW = (u32)std::max(vecBR.x - vecTL.x, 0.0f); + u32 scissorH = (u32)std::max(vecBR.y - vecTL.y, 0.0f); + + GX_SetScissor( scissorX, scissorY, scissorW, scissorH ); + + // load projection matrix + GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); + + if(gameBanner->getBanner()) + { + gameBanner->getBanner()->Render(modelview, ScreenProps, Settings.widescreen, BannerAlpha); + + // advance only when animation isnt running on certain options + if(Settings.BannerAnimStart != BANNER_START_AFTER_ZOOM || !AnimationRunning) + { + gameBanner->getBanner()->AdvanceFrame(); + + // skip every 6th frame on PAL50 since all banners are 60 Hz + if(Settings.PAL50 && (frameCount % 6 == 0)) { + gameBanner->getBanner()->AdvanceFrame(); + } + } + } + + // render big frame and animate button over effects + bannerFrame.Render(modelview, ScreenProps, Settings.widescreen, BannerAlpha); + bannerFrame.AdvanceFrame(); + + // Setup GX + ReSetup_GX(); + + if(AnimationRunning) { + // remove scissors again as we draw the background layout too + GX_SetScissor(0, 0, vmode->fbWidth, vmode->efbHeight); + + // only render gui stuff when animation is done + return; + } + + GuiWindow::Draw(); +} diff --git a/source/prompts/BannerWindow.hpp b/source/prompts/BannerWindow.hpp new file mode 100644 index 00000000..b0977024 --- /dev/null +++ b/source/prompts/BannerWindow.hpp @@ -0,0 +1,99 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef BANNERWINDOW_HPP_ +#define BANNERWINDOW_HPP_ + +#include "GUI/gui.h" +#include "GUI/gui_diskcover.h" +#include "menu/GameBrowseMenu.hpp" +#include "usbloader/disc.h" +#include "SystemMenu/SystemMenuResources.h" +#include "banner/Banner.h" + +#define FAVORITE_STARS 5 + +class BannerWindow : public GuiWindow +{ + public: + BannerWindow(GameBrowseMenu *m, struct discHdr *header); + virtual ~BannerWindow(); + int Run(); + int GetSelectedGame() { return gameSelected; } + void Draw(void); + void Test(); + protected: + int MainLoop(); + void Animate(void); + void ChangeGame(bool playsound); + + static const float fBannerWidth = 608.f; + static const float fBannerHeight = 448.f; + static const float fIconWidth = 128.f; + static const float fIconHeight = 96.f; + + static BannerFrame bannerFrame; + + bool reducedVol; + int returnVal; + int gameSelected; + GameBrowseMenu *browserMenu; + struct discHdr *dvdheader; + + const int MaxAnimSteps; + + int AnimStep; + float AnimPosX, AnimPosY; + float fAnimScale; + bool AnimZoomIn; + bool AnimationRunning; + bool oldAnimationRunning; + + u8 BGAlpha; + u8 BannerAlpha; + + Mtx modelview; + Mtx44 projection; + Mtx44 originalProjection; + Vec2f ScreenProps; + + Banner *gameBanner; + + GuiTrigger * trigA; + GuiTrigger * trigB; + GuiTrigger * trigL; + GuiTrigger * trigR; + GuiTrigger * trigPlus; + GuiTrigger * trigMinus; + + GuiImageData * imgLeft; + GuiImageData * imgRight; + + GuiImage * btnLeftImg; + GuiImage * btnRightImg; + + GuiText * playcntTxt; + + GuiButton * startBtn; + GuiButton * backBtn; + GuiButton * settingsBtn; + GuiButton * btnLeft; + GuiButton * btnRight; + + GuiSound * gameSound; +}; + +#endif diff --git a/source/prompts/CategoryPrompt.cpp b/source/prompts/CategoryPrompt.cpp index 86466b53..77418d6b 100644 --- a/source/prompts/CategoryPrompt.cpp +++ b/source/prompts/CategoryPrompt.cpp @@ -196,7 +196,8 @@ int CategoryPrompt::Show() { while(backBtn->GetState() != STATE_CLICKED) { - usleep(100); + usleep(10000); + if (shutdown) Sys_Shutdown(); else if (reset) diff --git a/source/prompts/CheckboxBrowserMenu.cpp b/source/prompts/CheckboxBrowserMenu.cpp new file mode 100644 index 00000000..66f4866d --- /dev/null +++ b/source/prompts/CheckboxBrowserMenu.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include +#include "CheckboxBrowserMenu.h" +#include "GameCube/GCGames.h" +#include "settings/CSettings.h" +#include "settings/GameTitles.h" +#include "language/gettext.h" +#include "themes/gettheme.h" +#include "themes/Resources.h" +#include "menu/menus.h" + +CheckboxBrowserMenu::CheckboxBrowserMenu(void) + : GuiWindow(0, 0) +{ + changed = false; + + trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); + trigB.SetButtonOnlyTrigger(-1, WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B, PAD_BUTTON_B); + trigHome.SetButtonOnlyTrigger(-1, WPAD_BUTTON_HOME | WPAD_CLASSIC_BUTTON_HOME, PAD_BUTTON_START); + + btnOutline = Resources::GetImageData("button_dialogue_box.png"); + bgImgData = Resources::GetImageData("categoryprompt.png"); + browserImgData = Resources::GetImageData("bg_options.png"); + + bgImg = new GuiImage(bgImgData); + Append(bgImg); + + width = bgImg->GetWidth(); + height = bgImg->GetHeight()+btnOutline->GetHeight()*0.9f; + + titleTxt = new GuiText("", 30, thColor("r=0 g=0 b=0 a=255 - check box browser prompt title text color")); + titleTxt->SetAlignment(thAlign("center - check box browser prompt title text align hor"), thAlign("top - check box browser prompt title text align ver")); + titleTxt->SetPosition(thInt("0 - check box browser prompt title text pos x"), thInt("10 - check box browser prompt title text pos y")); + Append(titleTxt); + + browserImg = new GuiImage(browserImgData); + browser = new GuiCheckboxBrowser(browserImg->GetWidth(), browserImg->GetHeight()); + browser->SetImage(browserImg); + browser->SetAlignment(thAlign("center - check box browser prompt browser align hor"), thAlign("top - check box browser prompt browser align ver")); + browser->SetPosition(thInt("0 - check box browser prompt browser pos x"), thInt("45 - check box browser prompt browser pos y")); + browser->checkBoxClicked.connect(this, &CheckboxBrowserMenu::OnCheckboxClick); + Append(browser); + + homeButton = new GuiButton(0, 0); + homeButton->SetTrigger(&trigHome); + Append(homeButton); + + button1Img = new GuiImage(btnOutline); + button1Img->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + button1Img->SetScale(0.9f); + button1Txt = new GuiText(tr("Install"), 22, thColor("r=0 g=0 b=0 a=255 - prompt windows button text color")); + button1 = new GuiButton(button1Img->GetWidth()*0.9f, button1Img->GetHeight()*0.9f); + button1->SetImage(button1Img); + button1->SetLabel(button1Txt); + button1->SetAlignment(thAlign("center - check box browser prompt install button align hor"), thAlign("bottom - check box browser prompt install button align ver")); + button1->SetPosition(thInt("-110 - check box browser prompt install button pos x"), thInt("0 - check box browser prompt install button pos y")); + button1->SetSoundOver(btnSoundOver); + button1->SetSoundClick(btnSoundClick); + button1->SetTrigger(&trigA); + button1->SetEffectGrow(); + Append(button1); + + backImg = new GuiImage(btnOutline); + backImg->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + backImg->SetScale(0.9f); + backTxt = new GuiText(tr("Back"), 22, thColor("r=0 g=0 b=0 a=255 - prompt windows button text color")); + backBtn = new GuiButton(backImg->GetWidth()*0.9f, backImg->GetHeight()*0.9f); + backBtn->SetImage(backImg); + backBtn->SetLabel(backTxt); + backBtn->SetAlignment(thAlign("center - check box browser prompt back button align hor"), thAlign("bottom - check box browser prompt back button align ver")); + backBtn->SetPosition(thInt("110 - check box browser prompt back button pos x"), thInt("0 - check box browser prompt back button pos y")); + backBtn->SetSoundOver(btnSoundOver); + backBtn->SetSoundClick(btnSoundClick); + backBtn->SetTrigger(&trigA); + backBtn->SetTrigger(&trigB); + backBtn->SetEffectGrow(); + Append(backBtn); +} + +CheckboxBrowserMenu::~CheckboxBrowserMenu() +{ + RemoveAll(); + delete browser; + + delete btnOutline; + delete bgImgData; + delete bgImg; + delete browserImgData; + delete browserImg; + delete backImg; + delete button1Img; + + delete backBtn; + delete homeButton; + delete button1; + + delete titleTxt; + delete backTxt; + delete button1Txt; +} + diff --git a/source/prompts/CheckboxBrowserMenu.h b/source/prompts/CheckboxBrowserMenu.h new file mode 100644 index 00000000..6287b996 --- /dev/null +++ b/source/prompts/CheckboxBrowserMenu.h @@ -0,0 +1,57 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef CHECKBOXBROWSERMENU_H +#define CHECKBOXBROWSERMENU_H + +#include "GUI/gui_checkboxbrowser.hpp" + +class CheckboxBrowserMenu : public GuiWindow, public sigslot::has_slots<> +{ + public: + CheckboxBrowserMenu(); + virtual ~CheckboxBrowserMenu(); + private: + void markChanged() { changed = true; } + void OnCheckboxClick(GuiCheckbox *, int) { markChanged(); } + protected: + bool changed; + + GuiCheckboxBrowser *browser; + + GuiImageData *bgImgData; + GuiImageData *browserImgData; + GuiImageData *btnOutline; + + GuiImage *browserImg; + GuiImage *bgImg; + GuiImage *backImg; + GuiImage *button1Img; + + GuiButton *backBtn; + GuiButton *homeButton; + GuiButton *button1; + + GuiText *titleTxt; + GuiText *backTxt; + GuiText *button1Txt; + + GuiTrigger trigA; + GuiTrigger trigB; + GuiTrigger trigHome; +}; + +#endif diff --git a/source/prompts/DiscBrowser.cpp b/source/prompts/DiscBrowser.cpp index 29011f11..70d8166a 100644 --- a/source/prompts/DiscBrowser.cpp +++ b/source/prompts/DiscBrowser.cpp @@ -106,7 +106,7 @@ int DiscBrowse(const char * GameID, char * alternatedname, int alternatedname_si trigB.SetButtonOnlyTrigger(-1, WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B, PAD_BUTTON_B); GuiText titleTxt(GameTitles.GetTitle(GameID), 28, ( GXColor ) {0, 0, 0, 255}); - titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetAlignment(ALIGN_CENTER, ALIGN_TOP); titleTxt.SetPosition(12, 40); titleTxt.SetMaxWidth(356, SCROLL_HORIZONTAL); @@ -131,7 +131,7 @@ int DiscBrowse(const char * GameID, char * alternatedname, int alternatedname_si GuiOptionBrowser optionBrowser3(396, 280, &options, "bg_options_settings.png"); optionBrowser3.SetPosition(0, 90); - optionBrowser3.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + optionBrowser3.SetAlignment(ALIGN_CENTER, ALIGN_TOP); HaltGui(); GuiWindow w(screenwidth, screenheight); diff --git a/source/prompts/GCDeleteMenu.cpp b/source/prompts/GCDeleteMenu.cpp new file mode 100644 index 00000000..75192805 --- /dev/null +++ b/source/prompts/GCDeleteMenu.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include +#include "GCDeleteMenu.h" +#include "GameCube/GCGames.h" +#include "settings/CSettings.h" +#include "settings/GameTitles.h" +#include "language/gettext.h" +#include "themes/gettheme.h" +#include "themes/Resources.h" +#include "menu/menus.h" + +GCDeleteMenu::GCDeleteMenu(void) +{ + titleTxt->SetText(tr("Game Cube Games Delete")); + button1Txt->SetText(tr("Delete")); + browserRefresh(); +} + +GCDeleteMenu::~GCDeleteMenu() +{ + for(u32 i = 0; i < sizeTxtList.size(); ++i) + delete sizeTxtList[i]; +} + +void GCDeleteMenu::browserRefresh(void) +{ + browser->Clear(); + browser->SetMaxTextWidth(200); + + for(u32 i = 0; i < sizeTxtList.size(); ++i) + delete sizeTxtList[i]; + + sizeTxtList.resize(GCGames::Instance()->GetSDHeaders().size()); + + for(u32 i = 0; i < GCGames::Instance()->GetSDHeaders().size(); ++i) + { + const struct discHdr *gcDiscHdr = &GCGames::Instance()->GetSDHeaders().at(i); + float fSize = GCGames::Instance()->GetGameSize((char*)gcDiscHdr->id); + + browser->AddEntrie(GameTitles.GetTitle(gcDiscHdr)); + + char size_text[20]; + snprintf(size_text, sizeof(size_text), "(%.2fGB)", fSize); + + sizeTxtList[i] = new GuiText(size_text, 18, thColor("r=0 g=0 b=0 a=255 - checkbox browser text color")); + sizeTxtList[i]->SetAlignment(ALIGN_RIGHT, ALIGN_TOP); + sizeTxtList[i]->SetPosition(-40, 5); + browser->GetCheckbox(i)->SetLabel(sizeTxtList[i]); + } +} + +int GCDeleteMenu::Show() +{ + while(true) + { + usleep(10000); + + if (shutdown) + Sys_Shutdown(); + else if (reset) + Sys_Reboot(); + + else if(backBtn->GetState() == STATE_CLICKED) + { + if(!changed || !WindowPrompt(tr("Do you want to discart changes?"), 0, tr("Yes"), tr("No"))) + break; + + backBtn->ResetState(); + } + + else if (homeButton->GetState() == STATE_CLICKED) + { + gprintf("\thomeButton clicked\n"); + WindowExitPrompt(); + mainWindow->SetState(STATE_DISABLED); + SetState(STATE_DEFAULT); + homeButton->ResetState(); + } + + else if(button1->GetState() == STATE_CLICKED) + { + if(!changed) + { + WindowPrompt(tr("Error:"), tr("Nothing selected to delete."), tr("OK")); + } + else if(WindowPrompt(tr("Attention!"), tr("Are you really sure you want to delete all selected games from the SD card?"), tr("Yes"), tr("Cancel"))) + { + DeleteSelectedGames(); + break; + } + + button1->ResetState(); + } + } + + return 0; +} + +void GCDeleteMenu::DeleteSelectedGames(void) +{ + for(u32 i = 0; i < GCGames::Instance()->GetSDHeaders().size(); ++i) + { + if(browser->IsChecked(i)) + { + GCGames::Instance()->RemoveSDGame((char*)GCGames::Instance()->GetSDHeaders().at(i).id); + } + } + GCGames::Instance()->LoadAllGames(); +} diff --git a/source/prompts/GCDeleteMenu.h b/source/prompts/GCDeleteMenu.h new file mode 100644 index 00000000..ee80cebc --- /dev/null +++ b/source/prompts/GCDeleteMenu.h @@ -0,0 +1,34 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef GCDELETEMENU_H +#define GCDELETEMENU_H + +#include "CheckboxBrowserMenu.h" + +class GCDeleteMenu : public CheckboxBrowserMenu +{ + public: + GCDeleteMenu(); + virtual ~GCDeleteMenu(); + int Show(); + private: + void DeleteSelectedGames(void); + void browserRefresh(); + std::vector sizeTxtList; +}; + +#endif // GCDELETEMENU_H diff --git a/source/prompts/GCMultiDiscMenu.cpp b/source/prompts/GCMultiDiscMenu.cpp new file mode 100644 index 00000000..a578a73b --- /dev/null +++ b/source/prompts/GCMultiDiscMenu.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include +#include "GCMultiDiscMenu.h" +#include "GameCube/GCGames.h" +#include "settings/CSettings.h" +#include "settings/GameTitles.h" +#include "language/gettext.h" +#include "themes/gettheme.h" +#include "themes/Resources.h" +#include "menu/menus.h" + +GCMultiDiscMenu::GCMultiDiscMenu(const std::vector &List) + : gcGameList(List) +{ + titleTxt->SetText(tr("Game Cube Install Menu")); + button1Txt->SetText(tr("Install")); + browserRefresh(); +} + +void GCMultiDiscMenu::browserRefresh(void) +{ + browser->Clear(); + + for(u32 i = 0; i < gcGameList.size(); ++i) + { + browser->AddEntrie(gcGameList[i].title); + } +} + +int GCMultiDiscMenu::ShowSelection() +{ + while(true) + { + usleep(10000); + + if (shutdown) + Sys_Shutdown(); + else if (reset) + Sys_Reboot(); + + else if(backBtn->GetState() == STATE_CLICKED) + { + if(!changed || !WindowPrompt(tr("Do you want to discart changes?"), 0, tr("Yes"), tr("No"))) + break; + + backBtn->ResetState(); + } + + else if (homeButton->GetState() == STATE_CLICKED) + { + gprintf("\thomeButton clicked\n"); + WindowExitPrompt(); + mainWindow->SetState(STATE_DISABLED); + SetState(STATE_DEFAULT); + homeButton->ResetState(); + } + + else if(button1->GetState() == STATE_CLICKED) + { + if(!changed) + { + WindowPrompt(tr("Error:"), tr("Nothing selected to install.")); + } + else if(WindowPrompt(tr("Do you want to install selected games?"), 0, tr("Yes"), tr("Cancel"))) + { + return 1; + } + + button1->ResetState(); + } + } + + return 0; +} + +std::vector GCMultiDiscMenu::GetSelectedGames(void) +{ + std::vector selectedGames; + + for(u32 i = 0; i < gcGameList.size(); ++i) + { + if(browser->IsChecked(i)) + { + selectedGames.push_back(i); + } + } + return selectedGames; +} diff --git a/source/prompts/GCMultiDiscMenu.h b/source/prompts/GCMultiDiscMenu.h new file mode 100644 index 00000000..f001e2c8 --- /dev/null +++ b/source/prompts/GCMultiDiscMenu.h @@ -0,0 +1,33 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef GCMULTIDISCMENU_H +#define GCMULTIDISCMENU_H + +#include "CheckboxBrowserMenu.h" + +class GCMultiDiscMenu : public CheckboxBrowserMenu +{ + public: + GCMultiDiscMenu(const std::vector &List); + int ShowSelection(); + std::vector GetSelectedGames(void); + private: + void browserRefresh(); + const std::vector &gcGameList; +}; + +#endif diff --git a/source/prompts/GameWindow.cpp b/source/prompts/GameWindow.cpp index 95b8b083..82c10253 100644 --- a/source/prompts/GameWindow.cpp +++ b/source/prompts/GameWindow.cpp @@ -29,20 +29,37 @@ #define IN 3 #define OUT 4 -GameWindow::GameWindow(int Selected, struct discHdr *dvd) +GameWindow::GameWindow(GameBrowseMenu *m, struct discHdr *header) : GuiWindow(472, 320) + , browserMenu(m) { returnVal = -1; - gameSelected = Selected; - dvdheader = dvd; + gameSelected = 0; + dvdheader = NULL; gameSound = NULL; diskImgData = NULL; diskImgData2 = NULL; hidden = false; reducedVol = false; - SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); SetPosition(0, -10); + int gameIdx; + + //! get the game index to this header + for(gameIdx = 0; gameIdx < gameList.size(); ++gameIdx) + { + if(gameList[gameIdx] == header) + { + gameSelected = gameIdx; + break; + } + } + + //! Set dvd header if the header does not match any of the list games + if(gameIdx == gameList.size()) + dvdheader = header; + dialogBox = Resources::GetImageData(Settings.widescreen ? "wdialogue_box_startgame.png" : "dialogue_box_startgame.png"); btnOutline = Resources::GetImageData("button_dialogue_box.png"); imgFavorite = Resources::GetImageData("favorite.png"); @@ -72,7 +89,7 @@ GameWindow::GameWindow(int Selected, struct discHdr *dvd) nameTxt->SetMaxWidth(350, SCROLL_HORIZONTAL); nameBtn = new GuiButton(120, 50); nameBtn->SetLabel(nameTxt); - nameBtn->SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + nameBtn->SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); nameBtn->SetPosition(0, -122); nameBtn->SetSoundOver(btnSoundOver); nameBtn->SetSoundClick(btnSoundClick2); @@ -94,17 +111,17 @@ GameWindow::GameWindow(int Selected, struct discHdr *dvd) diskImg->SetAngle(0); diskImg2 = new GuiDiskCover; diskImg2->SetWidescreen(Settings.widescreen); - diskImg2->SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + diskImg2->SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); diskImg2->SetPosition(0, -20); diskImg2->SetAngle(0); diskImg2->SetBeta(180); playcntTxt = new GuiText((char*) NULL, 18, thColor("r=0 g=0 b=0 a=255 - game window playcount text color")); - playcntTxt->SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + playcntTxt->SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); playcntTxt->SetPosition(-115, 45); gameBtn = new GuiButton(160, 160); - gameBtn->SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + gameBtn->SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); gameBtn->SetPosition(0, -20); gameBtn->SetImage(diskImg); gameBtn->SetSoundOver(btnSoundOver); @@ -122,7 +139,7 @@ GameWindow::GameWindow(int Selected, struct discHdr *dvd) backBtn = new GuiButton(backBtnImg, backBtnImg, 1, 5, 0, 0, trigA, btnSoundOver, btnSoundClick2, 1); backBtn->SetLabel(backBtnTxt); backBtn->SetTrigger(trigB); - backBtn->SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + backBtn->SetAlignment(ALIGN_CENTER, ALIGN_BOTTOM); backBtn->SetPosition(0, -40); settingsBtnTxt = new GuiText(tr( "Settings" ), 22, thColor("r=0 g=0 b=0 a=255 - prompt windows button text color")); @@ -141,7 +158,7 @@ GameWindow::GameWindow(int Selected, struct discHdr *dvd) FavoriteBtnImg[i] = new GuiImage; FavoriteBtnImg[i]->SetWidescreen(Settings.widescreen); FavoriteBtn[i] = new GuiButton(imgFavorite->GetWidth(), imgFavorite->GetHeight()); - FavoriteBtn[i]->SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + FavoriteBtn[i]->SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); FavoriteBtn[i]->SetPosition(xPos, -60); FavoriteBtn[i]->SetImage(FavoriteBtnImg[i]); FavoriteBtn[i]->SetSoundOver(btnSoundOver); @@ -167,7 +184,7 @@ GameWindow::GameWindow(int Selected, struct discHdr *dvd) detailsBtnTxt = new GuiText(tr( "Details" ), 22, thColor("r=0 g=0 b=0 a=255 - game window details button text color")); detailsBtnOverTxt = new GuiText(tr( "Details" ), 22, thColor("r=30 g=30 b=240 a=255 - game window details button over text color")); detailsBtn = new GuiButton(detailsBtnTxt->GetTextWidth(), 25); - detailsBtn->SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + detailsBtn->SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); detailsBtn->SetPosition(120, 45); detailsBtn->SetLabel(detailsBtnTxt); detailsBtn->SetLabelOver(detailsBtnOverTxt); @@ -283,6 +300,8 @@ void GameWindow::LoadGameSound(const struct discHdr * header) if (Settings.gamesoundvolume == 0) return; + BNRInstance::Instance()->Load(header); + if (gameSound) { gameSound->Stop(); @@ -290,20 +309,20 @@ void GameWindow::LoadGameSound(const struct discHdr * header) gameSound = NULL; } - u32 gameSoundDataLen; - const u8 *gameSoundData = NULL; - - if(header->tid != 0) - gameSoundData = BNRInstance::Instance()->GetBannerSound(header->tid, &gameSoundDataLen, - (header->type == TYPE_GAME_EMUNANDCHAN) ? Settings.NandEmuChanPath : ""); - else - gameSoundData = BNRInstance::Instance()->GetBannerSound(header->id, &gameSoundDataLen); - - if (gameSoundData) + if( (BNRInstance::Instance()->Get() != NULL) + && gameBanner.LoadSound(BNRInstance::Instance()->Get(), BNRInstance::Instance()->GetSize()) + && gameBanner.getSound()) + { + gameSound = new GuiSound(gameBanner.getSound(), gameBanner.getSoundSize(), Settings.gamesoundvolume); + } + else if((header->type == TYPE_GAME_GC_IMG) || (header->type == TYPE_GAME_GC_DISC) || (header->type == TYPE_GAME_GC_EXTRACTED)) + { + //! on game cube load the default sound + gameSound = new GuiSound(Resources::GetFile("gc_banner.ogg"), Resources::GetFileSize("gc_banner.ogg"), Settings.gamesoundvolume); + } + if(gameSound) { - gameSound = new GuiSound(gameSoundData, gameSoundDataLen, Settings.gamesoundvolume, true); bgMusic->SetVolume(0); - reducedVol = true; if (Settings.gamesound == 2) gameSound->SetLoop(1); gameSound->Play(); @@ -421,12 +440,16 @@ void GameWindow::SetWindowEffect(int direction, int in_out) while(parentElement && (this->GetEffect() > 0 || nameTxt->GetEffect() > 0 || diskImg->GetBetaRotateEffect())) { - usleep(100); + usleep(1000); } } void GameWindow::ChangeGame(int EffectDirection) { + //! Stop thread because all the extract functions are not thread safe + //! Let it finish the current loading though + BannerAsync::HaltThread(); + struct discHdr * header = (dvdheader ? dvdheader : gameList[gameSelected]); LoadGameSound(header); LoadDiscImage(header->id); @@ -472,6 +495,7 @@ void GameWindow::ChangeGame(int EffectDirection) EffectDirection = EffectDirection == LEFT ? RIGHT : EffectDirection == RIGHT ? LEFT : NONE; SetWindowEffect(EffectDirection, IN); + BannerAsync::ResumeThread(); } void GameWindow::Hide(void) @@ -501,7 +525,7 @@ int GameWindow::Run() while(choice == -1) { - usleep(1000); + usleep(50000); if (shutdown) //for power button Sys_Shutdown(); @@ -527,8 +551,13 @@ int GameWindow::MainLoop() // If this function was left then the game start was canceled GameWindow::BootGame(dvdheader ? dvdheader : gameList[gameSelected]); + // If it returns from that function reload the list + gameList.FilterList(); + // Show the window again Show(); + + gameBtn->ResetState(); } else if (backBtn->GetState() == STATE_CLICKED) //back diff --git a/source/prompts/GameWindow.hpp b/source/prompts/GameWindow.hpp index 200273ea..a5ed62a9 100644 --- a/source/prompts/GameWindow.hpp +++ b/source/prompts/GameWindow.hpp @@ -3,6 +3,7 @@ #include "GUI/gui.h" #include "GUI/gui_diskcover.h" +#include "banner/BannerAsync.h" #include "menu/GameBrowseMenu.hpp" #include "usbloader/disc.h" @@ -11,11 +12,10 @@ class GameWindow : public GuiWindow { public: - GameWindow(int GameSelected, struct discHdr *dvd); + GameWindow(GameBrowseMenu *m, struct discHdr *header); virtual ~GameWindow(); int Run(); - int GetSelectedGame() { return gameSelected; }; - void SetGameBrowseMenu(GameBrowseMenu *m) { browserMenu = m; }; + int GetSelectedGame() { return gameSelected; } static void BootGame(struct discHdr *header); protected: int MainLoop(); @@ -32,6 +32,7 @@ class GameWindow : public GuiWindow int gameSelected; GameBrowseMenu *browserMenu; struct discHdr *dvdheader; + Banner gameBanner; GuiTrigger * trigA; GuiTrigger * trigB; diff --git a/source/prompts/HomebrewPrompt.cpp b/source/prompts/HomebrewPrompt.cpp index 36c6ca6b..52c09789 100644 --- a/source/prompts/HomebrewPrompt.cpp +++ b/source/prompts/HomebrewPrompt.cpp @@ -34,7 +34,7 @@ HomebrewPrompt::HomebrewPrompt(const char *name, const char *coder, const char * GuiImageData * iconImgData, u64 filesize) : PromptWindow(NULL, NULL) { - SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); SetPosition(0, 6); whiteBox = Resources::GetImageData("bg_options.png"); @@ -48,11 +48,11 @@ HomebrewPrompt::HomebrewPrompt(const char *name, const char *coder, const char * whiteBoxImg = new GuiImage(whiteBox); whiteBoxImg->SetPosition(0, 110); - whiteBoxImg->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + whiteBoxImg->SetAlignment(ALIGN_CENTER, ALIGN_TOP); whiteBoxImg->SetSkew(0, 0, 0, 0, 0, -120, 0, -120); nameTxt = new GuiText(name, 30, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - nameTxt->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + nameTxt->SetAlignment(ALIGN_CENTER, ALIGN_TOP); nameTxt->SetPosition(0, -15); nameTxt->SetMaxWidth(430, SCROLL_HORIZONTAL); diff --git a/source/prompts/ProgressWindow.cpp b/source/prompts/ProgressWindow.cpp index 947f97a6..61805831 100644 --- a/source/prompts/ProgressWindow.cpp +++ b/source/prompts/ProgressWindow.cpp @@ -25,8 +25,8 @@ extern float gamesize; extern int install_abort_signal; /*** Variables used only in this file ***/ +static const float fFilterTime = 15.0f; // seconds static lwp_t progressthread = LWP_THREAD_NULL; -static mutex_t ProgressMutex = LWP_MUTEX_NULL; static bool CancelEnabled = false; static bool progressCanceled = false; static char progressTitle[75]; @@ -34,8 +34,11 @@ static char progressMsg1[75]; static char progressMsg2[75]; static Timer ProgressTimer; static int showProgress = 0; -static s64 progressDone = -1; -static s64 progressTotal = -1; +static s64 progressDone = 0.0f; +static s64 progressTotal = 0.0f; +static float fLastProgressDone = 0.0f; +static float fLastElapsedTime = 0.0f; +static float fSpeed = 0.0f; static bool showTime = false; static bool showSize = false; static bool changed = true; @@ -61,6 +64,10 @@ extern "C" void StartProgress(const char * title, const char * msg1, const char else progressMsg2[0] = '\0'; + fSpeed = 0.0f; + fLastElapsedTime = 0.0f; + fLastProgressDone = 0.0f; + progressTotal = progressDone = 0.0f; progressCanceled = false; showSize = swSize; showTime = swTime; @@ -78,63 +85,40 @@ extern "C" void StartProgress(const char * title, const char * msg1, const char ***************************************************************************/ extern "C" void ShowProgress(s64 done, s64 total) { - LWP_MutexLock(ProgressMutex); - progressDone = done; - progressTotal = total; + if (done > total) + done = total; + if(!done) { + fLastElapsedTime = 0.0f; + fLastProgressDone = 0.0f; + progressTotal = progressDone = 0.0f; ProgressTimer.reset(); LWP_ResumeThread(progressthread); showProgress = 1; } - changed = true; - LWP_MutexUnlock(ProgressMutex); -} - -void ShowProgress(const char *msg2, s64 done, s64 total) -{ - if (done > total) - done = total; - - LWP_MutexLock(ProgressMutex); if(total >= 0) { progressDone = done; progressTotal = total; } + changed = true; +} +void ShowProgress(const char *msg2, s64 done, s64 total) +{ if(msg2) strncpy(progressMsg2, msg2, sizeof(progressMsg2)-1); else progressMsg2[0] = '\0'; - if(!done) - { - ProgressTimer.reset(); - LWP_ResumeThread(progressthread); - showProgress = 1; - } - + ShowProgress(done, total); changedMessages = true; - changed = true; - - LWP_MutexUnlock(ProgressMutex); } void ShowProgress(const char *title, const char *msg1, const char *msg2, s64 done, s64 total, bool swSize, bool swTime) { - if (done > total) - done = total; - - LWP_MutexLock(ProgressMutex); - - if(total >= 0) - { - progressDone = done; - progressTotal = total; - } - if(title) strncpy(progressTitle, title, sizeof(progressTitle)-1); else @@ -153,17 +137,8 @@ void ShowProgress(const char *title, const char *msg1, const char *msg2, s64 don showSize = swSize; showTime = swTime; - if(!done) - { - ProgressTimer.reset(); - LWP_ResumeThread(progressthread); - showProgress = 1; - } - + ShowProgress(done, total); changedMessages = true; - changed = true; - - LWP_MutexUnlock(ProgressMutex); } /**************************************************************************** @@ -176,8 +151,6 @@ extern "C" void ProgressStop() progressTitle[0] = 0; progressMsg1[0] = 0; progressMsg2[0] = 0; - progressDone = -1; - progressTotal = -1; showTime = false; showSize = false; @@ -212,31 +185,42 @@ static void UpdateProgressValues(GuiImage *progressbarImg, GuiText *prTxt, GuiTe if(!changed) return; - LWP_MutexLock(ProgressMutex); changed = false; changedMessages = false; - s64 done = progressDone; - s64 total = progressTotal; - u32 speed = 0; - if(gamesize > 0.0f) + float done; + float total; + + if(gamesize > 0.0f && progressTotal > 0.0f) { - done = (s64) ((float) done / (float) total * gamesize); - total = (s64) gamesize; + done = ((float) progressDone / (float) progressTotal * gamesize); + total = gamesize; if(progressCanceled) install_abort_signal = 1; } + else + { + done = (float) progressDone; + total = (float) progressTotal; + } - //Calculate speed in KB/s - if (ProgressTimer.elapsed() > 0.0f) - speed = (u32) (done/ProgressTimer.elapsed()); + float fElapsedTime = ProgressTimer.elapsed(); + float fTimeDiff = fElapsedTime - fLastElapsedTime; + //! initialize filter to current value on 2nd run + if(fLastProgressDone == 0.0f && done > 0.0f && fTimeDiff > 0.0f) { + fSpeed = done / fTimeDiff; + } + else if(fTimeDiff > 0.0f) { + //! low pass filtering the speed + fSpeed += ((done - fLastProgressDone) - fSpeed * fTimeDiff) / fFilterTime; + //! store current elapse value for next cycle + fLastElapsedTime = fElapsedTime; + } - LWP_MutexUnlock(ProgressMutex); - - u32 TimeLeft = 0, h = 0, m = 0, s = 0; - if(speed > 0) - TimeLeft = (total-done)/speed; + s32 TimeLeft = 0, h = 0, m = 0, s = 0; + if(fSpeed > 0.0f) + TimeLeft = (total-done)/fSpeed; if(TimeLeft > 0) { @@ -245,17 +229,22 @@ static void UpdateProgressValues(GuiImage *progressbarImg, GuiText *prTxt, GuiTe s = TimeLeft % 60; } - float progressPercent = 100.0 * done / total; + float progressPercent; + if(total > 0.0f) + progressPercent = LIMIT(100.0f * done / total, 0.f, 100.f); + else + progressPercent = 100.0f; // total is 0 or below? i guess we are done... prTxt->SetTextf("%.2f", progressPercent); if (Settings.widescreen && Settings.wsprompt) - progressbarImg->SetSkew(0, 0, static_cast (progressbarImg->GetWidth() * progressPercent * Settings.WSFactor) - - progressbarImg->GetWidth(), 0, static_cast (progressbarImg->GetWidth() * progressPercent - * Settings.WSFactor) - progressbarImg->GetWidth(), 0, 0, 0); + progressbarImg->SetSkew(0, 0, (progressbarImg->GetWidth() * progressPercent + - progressbarImg->GetWidth()) * Settings.WSFactor, + 0, (progressbarImg->GetWidth() * progressPercent + - progressbarImg->GetWidth()) * Settings.WSFactor, 0, 0, 0); else - progressbarImg->SetSkew(0, 0, static_cast (progressbarImg->GetWidth() * progressPercent) - - progressbarImg->GetWidth(), 0, static_cast (progressbarImg->GetWidth() * progressPercent) + progressbarImg->SetSkew(0, 0, (progressbarImg->GetWidth() * progressPercent) + - progressbarImg->GetWidth(), 0, (progressbarImg->GetWidth() * progressPercent) - progressbarImg->GetWidth(), 0, 0, 0); if (showTime == true) @@ -272,8 +261,11 @@ static void UpdateProgressValues(GuiImage *progressbarImg, GuiText *prTxt, GuiTe else sizeTxt->SetTextf("%0.2fGB/%0.2fGB", done / GB_SIZE, total / GB_SIZE); - speedTxt->SetTextf("%dKB/s", (int) (speed/KB_SIZE)); + speedTxt->SetTextf("%dKB/s", (int) (fSpeed/KB_SIZE)); } + + //! store current done value for next cycle + fLastProgressDone = done; } /**************************************************************************** @@ -293,7 +285,7 @@ static void ProgressWindow(const char *title, const char *msg1, const char *msg2 const int ProgressPosY = 20; GuiWindow promptWindow(472, 320); - promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); @@ -330,16 +322,16 @@ static void ProgressWindow(const char *title, const char *msg1, const char *msg2 progressbarImg.SetPosition(35, ProgressPosY); GuiText titleTxt(title, 26, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetAlignment(ALIGN_CENTER, ALIGN_TOP); titleTxt.SetPosition(0, 50); GuiText msg1Txt(msg1, 22, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - msg1Txt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + msg1Txt.SetAlignment(ALIGN_CENTER, ALIGN_TOP); msg1Txt.SetPosition(0, 90); msg1Txt.SetMaxWidth(430, DOTTED); GuiText msg2Txt(msg2, 22, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - msg2Txt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + msg2Txt.SetAlignment(ALIGN_CENTER, ALIGN_TOP); msg2Txt.SetPosition(0, 125); msg2Txt.SetMaxWidth(430, DOTTED); @@ -365,7 +357,7 @@ static void ProgressWindow(const char *title, const char *msg1, const char *msg2 if ((Settings.wsprompt) && (Settings.widescreen)) /////////////adjust for widescreen { - progressbarOutlineImg.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + progressbarOutlineImg.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); progressbarOutlineImg.SetPosition(0, ProgressPosY); progressbarEmptyImg.SetPosition(80, ProgressPosY); progressbarEmptyImg.SetTileHorizontal(78); @@ -374,11 +366,8 @@ static void ProgressWindow(const char *title, const char *msg1, const char *msg2 msg2Txt.SetMaxWidth(380, DOTTED); timeTxt.SetPosition(250, -50); - timeTxt.SetFontSize(20); speedTxt.SetPosition(90, -74); - speedTxt.SetFontSize(20); sizeTxt.SetPosition(90, -50); - sizeTxt.SetFontSize(20); } GuiText cancelTxt(tr( "Cancel" ), 22, thColor("r=0 g=0 b=0 a=255 - prompt windows button text color")); @@ -426,7 +415,7 @@ static void ProgressWindow(const char *title, const char *msg1, const char *msg2 while (showProgress) { - usleep(50000); + usleep(100000); if (shutdown) Sys_Shutdown(); @@ -493,8 +482,7 @@ static void * ProgressThread(void *arg) ***************************************************************************/ void InitProgressThread() { - LWP_MutexInit(&ProgressMutex, true); - LWP_CreateThread(&progressthread, ProgressThread, NULL, NULL, 16384, 70); + LWP_CreateThread(&progressthread, ProgressThread, NULL, NULL, 16384, 60); memset(progressTitle, 0, sizeof(progressTitle)); memset(progressMsg1, 0, sizeof(progressMsg1)); @@ -510,6 +498,4 @@ void ExitProgressThread() { LWP_JoinThread(progressthread, NULL); progressthread = LWP_THREAD_NULL; - LWP_MutexUnlock(ProgressMutex); - LWP_MutexDestroy(ProgressMutex); } diff --git a/source/prompts/PromptWindow.cpp b/source/prompts/PromptWindow.cpp index 8dbb111e..304879f5 100644 --- a/source/prompts/PromptWindow.cpp +++ b/source/prompts/PromptWindow.cpp @@ -30,7 +30,7 @@ PromptWindow::PromptWindow(const char *title, const char *msg) : GuiWindow(472, 320) { - SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); SetPosition(0, -10); btnOutline = Resources::GetImageData("button_dialogue_box.png"); @@ -50,7 +50,7 @@ PromptWindow::PromptWindow(const char *title, const char *msg) Append(dialogBoxImg); titleTxt = new GuiText(title, 26, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - titleTxt->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt->SetAlignment(ALIGN_CENTER, ALIGN_TOP); titleTxt->SetPosition(0, 55); if(Settings.wsprompt && Settings.widescreen) titleTxt->SetMaxWidth(width-140, DOTTED); @@ -59,7 +59,7 @@ PromptWindow::PromptWindow(const char *title, const char *msg) Append(titleTxt); msgTxt = new GuiText(msg, 22, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - msgTxt->SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + msgTxt->SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); msgTxt->SetPosition(0, -40); if(Settings.wsprompt && Settings.widescreen) msgTxt->SetMaxWidth(width-140, WRAP); @@ -114,7 +114,7 @@ void PromptWindow::PositionButtons() default: break; case 1: - Button[0]->SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + Button[0]->SetAlignment(ALIGN_CENTER, ALIGN_BOTTOM); Button[0]->SetPosition(0, -55); break; case 2: @@ -128,7 +128,7 @@ void PromptWindow::PositionButtons() Button[0]->SetPosition(70, -120); Button[1]->SetAlignment(ALIGN_RIGHT, ALIGN_BOTTOM); Button[1]->SetPosition(-70, -120); - Button[2]->SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + Button[2]->SetAlignment(ALIGN_CENTER, ALIGN_BOTTOM); Button[2]->SetPosition(0, -55); break; case 4: @@ -150,7 +150,7 @@ void PromptWindow::PositionButtons() default: break; case 1: - Button[0]->SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + Button[0]->SetAlignment(ALIGN_CENTER, ALIGN_BOTTOM); Button[0]->SetPosition(0, -55); break; case 2: @@ -164,7 +164,7 @@ void PromptWindow::PositionButtons() Button[0]->SetPosition(50, -120); Button[1]->SetAlignment(ALIGN_RIGHT, ALIGN_BOTTOM); Button[1]->SetPosition(-50, -120); - Button[2]->SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + Button[2]->SetAlignment(ALIGN_CENTER, ALIGN_BOTTOM); Button[2]->SetPosition(0, -55); break; case 4: diff --git a/source/prompts/PromptWindows.cpp b/source/prompts/PromptWindows.cpp index a7b2ebca..f1356628 100644 --- a/source/prompts/PromptWindows.cpp +++ b/source/prompts/PromptWindows.cpp @@ -14,6 +14,7 @@ #include "usbloader/GameList.h" #include "language/gettext.h" #include "GUI/gui.h" +#include "GUI/gui_numpad.h" #include "GUI/gui_diskcover.h" #include "GUI/Text.hpp" #include "settings/CGameStatistics.h" @@ -90,16 +91,24 @@ int OnScreenNumpad(char * var, u32 maxlen) while (save == -1) { - VIDEO_WaitVSync(); + usleep(50000); if (okBtn.GetState() == STATE_CLICKED) save = 1; - else if (cancelBtn.GetState() == STATE_CLICKED) save = 0; + else if (cancelBtn.GetState() == STATE_CLICKED) + save = 0; } if (save == 1) { - snprintf(var, maxlen, "%s", numpad.kbtextstr); + snprintf(var, maxlen, "%s", numpad.GetText()); + + // convert all , to . characters + for(u32 i = 0; i < maxlen; ++i) + { + if(var[i] == ',') + var[i] = '.'; + } } HaltGui(); @@ -118,7 +127,6 @@ int OnScreenNumpad(char * var, u32 maxlen) ***************************************************************************/ int OnScreenKeyboard(char * var, u32 maxlen, int min, bool hide) { - int save = -1; gprintf("\nOnScreenKeyboard(%s, %i, %i) \n\tkeyset = %i", var, maxlen, min, Settings.keyset); @@ -163,11 +171,12 @@ int OnScreenKeyboard(char * var, u32 maxlen, int min, bool hide) while (save == -1) { - VIDEO_WaitVSync(); + usleep(50000); if (okBtn.GetState() == STATE_CLICKED) save = 1; - else if (cancelBtn.GetState() == STATE_CLICKED) save = 0; + else if (cancelBtn.GetState() == STATE_CLICKED) + save = 0; } if (save) @@ -204,14 +213,16 @@ void WindowCredits() bool exit = false; int i = 0; int y = 20; + float oldFontScale = Settings.FontScaleFactor; + Settings.FontScaleFactor = 1.0f; GuiWindow creditsWindow(screenwidth, screenheight); GuiWindow creditsWindowBox(580, 448); - creditsWindowBox.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + creditsWindowBox.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); GuiImageData creditsBox(Resources::GetFile("credits_bg.png"), Resources::GetFileSize("credits_bg.png")); GuiImage creditsBoxImg(&creditsBox); - creditsBoxImg.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + creditsBoxImg.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); creditsWindowBox.Append(&creditsBoxImg); GuiImageData star(Resources::GetFile("little_star.png"), Resources::GetFileSize("little_star.png")); @@ -220,12 +231,16 @@ void WindowCredits() starImg.SetAlignment(ALIGN_LEFT, ALIGN_TOP); starImg.SetPosition(505, 350); - const int numEntries = 25; + const int numEntries = 24; std::vector txt(numEntries); + const u8 *creditsFont = Resources::GetFile("font.ttf"); + u32 creditsFontSize = Resources::GetFileSize("font.ttf"); + txt[i] = new GuiText(tr( "Credits" ), 28, ( GXColor ) {255, 255, 255, 255}); - txt[i]->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + txt[i]->SetAlignment(ALIGN_CENTER, ALIGN_TOP); txt[i]->SetPosition(0, 12); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; char SvnRev[80]; @@ -243,28 +258,33 @@ void WindowCredits() txt[i] = new GuiText(SvnRev, 16, ( GXColor ) {255, 255, 255, 255}); txt[i]->SetAlignment(ALIGN_RIGHT, ALIGN_TOP); txt[i]->SetPosition(0, info ? y-10 : y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; txt[i] = new GuiText(IosInfo, 16, ( GXColor ) {255, 255, 255, 255}); txt[i]->SetAlignment(ALIGN_RIGHT, ALIGN_TOP); txt[i]->SetPosition(0, y+6); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 34; txt[i] = new GuiText("USB Loader GX", 24, ( GXColor ) {255, 255, 255, 255}); - txt[i]->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + txt[i]->SetAlignment(ALIGN_CENTER, ALIGN_TOP); txt[i]->SetPosition(0, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 24; txt[i] = new GuiText(tr( "Official Site:" ), 20, ( GXColor ) {255, 255, 255, 255}); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(10, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; txt[i] = new GuiText("http://code.google.com/p/usbloader-gui/", 20, ( GXColor ) {255, 255, 255, 255}); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(160, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 22; @@ -273,17 +293,20 @@ void WindowCredits() txt[i] = new GuiText(tr( "Coding:" )); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(10, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; txt[i] = new GuiText("Dimok / nIxx / giantpune / ardi"); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(160, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 20; txt[i] = new GuiText("hungyip84 / DrayX7 / lustar / r-win"); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(160, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 22; @@ -292,40 +315,40 @@ void WindowCredits() txt[i] = new GuiText(tr( "Design:" )); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(10, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; txt[i] = new GuiText("cyrex / NeoRame"); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(160, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 22; - txt[i] = new GuiText(tr( "Issue manager /" )); - txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); - txt[i]->SetPosition(10, y); - i++; - y += 20; - txt[i] = new GuiText(tr( "Main tester:" )); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(10, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; txt[i] = new GuiText("Cyan"); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(160, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 22; txt[i] = new GuiText(tr( "Big thanks to:" )); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(10, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; sprintf(text, "lustar %s", tr( "for GameTDB and hosting covers / disc images" )); txt[i] = new GuiText(text); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(160, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 20; @@ -333,6 +356,7 @@ void WindowCredits() txt[i] = new GuiText(text); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(160, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 20; @@ -340,6 +364,7 @@ void WindowCredits() txt[i] = new GuiText(text); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(160, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 20; @@ -347,12 +372,14 @@ void WindowCredits() txt[i] = new GuiText(text); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(160, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; - y += 22; + y += 24; txt[i] = new GuiText(tr( "Special thanks to:" )); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(10, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 20; @@ -360,6 +387,7 @@ void WindowCredits() txt[i] = new GuiText(text); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(10, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 20; @@ -367,6 +395,7 @@ void WindowCredits() txt[i] = new GuiText(text); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(10, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 20; @@ -374,6 +403,7 @@ void WindowCredits() txt[i] = new GuiText(text); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(10, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 20; @@ -381,6 +411,7 @@ void WindowCredits() txt[i] = new GuiText(text); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(10, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 20; @@ -388,6 +419,7 @@ void WindowCredits() txt[i] = new GuiText(text); txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(10, y); + txt[i]->SetFont(creditsFont, creditsFontSize); i++; y += 20; @@ -436,6 +468,7 @@ void WindowCredits() delete txt[i]; txt[i] = NULL; } + Settings.FontScaleFactor = oldFontScale; ResumeGui(); bgMusic->Resume(); @@ -647,7 +680,7 @@ int WindowExitPrompt() trigHome.SetButtonOnlyTrigger(-1, WPAD_BUTTON_HOME | WPAD_CLASSIC_BUTTON_HOME, 0); GuiText titleTxt(tr( "HOME Menu" ), 36, ( GXColor ) {255, 255, 255, 255}); - titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetAlignment(ALIGN_CENTER, ALIGN_TOP); titleTxt.SetPosition(-180, 40); titleTxt.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_IN, 50); @@ -661,7 +694,7 @@ int WindowExitPrompt() } GuiButton closeBtn(close.GetWidth(), close.GetHeight()); closeBtn.SetImage(&closeImg); - closeBtn.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + closeBtn.SetAlignment(ALIGN_CENTER, ALIGN_TOP); closeBtn.SetPosition(190, 30); closeBtn.SetLabel(&closeTxt); closeBtn.SetRumble(false); @@ -908,7 +941,7 @@ int DiscWait(const char *title, const char *msg, const char *btn1Label, const ch u32 cover = 0; GuiWindow promptWindow(472, 320); - promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); @@ -925,10 +958,10 @@ int DiscWait(const char *title, const char *msg, const char *btn1Label, const ch } GuiText titleTxt(title, 26, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetAlignment(ALIGN_CENTER, ALIGN_TOP); titleTxt.SetPosition(0, 60); GuiText msgTxt(msg, 22, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - msgTxt.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + msgTxt.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); msgTxt.SetPosition(0, -40); msgTxt.SetMaxWidth(430); @@ -948,7 +981,7 @@ int DiscWait(const char *title, const char *msg, const char *btn1Label, const ch } else { - btn1.SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + btn1.SetAlignment(ALIGN_CENTER, ALIGN_BOTTOM); btn1.SetPosition(0, -45); } @@ -977,13 +1010,13 @@ int DiscWait(const char *title, const char *msg, const char *btn1Label, const ch } else { - btn1.SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + btn1.SetAlignment(ALIGN_CENTER, ALIGN_BOTTOM); btn1.SetPosition(0, -80); } } GuiText timerTxt((char*) NULL, 26, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - timerTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + timerTxt.SetAlignment(ALIGN_CENTER, ALIGN_TOP); timerTxt.SetPosition(0, 160); promptWindow.Append(&dialogBoxImg); @@ -1058,7 +1091,7 @@ int FormatingPartition(const char *title, int part_num) int ret; GuiWindow promptWindow(472, 320); - promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); @@ -1074,7 +1107,7 @@ int FormatingPartition(const char *title, int part_num) } GuiText titleTxt(title, 26, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetAlignment(ALIGN_CENTER, ALIGN_TOP); titleTxt.SetPosition(0, 60); promptWindow.Append(&dialogBoxImg); @@ -1124,14 +1157,13 @@ int FormatingPartition(const char *title, int part_num) ***************************************************************************/ bool NetworkInitPrompt() { - gprintf("\nNetworkinitPrompt()"); if (IsNetworkInit()) return true; bool success = true; GuiWindow promptWindow(472, 320); - promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); @@ -1147,12 +1179,12 @@ bool NetworkInitPrompt() } GuiText titleTxt(tr( "Initializing Network" ), 26, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetAlignment(ALIGN_CENTER, ALIGN_TOP); titleTxt.SetPosition(0, 60); char msg[20] = " "; GuiText msgTxt(msg, 22, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - msgTxt.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + msgTxt.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); msgTxt.SetPosition(0, -40); GuiText btn1Txt(tr( "Cancel" ), 22, thColor("r=0 g=0 b=0 a=255 - prompt windows button text color")); @@ -1168,7 +1200,7 @@ bool NetworkInitPrompt() if ((Settings.wsprompt) && (Settings.widescreen)) /////////////adjust buttons for widescreen { - btn1.SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + btn1.SetAlignment(ALIGN_CENTER, ALIGN_BOTTOM); btn1.SetPosition(0, -80); } @@ -1183,16 +1215,17 @@ bool NetworkInitPrompt() mainWindow->Append(&promptWindow); ResumeGui(); + int iTimeout = 100 * 200; // 20s + + ResumeNetworkThread(); + while (!IsNetworkInit()) { + usleep(100000); - VIDEO_WaitVSync(); - - Initialize_Network(); - - if (!IsNetworkInit()) + if (--iTimeout == 0) { - msgTxt.SetText(tr( "Could not initialize network!" )); + msgTxt.SetText(tr( "Could not initialize network, time out!" )); sleep(3); success = false; break; @@ -1208,13 +1241,16 @@ bool NetworkInitPrompt() promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_OUT, 50); while (promptWindow.GetEffect() > 0) - usleep(100); + usleep(1000); HaltGui(); mainWindow->Remove(&promptWindow); mainWindow->SetState(STATE_DEFAULT); ResumeGui(); + if (IsNetworkInit()) + HaltNetworkThread(); + return success; } @@ -1229,7 +1265,7 @@ int CodeDownload(const char *id) int ret = -1; GuiWindow promptWindow(472, 320); - promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); @@ -1246,16 +1282,16 @@ int CodeDownload(const char *id) char title[50]; sprintf(title, "%s", tr( "Code Download" )); GuiText titleTxt(title, 26, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetAlignment(ALIGN_CENTER, ALIGN_TOP); titleTxt.SetPosition(0, 50); char msg[50]; sprintf(msg, "%s", tr( "Initializing Network" )); GuiText msgTxt(msg, 26, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - msgTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + msgTxt.SetAlignment(ALIGN_CENTER, ALIGN_TOP); msgTxt.SetPosition(0, 140); char msg2[50] = " "; GuiText msg2Txt(msg2, 26, thColor("r=0 g=0 b=0 a=255 - prompt windows text color")); - msg2Txt.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + msg2Txt.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); msg2Txt.SetPosition(0, 50); GuiText btn1Txt(tr( "Cancel" ), 22, thColor("r=0 g=0 b=0 a=255 - prompt windows button text color")); diff --git a/source/prompts/TitleBrowser.cpp b/source/prompts/TitleBrowser.cpp index d99586d6..493371bc 100644 --- a/source/prompts/TitleBrowser.cpp +++ b/source/prompts/TitleBrowser.cpp @@ -144,7 +144,7 @@ bool TitleSelector(char output[]) GuiOptionBrowser optionBrowser4(396, 280, &options4, "bg_options_settings.png"); optionBrowser4.SetPosition(0, 90); - optionBrowser4.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + optionBrowser4.SetAlignment(ALIGN_CENTER, ALIGN_TOP); HaltGui(); GuiWindow w(screenwidth, screenheight); @@ -351,7 +351,7 @@ int TitleBrowser() GuiText titleTxt(tr( "Title Launcher" ), 28, ( GXColor ) { 0, 0, 0, 255}); - titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetAlignment(ALIGN_CENTER, ALIGN_TOP); titleTxt.SetPosition(12, 40); titleTxt.SetMaxWidth(356, SCROLL_HORIZONTAL); @@ -376,7 +376,7 @@ int TitleBrowser() GuiOptionBrowser optionBrowser3(396, 280, &options3, "bg_options_settings.png"); optionBrowser3.SetPosition(0, 90); - optionBrowser3.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + optionBrowser3.SetAlignment(ALIGN_CENTER, ALIGN_TOP); GuiImageData wifiImgData(Resources::GetFile("wifi_btn.png"), Resources::GetFileSize("wifi_btn.png")); GuiImage wifiImg(&wifiImgData); diff --git a/source/prompts/filebrowser.cpp b/source/prompts/filebrowser.cpp index 8c297087..1588e3dc 100644 --- a/source/prompts/filebrowser.cpp +++ b/source/prompts/filebrowser.cpp @@ -79,9 +79,6 @@ int InitBrowsers() { if (strcmp(devoptab_list[i]->name, "stdnull") && devoptab_list[i]->write_r != NULL) { - if(Settings.USBPort == 2 && strcmp(devoptab_list[i]->name, "sd") != 0) - continue; - snprintf(rootdir, sizeof(rootdir), "%s:/", devoptab_list[i]->name); if ( DIR *dir = opendir( rootdir ) ) { @@ -317,7 +314,7 @@ int BrowseDevice(char * Path, int Path_size, int Flags, FILTERCASCADE *Filter/*= GuiImageData folderImgData(Resources::GetFile("icon_folder.png"), Resources::GetFileSize("icon_folder.png")); GuiImage folderImg(&folderImgData); GuiButton folderBtn(folderImg.GetWidth(), folderImg.GetHeight()); - folderBtn.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + folderBtn.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); folderBtn.SetPosition(-210, -145); folderBtn.SetImage(&folderImg); folderBtn.SetTrigger(&trigA); @@ -348,7 +345,7 @@ int BrowseDevice(char * Path, int Path_size, int Flags, FILTERCASCADE *Filter/*= usbBtnImg.SetWidescreen(Settings.widescreen); } GuiButton usbBtn(btnOutline.GetWidth(), btnOutline.GetHeight()); - usbBtn.SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + usbBtn.SetAlignment(ALIGN_CENTER, ALIGN_BOTTOM); usbBtn.SetPosition(0, -35); usbBtn.SetLabel(&usbBtnTxt); usbBtn.SetImage(&usbBtnImg); @@ -366,7 +363,7 @@ int BrowseDevice(char * Path, int Path_size, int Flags, FILTERCASCADE *Filter/*= okBtn.SetLabel(&okBtnTxt); GuiFileBrowser fileBrowser(396, 248); - fileBrowser.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + fileBrowser.SetAlignment(ALIGN_CENTER, ALIGN_TOP); fileBrowser.SetPosition(0, 120); GuiImageData Address(Resources::GetFile("addressbar_textbox.png"), Resources::GetFileSize("addressbar_textbox.png")); @@ -377,7 +374,7 @@ int BrowseDevice(char * Path, int Path_size, int Flags, FILTERCASCADE *Filter/*= AdressText.SetMaxWidth(Address.GetWidth() - 40, SCROLL_HORIZONTAL); GuiImage AdressbarImg(&Address); GuiButton Adressbar(Address.GetWidth(), Address.GetHeight()); - Adressbar.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + Adressbar.SetAlignment(ALIGN_CENTER, ALIGN_TOP); Adressbar.SetPosition(0, fileBrowser.GetTop() - 45); Adressbar.SetImage(&AdressbarImg); Adressbar.SetLabel(&AdressText); diff --git a/source/prompts/gameinfo.cpp b/source/prompts/gameinfo.cpp index d2681a84..6eaddc7e 100644 --- a/source/prompts/gameinfo.cpp +++ b/source/prompts/gameinfo.cpp @@ -33,7 +33,7 @@ static int InternalShowGameInfo(struct discHdr *header) mainWindow->SetState(STATE_DISABLED); char ID[7]; - snprintf(ID, sizeof(ID), "%s", (char *) header->id); + strlcpy(ID, (char *) header->id, sizeof(ID)); char xmlpath[300]; snprintf(xmlpath, sizeof(xmlpath), "%swiitdb.xml", Settings.titlestxt_path); @@ -132,17 +132,16 @@ static int InternalShowGameInfo(struct discHdr *header) GuiText * memTxt = NULL; GuiWindow gameinfoWindow(600, 308); - gameinfoWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + gameinfoWindow.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); gameinfoWindow.SetPosition(0, -50); GuiWindow InfoWindow(600, 308); InfoWindow.SetAlignment(ALIGN_LEFT, ALIGN_TOP); GuiWindow txtWindow(350, 270); - txtWindow.SetAlignment(ALIGN_CENTRE, ALIGN_RIGHT); - txtWindow.SetPosition(95, 55); + txtWindow.SetAlignment(ALIGN_CENTER, ALIGN_TOP); + txtWindow.SetPosition(95, 40); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData dialogBox1(Resources::GetFile("gameinfo1.png"), Resources::GetFileSize("gameinfo1.png")); GuiImageData dialogBox2(Resources::GetFile("gameinfo1a.png"), Resources::GetFileSize("gameinfo1a.png")); GuiImageData dialogBox3(Resources::GetFile("gameinfo2.png"), Resources::GetFileSize("gameinfo2.png")); @@ -626,7 +625,7 @@ static int InternalShowGameInfo(struct discHdr *header) { titleTxt = new GuiText(GameInfo.Title.c_str(), titlefontsize, ( GXColor ) {0, 0, 0, 255}); titleTxt->SetMaxWidth(350, SCROLL_HORIZONTAL); - titleTxt->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt->SetAlignment(ALIGN_CENTER, ALIGN_TOP); titleTxt->SetPosition(txtXOffset, 12 + titley); InfoWindow.Append(titleTxt); } @@ -801,7 +800,7 @@ static int InternalShowGameInfo(struct discHdr *header) } else { - strcpy(linebuf2, ""); + linebuf2[0] = 0; } wifiTxt[0] = new GuiText(linebuf2, 16, ( GXColor ) {0, 0, 0, 255}); wifiTxt[0]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); @@ -828,8 +827,12 @@ static int InternalShowGameInfo(struct discHdr *header) gametdb1Txt->SetAlignment(ALIGN_LEFT, ALIGN_BOTTOM); gametdb1Txt->SetPosition(40, -15); gameinfoWindow.Append(gametdb1Txt); - if(coverImg) gameinfoWindow.Append(coverImg); - + if(coverImg) + { + gameinfoWindow.Append(coverImg); + txtWindow.SetAlignment(ALIGN_CENTER, ALIGN_TOP); + txtWindow.SetPosition(0, -170); + } // Set info window first gameinfoWindow.Append(&InfoWindow); @@ -895,7 +898,7 @@ static int InternalShowGameInfo(struct discHdr *header) while(backBtn.GetState() != STATE_CLICKED && homeBtn.GetState() != STATE_CLICKED) { - usleep(100); + usleep(10000); if (shutdown) Sys_Shutdown(); else if (reset) @@ -1065,20 +1068,23 @@ int showGameInfo(int gameSelected, struct discHdr *dvdheader) return choice; } -bool save_gamelist(int txt) // save gamelist +/** + * Save the game list. + * @param csv If true, saves in CSV format; otherwise, saves in TXT format. + */ +bool save_gamelist(bool bCSV) // save gamelist { mainWindow->SetState(STATE_DISABLED); - char tmp[200]; - sprintf(tmp, "%s", Settings.update_path); - struct stat st; - if (stat(tmp, &st) != 0) - { - mkdir(tmp, 0777); - } - FILE *f; - sprintf(tmp, "%sGameList.txt", Settings.update_path); - if (txt == 1) sprintf(tmp, "%sGameList.csv", Settings.update_path); - f = fopen(tmp, "w"); + CreateSubfolder(Settings.update_path); + + // Save the game list. + char tmp[256]; + if(bCSV) + snprintf(tmp, sizeof(tmp), "%sGameList.csv", Settings.update_path); + else + snprintf(tmp, sizeof(tmp), "%sGameList.txt", Settings.update_path); + + FILE *f = fopen(tmp, "w"); if (!f) { mainWindow->SetState(STATE_DEFAULT); @@ -1093,13 +1099,18 @@ bool save_gamelist(int txt) // save gamelist WBFS_DiskSpace(&used, &freespace); - fprintf(f, "# USB Loader Has Saved this file\n"); - fprintf(f, "# This file was created based on your list of games and language settings.\n"); - fclose(f); - /* Closing and reopening because of a write issue we are having right now */ - f = fopen(tmp, "w"); + if (bCSV) + { + fprintf(f, "\"ID\",\"Size(GB)\",\"Name\"\n"); - if (txt == 0) + for (i = 0; i < gameList.size(); i++) + { + struct discHdr* header = gameList[i]; + WBFS_GameSize(header->id, &size); + fprintf(f, "\"%.6s\",\"%.2f\",\"%s\"\n", (char*)header->id, size, GameTitles.GetTitle(header)); + } + } + else { fprintf(f, "# USB Loader Has Saved this file\n"); fprintf(f, "# This file was created based on your list of games and language settings.\n\n"); @@ -1111,25 +1122,12 @@ bool save_gamelist(int txt) // save gamelist { struct discHdr* header = gameList[i]; WBFS_GameSize(header->id, &size); - fprintf(f, "%c%c%c%c%c%c", header->id[0], header->id[1], header->id[2], header->id[3], header->id[4], - header->id[5]); + fprintf(f, "%.6s", (char*)header->id); fprintf(f, " [%.2f] ", size); fprintf(f, " %s", GameTitles.GetTitle(header)); fprintf(f, "\n"); } } - else - { - fprintf(f, "\"ID\",\"Size(GB)\",\"Name\"\n"); - - for (i = 0; i < gameList.size(); i++) - { - struct discHdr* header = gameList[i]; - WBFS_GameSize(header->id, &size); - fprintf(f, "\"%c%c%c%c%c%c\",\"%.2f\",\"%s\"\n", header->id[0], header->id[1], header->id[2], - header->id[3], header->id[4], header->id[5], size, GameTitles.GetTitle(header)); - } - } fclose(f); gameList.FilterList(); diff --git a/source/prompts/gameinfo.h b/source/prompts/gameinfo.h index f400b93c..7f9311aa 100644 --- a/source/prompts/gameinfo.h +++ b/source/prompts/gameinfo.h @@ -11,6 +11,6 @@ #include "usbloader/disc.h" int showGameInfo(int selectedGame, struct discHdr *header); -bool save_gamelist(int txt); +bool save_gamelist(bool bCSV); #endif diff --git a/source/settings/CGameSettings.cpp b/source/settings/CGameSettings.cpp index 3a649dd1..05e6ac69 100644 --- a/source/settings/CGameSettings.cpp +++ b/source/settings/CGameSettings.cpp @@ -169,7 +169,7 @@ bool CGameSettings::ValidVersion(FILE * file) bool CGameSettings::Save() { char filepath[300]; - strcpy(filepath, ConfigPath.c_str()); + strlcpy(filepath, ConfigPath.c_str(), sizeof(filepath)); char * ptr = strrchr(filepath, '/'); if(ptr) @@ -205,6 +205,12 @@ bool CGameSettings::Save() fprintf(f, "NandEmuPath:%s; ", GameList[i].NandEmuPath.c_str()); fprintf(f, "Hooktype:%d; ", GameList[i].Hooktype); fprintf(f, "WiirdDebugger:%d; ", GameList[i].WiirdDebugger); + fprintf(f, "DMLVideo:%d; ", GameList[i].DMLVideo); + fprintf(f, "DMLNMM:%d; ", GameList[i].DMLNMM); + fprintf(f, "DMLActivityLED:%d; ", GameList[i].DMLActivityLED); + fprintf(f, "DMLPADHOOK:%d; ", GameList[i].DMLPADHOOK); + fprintf(f, "DMLNoDisc:%d; ", GameList[i].DMLNoDisc); + fprintf(f, "DMLDebug:%d; ", GameList[i].DMLDebug); fprintf(f, "Locked:%d;\n", GameList[i].Locked); } fprintf(f, "# END\n"); @@ -215,102 +221,64 @@ bool CGameSettings::Save() bool CGameSettings::SetSetting(GameCFG & game, const char *name, const char *value) { - int i = 0; - if (strcmp(name, "video") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.video = i; - } + game.video = atoi(value); return true; } else if(strcmp(name, "aspectratio") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.aspectratio = i; - } + game.aspectratio = atoi(value); return true; } else if(strcmp(name, "language") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.language = i; - } + game.language = atoi(value); return true; } else if(strcmp(name, "ocarina") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.ocarina = i; - } + game.ocarina = atoi(value); return true; } else if(strcmp(name, "vipatch") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.vipatch = i; - } + game.vipatch = atoi(value); return true; } else if(strcmp(name, "ios") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.ios = i; - } + game.ios = atoi(value); return true; } else if(strcmp(name, "parentalcontrol") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.parentalcontrol = i; - } + game.parentalcontrol = atoi(value); return true; } else if(strcmp(name, "errorfix002") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.errorfix002 = i; - } + game.errorfix002 = atoi(value); return true; } else if(strcmp(name, "iosreloadblock") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.iosreloadblock = i; - } + game.iosreloadblock = atoi(value); return true; } else if(strcmp(name, "loadalternatedol") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.loadalternatedol = i; - } + game.loadalternatedol = atoi(value); return true; } else if(strcmp(name, "alternatedolstart") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.alternatedolstart = i; - } + game.alternatedolstart = atoi(value); return true; } else if(strcmp(name, "patchcountrystrings") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.patchcountrystrings = i; - } + game.patchcountrystrings = atoi(value); return true; } else if(strcmp(name, "alternatedolname") == 0) @@ -320,26 +288,17 @@ bool CGameSettings::SetSetting(GameCFG & game, const char *name, const char *val } else if(strcmp(name, "returnTo") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.returnTo = i; - } + game.returnTo = atoi(value); return true; } else if(strcmp(name, "sneekVideoPatch") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.sneekVideoPatch = i; - } + game.sneekVideoPatch = atoi(value); return true; } else if(strcmp(name, "NandEmuMode") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.NandEmuMode = i; - } + game.NandEmuMode = atoi(value); return true; } else if(strcmp(name, "NandEmuPath") == 0) @@ -349,26 +308,47 @@ bool CGameSettings::SetSetting(GameCFG & game, const char *name, const char *val } else if(strcmp(name, "Hooktype") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.Hooktype = i; - } + game.Hooktype = atoi(value); return true; } else if(strcmp(name, "WiirdDebugger") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.WiirdDebugger = i; - } + game.WiirdDebugger = atoi(value); return true; } else if(strcmp(name, "Locked") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - game.Locked = i; - } + game.Locked = atoi(value); + return true; + } + else if(strcmp(name, "DMLVideo") == 0) + { + game.DMLVideo = atoi(value); + return true; + } + else if(strcmp(name, "DMLNMM") == 0) + { + game.DMLNMM = atoi(value); + return true; + } + else if(strcmp(name, "DMLActivityLED") == 0) + { + game.DMLActivityLED = atoi(value); + return true; + } + else if(strcmp(name, "DMLPADHOOK") == 0) + { + game.DMLPADHOOK = atoi(value); + return true; + } + else if(strcmp(name, "DMLNoDisc") == 0) + { + game.DMLNoDisc = atoi(value); + return true; + } + else if(strcmp(name, "DMLDebug") == 0) + { + game.DMLDebug = atoi(value); return true; } @@ -491,5 +471,11 @@ void CGameSettings::SetDefault(GameCFG &game) game.NandEmuPath.clear(); game.Hooktype = INHERIT; game.WiirdDebugger = INHERIT; + game.DMLVideo = INHERIT; + game.DMLNMM = INHERIT; + game.DMLActivityLED = INHERIT; + game.DMLPADHOOK = INHERIT; + game.DMLNoDisc = INHERIT; + game.DMLDebug = INHERIT; game.Locked = OFF; } diff --git a/source/settings/CGameSettings.h b/source/settings/CGameSettings.h index 7aa5a662..b8668943 100644 --- a/source/settings/CGameSettings.h +++ b/source/settings/CGameSettings.h @@ -29,6 +29,12 @@ typedef struct _GameCFG std::string NandEmuPath; short Hooktype; short WiirdDebugger; + short DMLVideo; + short DMLNMM; + short DMLActivityLED; + short DMLPADHOOK; + short DMLNoDisc; + short DMLDebug; short Locked; void operator=(const struct _GameCFG &game) @@ -53,6 +59,12 @@ typedef struct _GameCFG this->NandEmuPath = game.NandEmuPath; this->Hooktype = game.Hooktype; this->WiirdDebugger = game.WiirdDebugger; + this->DMLVideo = game.DMLVideo; + this->DMLNMM = game.DMLNMM; + this->DMLActivityLED = game.DMLActivityLED; + this->DMLPADHOOK = game.DMLPADHOOK; + this->DMLNoDisc = game.DMLNoDisc; + this->DMLDebug = game.DMLDebug; this->Locked = game.Locked; } } GameCFG; @@ -74,14 +86,14 @@ class CGameSettings bool RemoveAll(); //!Overload Reset for one Game bool Remove(const char * id); - bool Remove(const u8 * id) { return Remove((const char *) id); }; - bool Remove(const struct discHdr * game) { if(!game) return false; else return Remove(game->id); }; + bool Remove(const u8 * id) { return Remove((const char *) id); } + bool Remove(const struct discHdr * game) { if(!game) return false; else return Remove(game->id); } //!Get GameCFG GameCFG * GetGameCFG(const char * id); //!Overload - GameCFG * GetGameCFG(const u8 * id) { return GetGameCFG((const char *) id); }; + GameCFG * GetGameCFG(const u8 * id) { return GetGameCFG((const char *) id); } //!Overload - GameCFG * GetGameCFG(const struct discHdr * game) { if(!game) return NULL; else return GetGameCFG(game->id); }; + GameCFG * GetGameCFG(const struct discHdr * game) { if(!game) return NULL; else return GetGameCFG(game->id); } //!Quick settings to PEGI conversion static int GetPartenalPEGI(int parentalsetting); //!Set the default configuration block diff --git a/source/settings/CSettings.cpp b/source/settings/CSettings.cpp index 8f131c11..c4e03e96 100644 --- a/source/settings/CSettings.cpp +++ b/source/settings/CSettings.cpp @@ -62,6 +62,7 @@ void CSettings::SetDefault() snprintf(titlestxt_path, sizeof(titlestxt_path), "%s", ConfigPath); snprintf(languagefiles_path, sizeof(languagefiles_path), "%slanguage/", ConfigPath); snprintf(update_path, sizeof(update_path), "%s/apps/usbloader_gx/", BootDevice); + snprintf(BNRCachePath, sizeof(BNRCachePath), "%s/apps/usbloader_gx/cache_bnr/", BootDevice); snprintf(homebrewapps_path, sizeof(homebrewapps_path), "%s/apps/", BootDevice); snprintf(Cheatcodespath, sizeof(Cheatcodespath), "%s/codes/", BootDevice); snprintf(TxtCheatcodespath, sizeof(TxtCheatcodespath), "%s/txtcodes/", BootDevice); @@ -72,14 +73,19 @@ void CSettings::SetDefault() snprintf(theme_path, sizeof(theme_path), "%stheme/", ConfigPath); snprintf(dolpath, sizeof(dolpath), "%s/", BootDevice); snprintf(NandEmuPath, sizeof(NandEmuPath), "%s/nand/", BootDevice); - strcpy(NandEmuChanPath, NandEmuPath); - strcpy(theme, ""); - strcpy(language_path, ""); - strcpy(ogg_path, ""); - strcpy(unlockCode, ""); - strcpy(db_language, ""); - strcpy(returnTo, ""); - strcpy(GameCubePath, "sd:/games/"); + strlcpy(NandEmuChanPath, NandEmuPath, sizeof(NandEmuChanPath)); + strlcpy(GameCubePath, "sd:/games/", sizeof(GameCubePath)); + strlcpy(GameCubeSDPath, "sd:/games/", sizeof(GameCubeSDPath)); + theme[0] = 0; + language_path[0] = 0; + ogg_path[0] = 0; + unlockCode[0] = 0; + db_language[0] = 0; + returnTo[0] = 0; + + NTSC = (CONF_GetVideo() == CONF_VIDEO_NTSC); + PAL50 = (CONF_GetVideo() == CONF_VIDEO_PAL) && (CONF_GetEuRGB60() == 0); + widescreen = (CONF_GetAspectRatio() == CONF_ASPECT_16_9); godmode = 1; videomode = VIDEO_MODE_DISCDEFAULT; @@ -117,7 +123,6 @@ void CSettings::SetDefault() InstallToDir = INSTALL_TO_NAME_GAMEID; GameSplit = GAMESPLIT_4GB; InstallPartitions = ONLY_GAME_PARTITION; - widescreen = (CONF_GetAspectRatio() == CONF_ASPECT_16_9); HomeMenu = HOME_MENU_DEFAULT; MultiplePartitions = OFF; BlockIOSReload = AUTO; @@ -146,6 +151,25 @@ void CSettings::SetDefault() UseChanLauncher = OFF; AdjustOverscanX = 0; AdjustOverscanY = 0; + TooltipDelay = 1500; // ms + GameWindowMode = GAMEWINDOW_BANNER; + CacheBNRFiles = ON; + BannerAnimStart = BANNER_START_ON_ZOOM; + BannerGridSpeed = 25.6f; // pixel/frames + BannerZoomDuration = 30; // frames + BannerProjectionOffsetX = (!widescreen || PAL50) ? 0.0f : 2.0f; + BannerProjectionOffsetY = PAL50 ? -1.0f : (NTSC ? 0.0f : -4.0f); + BannerProjectionWidth = (Settings.widescreen ? (Settings.PAL50 ? 616 : 620.0f) : 608.0f); + BannerProjectionHeight = (Settings.PAL50 ? 448.0f : (NTSC ? 470.0f : 464.0f)); + GCBannerScale = 1.5f; + DMLVideo = DML_VID_DML_AUTO; + DMLNMM = OFF; + DMLActivityLED = OFF; + DMLPADHOOK = OFF; + DMLNoDisc = OFF; + DMLDebug = OFF; + GCInstallCompressed = OFF; + GCInstallAligned = OFF; } bool CSettings::Load() @@ -270,10 +294,11 @@ bool CSettings::Save() fprintf(file, "gameDisplay = %d\n", gameDisplay); fprintf(file, "update_path = %s\n", update_path); fprintf(file, "homebrewapps_path = %s\n", homebrewapps_path); + fprintf(file, "BNRCachePath = %s\n", BNRCachePath); fprintf(file, "Cheatcodespath = %s\n", Cheatcodespath); fprintf(file, "BcaCodepath = %s\n", BcaCodepath); fprintf(file, "WipCodepath = %s\n", WipCodepath); - fprintf(file, "WDMpath = %s\n ", WDMpath); + fprintf(file, "WDMpath = %s\n", WDMpath); fprintf(file, "titlesOverride = %d\n", titlesOverride); fprintf(file, "ForceDiscTitles = %d\n", ForceDiscTitles); fprintf(file, "patchcountrystrings = %d\n", patchcountrystrings); @@ -327,6 +352,27 @@ bool CSettings::Save() fprintf(file, "UseChanLauncher = %d\n", UseChanLauncher); fprintf(file, "AdjustOverscanX = %d\n", AdjustOverscanX); fprintf(file, "AdjustOverscanY = %d\n", AdjustOverscanY); + fprintf(file, "TooltipDelay = %d\n", TooltipDelay); + fprintf(file, "GameWindowMode = %d\n", GameWindowMode); + fprintf(file, "CacheBNRFiles = %d\n", CacheBNRFiles); + fprintf(file, "BannerAnimStart = %d\n", BannerAnimStart); + fprintf(file, "BannerGridSpeed = %g\n", BannerGridSpeed); + fprintf(file, "BannerZoomDuration = %d\n", BannerZoomDuration); + fprintf(file, "BannerProjectionOffsetX = %g\n", BannerProjectionOffsetX); + fprintf(file, "BannerProjectionOffsetY = %g\n", BannerProjectionOffsetY); + fprintf(file, "BannerProjectionWidth = %g\n", BannerProjectionWidth); + fprintf(file, "BannerProjectionHeight = %g\n", BannerProjectionHeight); + fprintf(file, "GCBannerScale = %g\n", GCBannerScale); + fprintf(file, "GameCubePath = %s\n", GameCubePath); + fprintf(file, "GameCubeSDPath = %s\n", GameCubeSDPath); + fprintf(file, "DMLVideo = %d\n", DMLVideo); + fprintf(file, "DMLNMM = %d\n", DMLNMM); + fprintf(file, "DMLActivityLED = %d\n", DMLActivityLED); + fprintf(file, "DMLPADHOOK = %d\n", DMLPADHOOK); + fprintf(file, "DMLNoDisc = %d\n", DMLNoDisc); + fprintf(file, "DMLDebug = %d\n", DMLDebug); + fprintf(file, "GCInstallCompressed = %d\n", GCInstallCompressed); + fprintf(file, "GCInstallAligned = %d\n", GCInstallAligned); fclose(file); return true; @@ -334,116 +380,75 @@ bool CSettings::Save() bool CSettings::SetSetting(char *name, char *value) { - int i = 0; - if (strcmp(name, "godmode") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - godmode = i; - } + godmode = atoi(value); return true; } else if (strcmp(name, "videomode") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - videomode = i; - } + videomode = atoi(value); return true; } else if (strcmp(name, "videopatch") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - videopatch = i; - } + videopatch = atoi(value); return true; } else if (strcmp(name, "language") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - language = i; - } + language = atoi(value); return true; } else if (strcmp(name, "ocarina") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - ocarina = i; - } + ocarina = atoi(value); return true; } else if (strcmp(name, "hddinfo") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - hddinfo = i; - } + hddinfo = atoi(value); return true; } else if (strcmp(name, "sinfo") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - sinfo = i; - } + sinfo = atoi(value); return true; } else if (strcmp(name, "rumble") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - rumble = i; - } + rumble = atoi(value); return true; } else if (strcmp(name, "volume") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - volume = i; - } + volume = atoi(value); return true; } else if (strcmp(name, "sfxvolume") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - sfxvolume = i; - } + sfxvolume = atoi(value); return true; } else if (strcmp(name, "gamesoundvolume") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - gamesoundvolume = i; - } + gamesoundvolume = atoi(value); return true; } else if (strcmp(name, "tooltips") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - tooltips = i; - } + tooltips = atoi(value); return true; } else if (strcmp(name, "RememberUnlock") == 0) { - if (sscanf(value, "%d", &i) == 1) - { - RememberUnlock = i; - } + RememberUnlock = atoi(value); return true; } else if (strcmp(name, "password") == 0) { char EncryptedTxt[50]; - strcpy(EncryptedTxt, value); + strlcpy(EncryptedTxt, value, sizeof(EncryptedTxt)); DecryptString(EncryptedTxt, unlockCode); if(!RememberUnlock && strlen(unlockCode) > 0 && strcmp(unlockCode, "not set") != 0) @@ -452,214 +457,214 @@ bool CSettings::SetSetting(char *name, char *value) } else if (strcmp(name, "GameSort") == 0) { - if (sscanf(value, "%d", &i) == 1) GameSort = i; + GameSort = atoi(value); return true; } else if (strcmp(name, "cios") == 0) { - if (sscanf(value, "%d", &i) == 1) cios = i; + cios = atoi(value); return true; } else if (strcmp(name, "keyset") == 0) { - if (sscanf(value, "%d", &i) == 1) keyset = i; + keyset = atoi(value); return true; } else if (strcmp(name, "xflip") == 0) { - if (sscanf(value, "%d", &i) == 1) xflip = i; + xflip = atoi(value); return true; } else if (strcmp(name, "gridRows") == 0) { - if (sscanf(value, "%d", &i) == 1) gridRows = i; + gridRows = atoi(value); return true; } else if (strcmp(name, "quickboot") == 0) { - if (sscanf(value, "%d", &i) == 1) quickboot = i; + quickboot = atoi(value); return true; } else if (strcmp(name, "partition") == 0) { - if (sscanf(value, "%d", &i) == 1) partition = i; + partition = atoi(value); return true; } else if (strcmp(name, "wsprompt") == 0) { - if (sscanf(value, "%d", &i) == 1) wsprompt = i; + wsprompt = atoi(value); return true; } else if (strcmp(name, "gameDisplay") == 0) { - if (sscanf(value, "%d", &i) == 1) gameDisplay = i; + gameDisplay = atoi(value); return true; } else if (strcmp(name, "parentalcontrol") == 0) { - if (sscanf(value, "%d", &i) == 1) parentalcontrol = i; + parentalcontrol = atoi(value); return true; } else if (strcmp(name, "screensaver") == 0) { - if (sscanf(value, "%d", &i) == 1) screensaver = i; + screensaver = atoi(value); return true; } else if (strcmp(name, "titlesOverride") == 0) { - if (sscanf(value, "%d", &i) == 1) titlesOverride = i; + titlesOverride = atoi(value); return true; } else if (strcmp(name, "ForceDiscTitles") == 0) { - if (sscanf(value, "%d", &i) == 1) ForceDiscTitles = i; + ForceDiscTitles = atoi(value); return true; } else if (strcmp(name, "musicloopmode") == 0) { - if (sscanf(value, "%d", &i) == 1) musicloopmode = i; + musicloopmode = atoi(value); return true; } else if (strcmp(name, "gamesound") == 0) { - if (sscanf(value, "%d", &i) == 1) gamesound = i; + gamesound = atoi(value); return true; } else if (strcmp(name, "wiilight") == 0) { - if (sscanf(value, "%d", &i) == 1) wiilight = i; + wiilight = atoi(value); return true; } else if (strcmp(name, "marknewtitles") == 0) { - if (sscanf(value, "%d", &i) == 1) marknewtitles = i; + marknewtitles = atoi(value); return true; } else if (strcmp(name, "ShowPlayCount") == 0) { - if (sscanf(value, "%d", &i) == 1) ShowPlayCount = i; + ShowPlayCount = atoi(value); return true; } else if (strcmp(name, "ShowFreeSpace") == 0) { - if (sscanf(value, "%d", &i) == 1) ShowFreeSpace = i; + ShowFreeSpace = atoi(value); return true; } else if (strcmp(name, "HomeMenu") == 0) { - if (sscanf(value, "%d", &i) == 1) HomeMenu = i; + HomeMenu = atoi(value); return true; } else if (strcmp(name, "MultiplePartitions") == 0) { - if (sscanf(value, "%d", &i) == 1) MultiplePartitions = i; + MultiplePartitions = atoi(value); return true; } else if (strcmp(name, "BlockIOSReload") == 0) { - if (sscanf(value, "%d", &i) == 1) BlockIOSReload = i; + BlockIOSReload = atoi(value); return true; } else if (strcmp(name, "USBPort") == 0) { - if (sscanf(value, "%d", &i) == 1) USBPort = i; + USBPort = atoi(value); return true; } else if (strcmp(name, "CacheTitles") == 0) { - if (sscanf(value, "%d", &i) == 1) CacheTitles = i; + CacheTitles = atoi(value); return true; } else if (strcmp(name, "patchcountrystrings") == 0) { - if (sscanf(value, "%d", &i) == 1) patchcountrystrings = i; + patchcountrystrings = atoi(value); return true; } else if (strcmp(name, "discart") == 0) { - if (sscanf(value, "%d", &i) == 1) discart = i; + discart = atoi(value); return true; } else if (strcmp(name, "error002") == 0) { - if (sscanf(value, "%d", &i) == 1) error002 = i; + error002 = atoi(value); return true; } else if (strcmp(name, "autonetwork") == 0) { - if (sscanf(value, "%d", &i) == 1) autonetwork = i; + autonetwork = atoi(value); return true; } else if (strcmp(name, "InstallToDir") == 0) { - if (sscanf(value, "%d", &i) == 1) InstallToDir = i; + InstallToDir = atoi(value); return true; } else if (strcmp(name, "GameSplit") == 0) { - if (sscanf(value, "%d", &i) == 1) GameSplit = i; + GameSplit = atoi(value); return true; } else if (strcmp(name, "PlaylogUpdate") == 0) { - if (sscanf(value, "%d", &i) == 1) PlaylogUpdate = i; + PlaylogUpdate = atoi(value); return true; } else if(strcmp(name, "Wiinnertag") == 0) { - if (sscanf(value, "%d", &i) == 1) Wiinnertag = i; + Wiinnertag = atoi(value); } else if(strcmp(name, "SelectedGame") == 0) { - if (sscanf(value, "%d", &i) == 1) SelectedGame = i; + SelectedGame = atoi(value); } else if(strcmp(name, "GameListOffset") == 0) { - if (sscanf(value, "%d", &i) == 1) GameListOffset = i; + GameListOffset = atoi(value); } else if(strcmp(name, "sneekVideoPatch") == 0) { - if (sscanf(value, "%d", &i) == 1) sneekVideoPatch = i; + sneekVideoPatch = atoi(value); } else if(strcmp(name, "UseSystemFont") == 0) { - if (sscanf(value, "%d", &i) == 1) UseSystemFont = i; + UseSystemFont = atoi(value); } else if(strcmp(name, "Hooktype") == 0) { - if (sscanf(value, "%d", &i) == 1) Hooktype = i; + Hooktype = atoi(value); } else if(strcmp(name, "WiirdDebugger") == 0) { - if (sscanf(value, "%d", &i) == 1) WiirdDebugger = i; + WiirdDebugger = atoi(value); } else if(strcmp(name, "WiirdDebuggerPause") == 0) { - if (sscanf(value, "%d", &i) == 1) WiirdDebuggerPause = i; + WiirdDebuggerPause = atoi(value); } else if(strcmp(name, "NandEmuMode") == 0) { - if (sscanf(value, "%d", &i) == 1) NandEmuMode = i; + NandEmuMode = atoi(value); } else if(strcmp(name, "NandEmuChanMode") == 0) { - if (sscanf(value, "%d", &i) == 1) NandEmuChanMode = i; + NandEmuChanMode = atoi(value); } else if(strcmp(name, "LoaderMode") == 0) { - if (sscanf(value, "%d", &i) == 1) LoaderMode = i; + LoaderMode = atoi(value); } else if(strcmp(name, "SearchMode") == 0) { - if (sscanf(value, "%d", &i) == 1) SearchMode = i; + SearchMode = atoi(value); } else if(strcmp(name, "GameAspectRatio") == 0) { - if (sscanf(value, "%d", &i) == 1) GameAspectRatio = i; + GameAspectRatio = atoi(value); } else if(strcmp(name, "UseChanLauncher") == 0) { - if (sscanf(value, "%d", &i) == 1) UseChanLauncher = i; + UseChanLauncher = atoi(value); } else if(strcmp(name, "AdjustOverscanX") == 0) { @@ -669,6 +674,26 @@ bool CSettings::SetSetting(char *name, char *value) { AdjustOverscanY = atoi(value); } + else if(strcmp(name, "TooltipDelay") == 0) + { + TooltipDelay = atoi(value); + } + else if(strcmp(name, "BannerZoomDuration") == 0) + { + BannerZoomDuration = atoi(value); + } + else if(strcmp(name, "GameWindowMode") == 0) + { + GameWindowMode = atoi(value); + } + else if(strcmp(name, "BannerAnimStart") == 0) + { + BannerAnimStart = atoi(value); + } + else if(strcmp(name, "CacheBNRFiles") == 0) + { + CacheBNRFiles = atoi(value); + } else if (strcmp(name, "InstallPartitions") == 0) { InstallPartitions = strtoul(value, 0, 16); @@ -689,119 +714,204 @@ bool CSettings::SetSetting(char *name, char *value) PointerSpeed = atof(value); return true; } + else if (strcmp(name, "BannerGridSpeed") == 0) + { + BannerGridSpeed = atof(value); + return true; + } + else if (strcmp(name, "BannerProjectionOffsetX") == 0) + { + BannerProjectionOffsetX = atof(value); + return true; + } + else if (strcmp(name, "BannerProjectionOffsetY") == 0) + { + BannerProjectionOffsetY = atof(value); + return true; + } + else if (strcmp(name, "BannerProjectionWidth") == 0) + { + BannerProjectionWidth = atof(value); + return true; + } + else if (strcmp(name, "BannerProjectionHeight") == 0) + { + BannerProjectionHeight = atof(value); + return true; + } + else if (strcmp(name, "GCBannerScale") == 0) + { + GCBannerScale = atof(value); + return true; + } else if (strcmp(name, "ParentalBlocks") == 0) { ParentalBlocks = strtoul(value, 0, 16); return true; } + else if (strcmp(name, "DMLVideo") == 0) + { + DMLVideo = atoi(value); + return true; + } + else if (strcmp(name, "DMLNMM") == 0) + { + DMLNMM = atoi(value); + return true; + } + else if (strcmp(name, "DMLActivityLED") == 0) + { + DMLActivityLED = atoi(value); + return true; + } + else if (strcmp(name, "DMLPADHOOK") == 0) + { + DMLPADHOOK = atoi(value); + return true; + } + else if (strcmp(name, "DMLNoDisc") == 0) + { + DMLNoDisc = atoi(value); + return true; + } + else if (strcmp(name, "DMLDebug") == 0) + { + DMLDebug = atoi(value); + return true; + } + else if (strcmp(name, "GCInstallCompressed") == 0) + { + GCInstallCompressed = atoi(value); + return true; + } + else if (strcmp(name, "GCInstallAligned") == 0) + { + GCInstallAligned = atoi(value); + return true; + } else if (strcmp(name, "covers_path") == 0) { - strcpy(covers_path, value); + strlcpy(covers_path, value, sizeof(covers_path)); return true; } else if (strcmp(name, "covers2d_path") == 0) { - strcpy(covers2d_path, value); + strlcpy(covers2d_path, value, sizeof(covers2d_path)); return true; } else if (strcmp(name, "coversFull_path") == 0) { - strcpy(coversFull_path, value); + strlcpy(coversFull_path, value, sizeof(coversFull_path)); return true; } else if (strcmp(name, "theme_path") == 0) { - strcpy(theme_path, value); + strlcpy(theme_path, value, sizeof(theme_path)); return true; } else if (strcmp(name, "theme") == 0) { - strcpy(theme, value); + strlcpy(theme, value, sizeof(theme)); return true; } else if (strcmp(name, "disc_path") == 0) { - strcpy(disc_path, value); + strlcpy(disc_path, value, sizeof(disc_path)); return true; } else if (strcmp(name, "language_path") == 0) { - strcpy(language_path, value); + strlcpy(language_path, value, sizeof(language_path)); return true; } else if (strcmp(name, "languagefiles_path") == 0) { - strcpy(languagefiles_path, value); + strlcpy(languagefiles_path, value, sizeof(languagefiles_path)); return true; } else if (strcmp(name, "TxtCheatcodespath") == 0) { - strcpy(TxtCheatcodespath, value); + strlcpy(TxtCheatcodespath, value, sizeof(TxtCheatcodespath)); return true; } else if (strcmp(name, "titlestxt_path") == 0) { - strcpy(titlestxt_path, value); + strlcpy(titlestxt_path, value, sizeof(titlestxt_path)); return true; } else if (strcmp(name, "dolpath") == 0) { - strcpy(dolpath, value); + strlcpy(dolpath, value, sizeof(dolpath)); return true; } else if (strcmp(name, "ogg_path") == 0) { - strcpy(ogg_path, value); + strlcpy(ogg_path, value, sizeof(ogg_path)); return true; } else if (strcmp(name, "update_path") == 0) { - strcpy(update_path, value); + strlcpy(update_path, value, sizeof(update_path)); return true; } else if (strcmp(name, "homebrewapps_path") == 0) { - strcpy(homebrewapps_path, value); + strlcpy(homebrewapps_path, value, sizeof(homebrewapps_path)); + return true; + } + else if (strcmp(name, "BNRCachePath") == 0) + { + strlcpy(BNRCachePath, value, sizeof(BNRCachePath)); return true; } else if (strcmp(name, "Cheatcodespath") == 0) { - strcpy(Cheatcodespath, value); + strlcpy(Cheatcodespath, value, sizeof(Cheatcodespath)); return true; } else if (strcmp(name, "BcaCodepath") == 0) { - strcpy(BcaCodepath, value); + strlcpy(BcaCodepath, value, sizeof(BcaCodepath)); return true; } else if (strcmp(name, "WipCodepath") == 0) { - strcpy(WipCodepath, value); + strlcpy(WipCodepath, value, sizeof(WipCodepath)); return true; } else if (strcmp(name, "WDMpath") == 0) { - strcpy(WDMpath, value); + strlcpy(WDMpath, value, sizeof(WDMpath)); return true; } else if (strcmp(name, "returnTo") == 0) { - strcpy(returnTo, value); + strlcpy(returnTo, value, sizeof(returnTo)); return true; } else if (strcmp(name, "WiinnertagPath") == 0) { - strcpy(WiinnertagPath, value); + strlcpy(WiinnertagPath, value, sizeof(WiinnertagPath)); return true; } else if (strcmp(name, "NandEmuPath") == 0) { - strcpy(NandEmuPath, value); + strlcpy(NandEmuPath, value, sizeof(NandEmuPath)); return true; } else if (strcmp(name, "NandEmuChanPath") == 0) { - strcpy(NandEmuChanPath, value); + strlcpy(NandEmuChanPath, value, sizeof(NandEmuChanPath)); + return true; + } + else if (strcmp(name, "GameCubePath") == 0) + { + strlcpy(GameCubePath, value, sizeof(GameCubePath)); + return true; + } + else if (strcmp(name, "GameCubeSDPath") == 0) + { + strlcpy(GameCubeSDPath, value, sizeof(GameCubeSDPath)); return true; } else if (strcmp(name, "EnabledCategories") == 0) @@ -830,56 +940,58 @@ bool CSettings::SetSetting(char *name, char *value) bool CSettings::FindConfig() { bool found = false; - char CheckDevice[10]; + char CheckDevice[12]; char CheckPath[300]; - for (int i = SD; i < MAXDEVICES; ++i) + // Enumerate the devices supported by libogc. + for (int i = SD; (i < MAXDEVICES) && !found; ++i) { - sprintf(CheckDevice, "%s:", DeviceName[i]); + snprintf(CheckDevice, sizeof(CheckDevice), "%s:", DeviceName[i]); if(!found) { - strcpy(BootDevice, CheckDevice); + // Check for the config file in the apps directory. + strlcpy(BootDevice, CheckDevice, sizeof(BootDevice)); snprintf(ConfigPath, sizeof(ConfigPath), "%s/apps/usbloader_gx/", BootDevice); snprintf(CheckPath, sizeof(CheckPath), "%sGXGlobal.cfg", ConfigPath); found = CheckFile(CheckPath); } if(!found) { - strcpy(BootDevice, CheckDevice); + // Check for the config file in the config directory. + strlcpy(BootDevice, CheckDevice, sizeof(BootDevice)); snprintf(ConfigPath, sizeof(ConfigPath), "%s/config/", BootDevice); snprintf(CheckPath, sizeof(CheckPath), "%sGXGlobal.cfg", ConfigPath); found = CheckFile(CheckPath); } } - if (!found) + FILE * testFp = NULL; + //! No existing config so try to find a place where we can write it too + for (int i = SD; (i < MAXDEVICES) && !found; ++i) { - FILE * testFp = NULL; - //! No existing config so try to find a place where we can write it too - for (int i = SD; i < MAXDEVICES; ++i) - { - sprintf(CheckDevice, "%s:", DeviceName[i]); + sprintf(CheckDevice, "%s:", DeviceName[i]); - if (!found) - { - strcpy(BootDevice, CheckDevice); - snprintf(ConfigPath, sizeof(ConfigPath), "%s/apps/usbloader_gx/", BootDevice); - snprintf(CheckPath, sizeof(CheckPath), "%sGXGlobal.cfg", ConfigPath); - testFp = fopen(CheckPath, "wb"); - found = (testFp != NULL); - fclose(testFp); - } - if (!found) - { - strcpy(BootDevice, CheckDevice); - snprintf(ConfigPath, sizeof(ConfigPath), "%s/config/", BootDevice); - CreateSubfolder(ConfigPath); - snprintf(CheckPath, sizeof(CheckPath), "%sGXGlobal.cfg", ConfigPath); - testFp = fopen(CheckPath, "wb"); - found = (testFp != NULL); - fclose(testFp); - } + if (!found) + { + // Check if we can write to the apps directory. + strlcpy(BootDevice, CheckDevice, sizeof(BootDevice)); + snprintf(ConfigPath, sizeof(ConfigPath), "%s/apps/usbloader_gx/", BootDevice); + snprintf(CheckPath, sizeof(CheckPath), "%sGXGlobal.cfg", ConfigPath); + testFp = fopen(CheckPath, "wb"); + found = (testFp != NULL); + if(testFp) fclose(testFp); + } + if (!found) + { + // Check if we can write to the config directory. + strlcpy(BootDevice, CheckDevice, sizeof(BootDevice)); + snprintf(ConfigPath, sizeof(ConfigPath), "%s/config/", BootDevice); + CreateSubfolder(ConfigPath); + snprintf(CheckPath, sizeof(CheckPath), "%sGXGlobal.cfg", ConfigPath); + testFp = fopen(CheckPath, "wb"); + found = (testFp != NULL); + if(testFp) fclose(testFp); } } @@ -960,8 +1072,8 @@ bool CSettings::LoadLanguage(const char *path, int lang) ret = gettextLoadLanguage(path); if (ret) { - snprintf(language_path, sizeof(language_path), path); - strcpy(db_language, GetLangCode(language_path)); + strlcpy(language_path, path, sizeof(language_path)); + strlcpy(db_language, GetLangCode(language_path), sizeof(db_language)); } else return LoadLanguage(NULL, CONSOLE_DEFAULT); @@ -970,7 +1082,7 @@ bool CSettings::LoadLanguage(const char *path, int lang) { char filepath[150]; char langpath[150]; - snprintf(langpath, sizeof(langpath), "%s", languagefiles_path); + strlcpy(langpath, languagefiles_path, sizeof(langpath)); if (langpath[strlen(langpath) - 1] != '/') { char * ptr = strrchr(langpath, '/'); @@ -1026,10 +1138,10 @@ bool CSettings::LoadLanguage(const char *path, int lang) snprintf(filepath, sizeof(filepath), "%s/korean.lang", langpath); } - strcpy(db_language, GetLangCode(filepath)); + strlcpy(db_language, GetLangCode(filepath), sizeof(db_language)); ret = gettextLoadLanguage(filepath); if (ret) - snprintf(language_path, sizeof(language_path), filepath); + strlcpy(language_path, filepath, sizeof(language_path)); } return ret; diff --git a/source/settings/CSettings.h b/source/settings/CSettings.h index 63e11c14..b9b3f9a5 100644 --- a/source/settings/CSettings.h +++ b/source/settings/CSettings.h @@ -29,6 +29,7 @@ #include #include #include "SettingsEnums.h" +#include "GameCube/DML_Config.h" class CSettings { @@ -49,6 +50,11 @@ class CSettings //!\param language bool LoadLanguage(const char *path, int language = -1); + //! System settings stuff + bool widescreen; + bool PAL50; + bool NTSC; + /** Variables **/ char BootDevice[10]; char unlockCode[20]; @@ -76,7 +82,9 @@ class CSettings char WiinnertagPath[100]; char NandEmuPath[50]; char NandEmuChanPath[50]; + char BNRCachePath[50]; char GameCubePath[100]; + char GameCubeSDPath[100]; short videomode; short language; short ocarina; @@ -101,7 +109,6 @@ class CSettings short screensaver; short partition; short musicloopmode; - short widescreen; short godmode; short error002; short titlesOverride; // db_titles @@ -145,6 +152,25 @@ class CSettings int AdjustOverscanX; int AdjustOverscanY; short ForceDiscTitles; + short TooltipDelay; + short GameWindowMode; + short CacheBNRFiles; + short BannerAnimStart; + float BannerGridSpeed; + short BannerZoomDuration; + float BannerProjectionOffsetX; + float BannerProjectionOffsetY; + float BannerProjectionWidth; + float BannerProjectionHeight; + float GCBannerScale; + short DMLVideo; + short DMLNMM; + short DMLActivityLED; + short DMLPADHOOK; + short DMLNoDisc; + short DMLDebug; + short GCInstallCompressed; + short GCInstallAligned; // This variable is not saved to the settings file bool FirstTimeRun; diff --git a/source/settings/SettingsEnums.h b/source/settings/SettingsEnums.h index e106c858..e2747039 100644 --- a/source/settings/SettingsEnums.h +++ b/source/settings/SettingsEnums.h @@ -15,18 +15,19 @@ enum TYPE_GAME_WII_DISC = 0x01, TYPE_GAME_GC_IMG = 0x02, TYPE_GAME_GC_DISC = 0x03, - TYPE_GAME_NANDCHAN = 0x04, - TYPE_GAME_EMUNANDCHAN = 0x05, + TYPE_GAME_GC_EXTRACTED = 0x04, + TYPE_GAME_NANDCHAN = 0x05, + TYPE_GAME_EMUNANDCHAN = 0x06 }; enum { MODE_NONE = 0x00, MODE_WIIGAMES = 0x01, - MODE_GCGAMES = 0x02, - MODE_NANDCHANNELS = 0x04, - MODE_EMUCHANNELS = 0x08, - MODE_ALL = 0xFF, + MODE_NANDCHANNELS = 0x02, + MODE_EMUCHANNELS = 0x04, + MODE_GCGAMES = 0x08, + MODE_ALL = 0xFF }; enum @@ -42,7 +43,7 @@ enum T_CHINESE, KOREAN, CONSOLE_DEFAULT, - MAX_LANGUAGE, + MAX_LANGUAGE }; enum @@ -66,6 +67,13 @@ enum ASPECT_MAX }; +enum +{ + GAMEWINDOW_BANNER, + GAMEWINDOW_DISC, + GAMEWINDOW_MAX +}; + enum { OFF, @@ -127,7 +135,7 @@ enum SORT_PLAYCOUNT = 0x02, SORT_RANKING = 0x04, SORT_FAVORITE = 0x08, - SORT_PLAYERS = 0x10, + SORT_PLAYERS = 0x10 }; enum { @@ -159,14 +167,15 @@ enum { LIST_MODE, GRID_MODE, - CAROUSEL_MODE + CAROUSEL_MODE, + BANNERGRID_MODE }; enum { DISCARTS_ORIGINALS_CUSTOMS, DISCARTS_CUSTOMS_ORIGINALS, - DISCARTS_MAX_CHOICE, + DISCARTS_MAX_CHOICE }; enum @@ -175,7 +184,7 @@ enum PARENTAL_LVL_CHILD, PARENTAL_LVL_TEEN, PARENTAL_LVL_MATURE, - PARENTAL_LVL_ADULT, + PARENTAL_LVL_ADULT }; enum @@ -204,7 +213,8 @@ enum BLOCK_SD_RELOAD_BUTTON = 0x100000, BLOCK_PRIILOADER_OVERRIDE = 0x200000, BLOCK_LOADER_MODE_BUTTON = 0x400000, - BLOCK_ALL = 0xFFFFFFFF, + BLOCK_BANNER_SETTINGS = 0x800000, + BLOCK_ALL = 0xFFFFFFFF }; enum @@ -214,7 +224,7 @@ enum ALT_DOL_FROM_SD_USB, ALT_DOL_ON_LAUNCH, ALT_DOL_DEFAULT, - ALT_DOL_MAX_CHOICE, + ALT_DOL_MAX_CHOICE }; enum @@ -222,7 +232,7 @@ enum HOME_MENU_SYSTEM, HOME_MENU_FULL, HOME_MENU_DEFAULT, - HOME_MENU_MAX_CHOICE, + HOME_MENU_MAX_CHOICE }; enum @@ -232,4 +242,10 @@ enum SEARCH_MAX_CHOICE }; +enum +{ + BANNER_START_ON_ZOOM, + BANNER_START_AFTER_ZOOM +}; + #endif diff --git a/source/settings/SettingsPrompts.cpp b/source/settings/SettingsPrompts.cpp index 1660d9b0..45098124 100644 --- a/source/settings/SettingsPrompts.cpp +++ b/source/settings/SettingsPrompts.cpp @@ -30,13 +30,15 @@ extern u8 reset; bool MenuBackgroundMusic() { bool ret = false; - char entered[1024]; int result = -1; - snprintf( entered, sizeof( entered ), "%s", Settings.ogg_path ); + char entered[1024]; + strlcpy(entered, Settings.ogg_path, sizeof(entered)); - if ( strcmp( entered, "" ) == 0 ) + // Check the OGG path. + if (entered[0] == 0 ) { - sprintf( entered, "%s", Settings.BootDevice ); + // OGG path is empty. + strlcpy(entered, Settings.BootDevice, sizeof(entered)); } else { @@ -61,7 +63,7 @@ bool MenuBackgroundMusic() return true; } else - sprintf( entered, "%s", Settings.BootDevice ); + strlcpy(entered, Settings.BootDevice, sizeof(entered)); } result = BrowseDevice( entered, sizeof( entered ), FB_DEFAULT ); @@ -98,23 +100,20 @@ int MenuLanguageSelect() GuiTrigger trigB; trigB.SetButtonOnlyTrigger( -1, WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B, PAD_BUTTON_B ); - char fullpath[100]; + char fullpath[150]; DirList Dir(Settings.languagefiles_path, ".lang"); - if ( !strcmp( "", Settings.language_path ) ) - { - sprintf( fullpath, "%s", tr( "Default" ) ); - } + // Check if a language is specified. + if (Settings.language_path[0] == 0) + strlcpy(fullpath, tr( "Default" ), sizeof(fullpath)); else - { - sprintf( fullpath, "%s", Settings.languagefiles_path ); - } + strlcpy(fullpath, Settings.languagefiles_path, sizeof(fullpath)); GuiText titleTxt( fullpath, 24, ( GXColor ) {0, 0, 0, 255} ); - titleTxt.SetAlignment( ALIGN_CENTRE, ALIGN_MIDDLE ); + titleTxt.SetAlignment( ALIGN_CENTER, ALIGN_MIDDLE ); titleTxt.SetPosition( 0, 0 ); GuiButton pathBtn( 300, 50 ); - pathBtn.SetAlignment( ALIGN_CENTRE, ALIGN_TOP ); + pathBtn.SetAlignment( ALIGN_CENTER, ALIGN_TOP ); pathBtn.SetPosition( 0, 28 ); pathBtn.SetLabel( &titleTxt ); pathBtn.SetSoundOver( btnSoundOver ); @@ -135,7 +134,7 @@ int MenuLanguageSelect() backBtnImg.SetWidescreen( Settings.widescreen ); } GuiButton backBtn( btnOutline.GetWidth(), btnOutline.GetHeight() ); - backBtn.SetAlignment( ALIGN_CENTRE, ALIGN_TOP ); + backBtn.SetAlignment( ALIGN_CENTER, ALIGN_TOP ); backBtn.SetPosition( -190, 400 ); backBtn.SetLabel( &backBtnTxt ); backBtn.SetImage( &backBtnImg ); @@ -154,7 +153,7 @@ int MenuLanguageSelect() defaultBtnImg.SetWidescreen( Settings.widescreen ); } GuiButton defaultBtn( btnOutline.GetWidth(), btnOutline.GetHeight() ); - defaultBtn.SetAlignment( ALIGN_CENTRE, ALIGN_TOP ); + defaultBtn.SetAlignment( ALIGN_CENTER, ALIGN_TOP ); defaultBtn.SetPosition( 190, 400 ); defaultBtn.SetLabel( &defaultBtnTxt ); defaultBtn.SetImage( &defaultBtnImg ); @@ -172,7 +171,7 @@ int MenuLanguageSelect() updateBtnImg.SetWidescreen( Settings.widescreen ); } GuiButton updateBtn( btnOutline.GetWidth(), btnOutline.GetHeight() ); - updateBtn.SetAlignment( ALIGN_CENTRE, ALIGN_TOP ); + updateBtn.SetAlignment( ALIGN_CENTER, ALIGN_TOP ); updateBtn.SetPosition( 0, 400 ); updateBtn.SetLabel( &updateBtnTxt ); updateBtn.SetImage( &updateBtnImg ); @@ -185,6 +184,9 @@ int MenuLanguageSelect() for ( cnt = 0; cnt < Dir.GetFilecount(); cnt++ ) { + if(!Dir.GetFilename( cnt )) + continue; + char filename[64]; strlcpy( filename, Dir.GetFilename( cnt ), sizeof( filename ) ); char *dot = strchr( filename, '.' ); @@ -196,7 +198,7 @@ int MenuLanguageSelect() GuiOptionBrowser optionBrowser4( 396, 280, &options2, "bg_options_settings.png"); optionBrowser4.SetPosition( 0, 90 ); - optionBrowser4.SetAlignment( ALIGN_CENTRE, ALIGN_TOP ); + optionBrowser4.SetAlignment( ALIGN_CENTER, ALIGN_TOP ); HaltGui(); GuiWindow w( screenwidth, screenheight ); diff --git a/source/settings/menus/BannerSettingsMenu.cpp b/source/settings/menus/BannerSettingsMenu.cpp new file mode 100644 index 00000000..27d30ea6 --- /dev/null +++ b/source/settings/menus/BannerSettingsMenu.cpp @@ -0,0 +1,179 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include +#include "BannerSettingsMenu.hpp" +#include "settings/CSettings.h" +#include "settings/SettingsPrompts.h" +#include "prompts/PromptWindows.h" +#include "language/gettext.h" + +static const char * AnimStartText[] = +{ + trNOOP( "During zoom" ), + trNOOP( "After zoom" ), +}; + +static const char * OnOffText[] = +{ + trNOOP( "OFF" ), + trNOOP( "ON" ) +}; + +BannerSettingsMenu::BannerSettingsMenu() + : SettingsMenu(tr("Banner Animation Settings"), &GuiOptions, MENU_NONE) +{ + int Idx = 0; + Options->SetName(Idx++, "%s", tr( "Cache BNR Files" )); + Options->SetName(Idx++, "%s", tr( "Animation Start" )); + Options->SetName(Idx++, "%s", tr( "Zoom Duration (Speed)" )); + Options->SetName(Idx++, "%s", tr( "Grid Scroll Speed" )); + Options->SetName(Idx++, "%s", tr( "Frame Projection X-Offset" )); + Options->SetName(Idx++, "%s", tr( "Frame Projection Y-Offset" )); + Options->SetName(Idx++, "%s", tr( "Frame Projection Width" )); + Options->SetName(Idx++, "%s", tr( "Frame Projection Height" )); + Options->SetName(Idx++, "%s", tr( "GC Banner Scale" )); + + SetOptionValues(); +} + +void BannerSettingsMenu::SetOptionValues() +{ + int Idx = 0; + + //! Settings: Cache BNR Files + Options->SetValue(Idx++, "%s", tr(OnOffText[Settings.CacheBNRFiles])); + + //! Settings: Animation Start + Options->SetValue(Idx++, "%s", tr(AnimStartText[Settings.BannerAnimStart])); + + //! Settings: Zoom Duration (Speed) + Options->SetValue(Idx++, "%i %s", Settings.BannerZoomDuration, tr("Frames")); + + //! Settings: Grid Scroll Speed + Options->SetValue(Idx++, "%g %s/%s", Settings.BannerGridSpeed, tr("Pixels"), tr("Frame")); + + //! Settings: Frame Projection X-Offset + Options->SetValue(Idx++, "%g %s", Settings.BannerProjectionOffsetX, tr("Pixels")); + + //! Settings: Frame Projection Y-Offset + Options->SetValue(Idx++, "%g %s", Settings.BannerProjectionOffsetY, tr("Pixels")); + + //! Settings: Frame Projection Width + Options->SetValue(Idx++, "%g %s", Settings.BannerProjectionWidth, tr("Pixels")); + + //! Settings: Frame Projection Height + Options->SetValue(Idx++, "%g %s", Settings.BannerProjectionHeight, tr("Pixels")); + + //! Settings: GC Banner Scale + Options->SetValue(Idx++, "%g", Settings.GCBannerScale); +} + +int BannerSettingsMenu::GetMenuInternal() +{ + int ret = optionBrowser->GetClickedOption(); + + if (ret < 0) + return MENU_NONE; + + int Idx = -1; + + //! Settings: Cache BNR Files + if (ret == ++Idx) + { + if (++Settings.CacheBNRFiles >= MAX_ON_OFF) Settings.CacheBNRFiles = 0; + } + + //! Settings: Animation Start + else if (ret == ++Idx) + { + if (++Settings.BannerAnimStart > 1) Settings.BannerAnimStart = 0; + } + + //! Settings: Zoom Duration (Speed) + else if (ret == ++Idx) + { + char entrie[20]; + snprintf(entrie, sizeof(entrie), "%i", Settings.BannerZoomDuration); + int ret = OnScreenNumpad(entrie, sizeof(entrie)); + if(ret) + Settings.BannerZoomDuration = atoi(entrie); + } + + //! Settings: Grid Scroll Speed + else if (ret == ++Idx) + { + char entrie[20]; + snprintf(entrie, sizeof(entrie), "%g", Settings.BannerGridSpeed); + int ret = OnScreenNumpad(entrie, sizeof(entrie)); + if(ret) + Settings.BannerGridSpeed = atof(entrie); + } + + //! Settings: Frame Projection X-Offset + else if (ret == ++Idx) + { + char entrie[20]; + snprintf(entrie, sizeof(entrie), "%g", Settings.BannerProjectionOffsetX); + int ret = OnScreenNumpad(entrie, sizeof(entrie)); + if(ret) + Settings.BannerProjectionOffsetX = atof(entrie); + } + + //! Settings: Frame Projection Y-Offset + else if (ret == ++Idx) + { + char entrie[20]; + snprintf(entrie, sizeof(entrie), "%g", Settings.BannerProjectionOffsetY); + int ret = OnScreenNumpad(entrie, sizeof(entrie)); + if(ret) + Settings.BannerProjectionOffsetY = atof(entrie); + } + + //! Settings: Frame Projection Width + else if (ret == ++Idx) + { + char entrie[20]; + snprintf(entrie, sizeof(entrie), "%g", Settings.BannerProjectionWidth); + int ret = OnScreenNumpad(entrie, sizeof(entrie)); + if(ret) + Settings.BannerProjectionWidth = atof(entrie); + } + + //! Settings: Frame Projection Height + else if (ret == ++Idx) + { + char entrie[20]; + snprintf(entrie, sizeof(entrie), "%g", Settings.BannerProjectionHeight); + int ret = OnScreenNumpad(entrie, sizeof(entrie)); + if(ret) + Settings.BannerProjectionHeight = atof(entrie); + } + + //! Settings: GC Banner Scale + else if (ret == ++Idx) + { + char entrie[20]; + snprintf(entrie, sizeof(entrie), "%g", Settings.GCBannerScale); + int ret = OnScreenNumpad(entrie, sizeof(entrie)); + if(ret) + Settings.GCBannerScale = atof(entrie); + } + + SetOptionValues(); + + return MENU_NONE; +} diff --git a/source/settings/menus/BannerSettingsMenu.hpp b/source/settings/menus/BannerSettingsMenu.hpp new file mode 100644 index 00000000..ddb0bd4a --- /dev/null +++ b/source/settings/menus/BannerSettingsMenu.hpp @@ -0,0 +1,34 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef BANNERSETTINGS_MENU_HPP_ +#define BANNERSETTINGS_MENU_HPP_ + +#include "SettingsMenu.hpp" + +class BannerSettingsMenu : public SettingsMenu +{ + public: + BannerSettingsMenu(); + protected: + void SetOptionValues(); + int GetMenuInternal(); + + OptionList GuiOptions; +}; + + +#endif diff --git a/source/settings/menus/CustomPathsSM.cpp b/source/settings/menus/CustomPathsSM.cpp index a084df68..c1e2375a 100644 --- a/source/settings/menus/CustomPathsSM.cpp +++ b/source/settings/menus/CustomPathsSM.cpp @@ -24,6 +24,7 @@ #include #include "Channels/channels.h" #include "Controls/DeviceHandler.hpp" +#include "GameCube/GCGames.h" #include "CustomPathsSM.hpp" #include "settings/SettingsPrompts.h" #include "settings/CSettings.h" @@ -56,6 +57,9 @@ CustomPathsSM::CustomPathsSM() Options->SetName(Idx++, tr("Wiinnertag Path")); Options->SetName(Idx++, tr("Nand Emu Path")); Options->SetName(Idx++, tr("Nand Emu Channel Path")); + Options->SetName(Idx++, tr("Main GameCube Path")); + Options->SetName(Idx++, tr("SD GameCube Path")); + Options->SetName(Idx++, tr("Cache BNR Files Path")); SetOptionValues(); } @@ -117,6 +121,15 @@ void CustomPathsSM::SetOptionValues() //! Settings: Nand Emu Channel Path Options->SetValue(Idx++, Settings.NandEmuChanPath); + + //! Settings: GameCube Games Path + Options->SetValue(Idx++, Settings.GameCubePath); + + //! Settings: SD GameCube Games Path + Options->SetValue(Idx++, Settings.GameCubeSDPath); + + //! Settings: Cache BNR Files Path + Options->SetValue(Idx++, Settings.BNRCachePath); } int CustomPathsSM::GetMenuInternal() @@ -274,6 +287,44 @@ int CustomPathsSM::GetMenuInternal() } } + //! Settings: GameCube Games Path + else if (ret == ++Idx) + { + titleTxt->SetText(tr( "Main GameCube Games Path" )); + if(ChangePath(Settings.GameCubePath, sizeof(Settings.GameCubePath))) + { + GCGames::Instance()->LoadAllGames(); + } + } + + //! Settings: SD GameCube Games Path + else if (ret == ++Idx) + { + char tmp_path[sizeof(Settings.GameCubeSDPath)]; + snprintf(tmp_path, sizeof(tmp_path), "%s", Settings.GameCubeSDPath); + + titleTxt->SetText(tr( "SD GameCube Games Path" )); + if(ChangePath(tmp_path, sizeof(tmp_path))) + { + if(strncmp(tmp_path, "sd", 2) != 0) + { + WindowPrompt(tr("Error:"), tr("This path must be on SD!"), tr("OK")); + } + else + { + snprintf(Settings.GameCubeSDPath, sizeof(Settings.GameCubeSDPath), "%s", tmp_path); + GCGames::Instance()->LoadAllGames(); + } + } + } + + //! Settings: Cache BNR Files Path + else if (ret == ++Idx) + { + titleTxt->SetText(tr( "Cache BNR Files Path" )); + ChangePath(Settings.BNRCachePath, sizeof(Settings.BNRCachePath)); + } + //! Global set back of the titleTxt after a change titleTxt->SetText(tr( "Custom Paths" )); SetOptionValues(); diff --git a/source/settings/menus/FeatureSettingsMenu.cpp b/source/settings/menus/FeatureSettingsMenu.cpp index 8ea1ee71..93526acf 100644 --- a/source/settings/menus/FeatureSettingsMenu.cpp +++ b/source/settings/menus/FeatureSettingsMenu.cpp @@ -66,6 +66,8 @@ FeatureSettingsMenu::FeatureSettingsMenu() Options->SetName(Idx++, "%s", tr( "Wiinnertag" )); Options->SetName(Idx++, "%s", tr( "Import Categories" )); Options->SetName(Idx++, "%s", tr( "Export All Saves to EmuNand" )); + Options->SetName(Idx++, "%s", tr( "Export Miis to EmuNand" )); + Options->SetName(Idx++, "%s", tr( "Export SYSCONF to EmuNand" )); Options->SetName(Idx++, "%s", tr( "Dump NAND to EmuNand" )); Options->SetName(Idx++, "%s", tr( "Install WAD to EmuNand" )); @@ -133,6 +135,12 @@ void FeatureSettingsMenu::SetOptionValues() //! Settings: Export Savegames to EmuNand Options->SetValue(Idx++, " "); + //! Settings: Export Miis to EmuNand + Options->SetValue(Idx++, " "); + + //! Settings: Export SYSCONF to EmuNand + Options->SetValue(Idx++, " "); + //! Settings: Dump NAND to EmuNand Options->SetValue(Idx++, " "); @@ -256,7 +264,7 @@ int FeatureSettingsMenu::GetMenuInternal() //! Settings: Export Savegames to EmuNand else if (ret == ++Idx) { - int choice = WindowPrompt(tr( "Do you want to extract all the save games?" ), tr("The save games will be extracted to your emu nand path. Attention: All existing saves will be overwritten."), tr( "Yes" ), tr( "Cancel" )); + int choice = WindowPrompt(tr( "Do you want to extract all the save games?" ), tr("The save games will be extracted to your emu nand save and channel path. Attention: All existing files will be overwritten."), tr( "Yes" ), tr( "Cancel" )); if (choice == 1) { ProgressCancelEnable(true); @@ -268,6 +276,15 @@ int FeatureSettingsMenu::GetMenuInternal() wString filter(gameList.GetCurrentFilter()); gameList.LoadUnfiltered(); + //! extract the Mii file + snprintf(nandPath, sizeof(nandPath), "/shared2/menu/FaceLib/RFL_DB.dat"); + snprintf(filePath, sizeof(filePath), "%s%s", Settings.NandEmuChanPath, nandPath); + if(!CheckFile(filePath)) + NandTitle::ExtractDir(nandPath, filePath); + snprintf(filePath, sizeof(filePath), "%s%s", Settings.NandEmuPath, nandPath); + if(!CheckFile(filePath)) + NandTitle::ExtractDir(nandPath, filePath); + for(int i = 0; i < gameList.size(); ++i) { if( gameList[i]->type != TYPE_GAME_WII_IMG @@ -327,10 +344,72 @@ int FeatureSettingsMenu::GetMenuInternal() } } + //! Settings: Export Miis to EmuNand + else if (ret == ++Idx) + { + int choice = WindowPrompt(tr( "Extract Miis to the Emu NAND?" ), tr("The Miis will be extracted to your emu nand path and emu nand channel path. Attention: All existing files will be overwritten."), tr( "Yes" ), tr( "Cancel" )); + if (choice == 1) + { + char filePath[512]; + char nandPath[ISFS_MAXPATH]; + bool Error = false; + ProgressCancelEnable(true); + StartProgress(tr("Extracting file:"), 0, 0, true, false); + + //! extract the Mii file + snprintf(nandPath, sizeof(nandPath), "/shared2/menu/FaceLib/RFL_DB.dat"); + snprintf(filePath, sizeof(filePath), "%s%s", Settings.NandEmuChanPath, nandPath); + if(NandTitle::ExtractFile(nandPath, filePath) < 0) + Error = true; + snprintf(filePath, sizeof(filePath), "%s%s", Settings.NandEmuPath, nandPath); + if(NandTitle::ExtractFile(nandPath, filePath) < 0) + Error = true; + + ProgressStop(); + ProgressCancelEnable(false); + + if(Error) + WindowPrompt(tr("Process finished."), tr("Errors occured."), tr("OK")); + else + WindowPrompt(tr("Success."), tr("All files extracted."), tr("OK")); + } + } + + //! Settings: Export SYSCONF to EmuNand + else if (ret == ++Idx) + { + int choice = WindowPrompt(tr( "Extract SYSCONF to the Emu NAND?" ), tr("The SYSCONF file will be extracted to your emu nand path and emu nand channel path. Attention: All existing files will be overwritten."), tr( "Yes" ), tr( "Cancel" )); + if (choice == 1) + { + char filePath[512]; + char nandPath[ISFS_MAXPATH]; + bool Error = false; + ProgressCancelEnable(true); + StartProgress(tr("Extracting file:"), 0, 0, true, false); + + //! extract the Mii file + snprintf(nandPath, sizeof(nandPath), "/shared2/sys/SYSCONF"); + snprintf(filePath, sizeof(filePath), "%s%s", Settings.NandEmuChanPath, nandPath); + if(NandTitle::ExtractFile(nandPath, filePath) < 0) + Error = true; + snprintf(filePath, sizeof(filePath), "%s%s", Settings.NandEmuPath, nandPath); + if(NandTitle::ExtractFile(nandPath, filePath) < 0) + Error = true; + + ProgressStop(); + ProgressCancelEnable(false); + + if(Error) + WindowPrompt(tr("Process finished."), tr("Errors occured."), tr("OK")); + else + WindowPrompt(tr("Success."), tr("All files extracted."), tr("OK")); + } + } + //! Settings: Dump NAND to EmuNand else if (ret == ++Idx) { - int choice = WindowPrompt(tr( "What to extract from NAND?" ), tr("The files will be extracted to your emu nand path. Attention: All existing files will be overwritten."), tr( "Everything" ), tr("Enter Path"), tr( "Cancel" )); + int choice = WindowPrompt(tr( "What to extract from NAND?" ), tr("The files will be extracted to your emu nand save and channel path. Attention: All existing files will be overwritten."), tr( "Everything" ), tr("Enter Path"), tr( "Cancel" )); if (choice) { char filePath[255]; @@ -390,7 +469,7 @@ int FeatureSettingsMenu::GetMenuInternal() this->Remove(optionBrowser); char wadpath[150]; - sprintf(wadpath, "%s/wad/", Settings.BootDevice); + snprintf(wadpath, sizeof(wadpath), "%s/wad/", Settings.BootDevice); int result = BrowseDevice(wadpath, sizeof(wadpath), FB_DEFAULT); if(result) diff --git a/source/settings/menus/FlyingButtonsMenu.cpp b/source/settings/menus/FlyingButtonsMenu.cpp index 78872312..f2cdf687 100644 --- a/source/settings/menus/FlyingButtonsMenu.cpp +++ b/source/settings/menus/FlyingButtonsMenu.cpp @@ -102,7 +102,7 @@ FlyingButtonsMenu::~FlyingButtonsMenu() ResumeGui(); SetEffect(EFFECT_FADE, -FADE_SPEED); - while(parentElement && this->GetEffect() > 0) usleep(100); + while(parentElement && this->GetEffect() > 0) usleep(10000); HaltGui(); if(parentElement) @@ -170,7 +170,7 @@ void FlyingButtonsMenu::SetPageIndicators() PageindicatorImg.push_back(new GuiImage(PageindicatorImgData)); PageindicatorTxt.push_back(new GuiText(fmt("%i", n+1), 22, ( GXColor ) {0, 0, 0, 255})); PageIndicatorBtn.push_back(new GuiButton(PageindicatorImgData->GetWidth(), PageindicatorImgData->GetHeight())); - PageIndicatorBtn[i]->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + PageIndicatorBtn[i]->SetAlignment(ALIGN_CENTER, ALIGN_TOP); PageIndicatorBtn[i]->SetPosition(270-DisplayedIndicators*35+35*i, 400); PageIndicatorBtn[i]->SetImage(PageindicatorImg[i]); PageIndicatorBtn[i]->SetLabel(PageindicatorTxt[i]); @@ -210,7 +210,7 @@ void FlyingButtonsMenu::SetMainButton(int position, const char * ButtonText, Gui MainButtonTxt[position]->SetMaxWidth(MainButtonImg[position]->GetWidth()); MainButton[position] = new GuiButton(imageData->GetWidth(), imageData->GetHeight()); - MainButton[position]->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + MainButton[position]->SetAlignment(ALIGN_CENTER, ALIGN_TOP); MainButton[position]->SetPosition(0, 90+(position % DISPLAY_BUTTONS)*70); MainButton[position]->SetImage(MainButtonImg[position]); MainButton[position]->SetImageOver(MainButtonImgOver[position]); @@ -279,7 +279,7 @@ void FlyingButtonsMenu::ShowMenu() HideMenu(); titleTxt = new GuiText(MenuTitle.c_str(), 28, thColor("r=0 g=0 b=0 a=255 - settings title text color")); - titleTxt->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt->SetAlignment(ALIGN_CENTER, ALIGN_TOP); titleTxt->SetPosition(0, 40); titleTxt->SetMaxWidth(310, SCROLL_HORIZONTAL); Append(titleTxt); @@ -344,7 +344,6 @@ void FlyingButtonsMenu::ShowButtonsEffects(int effect, int effect_speed) for(int i = FirstItem; i < (int) MainButton.size() && i < FirstItem+DISPLAY_BUTTONS; ++i) { MainButton[i]->StopEffect(); - MainButton[i]->SetEffectGrow(); MainButton[i]->SetEffect(effect, effect_speed); } @@ -358,7 +357,11 @@ void FlyingButtonsMenu::ShowButtonsEffects(int effect, int effect_speed) return; while (parentElement && MainButton[FirstItem]->GetEffect() > 0) - usleep(100); + usleep(10000); + + //! Allow button grow only after slide animation is done + for(int i = FirstItem; i < (int) MainButton.size() && i < FirstItem+DISPLAY_BUTTONS; ++i) + MainButton[i]->SetEffectGrow(); } void FlyingButtonsMenu::SlideButtons(int direction) @@ -393,7 +396,7 @@ void FlyingButtonsMenu::SlideButtons(int direction) int FlyingButtonsMenu::MainLoop() { - usleep(100); + usleep(50000); if(shutdown) Sys_Shutdown(); diff --git a/source/settings/menus/GCGameLoadSM.cpp b/source/settings/menus/GCGameLoadSM.cpp new file mode 100644 index 00000000..5a8ec6ff --- /dev/null +++ b/source/settings/menus/GCGameLoadSM.cpp @@ -0,0 +1,305 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include +#include +#include "settings/CSettings.h" +#include "settings/CGameStatistics.h" +#include "themes/CTheme.h" +#include "prompts/PromptWindows.h" +#include "prompts/DiscBrowser.h" +#include "prompts/filebrowser.h" +#include "usbloader/AlternateDOLOffsets.h" +#include "language/gettext.h" +#include "wad/nandtitle.h" +#include "system/IosLoader.h" +#include "GCGameLoadSM.hpp" + +static const char * OnOffText[] = +{ + trNOOP( "OFF" ), + trNOOP( "ON" ), + trNOOP( "Auto" ) +}; + +static const char * VideoModeText[] = +{ + trNOOP( "System Default" ), + trNOOP( "Disc Default" ), + trNOOP( "Force PAL50" ), + trNOOP( "Force PAL60" ), + trNOOP( "Force NTSC" ), + trNOOP( "Region Patch" ), + trNOOP( "Force PAL480p" ), + trNOOP( "Force NTSC480p" ), +}; + +static const char * ParentalText[] = +{ + trNOOP( "0 (Everyone)" ), + trNOOP( "1 (Child 7+)" ), + trNOOP( "2 (Teen 12+)" ), + trNOOP( "3 (Mature 16+)" ), + trNOOP( "4 (Adults Only 18+)" ) +}; + +static const char * DMLVideoModeText[] = +{ + trNOOP( "DML Auto" ), + trNOOP( "DML None" ), + trNOOP( "Force PAL50" ), + trNOOP( "Force PAL60" ), + trNOOP( "Force NTSC" ), + trNOOP( "Force PAL480p" ), + trNOOP( "Force NTSC480p" ), +}; + +static const char * DMLNMMMode[] = +{ + trNOOP( "OFF" ), + trNOOP( "ON" ), + trNOOP( "Debug" ), +}; + +static const char * DMLDebug[] = +{ + trNOOP( "OFF" ), + trNOOP( "ON" ), + trNOOP( "Debug Wait" ), +}; + +GCGameLoadSM::GCGameLoadSM(struct discHdr *hdr) + : SettingsMenu(tr("Game Load"), &GuiOptions, MENU_NONE), + Header(hdr) +{ + GameConfig = *GameSettings.GetGameCFG((const char *) Header->id); + + if(!btnOutline) + btnOutline = Resources::GetImageData("button_dialogue_box.png"); + if(!trigA) + trigA = new GuiTrigger(); + trigA->SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); + + saveBtnTxt = new GuiText(tr( "Save" ), 22, thColor("r=0 g=0 b=0 a=255 - prompt windows button text color")); + saveBtnTxt->SetMaxWidth(btnOutline->GetWidth() - 30); + saveBtnImg = new GuiImage (btnOutline); + if (Settings.wsprompt == ON) + { + saveBtnTxt->SetWidescreen(Settings.widescreen); + saveBtnImg->SetWidescreen(Settings.widescreen); + } + saveBtn = new GuiButton(saveBtnImg, saveBtnImg, 2, 3, 180, 400, trigA, btnSoundOver, btnSoundClick2, 1); + saveBtn->SetLabel(saveBtnTxt); + Append(saveBtn); + + SetOptionNames(); + SetOptionValues(); +} + +GCGameLoadSM::~GCGameLoadSM() +{ + HaltGui(); + //! The rest is destroyed in SettingsMenu.cpp + Remove(saveBtn); + delete saveBtnTxt; + delete saveBtnImg; + delete saveBtn; + ResumeGui(); +} + +void GCGameLoadSM::SetDefaultConfig() +{ + char id[7]; + snprintf(id, sizeof(id), GameConfig.id); + GameSettings.SetDefault(GameConfig); + snprintf(GameConfig.id, sizeof(GameConfig.id), id); +} + +void GCGameLoadSM::SetOptionNames() +{ + int Idx = 0; + + Options->SetName(Idx++, "%s", tr( "Game Lock" )); + Options->SetName(Idx++, "%s", tr( "Favorite Level" )); + Options->SetName(Idx++, "%s", tr( "Video Mode" )); + Options->SetName(Idx++, "%s", tr( "Ocarina" )); + Options->SetName(Idx++, "%s", tr( "Parental Control" )); + Options->SetName(Idx++, "%s", tr( "DML Video Mode" )); + Options->SetName(Idx++, "%s", tr( "DML NMM Mode" )); + Options->SetName(Idx++, "%s", tr( "DML LED Activity" )); + Options->SetName(Idx++, "%s", tr( "DML PAD Hook" )); + Options->SetName(Idx++, "%s", tr( "DML No Disc" )); + Options->SetName(Idx++, "%s", tr( "DML Debug" )); +} + +void GCGameLoadSM::SetOptionValues() +{ + int Idx = 0; + + //! Settings: Game Lock + Options->SetValue(Idx++, "%s", tr( OnOffText[GameConfig.Locked] )); + + //! Settings: Favorite Level + Options->SetValue(Idx++, "%i", GameStatistics.GetFavoriteRank(Header->id)); + + //! Settings: Video Mode + if(GameConfig.video == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr(VideoModeText[GameConfig.video])); + + //! Settings: Ocarina + if(GameConfig.ocarina == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr(OnOffText[GameConfig.ocarina])); + + //! Settings: Parental Control + Options->SetValue(Idx++, "%s", tr(ParentalText[GameConfig.parentalcontrol])); + + //! Settings: DML Video Mode + if(GameConfig.DMLVideo == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr(DMLVideoModeText[GameConfig.DMLVideo])); + + //! Settings: DML NMM Mode + if(GameConfig.DMLNMM == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr(DMLNMMMode[GameConfig.DMLNMM])); + + //! Settings: DML LED Activity + if(GameConfig.DMLActivityLED == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr(OnOffText[GameConfig.DMLActivityLED])); + + //! Settings: DML PAD Hook + if(GameConfig.DMLPADHOOK == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr(OnOffText[GameConfig.DMLPADHOOK])); + + //! Settings: DML No Disc + if(GameConfig.DMLNoDisc == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr(OnOffText[GameConfig.DMLNoDisc])); + + //! Settings: DML Debug + if(GameConfig.DMLDebug == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr(DMLDebug[GameConfig.DMLDebug])); +} + +int GCGameLoadSM::GetMenuInternal() +{ + if (saveBtn->GetState() == STATE_CLICKED) + { + if (GameSettings.AddGame(GameConfig) && GameSettings.Save()) + { + WindowPrompt(tr( "Successfully Saved" ), 0, tr( "OK" )); + } + else + WindowPrompt(tr( "Save Failed. No device inserted?" ), 0, tr( "OK" )); + + saveBtn->ResetState(); + } + + int ret = optionBrowser->GetClickedOption(); + + if (ret < 0) + return MENU_NONE; + + int Idx = -1; + + //! Settings: Game Lock + if (ret == ++Idx) + { + if (++GameConfig.Locked >= MAX_ON_OFF) GameConfig.Locked = 0; + } + + //! Settings: Favorite Level + else if (ret == ++Idx) + { + int Level = GameStatistics.GetFavoriteRank(Header->id); + if (++Level > 5) Level = 0; + + GameStatistics.SetFavoriteRank(Header->id, Level); + GameStatistics.Save(); + } + + //! Settings: Video Mode + else if (ret == ++Idx) + { + if (++GameConfig.video >= VIDEO_MODE_MAX) GameConfig.video = INHERIT; + } + + //! Settings: Ocarina + else if (ret == ++Idx) + { + if (++GameConfig.ocarina >= MAX_ON_OFF) GameConfig.ocarina = INHERIT; + } + + //! Settings: Parental Control + else if (ret == ++Idx) + { + if (++GameConfig.parentalcontrol >= 5) GameConfig.parentalcontrol = 0; + } + + //! Settings: DML Video Mode + else if (ret == ++Idx) + { + if (++GameConfig.DMLVideo >= 7) GameConfig.DMLVideo = INHERIT; + } + + //! Settings: DML NMM Mode + else if (ret == ++Idx) + { + if (++GameConfig.DMLNMM >= 3) GameConfig.DMLNMM = INHERIT; + } + + //! Settings: DML LED Activity + else if (ret == ++Idx) + { + if (++GameConfig.DMLActivityLED >= MAX_ON_OFF) GameConfig.DMLActivityLED = INHERIT; + } + + //! Settings: DML PAD Hook + else if (ret == ++Idx) + { + if (++GameConfig.DMLPADHOOK >= MAX_ON_OFF) GameConfig.DMLPADHOOK = INHERIT; + } + + //! Settings: DML No Disc + else if (ret == ++Idx) + { + if (++GameConfig.DMLNoDisc >= MAX_ON_OFF) GameConfig.DMLNoDisc = INHERIT; + } + + //! Settings: DML Debug + else if (ret == ++Idx) + { + if (++GameConfig.DMLDebug >= 3) GameConfig.DMLDebug = INHERIT; + } + + SetOptionValues(); + + return MENU_NONE; +} + diff --git a/source/settings/menus/GCGameLoadSM.hpp b/source/settings/menus/GCGameLoadSM.hpp new file mode 100644 index 00000000..e4eb035b --- /dev/null +++ b/source/settings/menus/GCGameLoadSM.hpp @@ -0,0 +1,44 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef GCGAMELOAD_SM_HPP +#define GCGAMELOAD_SM_HPP + +#include "SettingsMenu.hpp" +#include "settings/CGameSettings.h" + +class GCGameLoadSM : public SettingsMenu +{ + public: + GCGameLoadSM(struct discHdr *Header); + virtual ~GCGameLoadSM(); + protected: + void SetDefaultConfig(); + void SetOptionNames(); + void SetOptionValues(); + int GetMenuInternal(); + + struct discHdr *Header; + GameCFG GameConfig; + OptionList GuiOptions; + + GuiText * saveBtnTxt; + GuiImage * saveBtnImg; + GuiButton * saveBtn; +}; + + +#endif diff --git a/source/settings/menus/GUISettingsMenu.cpp b/source/settings/menus/GUISettingsMenu.cpp index 4616a13f..5a353d4e 100644 --- a/source/settings/menus/GUISettingsMenu.cpp +++ b/source/settings/menus/GUISettingsMenu.cpp @@ -30,6 +30,8 @@ #include "settings/SettingsPrompts.h" #include "settings/GameTitles.h" #include "settings/CGameCategories.hpp" +#include "SystemMenu/SystemMenuResources.h" +#include "system/IosLoader.h" #include "usbloader/wbfs.h" #include "themes/CTheme.h" #include "utils/tools.h" @@ -63,6 +65,12 @@ static const char * PromptButtonsText[MAX_ON_OFF] = trNOOP( "Widescreen Fix" ), }; +static const char * GameWindowText[2] = +{ + trNOOP( "Banner Animation" ), + trNOOP( "Rotating Disc" ), +}; + static const char * KeyboardText[KEYBOARD_MAX] = { "QWERTY", @@ -110,7 +118,9 @@ GuiSettingsMenu::GuiSettingsMenu() Options->SetName(Idx++, "%s", tr( "Display" )); Options->SetName(Idx++, "%s", tr( "Clock" )); Options->SetName(Idx++, "%s", tr( "Tooltips" )); + Options->SetName(Idx++, "%s", tr( "Tooltip Delay" )); Options->SetName(Idx++, "%s", tr( "Flip-X" )); + Options->SetName(Idx++, "%s", tr( "Game Window Mode" )); Options->SetName(Idx++, "%s", tr( "Prompts Buttons" )); Options->SetName(Idx++, "%s", tr( "Widescreen Factor" )); Options->SetName(Idx++, "%s", tr( "Font Scale Factor" )); @@ -157,18 +167,24 @@ void GuiSettingsMenu::SetOptionValues() //! Settings: Tooltips Options->SetValue(Idx++, "%s", tr(OnOffText[Settings.tooltips])); + //! Settings: Tooltip Delay + Options->SetValue(Idx++, "%i %s", Settings.TooltipDelay, tr("ms")); + //! Settings: Flip-X Options->SetValue(Idx++, "%s%s%s", tr(FlipXText[Settings.xflip][0]), FlipXText[Settings.xflip][1], tr( FlipXText[Settings.xflip][2] )); + //! Settings: Game Window Mode + Options->SetValue(Idx++, "%s", tr( GameWindowText[Settings.GameWindowMode] )); + //! Settings: Prompts Buttons Options->SetValue(Idx++, "%s", tr( PromptButtonsText[Settings.wsprompt] )); //! Settings: Widescreen Factor - Options->SetValue(Idx++, "%0.3f", Settings.WSFactor); + Options->SetValue(Idx++, "%g", Settings.WSFactor); //! Settings: Font Scale Factor - Options->SetValue(Idx++, "%0.3f", Settings.FontScaleFactor); + Options->SetValue(Idx++, "%g", Settings.FontScaleFactor); //! Settings: Keyboard Options->SetValue(Idx++, "%s", KeyboardText[Settings.keyset]); @@ -275,12 +291,33 @@ int GuiSettingsMenu::GetMenuInternal() if (++Settings.tooltips >= MAX_ON_OFF) Settings.tooltips = 0; } + //! Settings: Tooltip Delay + else if (ret == ++Idx) + { + char entrie[20]; + snprintf(entrie, sizeof(entrie), "%i", Settings.TooltipDelay); + int ret = OnScreenNumpad(entrie, sizeof(entrie)); + if(ret) + Settings.TooltipDelay = atoi(entrie); + } + //! Settings: Flip-X else if (ret == ++Idx) { if (++Settings.xflip >= XFLIP_MAX) Settings.xflip = 0; } + //! Settings: Game Window Mode + else if (ret == ++Idx) + { + if (++Settings.GameWindowMode >= 2) Settings.GameWindowMode = 0; + + if(Settings.GameWindowMode == GAMEWINDOW_BANNER && !SystemMenuResources::Instance()->IsLoaded()) { + WindowPrompt(tr( "Error:" ), tr( "Banner window is only available with AHBPROT! Please consider installing new HBC version." ), tr( "OK" )); + Settings.GameWindowMode = GAMEWINDOW_DISC; + } + } + //! Settings: Prompts Buttons else if (ret == ++Idx) { @@ -291,36 +328,20 @@ int GuiSettingsMenu::GetMenuInternal() else if (ret == ++Idx) { char entrie[20]; - snprintf(entrie, sizeof(entrie), "%0.3f", Settings.WSFactor); - int ret = OnScreenKeyboard(entrie, sizeof(entrie), 0); + snprintf(entrie, sizeof(entrie), "%g", Settings.WSFactor); + int ret = OnScreenNumpad(entrie, sizeof(entrie)); if(ret) - { - for(u32 i = 0; i < sizeof(entrie); ++i) - { - if(entrie[i] == ',') - entrie[i] = '.'; - } - Settings.WSFactor = LIMIT(atof(entrie), 0.01f, 1.5f); - } } //! Settings: Font Scale Factor else if (ret == ++Idx) { char entrie[20]; - snprintf(entrie, sizeof(entrie), "%0.3f", Settings.FontScaleFactor); - int ret = OnScreenKeyboard(entrie, sizeof(entrie), 0); + snprintf(entrie, sizeof(entrie), "%g", Settings.FontScaleFactor); + int ret = OnScreenNumpad(entrie, sizeof(entrie)); if(ret) - { - for(u32 i = 0; i < sizeof(entrie); ++i) - { - if(entrie[i] == ',') - entrie[i] = '.'; - } - Settings.FontScaleFactor = LIMIT(atof(entrie), 0.01f, 1.5f); - } } //! Settings: Keyboard @@ -393,17 +414,9 @@ int GuiSettingsMenu::GetMenuInternal() { char entrie[20]; snprintf(entrie, sizeof(entrie), "%g", Settings.PointerSpeed); - int ret = OnScreenKeyboard(entrie, sizeof(entrie), 0); + int ret = OnScreenNumpad(entrie, sizeof(entrie)); if(ret) - { - for(u32 i = 0; i < sizeof(entrie); ++i) - { - if(entrie[i] == ',') - entrie[i] = '.'; - } - Settings.PointerSpeed = atof(entrie); - } } //! Settings: Adjust Overscan X @@ -411,7 +424,7 @@ int GuiSettingsMenu::GetMenuInternal() { char entrie[20]; snprintf(entrie, sizeof(entrie), "%i", Settings.AdjustOverscanX); - int ret = OnScreenKeyboard(entrie, sizeof(entrie), 0); + int ret = OnScreenNumpad(entrie, sizeof(entrie)); if(ret) { Settings.AdjustOverscanX = atoi(entrie); @@ -424,7 +437,7 @@ int GuiSettingsMenu::GetMenuInternal() { char entrie[20]; snprintf(entrie, sizeof(entrie), "%i", Settings.AdjustOverscanY); - int ret = OnScreenKeyboard(entrie, sizeof(entrie), 0); + int ret = OnScreenNumpad(entrie, sizeof(entrie)); if(ret) { Settings.AdjustOverscanY = atoi(entrie); diff --git a/source/settings/menus/GameLoadSM.cpp b/source/settings/menus/GameLoadSM.cpp index 9203a961..8d963971 100644 --- a/source/settings/menus/GameLoadSM.cpp +++ b/source/settings/menus/GameLoadSM.cpp @@ -24,6 +24,7 @@ #include #include #include "settings/CSettings.h" +#include "settings/CGameStatistics.h" #include "themes/CTheme.h" #include "prompts/PromptWindows.h" #include "prompts/DiscBrowser.h" @@ -171,31 +172,21 @@ void GameLoadSM::SetOptionNames() int Idx = 0; Options->SetName(Idx++, "%s", tr( "Game Lock" )); + Options->SetName(Idx++, "%s", tr( "Favorite Level" )); Options->SetName(Idx++, "%s", tr( "Video Mode" )); - - //! Not available on GC - if(Header->type != TYPE_GAME_GC_IMG && Header->type != TYPE_GAME_GC_DISC) - { - Options->SetName(Idx++, "%s", tr( "VIDTV Patch" )); - Options->SetName(Idx++, "%s", tr( "Sneek Video Patch" )); - Options->SetName(Idx++, "%s", tr( "Aspect Ratio" )); - Options->SetName(Idx++, "%s", tr( "Patch Country Strings" )); - } - + Options->SetName(Idx++, "%s", tr( "VIDTV Patch" )); + Options->SetName(Idx++, "%s", tr( "Sneek Video Patch" )); + Options->SetName(Idx++, "%s", tr( "Aspect Ratio" )); + Options->SetName(Idx++, "%s", tr( "Patch Country Strings" )); Options->SetName(Idx++, "%s", tr( "Game Language" )); Options->SetName(Idx++, "%s", tr( "Ocarina" )); Options->SetName(Idx++, "%s", tr( "Parental Control" )); - - //! Not available on GC - if(Header->type != TYPE_GAME_GC_IMG && Header->type != TYPE_GAME_GC_DISC) - { - Options->SetName(Idx++, "%s", tr( "Hooktype" )); - Options->SetName(Idx++, "%s", tr( "Wiird Debugger" )); - Options->SetName(Idx++, "%s", tr( "Game IOS" )); - Options->SetName(Idx++, "%s", tr( "Error 002 fix" )); - Options->SetName(Idx++, "%s", tr( "Return To" )); - Options->SetName(Idx++, "%s", tr( "Block IOS Reload" )); - } + Options->SetName(Idx++, "%s", tr( "Hooktype" )); + Options->SetName(Idx++, "%s", tr( "Wiird Debugger" )); + Options->SetName(Idx++, "%s", tr( "Game IOS" )); + Options->SetName(Idx++, "%s", tr( "Error 002 fix" )); + Options->SetName(Idx++, "%s", tr( "Return To" )); + Options->SetName(Idx++, "%s", tr( "Block IOS Reload" )); //! Only wii games and emu nand channels if( Header->type == TYPE_GAME_WII_IMG @@ -221,39 +212,38 @@ void GameLoadSM::SetOptionValues() //! Settings: Game Lock Options->SetValue(Idx++, "%s", tr( OnOffText[GameConfig.Locked] )); + //! Settings: Favorite Level + Options->SetValue(Idx++, "%i", GameStatistics.GetFavoriteRank(Header->id)); + //! Settings: Video Mode if(GameConfig.video == INHERIT) Options->SetValue(Idx++, tr("Use global")); else Options->SetValue(Idx++, "%s", tr(VideoModeText[GameConfig.video])); - //! Not available on GC - if(Header->type != TYPE_GAME_GC_IMG && Header->type != TYPE_GAME_GC_DISC) - { - //! Settings: VIDTV Patch - if(GameConfig.vipatch == INHERIT) - Options->SetValue(Idx++, tr("Use global")); - else - Options->SetValue(Idx++, "%s", tr(OnOffText[GameConfig.vipatch])); + //! Settings: VIDTV Patch + if(GameConfig.vipatch == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr(OnOffText[GameConfig.vipatch])); - //! Settings: Sneek Video Patch - if(GameConfig.sneekVideoPatch == INHERIT) - Options->SetValue(Idx++, tr("Use global")); - else - Options->SetValue(Idx++, "%s", tr(OnOffText[GameConfig.sneekVideoPatch])); + //! Settings: Sneek Video Patch + if(GameConfig.sneekVideoPatch == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr(OnOffText[GameConfig.sneekVideoPatch])); - //! Settings: Aspect Ratio - if(GameConfig.aspectratio == INHERIT) - Options->SetValue(Idx++, tr("Use global")); - else - Options->SetValue(Idx++, "%s", tr(AspectText[GameConfig.aspectratio])); + //! Settings: Aspect Ratio + if(GameConfig.aspectratio == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr(AspectText[GameConfig.aspectratio])); - //! Settings: Patch Country Strings - if(GameConfig.patchcountrystrings == INHERIT) - Options->SetValue(Idx++, tr("Use global")); - else - Options->SetValue(Idx++, "%s", tr(OnOffText[GameConfig.patchcountrystrings])); - } + //! Settings: Patch Country Strings + if(GameConfig.patchcountrystrings == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr(OnOffText[GameConfig.patchcountrystrings])); //! Settings: Game Language if(GameConfig.language == INHERIT) @@ -270,54 +260,50 @@ void GameLoadSM::SetOptionValues() //! Settings: Parental Control Options->SetValue(Idx++, "%s", tr(ParentalText[GameConfig.parentalcontrol])); - //! Not available on GC - if(Header->type != TYPE_GAME_GC_IMG && Header->type != TYPE_GAME_GC_DISC) + //! Settings: Hooktype + if(GameConfig.Hooktype == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr( HooktypeText[GameConfig.Hooktype] )); + + //! Settings: Wiird Debugger + if(GameConfig.WiirdDebugger == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr( OnOffText[GameConfig.WiirdDebugger] )); + + //! Settings: Game IOS + if(GameConfig.ios == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%i", GameConfig.ios); + + //! Settings: Error 002 fix + if(GameConfig.errorfix002 == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr(Error002Text[GameConfig.errorfix002])); + + //! Settings: Return To + if(GameConfig.returnTo) { - //! Settings: Hooktype - if(GameConfig.Hooktype == INHERIT) - Options->SetValue(Idx++, tr("Use global")); - else - Options->SetValue(Idx++, "%s", tr( HooktypeText[GameConfig.Hooktype] )); - - //! Settings: Wiird Debugger - if(GameConfig.WiirdDebugger == INHERIT) - Options->SetValue(Idx++, tr("Use global")); - else - Options->SetValue(Idx++, "%s", tr( OnOffText[GameConfig.WiirdDebugger] )); - - //! Settings: Game IOS - if(GameConfig.ios == INHERIT) - Options->SetValue(Idx++, tr("Use global")); - else - Options->SetValue(Idx++, "%i", GameConfig.ios); - - //! Settings: Error 002 fix - if(GameConfig.errorfix002 == INHERIT) - Options->SetValue(Idx++, tr("Use global")); - else - Options->SetValue(Idx++, "%s", tr(Error002Text[GameConfig.errorfix002])); - - //! Settings: Return To - if(GameConfig.returnTo) - { - const char* TitleName = NULL; - u64 tid = NandTitles.FindU32(Settings.returnTo); - if (tid > 0) - TitleName = NandTitles.NameOf(tid); - Options->SetValue(Idx++, "%s", TitleName ? TitleName : strlen(Settings.returnTo) > 0 ? - Settings.returnTo : tr( OnOffText[0] )); - } - else - { - Options->SetValue(Idx++, "%s", tr( OnOffText[0] )); - } - - //! Settings: Block IOS Reload - if(GameConfig.iosreloadblock == INHERIT) - Options->SetValue(Idx++, tr("Use global")); - else - Options->SetValue(Idx++, "%s", tr( OnOffText[GameConfig.iosreloadblock]) ); + const char* TitleName = NULL; + u64 tid = NandTitles.FindU32(Settings.returnTo); + if (tid > 0) + TitleName = NandTitles.NameOf(tid); + Options->SetValue(Idx++, "%s", TitleName ? TitleName : strlen(Settings.returnTo) > 0 ? + Settings.returnTo : tr( OnOffText[0] )); } + else + { + Options->SetValue(Idx++, "%s", tr( OnOffText[0] )); + } + + //! Settings: Block IOS Reload + if(GameConfig.iosreloadblock == INHERIT) + Options->SetValue(Idx++, tr("Use global")); + else + Options->SetValue(Idx++, "%s", tr( OnOffText[GameConfig.iosreloadblock]) ); //! Only wii games and emu nand channels if( Header->type == TYPE_GAME_WII_IMG @@ -383,38 +369,44 @@ int GameLoadSM::GetMenuInternal() if (++GameConfig.Locked >= MAX_ON_OFF) GameConfig.Locked = 0; } + //! Settings: Favorite Level + else if (ret == ++Idx) + { + int Level = GameStatistics.GetFavoriteRank(Header->id); + if (++Level > 5) Level = 0; + + GameStatistics.SetFavoriteRank(Header->id, Level); + GameStatistics.Save(); + } + //! Settings: Video Mode else if (ret == ++Idx) { if (++GameConfig.video >= VIDEO_MODE_MAX) GameConfig.video = INHERIT; } - //! Not available on GC - if(Header->type != TYPE_GAME_GC_IMG && Header->type != TYPE_GAME_GC_DISC) + //! Settings: VIDTV Patch + if (ret == ++Idx) { - //! Settings: VIDTV Patch - if (ret == ++Idx) - { - if (++GameConfig.vipatch >= MAX_ON_OFF) GameConfig.vipatch = INHERIT; - } + if (++GameConfig.vipatch >= MAX_ON_OFF) GameConfig.vipatch = INHERIT; + } - //! Settings: Sneek Video Patch - else if (ret == ++Idx) - { - if (++GameConfig.sneekVideoPatch >= MAX_ON_OFF) GameConfig.sneekVideoPatch = INHERIT; - } + //! Settings: Sneek Video Patch + else if (ret == ++Idx) + { + if (++GameConfig.sneekVideoPatch >= MAX_ON_OFF) GameConfig.sneekVideoPatch = INHERIT; + } - //! Settings: Aspect Ratio - else if (ret == ++Idx) - { - if (++GameConfig.aspectratio >= ASPECT_MAX) GameConfig.aspectratio = INHERIT; - } + //! Settings: Aspect Ratio + else if (ret == ++Idx) + { + if (++GameConfig.aspectratio >= ASPECT_MAX) GameConfig.aspectratio = INHERIT; + } - //! Settings: Patch Country Strings - if (ret == ++Idx) - { - if (++GameConfig.patchcountrystrings >= MAX_ON_OFF) GameConfig.patchcountrystrings = INHERIT; - } + //! Settings: Patch Country Strings + if (ret == ++Idx) + { + if (++GameConfig.patchcountrystrings >= MAX_ON_OFF) GameConfig.patchcountrystrings = INHERIT; } //! Settings: Game Language @@ -435,30 +427,33 @@ int GameLoadSM::GetMenuInternal() if (++GameConfig.parentalcontrol >= 5) GameConfig.parentalcontrol = 0; } - //! Not available on GC - if(Header->type != TYPE_GAME_GC_IMG && Header->type != TYPE_GAME_GC_DISC) + //! Settings: Hooktype + if (ret == ++Idx) { - //! Settings: Hooktype - if (ret == ++Idx) - { - if (++GameConfig.Hooktype >= 8) GameConfig.Hooktype = INHERIT; - } + if (++GameConfig.Hooktype >= 8) GameConfig.Hooktype = INHERIT; + } - //! Settings: Wiird Debugger - else if (ret == ++Idx) - { - if (++GameConfig.WiirdDebugger >= MAX_ON_OFF) GameConfig.WiirdDebugger = INHERIT; - } + //! Settings: Wiird Debugger + else if (ret == ++Idx) + { + if (++GameConfig.WiirdDebugger >= MAX_ON_OFF) GameConfig.WiirdDebugger = INHERIT; + } - //! Settings: Game IOS - else if (ret == ++Idx) + //! Settings: Game IOS + else if (ret == ++Idx) + { + char entered[4]; + snprintf(entered, sizeof(entered), "%i", GameConfig.ios); + if(OnScreenNumpad(entered, sizeof(entered))) { - char entered[4]; - snprintf(entered, sizeof(entered), "%i", GameConfig.ios); - if(OnScreenKeyboard(entered, sizeof(entered), 0)) + int newIOS = atoi(entered); + + if(newIOS != INHERIT && (newIOS < 200 || newIOS > 255)) { + WindowPrompt(tr("Error:"), tr("Invalid IOS number entered. Number must be -1 for inherit or 200 - 255."), tr("OK")); + } + else { - GameConfig.ios = atoi(entered) & 0xFF; - if(GameConfig.ios < 200 && GameConfig.ios != INHERIT) GameConfig.ios = 200; + GameConfig.ios = newIOS; if(GameConfig.ios != INHERIT && NandTitles.IndexOf(TITLE_ID(1, GameConfig.ios)) < 0) { @@ -470,24 +465,24 @@ int GameLoadSM::GetMenuInternal() } } } + } - //! Settings: Error 002 fix - else if (ret == ++Idx) - { - if (++GameConfig.errorfix002 >= 3) GameConfig.errorfix002 = INHERIT; - } + //! Settings: Error 002 fix + else if (ret == ++Idx) + { + if (++GameConfig.errorfix002 >= 3) GameConfig.errorfix002 = INHERIT; + } - //! Settings: Return To - else if (ret == ++Idx) - { - if (++GameConfig.returnTo >= MAX_ON_OFF) GameConfig.returnTo = 0; - } + //! Settings: Return To + else if (ret == ++Idx) + { + if (++GameConfig.returnTo >= MAX_ON_OFF) GameConfig.returnTo = 0; + } - //! Settings: Block IOS Reload - if (ret == ++Idx) - { - if(++GameConfig.iosreloadblock >= 3) GameConfig.iosreloadblock = INHERIT; - } + //! Settings: Block IOS Reload + if (ret == ++Idx) + { + if(++GameConfig.iosreloadblock >= 3) GameConfig.iosreloadblock = INHERIT; } //! Only wii games and emu nand channels @@ -552,7 +547,7 @@ int GameLoadSM::GetMenuInternal() } //! Settings: Select DOL Offset from Game - else if ( (ret == ++Idx) + else if ( (ret == ++Idx) && (GameConfig.loadalternatedol == 1)) { GuiWindow * parentWindow = (GuiWindow *) parentElement; diff --git a/source/settings/menus/GameSettingsMenu.cpp b/source/settings/menus/GameSettingsMenu.cpp index 595f6ed2..020592ef 100644 --- a/source/settings/menus/GameSettingsMenu.cpp +++ b/source/settings/menus/GameSettingsMenu.cpp @@ -24,6 +24,7 @@ #include #include "GameSettingsMenu.hpp" #include "themes/CTheme.h" +#include "FileOperations/fileops.h" #include "prompts/PromptWindows.h" #include "prompts/ProgressWindow.h" #include "prompts/CategorySelectPrompt.hpp" @@ -33,6 +34,7 @@ #include "wad/nandtitle.h" #include "cheats/cheatmenu.h" #include "GameLoadSM.hpp" +#include "GCGameLoadSM.hpp" #include "UninstallSM.hpp" GameSettingsMenu::GameSettingsMenu(GameBrowseMenu *parent, struct discHdr * header) @@ -92,7 +94,16 @@ void GameSettingsMenu::CreateSettingsMenu(int menuNr) { HideMenu(); ResumeGui(); - CurrentMenu = new GameLoadSM(DiscHeader); + if( DiscHeader->type == TYPE_GAME_GC_IMG + || DiscHeader->type == TYPE_GAME_GC_DISC + || DiscHeader->type == TYPE_GAME_GC_EXTRACTED) + { + CurrentMenu = new GCGameLoadSM(DiscHeader); + } + else + { + CurrentMenu = new GameLoadSM(DiscHeader); + } Append(CurrentMenu); } @@ -169,6 +180,13 @@ void GameSettingsMenu::CreateSettingsMenu(int menuNr) snprintf(filePath, sizeof(filePath), "%s%s", Settings.NandEmuPath, nandPath); ret = NandTitle::ExtractDir(nandPath, filePath); } + + //! extract the Mii file if not yet done + snprintf(nandPath, sizeof(nandPath), "/shared2/menu/FaceLib/RFL_DB.dat"); + snprintf(filePath, sizeof(filePath), "%s%s", (DiscHeader->tid != 0) ? Settings.NandEmuChanPath : Settings.NandEmuPath, nandPath); + if(!CheckFile(filePath)) + NandTitle::ExtractDir(nandPath, filePath); + ProgressStop(); ProgressCancelEnable(false); diff --git a/source/settings/menus/GlobalSettings.cpp b/source/settings/menus/GlobalSettings.cpp index 8f13fb24..c1028433 100644 --- a/source/settings/menus/GlobalSettings.cpp +++ b/source/settings/menus/GlobalSettings.cpp @@ -33,6 +33,7 @@ #include "CustomPathsSM.hpp" #include "FeatureSettingsMenu.hpp" #include "HardDriveSM.hpp" +#include "BannerSettingsMenu.hpp" GlobalSettings::GlobalSettings() : FlyingButtonsMenu(tr("Global Settings")) @@ -73,11 +74,12 @@ void GlobalSettings::SetupMainButtons() SetMainButton(pos++, tr( "Loader Settings" ), MainButtonImgData, MainButtonImgOverData); SetMainButton(pos++, tr( "Hard Drive Settings" ), MainButtonImgData, MainButtonImgOverData); SetMainButton(pos++, tr( "Features" ), MainButtonImgData, MainButtonImgOverData); - SetMainButton(pos++, tr( "Parental Control" ), MainButtonImgData, MainButtonImgOverData); + SetMainButton(pos++, tr( "Banner Animation Settings" ), MainButtonImgData, MainButtonImgOverData); SetMainButton(pos++, tr( "Sound" ), MainButtonImgData, MainButtonImgOverData); + SetMainButton(pos++, tr( "Parental Control" ), MainButtonImgData, MainButtonImgOverData); + SetMainButton(pos++, tr( "Custom Paths" ), MainButtonImgData, MainButtonImgOverData); SetMainButton(pos++, tr( "Theme Menu" ), MainButtonImgData, MainButtonImgOverData); SetMainButton(pos++, tr( "Theme Downloader" ), MainButtonImgData, MainButtonImgOverData); - SetMainButton(pos++, tr( "Custom Paths" ), MainButtonImgData, MainButtonImgOverData); SetMainButton(pos++, tr( "Update" ), MainButtonImgData, MainButtonImgOverData); SetMainButton(pos++, tr( "Default Settings" ), MainButtonImgData, MainButtonImgOverData); SetMainButton(pos++, tr( "Credits" ), creditsImgData, creditsImgOverData); @@ -146,10 +148,10 @@ void GlobalSettings::CreateSettingsMenu(int menuNr) CurrentMenu = new FeatureSettingsMenu(); Append(CurrentMenu); } - //! Parental Control + //! Banner Animation Settings else if(menuNr == Idx++) { - if(!Settings.godmode && (Settings.ParentalBlocks & BLOCK_PARENTAL_SETTINGS)) + if(!Settings.godmode && (Settings.ParentalBlocks & BLOCK_BANNER_SETTINGS)) { WindowPrompt(tr( "Permission denied." ), tr( "Console must be unlocked for this option." ), tr( "OK" )); return; @@ -157,7 +159,7 @@ void GlobalSettings::CreateSettingsMenu(int menuNr) HideMenu(); ResumeGui(); - CurrentMenu = new ParentalControlSM(); + CurrentMenu = new BannerSettingsMenu(); Append(CurrentMenu); } //! Sound @@ -174,6 +176,34 @@ void GlobalSettings::CreateSettingsMenu(int menuNr) CurrentMenu = new SoundSettingsMenu(); Append(CurrentMenu); } + //! Parental Control + else if(menuNr == Idx++) + { + if(!Settings.godmode && (Settings.ParentalBlocks & BLOCK_PARENTAL_SETTINGS)) + { + WindowPrompt(tr( "Permission denied." ), tr( "Console must be unlocked for this option." ), tr( "OK" )); + return; + } + + HideMenu(); + ResumeGui(); + CurrentMenu = new ParentalControlSM(); + Append(CurrentMenu); + } + //! Custom Paths + else if(menuNr == Idx++) + { + if(!Settings.godmode && (Settings.ParentalBlocks & BLOCK_CUSTOMPATH_SETTINGS)) + { + WindowPrompt(tr( "Permission denied." ), tr( "Console must be unlocked for this option." ), tr( "OK" )); + return; + } + + HideMenu(); + ResumeGui(); + CurrentMenu = new CustomPathsSM(); + Append(CurrentMenu); + } //! Theme Menu else if(menuNr == Idx++) { @@ -196,20 +226,6 @@ void GlobalSettings::CreateSettingsMenu(int menuNr) returnMenu = MENU_THEMEDOWNLOADER; } - //! Custom Paths - else if(menuNr == Idx++) - { - if(!Settings.godmode && (Settings.ParentalBlocks & BLOCK_CUSTOMPATH_SETTINGS)) - { - WindowPrompt(tr( "Permission denied." ), tr( "Console must be unlocked for this option." ), tr( "OK" )); - return; - } - - HideMenu(); - ResumeGui(); - CurrentMenu = new CustomPathsSM(); - Append(CurrentMenu); - } //! Update else if(menuNr == Idx++) { diff --git a/source/settings/menus/HardDriveSM.cpp b/source/settings/menus/HardDriveSM.cpp index 5c0cfeea..87bc5544 100644 --- a/source/settings/menus/HardDriveSM.cpp +++ b/source/settings/menus/HardDriveSM.cpp @@ -78,6 +78,8 @@ HardDriveSM::HardDriveSM() Options->SetName(Idx++, "%s", tr( "Install Directories" )); Options->SetName(Idx++, "%s", tr( "Game Split Size" )); Options->SetName(Idx++, "%s", tr( "Install Partitions" )); + Options->SetName(Idx++, "%s", tr( "GC Install Compressed" )); + Options->SetName(Idx++, "%s", tr( "GC Install 32K Aligned" )); Options->SetName(Idx++, "%s", tr( "Sync FAT32 FS Info" )); OldSettingsPartition = Settings.partition; @@ -155,6 +157,12 @@ void HardDriveSM::SetOptionValues() else if(Settings.InstallPartitions == REMOVE_UPDATE_PARTITION) Options->SetValue(Idx++, "%s", tr("Remove update")); + //! Settings: GC Install Compressed + Options->SetValue(Idx++, "%s", tr( OnOffText[Settings.GCInstallCompressed] )); + + //! Settings: GC Install 32K Aligned + Options->SetValue(Idx++, "%s", tr( OnOffText[Settings.GCInstallAligned] )); + //! Settings: Sync FAT32 FS Info Options->SetValue(Idx++, " "); } @@ -195,14 +203,14 @@ int HardDriveSM::GetMenuInternal() //! Settings: USB Port else if (ret == ++Idx) { - if(!IosLoader::IsHermesIOS()) + if(!IosLoader::IsHermesIOS() && !IosLoader::IsD2X()) { WindowPrompt(tr("ERROR:"), tr("USB Port changing is only supported on Hermes cIOS."), tr("OK")); NewSettingsUSBPort = 0; Settings.USBPort = 0; } - else if (++NewSettingsUSBPort >= 2) // 2 = both ports + else if (++NewSettingsUSBPort >= 3) // 2 = both ports NewSettingsUSBPort = 0; } @@ -242,6 +250,18 @@ int HardDriveSM::GetMenuInternal() } } + //! Settings: GC Install Compressed + else if (ret == ++Idx) + { + if (++Settings.GCInstallCompressed >= MAX_ON_OFF) Settings.GCInstallCompressed = 0; + } + + //! Settings: GC Install 32K Aligned + else if (ret == ++Idx) + { + if (++Settings.GCInstallAligned >= MAX_ON_OFF) Settings.GCInstallAligned = 0; + } + //! Settings: Sync FAT32 FS Info else if (ret == ++Idx ) { diff --git a/source/settings/menus/LoaderSettings.cpp b/source/settings/menus/LoaderSettings.cpp index 84ef6fc2..37e90f8f 100644 --- a/source/settings/menus/LoaderSettings.cpp +++ b/source/settings/menus/LoaderSettings.cpp @@ -33,6 +33,7 @@ #include "system/IosLoader.h" #include "usbloader/wbfs.h" #include "usbloader/GameList.h" +#include "utils/tools.h" #include "menu.h" static const char * loaderModeText[] = @@ -117,6 +118,31 @@ static const char * ChannelLaunchText[] = trNOOP( "Boot Content" ), }; +static const char * DMLVideoModeText[] = +{ + trNOOP( "DML Auto" ), + trNOOP( "DML None" ), + trNOOP( "Force PAL50" ), + trNOOP( "Force PAL60" ), + trNOOP( "Force NTSC" ), + trNOOP( "Force PAL480p" ), + trNOOP( "Force NTSC480p" ), +}; + +static const char * DMLNMMMode[] = +{ + trNOOP( "OFF" ), + trNOOP( "ON" ), + trNOOP( "Debug" ), +}; + +static const char * DMLDebug[] = +{ + trNOOP( "OFF" ), + trNOOP( "ON" ), + trNOOP( "Debug Wait" ), +}; + LoaderSettings::LoaderSettings() : SettingsMenu(tr("Loader Settings"), &GuiOptions, MENU_NONE) { @@ -141,6 +167,12 @@ LoaderSettings::LoaderSettings() Options->SetName(Idx++, "%s", tr( "Wiird Debugger" )); Options->SetName(Idx++, "%s", tr( "Debugger Paused Start" )); Options->SetName(Idx++, "%s", tr( "Channel Launcher" )); + Options->SetName(Idx++, "%s", tr( "DML Video Mode" )); + Options->SetName(Idx++, "%s", tr( "DML NMM Mode" )); + Options->SetName(Idx++, "%s", tr( "DML LED Activity" )); + Options->SetName(Idx++, "%s", tr( "DML PAD Hook" )); + Options->SetName(Idx++, "%s", tr( "DML No Disc" )); + Options->SetName(Idx++, "%s", tr( "DML Debug" )); SetOptionValues(); @@ -172,6 +204,7 @@ void LoaderSettings::SetOptionValues() else if(Settings.LoaderMode & MODE_WIIGAMES) Options->SetValue(Idx++, "%s", tr(loaderModeText[1])); else if(Settings.LoaderMode & MODE_NANDCHANNELS) Options->SetValue(Idx++, "%s", tr(loaderModeText[2])); else if(Settings.LoaderMode & MODE_EMUCHANNELS) Options->SetValue(Idx++, "%s", tr(loaderModeText[3])); + else Options->SetValue(Idx++, "%s", tr("Mixed")); //! Settings: Video Mode Options->SetValue(Idx++, "%s", tr(VideoModeText[Settings.videomode])); @@ -200,7 +233,6 @@ void LoaderSettings::SetOptionValues() else Options->SetValue(Idx++, "********"); - //! Settings: Quick Boot Options->SetValue(Idx++, "%s", tr( OnOffText[Settings.quickboot] )); @@ -235,6 +267,24 @@ void LoaderSettings::SetOptionValues() //! Settings: Channel Launcher Options->SetValue(Idx++, "%s", tr( ChannelLaunchText[Settings.UseChanLauncher] )); + + //! Settings: DML Video Mode + Options->SetValue(Idx++, "%s", tr(DMLVideoModeText[Settings.DMLVideo])); + + //! Settings: DML NMM Mode + Options->SetValue(Idx++, "%s", tr(DMLNMMMode[Settings.DMLNMM])); + + //! Settings: DML LED Activity + Options->SetValue(Idx++, "%s", tr(OnOffText[Settings.DMLActivityLED])); + + //! Settings: DML PAD Hook + Options->SetValue(Idx++, "%s", tr(OnOffText[Settings.DMLPADHOOK])); + + //! Settings: DML No Disc + Options->SetValue(Idx++, "%s", tr(OnOffText[Settings.DMLNoDisc])); + + //! Settings: DML Debug + Options->SetValue(Idx++, "%s", tr(DMLDebug[Settings.DMLDebug])); } int LoaderSettings::GetMenuInternal() @@ -257,7 +307,7 @@ int LoaderSettings::GetMenuInternal() } //! Settings: Video Mode - if (ret == ++Idx) + else if (ret == ++Idx) { if (++Settings.videomode >= VIDEO_MODE_MAX) Settings.videomode = 0; } @@ -306,11 +356,9 @@ int LoaderSettings::GetMenuInternal() char entered[4]; snprintf(entered, sizeof(entered), "%i", Settings.cios); - if(OnScreenKeyboard(entered, sizeof(entered), 0)) + if(OnScreenNumpad(entered, sizeof(entered))) { - Settings.cios = atoi(entered); - if(Settings.cios < 200) Settings.cios = 200; - else if(Settings.cios > 255) Settings.cios = 255; + Settings.cios = LIMIT(atoi(entered), 200, 255); if(NandTitles.IndexOf(TITLE_ID(1, Settings.cios)) < 0) { @@ -390,6 +438,42 @@ int LoaderSettings::GetMenuInternal() if (++Settings.UseChanLauncher >= MAX_ON_OFF) Settings.UseChanLauncher = 0; } + //! Settings: DML Video Mode + else if (ret == ++Idx) + { + if (++Settings.DMLVideo >= 7) Settings.DMLVideo = 0; + } + + //! Settings: DML NMM Mode + else if (ret == ++Idx) + { + if (++Settings.DMLNMM >= 3) Settings.DMLNMM = 0; + } + + //! Settings: DML LED Activity + else if (ret == ++Idx) + { + if (++Settings.DMLActivityLED >= MAX_ON_OFF) Settings.DMLActivityLED = 0; + } + + //! Settings: DML PAD Hook + else if (ret == ++Idx) + { + if (++Settings.DMLPADHOOK >= MAX_ON_OFF) Settings.DMLPADHOOK = 0; + } + + //! Settings: DML No Disc + else if (ret == ++Idx) + { + if (++Settings.DMLNoDisc >= MAX_ON_OFF) Settings.DMLNoDisc = 0; + } + + //! Settings: DML Debug + else if (ret == ++Idx) + { + if (++Settings.DMLDebug >= 3) Settings.DMLDebug = 0; + } + SetOptionValues(); return MENU_NONE; diff --git a/source/settings/menus/UninstallSM.cpp b/source/settings/menus/UninstallSM.cpp index 4d46659b..d1f4256f 100644 --- a/source/settings/menus/UninstallSM.cpp +++ b/source/settings/menus/UninstallSM.cpp @@ -131,7 +131,7 @@ int UninstallSM::GetMenuInternal() { GCGames::Instance()->RemoveGame(GameID); // Reload list - GCGames::Instance()->LoadGameList(Settings.GameCubePath); + GCGames::Instance()->LoadAllGames(); } else if(DiscHeader->type == TYPE_GAME_EMUNANDCHAN && DiscHeader->tid != 0) { diff --git a/source/sys.cpp b/source/sys.cpp index 969998ba..b9a2f433 100644 --- a/source/sys.cpp +++ b/source/sys.cpp @@ -3,6 +3,7 @@ #include #include "mload/mload.h" +#include "banner/BannerAsync.h" #include "Controls/DeviceHandler.hpp" #include "FileOperations/fileops.h" #include "homebrewboot/BootHomebrew.h" @@ -93,6 +94,7 @@ void AppCleanUp(void) gettextCleanUp(); Theme::CleanUp(); + BannerAsync::ThreadExit(); NewTitles::DestroyInstance(); ThreadedTask::DestroyInstance(); SoundHandler::DestroyInstance(); @@ -231,14 +233,24 @@ void ScreenShot() { time_t rawtime; struct tm * timeinfo; - char buffer[150]; - char buffer2[300]; + char filename[100]; // Filename, with current date/time. + char fullPath[300]; // Full pathname: ConfigPath + filename. time(&rawtime); timeinfo = localtime(&rawtime); - //USBLoader_GX_ScreenShot-Month_Day_Hour_Minute_Second_Year.png - strftime(buffer, 80, "USBLoader_GX_ScreenShot-%b%d%H%M%S%y.png", timeinfo); - sprintf(buffer2, "%s%s", Settings.ConfigPath, buffer); + + // Create the filename with the current date/time. + // Format: USBLoader_GX_ScreenShot-Month_Day_Hour_Minute_Second_Year.png + int ret = strftime(filename, sizeof(filename), "USBLoader_GX_ScreenShot-%b%d%H%M%S%y.png", timeinfo); + if (ret == 0) + { + // Error formatting the time. + // Use the raw time in seconds as a fallback. + snprintf(filename, sizeof(filename), "USBLoader_GX_ScreenShot-%ld.png", rawtime); + } + + // Create the full pathname. + snprintf(fullPath, sizeof(fullPath), "%s%s", Settings.ConfigPath, filename); if(!CreateSubfolder(Settings.ConfigPath)) { @@ -246,5 +258,5 @@ void ScreenShot() return; } - TakeScreenshot(buffer2); + TakeScreenshot(fullPath); } diff --git a/source/system/IosLoader.cpp b/source/system/IosLoader.cpp index 6feb7cee..c6c19cbb 100644 --- a/source/system/IosLoader.cpp +++ b/source/system/IosLoader.cpp @@ -18,7 +18,6 @@ #include "utils/tools.h" #include "gecko.h" -#define CheckAHBPROT() (read32(0x0D800064) == 0xFFFFFFFF) #define MEM2_PROT 0x0D8B420A #define ES_MODULE_START ((u16 *)0x939F0000) #define ES_MODULE_END (ES_MODULE_START + 0x4000) @@ -170,12 +169,12 @@ s32 IosLoader::ReloadIosKeepingRights(s32 ios) if (CheckAHBPROT()) { static const u16 ticket_check[] = { - 0x685B, // ldr r3, [r3, #4] ; Get TMD pointer - 0x22EC, 0x0052, // movs r2, 0x1D8 ; Set offset of access rights field in TMD - 0x189B, // adds r3, r3, r2 ; Add offset to TMD pointer - 0x681B, // ldr r3, [r3] ; Load access rights. We'll hack it with full access rights!!! - 0x4698, // mov r8, r3 ; Store it for the DVD video bitcheck later - 0x07DB // lsls r3, r3, 0x1F ; check AHBPROT bit + 0x685B, // ldr r3, [r3, #4] ; Get TMD pointer + 0x22EC, 0x0052, // movs r2, 0x1D8 ; Set offset of access rights field in TMD + 0x189B, // adds r3, r3, r2 ; Add offset to TMD pointer + 0x681B, // ldr r3, [r3] ; Load access rights. We'll hack it with full access rights!!! + 0x4698, // mov r8, r3 ; Store it for the DVD video bitcheck later + 0x07DB // lsls r3, r3, 0x1F ; check AHBPROT bit }; /* Disable MEM 2 protection */ @@ -277,7 +276,7 @@ iosinfo_t *IosLoader::GetIOSInfo(s32 ios) return NULL; } - sprintf(filepath, "/title/%08x/%08x/content/%08x.app", 0x00000001, ios, *(u8 *)((u32)TMD+0x1E7)); + snprintf(filepath, sizeof(filepath), "/title/%08x/%08x/content/%08x.app", 0x00000001, ios, *(u8 *)((u32)TMD+0x1E7)); free(TMD); diff --git a/source/system/IosLoader.h b/source/system/IosLoader.h index a90a2bcb..7b8ca8fe 100644 --- a/source/system/IosLoader.h +++ b/source/system/IosLoader.h @@ -2,6 +2,9 @@ #define _IOSLOADER_H_ #include +#include + +#define CheckAHBPROT() (read32(0x0D800064) == 0xFFFFFFFF) typedef struct _iosinfo_t { diff --git a/source/themes/CTheme.cpp b/source/themes/CTheme.cpp index db51342c..b698bb09 100644 --- a/source/themes/CTheme.cpp +++ b/source/themes/CTheme.cpp @@ -31,22 +31,12 @@ #include "settings/CSettings.h" #include "banner/OpeningBNR.hpp" #include "FileOperations/fileops.h" +#include "SystemMenu/SystemMenuResources.h" #include "menu/menus.h" #include "wad/nandtitle.h" #include "FreeTypeGX.h" -typedef struct map_entry -{ - char name[8]; - u8 hash[20]; -} __attribute__((packed)) map_entry_t; - -static const u8 WIIFONT_HASH[] = {0x32, 0xb3, 0x39, 0xcb, 0xbb, 0x50, 0x7d, 0x50, 0x27, 0x79, 0x25, 0x9a, 0x78, 0x66, 0x99, 0x5d, 0x03, 0x0b, 0x1d, 0x88}; -static const u8 WIIFONT_HASH_KOR[] = {0xb7, 0x15, 0x6d, 0xf0, 0xf4, 0xae, 0x07, 0x8f, 0xd1, 0x53, 0x58, 0x3e, 0x93, 0x6e, 0x07, 0xc0, 0x98, 0x77, 0x49, 0x0e}; - FreeTypeGX * fontSystem = NULL; -static FT_Byte * systemFont = NULL; -static u32 systemFontSize = 0; static FT_Byte * customFont = NULL; static u32 customFontSize = 0; @@ -161,69 +151,6 @@ bool Theme::Load(const char * theme_file_path) return result; } -bool Theme::loadSystemFont(bool korean) -{ - const char contentMapPath[] ATTRIBUTE_ALIGN(32) = "/shared1/content.map"; - u8 *contentMap = NULL; - u32 mapsize = 0; - - NandTitle::LoadFileFromNand(contentMapPath, &contentMap, &mapsize); - if(!contentMap) - return false; - - int fileCount = mapsize / sizeof(map_entry_t); - - map_entry_t *mapEntryList = (map_entry_t *) contentMap; - - for (int i = 0; i < fileCount; i++) - { - if (memcmp(mapEntryList[i].hash, korean ? WIIFONT_HASH_KOR : WIIFONT_HASH, 20) != 0) - continue; - - // Name found, load it and unpack it - char font_filename[32] ATTRIBUTE_ALIGN(32); - snprintf(font_filename, sizeof(font_filename), "/shared1/%.8s.app", mapEntryList[i].name); - - u8 *fontArchive = NULL; - u32 filesize = 0; - - NandTitle::LoadFileFromNand(font_filename, &fontArchive, &filesize); - if(!fontArchive) - continue; - - const U8Header *u8Archive = (U8Header *) fontArchive; - const U8Entry *fst = (const U8Entry *) (((const u8 *) u8Archive) + u8Archive->rootNodeOffset); - - if(fst[0].numEntries < 1) - { - free(fontArchive); - continue; - } - - if(systemFont) - delete [] systemFont; - - systemFontSize = fst[1].fileLength; - systemFont = new (std::nothrow) FT_Byte[systemFontSize]; - if(!systemFont) - { - free(fontArchive); - continue; - } - - memcpy(systemFont, fontArchive + fst[1].fileOffset, systemFontSize); - - free(fontArchive); - free(contentMap); - gprintf("Loaded Wii System Font: %s\n", u8Filename(fst, 1)); - return true; - } - - free(contentMap); - - return false; -} - bool Theme::LoadFont(const char *path) { char FontPath[300]; @@ -252,19 +179,17 @@ bool Theme::LoadFont(const char *path) fclose(pfile); } + bool isSystemFont = false; FT_Byte *loadedFont = customFont; u32 loadedFontSize = customFontSize; if(!loadedFont && Settings.UseSystemFont) { - if(!systemFont) - loadSystemFont(CONF_GetLanguage() == CONF_LANG_KOREAN); - if(!systemFont) - loadSystemFont(CONF_GetLanguage() != CONF_LANG_KOREAN); - //! Default to system font if no custom is loaded - loadedFont = systemFont; - loadedFontSize = systemFontSize; + loadedFont = (u8 *) SystemMenuResources::Instance()->GetSystemFont(); + loadedFontSize = SystemMenuResources::Instance()->GetSystemFontSize(); + if(loadedFont) + isSystemFont = true; } if(!loadedFont) { @@ -274,7 +199,7 @@ bool Theme::LoadFont(const char *path) delete fontSystem; - fontSystem = new FreeTypeGX(loadedFont, loadedFontSize, loadedFont == systemFont); + fontSystem = new FreeTypeGX(loadedFont, loadedFontSize, isSystemFont); return result; } @@ -288,8 +213,4 @@ void Theme::ClearFontData() if(customFont) delete [] customFont; customFont = NULL; - - if (systemFont) - delete [] systemFont; - systemFont = NULL; } diff --git a/source/themes/ThemeDownloader.cpp b/source/themes/ThemeDownloader.cpp index f4073208..0e37a63a 100644 --- a/source/themes/ThemeDownloader.cpp +++ b/source/themes/ThemeDownloader.cpp @@ -78,7 +78,7 @@ ThemeDownloader::ThemeDownloader() defaultBtnImg->SetWidescreen(Settings.widescreen); } defaultBtn = new GuiButton(btnOutline->GetWidth(), btnOutline->GetHeight()); - defaultBtn->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + defaultBtn->SetAlignment(ALIGN_CENTER, ALIGN_TOP); defaultBtn->SetPosition(-20, 400); defaultBtn->SetLabel(defaultBtnTxt); defaultBtn->SetImage(defaultBtnImg); @@ -301,7 +301,7 @@ void ThemeDownloader::MainButtonClicked(int button) GuiImage dialogBoxImg(&dialogBox); GuiWindow promptWindow(dialogBox.GetWidth(), dialogBox.GetHeight()); - promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); GuiTrigger trigA; diff --git a/source/themes/ThemeMenu.cpp b/source/themes/ThemeMenu.cpp index 6cf25240..23a9e572 100644 --- a/source/themes/ThemeMenu.cpp +++ b/source/themes/ThemeMenu.cpp @@ -62,7 +62,7 @@ ThemeMenu::ThemeMenu() defaultBtnImg->SetWidescreen(Settings.widescreen); } defaultBtn = new GuiButton(btnOutline->GetWidth(), btnOutline->GetHeight()); - defaultBtn->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + defaultBtn->SetAlignment(ALIGN_CENTER, ALIGN_TOP); defaultBtn->SetPosition(-20, 400); defaultBtn->SetLabel(defaultBtnTxt); defaultBtn->SetImage(defaultBtnImg); @@ -299,7 +299,7 @@ void ThemeMenu::MainButtonClicked(int button) GuiImage dialogBoxImg(&dialogBox); GuiWindow promptWindow(dialogBox.GetWidth(), dialogBox.GetHeight()); - promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); GuiTrigger trigA; diff --git a/source/themes/filelist.h b/source/themes/filelist.h index 13382047..5d845e2b 100644 --- a/source/themes/filelist.h +++ b/source/themes/filelist.h @@ -1,7 +1,7 @@ /**************************************************************************** * USB Loader GX resource files. * This file is generated automatically. - * Includes 190 files. + * Includes 198 files. * * NOTE: * Any manual modification of this file will be overwriten by the generation. @@ -20,6 +20,12 @@ extern const u32 add_png_size; extern const u8 addressbar_textbox_png[]; extern const u32 addressbar_textbox_png_size; +extern const u8 arrangeBannerGrid_png[]; +extern const u32 arrangeBannerGrid_png_size; + +extern const u8 arrangeBannerGrid_gray_png[]; +extern const u32 arrangeBannerGrid_gray_png_size; + extern const u8 arrangeCarousel_png[]; extern const u32 arrangeCarousel_png_size; @@ -230,6 +236,12 @@ extern const u32 gcncontroller_png_size; extern const u8 gcncontrollerR_png[]; extern const u32 gcncontrollerR_png_size; +extern const u8 gc_banner_bg_png[]; +extern const u32 gc_banner_bg_png_size; + +extern const u8 gc_icon_bg_png[]; +extern const u32 gc_icon_bg_png_size; + extern const u8 guitar_png[]; extern const u32 guitar_png_size; @@ -566,6 +578,9 @@ extern const u32 button_over_wav_size; extern const u8 credits_music_ogg[]; extern const u32 credits_music_ogg_size; +extern const u8 gc_banner_ogg[]; +extern const u32 gc_banner_ogg_size; + extern const u8 menuin_ogg[]; extern const u32 menuin_ogg_size; @@ -581,11 +596,22 @@ extern const u32 clock_ttf_size; extern const u8 font_ttf[]; extern const u32 font_ttf_size; +extern const u8 app_booter_bin[]; +extern const u32 app_booter_bin_size; + +extern const u8 custom_banner_bnr[]; +extern const u32 custom_banner_bnr_size; + +extern const u8 stub_bin[]; +extern const u32 stub_bin_size; + RecourceFile Resources::RecourceFiles[] = { {"abcIcon.png", abcIcon_png, abcIcon_png_size, NULL, 0}, {"add.png", add_png, add_png_size, NULL, 0}, {"addressbar_textbox.png", addressbar_textbox_png, addressbar_textbox_png_size, NULL, 0}, + {"arrangeBannerGrid.png", arrangeBannerGrid_png, arrangeBannerGrid_png_size, NULL, 0}, + {"arrangeBannerGrid_gray.png", arrangeBannerGrid_gray_png, arrangeBannerGrid_gray_png_size, NULL, 0}, {"arrangeCarousel.png", arrangeCarousel_png, arrangeCarousel_png_size, NULL, 0}, {"arrangeCarousel_gray.png", arrangeCarousel_gray_png, arrangeCarousel_gray_png_size, NULL, 0}, {"arrangeGrid.png", arrangeGrid_png, arrangeGrid_png_size, NULL, 0}, @@ -656,6 +682,8 @@ RecourceFile Resources::RecourceFiles[] = {"gameinfo2a.png", gameinfo2a_png, gameinfo2a_png_size, NULL, 0}, {"gcncontroller.png", gcncontroller_png, gcncontroller_png_size, NULL, 0}, {"gcncontrollerR.png", gcncontrollerR_png, gcncontrollerR_png_size, NULL, 0}, + {"gc_banner_bg.png", gc_banner_bg_png, gc_banner_bg_png_size, NULL, 0}, + {"gc_icon_bg.png", gc_icon_bg_png, gc_icon_bg_png_size, NULL, 0}, {"guitar.png", guitar_png, guitar_png_size, NULL, 0}, {"guitarR.png", guitarR_png, guitarR_png_size, NULL, 0}, {"gxlogo.png", gxlogo_png, gxlogo_png_size, NULL, 0}, @@ -768,11 +796,15 @@ RecourceFile Resources::RecourceFiles[] = {"button_click2.wav", button_click2_wav, button_click2_wav_size, NULL, 0}, {"button_over.wav", button_over_wav, button_over_wav_size, NULL, 0}, {"credits_music.ogg", credits_music_ogg, credits_music_ogg_size, NULL, 0}, + {"gc_banner.ogg", gc_banner_ogg, gc_banner_ogg_size, NULL, 0}, {"menuin.ogg", menuin_ogg, menuin_ogg_size, NULL, 0}, {"menuout.ogg", menuout_ogg, menuout_ogg_size, NULL, 0}, {"success.ogg", success_ogg, success_ogg_size, NULL, 0}, {"clock.ttf", clock_ttf, clock_ttf_size, NULL, 0}, {"font.ttf", font_ttf, font_ttf_size, NULL, 0}, + {"app_booter.bin", app_booter_bin, app_booter_bin_size, NULL, 0}, + {"custom_banner.bnr", custom_banner_bnr, custom_banner_bnr_size, NULL, 0}, + {"stub.bin", stub_bin, stub_bin_size, NULL, 0}, {"listBackground.png", NULL, 0, NULL, 0}, // Optional {"carouselBackground.png", NULL, 0, NULL, 0}, // Optional {"gridBackground.png", NULL, 0, NULL, 0}, // Optional diff --git a/source/themes/gettheme.c b/source/themes/gettheme.c index f84d122c..a76cfcb7 100644 --- a/source/themes/gettheme.c +++ b/source/themes/gettheme.c @@ -6,7 +6,7 @@ enum { - ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTRE, ALIGN_TOP, ALIGN_BOTTOM, ALIGN_MIDDLE + ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_TOP, ALIGN_BOTTOM, ALIGN_MIDDLE }; typedef struct _MSG @@ -193,7 +193,7 @@ int getThemeAlignment(const char *msgid) return ALIGN_RIGHT; else if(strncasecmp(string, "center", strlen("center")) == 0) - return ALIGN_CENTRE; + return ALIGN_CENTER; else if(strncasecmp(string, "top", strlen("top")) == 0) return ALIGN_TOP; diff --git a/source/usbloader/GameBooter.cpp b/source/usbloader/GameBooter.cpp index 42b8bb2f..41ab952e 100644 --- a/source/usbloader/GameBooter.cpp +++ b/source/usbloader/GameBooter.cpp @@ -61,42 +61,121 @@ extern "C" int GameBooter::BootGCMode(struct discHdr *gameHdr) { - if(gameHdr->type == TYPE_GAME_GC_IMG) + const char *RealPath = GCGames::Instance()->GetPath((const char *) gameHdr->id); + + if(((gameHdr->type == TYPE_GAME_GC_IMG) || (gameHdr->type == TYPE_GAME_GC_EXTRACTED)) && strncmp(RealPath, "usb", 3) == 0) { - char path[50]; - snprintf(path, sizeof(path), "%sboot.bin", Settings.GameCubePath); - FILE *f = fopen(path, "wb"); - if(f) - { - const char *gamePath = GCGames::Instance()->GetPath((const char *) gameHdr->id); - fwrite(gamePath, 1, strlen(gamePath) + 1, f); - fclose(f); - } + if(!GCGames::Instance()->CopyUSB2SD(gameHdr)) + return 0; + + RealPath = GCGames::Instance()->GetPath((const char *) gameHdr->id); } + const char *gcPath = strchr(RealPath, '/'); + if(!gcPath) gcPath = ""; + + char gamePath[255]; + snprintf(gamePath, sizeof(gamePath), "%s", gcPath); + ExitApp(); gprintf("\nLoading BC for GameCube"); + // check the settings GameCFG * game_cfg = GameSettings.GetGameCFG(gameHdr->id); u8 videoChoice = game_cfg->video == INHERIT ? Settings.videomode : game_cfg->video; + u8 ocarinaChoice = game_cfg->ocarina == INHERIT ? Settings.ocarina : game_cfg->ocarina; + u8 dmlVideoChoice = game_cfg->DMLVideo == INHERIT ? Settings.DMLVideo : game_cfg->DMLVideo; + u8 dmlNMMChoice = game_cfg->DMLNMM == INHERIT ? Settings.DMLNMM : game_cfg->DMLNMM; + u8 dmlActivityLEDChoice = game_cfg->DMLActivityLED == INHERIT ? Settings.DMLActivityLED : game_cfg->DMLActivityLED; + u8 dmlPADHookChoice = game_cfg->DMLPADHOOK == INHERIT ? Settings.DMLPADHOOK : game_cfg->DMLPADHOOK; + u8 dmlNoDiscChoice = game_cfg->DMLNoDisc == INHERIT ? Settings.DMLNoDisc : game_cfg->DMLNoDisc; + u8 dmlDebugChoice = game_cfg->DMLDebug == INHERIT ? Settings.DMLDebug : game_cfg->DMLDebug; // Game ID - memcpy((u8 *)0x80000000, gameHdr->id, 6); - DCFlushRange((u8 *)0x80000000, 6); - - if(gameHdr->type == TYPE_GAME_GC_IMG) - { - // Tell DML to boot the game from sd card - *(vu32*)0x80001800 = 0xB002D105; - DCFlushRange((u8 *)0x80001800, 4); - ICInvalidateRange((u8 *)0x80001800, 4); - } + memcpy((u8 *)Disc_ID, gameHdr->id, 6); + DCFlushRange((u8 *)Disc_ID, 6); *(vu32*)0xCC003024 |= 7; Disc_SelectVMode(videoChoice, true); Disc_SetVMode(); + DML_CFG *dml_config = (DML_CFG *) DML_CONFIG_ADDRESS; + memset(dml_config, 0, sizeof(DML_CFG)); + + // Magic and version for DML + dml_config->Magicbytes = DML_MAGIC; + dml_config->Version = DML_VERSION; + + // Select disc source + if((gameHdr->type == TYPE_GAME_GC_IMG) || (gameHdr->type == TYPE_GAME_GC_EXTRACTED)) { + dml_config->Config |= DML_CFG_GAME_PATH; + strncpy(dml_config->GamePath, gamePath, sizeof(dml_config->GamePath)); + // use no disc patch + if(dmlNoDiscChoice) + dml_config->Config |= DML_CFG_NODISC; + + gprintf("DML: Loading game %s\n", dml_config->GamePath); + } + else { + dml_config->Config |= DML_CFG_BOOT_DISC; + } + + // internal DML video mode methods + switch(dmlVideoChoice) + { + default: + case 0: + dml_config->VideoMode = DML_VID_DML_AUTO; + break; + case 1: + dml_config->VideoMode = DML_VID_NONE; + break; + case VIDEO_MODE_PAL50: + dml_config->VideoMode = DML_VID_FORCE_PAL50 | DML_VID_FORCE; + break; + case VIDEO_MODE_PAL60: + dml_config->VideoMode = DML_VID_FORCE_PAL60 | DML_VID_FORCE; + break; + case VIDEO_MODE_NTSC: + dml_config->VideoMode = DML_VID_FORCE_NTSC | DML_VID_FORCE; + break; + case VIDEO_MODE_PAL480P-1: + dml_config->VideoMode = DML_VID_FORCE_PAL60 | DML_VID_FORCE_PROG | DML_VID_PROG_PATCH | DML_VID_FORCE; + break; + case VIDEO_MODE_NTSC480P-1: + dml_config->VideoMode = DML_VID_FORCE_NTSC | DML_VID_FORCE_PROG | DML_VID_PROG_PATCH | DML_VID_FORCE; + break; + } + + // setup cheat and path + if(ocarinaChoice) { + dml_config->Config |= DML_CFG_CHEATS | DML_CFG_CHEAT_PATH; + const char *CheatPath = strchr(Settings.Cheatcodespath, '/'); + if(!CheatPath) CheatPath = ""; + snprintf(dml_config->CheatPath, sizeof(dml_config->CheatPath), "%s%.6s.gct", CheatPath, (char *)gameHdr->id); + + gprintf("DML: Loading cheat %s\n", dml_config->CheatPath); + } + + // other DMl configs + if(dmlPADHookChoice) + dml_config->Config |= DML_CFG_PADHOOK; + if(dmlActivityLEDChoice) + dml_config->Config |= DML_CFG_ACTIVITY_LED; + if(dmlNMMChoice) + dml_config->Config |= dmlNMMChoice == ON ? DML_CFG_NMM : DML_CFG_NMM_DEBUG; + if(dmlDebugChoice) + dml_config->Config |= dmlDebugChoice == ON ? DML_CFG_DEBUGGER : DML_CFG_DEBUGWAIT; + + DCFlushRange(dml_config, sizeof(DML_CFG)); + memcpy((u8*)DML_CONFIG_ADDRESS_V1_2, dml_config, sizeof(DML_CFG)); + DCFlushRange((u8*)DML_CONFIG_ADDRESS_V1_2, sizeof(DML_CFG)); + + // print the config set for DML + gprintf("DML: setup configuration 0x%X\n", dml_config->Config); + gprintf("DML: setup video mode 0x%X\n", dml_config->VideoMode); + syssram *sram = __SYS_LockSram(); if (*Video_Mode == VI_NTSC) sram->flags &= ~1; // Clear bit 0 to set the video mode to NTSC @@ -178,7 +257,7 @@ void GameBooter::SetupNandEmu(u8 NandEmuMode, const char *NandEmuPath, struct di { //! Set which partition to use (USB only) partition = atoi(NandEmuPath+3)-1; - Set_Partition(partition); + Set_Partition(DeviceHandler::PartitionToPortPartition(partition)); DeviceHandler::Instance()->UnMount(USB1 + partition); } else @@ -258,7 +337,7 @@ int GameBooter::BootGame(struct discHdr *gameHdr) if(Settings.Wiinnertag) Wiinnertag::TagGame((const char *) gameHeader.id); - if(gameHeader.type == TYPE_GAME_GC_IMG || gameHeader.type == TYPE_GAME_GC_DISC) + if(gameHeader.type == TYPE_GAME_GC_IMG || gameHeader.type == TYPE_GAME_GC_DISC || gameHdr->type == TYPE_GAME_GC_EXTRACTED) return BootGCMode(&gameHeader); AppCleanUp(); @@ -315,7 +394,10 @@ int GameBooter::BootGame(struct discHdr *gameHdr) //! Modify Wii Message Board to display the game starting here (before Nand Emu) if(Settings.PlaylogUpdate) + { + BNRInstance::Instance()->Load(&gameHeader); Playlog_Update((char *) gameHeader.id, BNRInstance::Instance()->GetIMETTitle(CONF_GetLanguage())); + } //! Load wip codes load_wip_code(gameHeader.id); @@ -386,12 +468,12 @@ int GameBooter::BootGame(struct discHdr *gameHdr) else { //! shutdown now and avoid later crashs with free if memory gets overwritten by channel - ShutDownDevices(usbport); + ShutDownDevices(DeviceHandler::PartitionToUSBPort(std::max(atoi(NandEmuPath+3)-1, 0))); gprintf("\tChannel Boot\n"); - // Load dol - AppEntrypoint = Channels::LoadChannel(gameHeader.tid); /* Setup video mode */ Disc_SelectVMode(videoChoice, false); + // Load dol + AppEntrypoint = Channels::LoadChannel(gameHeader.tid); } //! No entrypoint found...back to HBC/SystemMenu diff --git a/source/usbloader/disc.c b/source/usbloader/disc.c index dcb612ce..e8dc49d0 100644 --- a/source/usbloader/disc.c +++ b/source/usbloader/disc.c @@ -27,14 +27,14 @@ extern u32 AppEntrypoint; /* Disc pointers */ static u32 *buffer = (u32 *) 0x93000000; static u8 *diskid = (u8 *) Disc_ID; -static GXRModeObj *vmode = NULL; -static u32 vmode_reg = 0; +static GXRModeObj *rmode = NULL; +static u32 rmode_reg = 0; void Disc_SetLowMem(void) { /* Setup low memory */ *Sys_Magic = 0x0D15EA5E; // Standard Boot Code - *Version = 0x00000001; // Version + *Sys_Version = 0x00000001; // Version *Arena_L = 0x00000000; // Arena Low *BI2 = 0x817E5480; // BI2 *Bus_Speed = 0x0E7BE2C0; // Console Bus Speed @@ -57,7 +57,7 @@ void Disc_SetLowMem(void) void Disc_SelectVMode(u8 videoselected, u8 ignore_progressive) { - vmode = VIDEO_GetPreferredMode(0); + rmode = VIDEO_GetPreferredMode(0); /* Get video mode configuration */ bool progressive = (CONF_GetProgressiveScan() > 0) && VIDEO_HaveComponentCable() && !ignore_progressive; @@ -68,17 +68,17 @@ void Disc_SelectVMode(u8 videoselected, u8 ignore_progressive) switch (tvmode) { case CONF_VIDEO_PAL: - vmode_reg = PAL60 ? VI_EURGB60 : VI_PAL; + rmode_reg = PAL60 ? VI_EURGB60 : VI_PAL; if(PAL60) - vmode = progressive ? &TVNtsc480Prog : &TVEurgb60Hz480IntDf; + rmode = progressive ? &TVNtsc480Prog : &TVEurgb60Hz480IntDf; break; case CONF_VIDEO_MPAL: - vmode_reg = VI_MPAL; + rmode_reg = VI_MPAL; break; case CONF_VIDEO_NTSC: - vmode_reg = VI_NTSC; + rmode_reg = VI_NTSC; break; } @@ -97,8 +97,8 @@ void Disc_SelectVMode(u8 videoselected, u8 ignore_progressive) case 'Y': if (tvmode != CONF_VIDEO_PAL) { - vmode_reg = PAL60 ? VI_EURGB60 : VI_PAL; - vmode = progressive ? &TVNtsc480Prog : (PAL60 ? &TVEurgb60Hz480IntDf : &TVPal528IntDf); + rmode_reg = PAL60 ? VI_EURGB60 : VI_PAL; + rmode = progressive ? &TVNtsc480Prog : (PAL60 ? &TVEurgb60Hz480IntDf : &TVPal528IntDf); } break; // NTSC @@ -106,8 +106,8 @@ void Disc_SelectVMode(u8 videoselected, u8 ignore_progressive) case 'J': if (tvmode != CONF_VIDEO_NTSC) { - vmode_reg = VI_NTSC; - vmode = progressive ? &TVNtsc480Prog : &TVNtsc480IntDf; + rmode_reg = VI_NTSC; + rmode = progressive ? &TVNtsc480Prog : &TVNtsc480IntDf; } break; default: @@ -115,24 +115,24 @@ void Disc_SelectVMode(u8 videoselected, u8 ignore_progressive) } break; case VIDEO_MODE_PAL50: // PAL50 - vmode = &TVPal528IntDf; - vmode_reg = VI_PAL; + rmode = &TVPal528IntDf; + rmode_reg = VI_PAL; break; case VIDEO_MODE_PAL60: // PAL60 - vmode = progressive ? &TVNtsc480Prog : &TVEurgb60Hz480IntDf; - vmode_reg = VI_EURGB60; + rmode = progressive ? &TVNtsc480Prog : &TVEurgb60Hz480IntDf; + rmode_reg = VI_EURGB60; break; case VIDEO_MODE_NTSC: // NTSC - vmode = progressive ? &TVNtsc480Prog : &TVNtsc480IntDf; - vmode_reg = VI_NTSC; + rmode = progressive ? &TVNtsc480Prog : &TVNtsc480IntDf; + rmode_reg = VI_NTSC; break; case VIDEO_MODE_PAL480P: - vmode = &TVNtsc480Prog; - vmode_reg = VI_EURGB60; + rmode = &TVNtsc480Prog; + rmode_reg = VI_EURGB60; break; case VIDEO_MODE_NTSC480P: - vmode = &TVNtsc480Prog; - vmode_reg = VI_NTSC; + rmode = &TVNtsc480Prog; + rmode_reg = VI_NTSC; break; case VIDEO_MODE_SYSDEFAULT: // AUTO PATCH TO SYSTEM break; @@ -142,18 +142,18 @@ void Disc_SelectVMode(u8 videoselected, u8 ignore_progressive) void Disc_SetVMode(void) { /* Set video mode register */ - *Video_Mode = vmode_reg; + *Video_Mode = rmode_reg; DCFlushRange((void *) Video_Mode, 4); /* Set video mode */ - if (vmode != NULL) - VIDEO_Configure(vmode); + if (rmode != NULL) + VIDEO_Configure(rmode); /* Setup video */ VIDEO_SetBlack(TRUE); VIDEO_Flush(); VIDEO_WaitVSync(); - if (vmode->viTVMode & VI_NON_INTERLACE) + if (rmode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync(); } diff --git a/source/usbloader/usbstorage2.c b/source/usbloader/usbstorage2.c index a635b59d..ed09034b 100644 --- a/source/usbloader/usbstorage2.c +++ b/source/usbloader/usbstorage2.c @@ -147,7 +147,7 @@ s32 USBStorage2_GetCapacity(u32 port, u32 *_sector_size) { s32 ret; u32 sector_size = 0; - //USBStorage2_SetPort(port); + USBStorage2_SetPort(port); ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_GET_CAPACITY, ":i", §or_size); @@ -170,7 +170,7 @@ s32 USBStorage2_ReadSectors(u32 port, u32 sector, u32 numSectors, void *buffer) if (!mem2_ptr) mem2_ptr = (u8 *) MEM2_alloc(MAX_SECTOR_SIZE * MAX_BUFFER_SECTORS); - //USBStorage2_SetPort(port); + USBStorage2_SetPort(port); s32 read_secs, read_size; @@ -216,7 +216,7 @@ s32 USBStorage2_WriteSectors(u32 port, u32 sector, u32 numSectors, const void *b if (!mem2_ptr) mem2_ptr = (u8 *) MEM2_alloc(MAX_SECTOR_SIZE * MAX_BUFFER_SECTORS); - //USBStorage2_SetPort(port); + USBStorage2_SetPort(port); s32 write_size, write_secs; diff --git a/source/usbloader/wbfs/wbfs_fat.cpp b/source/usbloader/wbfs/wbfs_fat.cpp index 7f0a2a54..3ca812b5 100644 --- a/source/usbloader/wbfs/wbfs_fat.cpp +++ b/source/usbloader/wbfs/wbfs_fat.cpp @@ -65,7 +65,7 @@ s32 Wbfs_Fat::Open() int portPart = DeviceHandler::PartitionToPortPartition(partition); if (lba == usbHandle->GetLBAStart(portPart)) { - sprintf(wbfs_fs_drive, "%s:", usbHandle->MountName(portPart)); + snprintf(wbfs_fs_drive, sizeof(wbfs_fs_drive), "%s:", usbHandle->MountName(portPart)); return 0; } } @@ -429,7 +429,7 @@ s32 Wbfs_Fat::GetHeadersCount() // or usb:/wbfs/GAMEID.iso // or usb:/wbfs/GAMEID.ciso int n = fileext - fname; // length withouth .wbfs - sprintf((char *) id, "%.6s", fname); + memcpy(id, fname, 6); if (n != 6) { // TITLE [GAMEID].wbfs @@ -500,11 +500,11 @@ s32 Wbfs_Fat::GetHeadersCount() if (stat(fpath, &st) != 0) { // look for direct .iso file - sprintf(strrchr(fpath, '.'), ".iso"); // replace .wbfs with .iso + strcpy(strrchr(fpath, '.'), ".iso"); // replace .wbfs with .iso if (stat(fpath, &st) != 0) { // look for direct .ciso file - sprintf(strrchr(fpath, '.'), ".ciso"); // replace .iso with .ciso + strcpy(strrchr(fpath, '.'), ".ciso"); // replace .iso with .ciso if (stat(fpath, &st) != 0) continue; } } @@ -787,7 +787,7 @@ void Wbfs_Fat::mk_gameid_title(struct discHdr *header, char *name, int re_space, snprintf(id, sizeof(id), (char *) header->id); snprintf(title, sizeof(title), header->title); - title_filename(title); + CleanTitleCharacters(title); if (layout == 0) { @@ -809,7 +809,7 @@ void Wbfs_Fat::mk_gameid_title(struct discHdr *header, char *name, int re_space, } } -void Wbfs_Fat::title_filename(char *title) +void Wbfs_Fat::CleanTitleCharacters(char *title) { int i, len; // trim leading space diff --git a/source/usbloader/wbfs/wbfs_fat.h b/source/usbloader/wbfs/wbfs_fat.h index 570f0a65..118fb7bb 100644 --- a/source/usbloader/wbfs/wbfs_fat.h +++ b/source/usbloader/wbfs/wbfs_fat.h @@ -36,6 +36,7 @@ class Wbfs_Fat: public Wbfs virtual u8 GetFSType(void) { return PART_FS_FAT; } static bool CheckLayoutB(char *fname, int len, u8* id, char *fname_title); + static void CleanTitleCharacters(char *title); protected: split_info_t split; @@ -53,7 +54,6 @@ class Wbfs_Fat: public Wbfs void GetDir(struct discHdr *header, char *path); void mk_gameid_title(struct discHdr *header, char *name, int re_space, int layout); - void title_filename(char *title); static int nop_rw_sector(void *_fp, u32 lba, u32 count, void* buf) { return 0; } }; diff --git a/source/usbloader/wbfs/wbfs_rw.c b/source/usbloader/wbfs/wbfs_rw.c index c2455a95..60dcfc66 100644 --- a/source/usbloader/wbfs/wbfs_rw.c +++ b/source/usbloader/wbfs/wbfs_rw.c @@ -172,3 +172,47 @@ s32 __WriteSDHC(void *fp, u32 lba, u32 count, void *iobuf) return 0; } + +s32 __ReadDVDPlain(void *iobuf, u32 len, u64 offset) +{ + void *buffer = NULL; + + u32 mod, size; + s32 ret; + + /* Calcualte sizes */ + mod = len % 32; + size = len - mod; + + /* Read aligned data */ + if (size) + { + ret = WDVD_UnencryptedRead(iobuf, size, offset); + if (ret < 0) goto out; + } + + /* Read non-aligned data */ + if (mod) + { + /* Allocate memory */ + buffer = memalign(32, 0x20); + if (!buffer) return -1; + + /* Read data */ + ret = WDVD_UnencryptedRead(buffer, 0x20, offset + size); + if (ret < 0) goto out; + + /* Copy data */ + void *ptr = ((u8 *) iobuf) + size; + memcpy(ptr, buffer, mod); + } + + /* Success */ + ret = 0; + + out: + /* Free memory */ + if (buffer) free(buffer); + + return ret; +} diff --git a/source/usbloader/wbfs/wbfs_rw.h b/source/usbloader/wbfs/wbfs_rw.h index d0bb775d..c84053e3 100644 --- a/source/usbloader/wbfs/wbfs_rw.h +++ b/source/usbloader/wbfs/wbfs_rw.h @@ -26,6 +26,7 @@ extern "C" s32 __WriteUSB(void *fp, u32 lba, u32 count, void *iobuf); s32 __ReadSDHC(void *fp, u32 lba, u32 count, void *iobuf); s32 __WriteSDHC(void *fp, u32 lba, u32 count, void *iobuf); + s32 __ReadDVDPlain(void *iobuf, u32 len, u64 offset); #ifdef __cplusplus } diff --git a/source/utils/LanguageCode.c b/source/utils/LanguageCode.c new file mode 100644 index 00000000..415f60f4 --- /dev/null +++ b/source/utils/LanguageCode.c @@ -0,0 +1,26 @@ +#include + +const char* CONF_GetLanguageString(void) +{ + static int confLang = 0xdead; + + if(confLang == 0xdead) + confLang = CONF_GetLanguage(); + + const char*lang; + switch( confLang ) + { + case CONF_LANG_JAPANESE: lang = "JPN"; break; + default: + case CONF_LANG_ENGLISH: lang = "ENG"; break; + case CONF_LANG_GERMAN: lang = "GER"; break; + case CONF_LANG_FRENCH: lang = "FRA"; break; + case CONF_LANG_SPANISH: lang = "SPA"; break; + case CONF_LANG_ITALIAN: lang = "ITA"; break; + case CONF_LANG_DUTCH: lang = "NED"; break; + case CONF_LANG_SIMP_CHINESE: + case CONF_LANG_TRAD_CHINESE: lang = "CHN"; break; + case CONF_LANG_KOREAN: lang = "KOR"; break; + } + return lang; +} diff --git a/source/utils/LanguageCode.h b/source/utils/LanguageCode.h new file mode 100644 index 00000000..c80008b9 --- /dev/null +++ b/source/utils/LanguageCode.h @@ -0,0 +1,14 @@ +#ifndef __LANGUAGE_CODE_H_ +#define __LANGUAGE_CODE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +const char* CONF_GetLanguageString(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/utils/StringTools.c b/source/utils/StringTools.c index 194e7ffb..6cc4b72f 100644 --- a/source/utils/StringTools.c +++ b/source/utils/StringTools.c @@ -87,7 +87,7 @@ const wchar_t * wfmt(const char * format, ...) if(tmp) free(tmp); - return NULL; + return (const wchar_t *) strWChar; } bool char2wchar_t(const char * strChar, wchar_t * dest) @@ -127,7 +127,7 @@ int strtokcmp(const char * string, const char * compare, const char * separator) return -1; } -inline const char * FullpathToFilename(const char *path) +const char * FullpathToFilename(const char *path) { if(!path) return path; diff --git a/source/utils/ThreadedTask.cpp b/source/utils/ThreadedTask.cpp index 94b28934..4d7b7e20 100644 --- a/source/utils/ThreadedTask.cpp +++ b/source/utils/ThreadedTask.cpp @@ -28,7 +28,7 @@ ThreadedTask * ThreadedTask::instance = NULL; ThreadedTask::ThreadedTask() : ExitRequested(false) { - LWP_CreateThread (&Thread, ThreadCallback, this, NULL, 16384, 80); + LWP_CreateThread (&Thread, ThreadCallback, this, NULL, 16384, 70); } ThreadedTask::~ThreadedTask() diff --git a/source/utils/U8Archive.cpp b/source/utils/U8Archive.cpp new file mode 100644 index 00000000..55e56f50 --- /dev/null +++ b/source/utils/U8Archive.cpp @@ -0,0 +1,526 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok and giantpune + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include +#include +#include +#include + +#include "ash.h" +#include "lz77.h" +#include "gecko.h" +#include "U8Archive.h" + +#define RU(N, S) ((((N) + (S) - 1) / (S)) * (S)) + +U8Archive::U8Archive(const u8 *stuff, u32 len ) + : fst( NULL ), + name_table( NULL ), + data( NULL ) +{ + if( stuff ) + { + SetData( stuff, len ); + } +} + +void U8Archive::SetData( const u8 *stuff, u32 len ) +{ + fst = NULL; + name_table = NULL; + data = NULL; + if( !stuff || len < 0x40 ) + { + gprintf( "SetData(): !stuff || len < 0x40 %p %08x\n", stuff, len ); + return; + } + stuff = FindU8Tag( stuff, len ); + if( !stuff ) + { + gprintf( "U8 tag not found\n" ); + return; + } + const U8Header * binHdr = (const U8Header *)stuff; + + const u8* fst_buffer = stuff + binHdr->rootNodeOffset; + fst = (FstEntry *)fst_buffer; + u32 name_table_offset = fst->filelen * 0xC; + name_table = (char *)( fst_buffer + name_table_offset ); + data = (u8*)stuff; +} + +const u8 *U8Archive::FindU8Tag( const u8* stuff, u32 len ) +{ + if( !stuff || len < 0x20 ) + { + return NULL; + } + if( *(u32*)( stuff ) == 0x55AA382D ) + { + return stuff; + } + if( len > 0x620 && *(u32*)( stuff + 0x600 ) == 0x55AA382D ) + { + return stuff + 0x600; + } + if( len > 0x660 && *(u32*)( stuff + 0x640 ) == 0x55AA382D ) + { + return stuff + 0x640; + } + return NULL; +} + +u8 *U8Archive::GetFile( const std::string &path, u32 *size ) const +{ + return GetFile( path.c_str(), size ); +} + +u8 *U8Archive::GetFile( const char *path, u32 *size ) const +{ + if( !path || !fst || !name_table ) + { + return NULL; + } + + s32 entryNo = EntryFromPath( path ); + if( entryNo < 1 ) + { + //gprintf( "U8: entry wasn\'t found in the archive \"%s\"\n", path ); + return NULL; + } + if( fst[ entryNo ].filetype ) + { + gprintf( "U8: \"%s\" is a folder\n", path ); + return NULL; + } + if( size ) + { + *size = fst[ entryNo ].filelen; + } + return data + fst[ entryNo ].fileoffset; +} + +u8 *U8Archive::GetFile( u32 fstIdx, u32 *size ) const +{ + if( !fst || !name_table ) + { + return NULL; + } + + if( fstIdx >= fst[0].filelen || fst[ fstIdx ].filetype ) + { + gprintf( "%i is a folder\n", fstIdx ); + return NULL; + } + + if( size ) + { + *size = fst[ fstIdx ].filelen; + } + return data + fst[ fstIdx ].fileoffset; +} + +u8* U8Archive::GetFileAllocated( const std::string &path, u32 *size ) const +{ + return GetFileAllocated( path.c_str(), size ); +} + +u8 *U8Archive::DecompressCopy( const u8 * stuff, u32 len, u32 *size ) const +{ + // check for IMD5 header and skip it + if( len > 0x40 && *(u32*)stuff == 0x494d4435 )// IMD5 + { + stuff += 0x20; + len -= 0x20; + } + + u8* ret = NULL; + // determine if it needs to be decompressed + if( IsAshCompressed( stuff, len ) ) + { + //u32 len2 = len; + // ASH0 + ret = DecompressAsh( stuff, len ); + if( !ret ) + { + gprintf( "out of memory\n" ); + return NULL; + } + } + else if( isLZ77compressed( stuff ) ) + { + // LZ77 with no magic word + if( decompressLZ77content( stuff, len, &ret, &len ) ) + { + return NULL; + } + } + else if( *(u32*)( stuff ) == 0x4C5A3737 )// LZ77 + { + // LZ77 with a magic word + if( decompressLZ77content( stuff + 4, len - 4, &ret, &len ) ) + { + return NULL; + } + } + else + { + // just copy the data out of the archive + ret = (u8*)memalign( 32, len ); + if( !ret ) + { + gprintf( "out of memory\n" ); + return NULL; + } + memcpy( ret, stuff, len ); + } + if( size ) + { + *size = len; + } + + // flush the cache so if there are any textures in this data, it will be ready for the GX + DCFlushRange( ret, len ); + return ret; +} + +u8* U8Archive::GetFileAllocated( const char *path, u32 *size ) const +{ + u32 len; + const u8 *stuff = GetFile( path, &len ); + if( !stuff ) + { + return NULL; + } + + return DecompressCopy( stuff, len, size ); +} + +u8* U8Archive::GetFileAllocated( u32 fstIdx, u32 *size ) const +{ + u32 len; + const u8 *stuff = GetFile( fstIdx, &len ); + if( !stuff ) + { + return NULL; + } + + return DecompressCopy( stuff, len, size ); +} + + +u32 U8Archive::FileDescriptor( const char *path ) const +{ + int ret = EntryFromPath( path ); + if( ret < 1 ) + { + return 0; + } + return ret; +} + +u8* U8Archive::GetFileFromFd( u32 fd, u32 *size )const +{ + if( !fst || !name_table || fd >= fst[ 0 ].filelen ) + { + return NULL; + } + if( fst[ fd ].filetype ) + { + gprintf( "U8: \"%s\" is a folder\n", FstName( &fst[ fd ] ) ); + return NULL; + } + if( size ) + { + *size = fst[ fd ].filelen; + } + return data + fst[ fd ].fileoffset; +} + +char *U8Archive::FstName( const FstEntry *entry ) const +{ + if( entry == &fst[ 0 ] ) + { + return NULL; + } + + return (char*)name_table + ( *((u32 *)entry) & 0x00ffffff ); +} + +int U8Archive::strcasecmp_slash( const char *s1, const char *s2 ) +{ + while( toupper( *s1 ) == toupper( *s2++ ) ) + { + if( *s1++ == 0 || *s1 == '/' ) + { + return 0; + } + } + + return( *(const unsigned char*)s1 - *(const unsigned char *)( s2 - 1 ) ); +} + +int U8Archive::strlen_slash( const char *s ) +{ + int ret = 0; + while( *s && *s++ != '/' ) + { + ret++; + } + + return ret; +} + +u32 U8Archive::NextEntryInFolder( u32 current, u32 directory ) const +{ + u32 next = ( fst[ current ].filetype ? fst[ current ].filelen : current + 1 ); + if( next < fst[ directory ].filelen ) + return next; + + return 0; +} + +s32 U8Archive::EntryFromPath( const char *path, int d ) const +{ + //gprintf( "EntryFromPath( \"%s\", %d )\n", path, d ); + while( *path == '/' ) + { + path++; + } + + if( !fst[ d ].filetype ) + { + gprintf("ERROR!! %s is not a directory\n", FstName( &fst[ d ] ) ); + return -1; + } + + u32 next = d + 1; + + FstEntry *entry = &fst[ next ]; + + while( next ) + { + //does this entry match. + //strlen_slash is used because if looking for "dvd:/gameboy/" it would return a false positive if it hit "dvd:/gameboy advance/" first + if( !strcasecmp_slash( path, FstName( entry ) ) && ( strlen( FstName( entry ) ) == (u32)strlen_slash( path ) ) ) + { + char *slash = strchr( path, '/' ); + if( slash && *( slash + 1 ) ) + { + //we are looking for a file somewhere in this folder + return EntryFromPath( slash + 1, next ); + } + //this is the actual entry we were looking for + return next; + } + + //find the next entry in this folder + next = NextEntryInFolder( next, d ); + entry = &fst[ next ]; + } + + //no entry with the given path was found + return -1; +} + +U8NandArchive::U8NandArchive( const char* nandPath ) + : U8Archive( NULL, 0 ), + fd( -1 ), + dataOffset( 0 ) +{ + if( nandPath ) + { + SetFile( nandPath ); + } +} + +U8NandArchive::~U8NandArchive() +{ + if( fd >= 0 ) + { + ISFS_Close( fd ); + } + free( fst ); +} + +bool U8NandArchive::SetFile( const char* nandPath ) +{ + if(fst) + free(fst); + if(name_table) + free(name_table); + CloseFile(); + + // open file + if( (fd = ISFS_Open( nandPath, ISFS_OPEN_READ ) ) < 0 ) + { + gprintf( "U8NandArchive: ISFS_Open( \"%s\" ) failed\n", nandPath ); + return false; + } + + // get file size + fstats stats __attribute__(( aligned( 32 ) )); + int ret = ISFS_GetFileStats( fd, &stats ); + if( ret < 0 ) + { + CloseFile(); + gprintf( "U8NandArchive: ISFS_GetFileStats( \"%s\" ) failed\n", nandPath ); + return false; + } + + // buffer for reading the header and stuff + u8* buffer = (u8*)memalign( 32, 0x800 ); + if( !buffer ) + { + CloseFile(); + gprintf( "U8NandArchive: enomem\n" ); + return false; + } + + // read a chunk big enough that it should contain the U8 header if there is going to be one + if( (ret = ISFS_Read( fd, buffer, 0x800 )) != 0x800 ) + { + free( buffer ); + CloseFile(); + gprintf( "U8NandArchive: ISFS_Read( 0x800 ) = %i\n", ret ); + return false; + } + + // find the start of the U8 data + U8Header* tagStart = (U8Header*)FindU8Tag( buffer, ret ); + if( !tagStart ) + { + free( buffer ); + CloseFile(); + gprintf( "U8NandArchive: didn't see a U8 tag\n" ); + return false; + } + + // remember where in the file the U8 starts + dataOffset = ( (u8*)tagStart - buffer ); + + // allocate memory and read the fst + if( !(fst = (FstEntry *)memalign( 32, RU( tagStart->dataOffset - dataOffset, 32 ) ) ) + || ( ISFS_Seek( fd, dataOffset + tagStart->rootNodeOffset, SEEK_SET ) != (s32)( dataOffset + tagStart->rootNodeOffset ) ) + || ( ISFS_Read( fd, fst, tagStart->dataOffset - dataOffset ) != (s32)( tagStart->dataOffset - dataOffset ) ) + || ( fst->filelen * 0xC > tagStart->dataOffset ) ) + { + dataOffset = 0; + free( buffer ); + if( fst ) + free( fst ); + CloseFile(); + gprintf( "U8NandArchive: error reading fst\n" ); + } + + // set name table pointer + u32 name_table_offset = fst->filelen * 0xC; + name_table = ((char *)fst) + name_table_offset; + + free( buffer ); + return true; +} + +u8* U8NandArchive::GetFileAllocated( const char *path, u32 *size ) const +{ + //gprintf( "U8NandArchive::GetFileAllocated( %s )\n" ); + if( !path || !fst ) + { + return NULL; + } + + // find file + int f = EntryFromPath( path, 0 ); + if( f < 1 || f >= (int)fst[ 0 ].filelen ) + { + gprintf( "U8: \"%s\" wasn't found in the archive.\n", path ); + return NULL; + } + if( fst[ f ].filetype ) + { + gprintf( "U8: \"%s\" is a folder\n", path ); + return NULL; + } + + // create a buffer + u8* ret = (u8*)memalign( 32, RU( fst[ f ].filelen, 32 ) ); + if( !ret ) + { + gprintf( "U8: out of memory\n" ); + return NULL; + } + + // seek and read + if( ISFS_Seek( fd, dataOffset + fst[ f ].fileoffset, SEEK_SET ) != (s32)( dataOffset + fst[ f ].fileoffset ) + || ISFS_Read( fd, ret, fst[ f ].filelen ) != (s32)fst[ f ].filelen ) + { + free( ret ); + gprintf( "U8: error reading data from nand\n" ); + gprintf( "fd: %i fst[ fd ].filelen: %08x\n", fd, fst[ f ].filelen ); + return NULL; + } + + u32 len = fst[ f ].filelen; + u8* ret2; + // determine if it needs to be decompressed + if( IsAshCompressed( ret, len ) ) + { + // ASH0 + ret2 = DecompressAsh( ret, len ); + if( !ret2 ) + { + free( ret ); + gprintf( "out of memory\n" ); + return NULL; + } + free( ret ); + } + else if( isLZ77compressed( ret ) ) + { + // LZ77 with no magic word + if( decompressLZ77content( ret, len, &ret2, &len ) ) + { + free( ret ); + return NULL; + } + free( ret ); + } + else if( *(u32*)( ret ) == 0x4C5A3737 )// LZ77 + { + // LZ77 with a magic word + if( decompressLZ77content( ret + 4, len - 4, &ret2, &len ) ) + { + free( ret ); + return NULL; + } + free( ret ); + } + else + { + // already got what we are after + ret2 = ret; + } + + if( size ) + { + *size = len; + } + + // flush the cache so if there are any textures in this data, it will be ready for the GX + DCFlushRange( ret2, len ); + return ret2; +} + diff --git a/source/utils/U8Archive.h b/source/utils/U8Archive.h new file mode 100644 index 00000000..4f0c697c --- /dev/null +++ b/source/utils/U8Archive.h @@ -0,0 +1,128 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok and giantpune + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef U8ARCHIVE_H +#define U8ARCHIVE_H + +#include +#include +#include "tools.h" + +class U8Archive +{ +public: + U8Archive( const u8 *stuff, u32 len ); + + // set the data used for this archive + virtual void SetData( const u8 *stuff, u32 len ); + + // returns a pointer to a file within the archive + virtual u8* GetFile( const char *path, u32 *size = NULL ) const; + virtual u8* GetFile( const std::string &path, u32 *size = NULL ) const; + virtual u8* GetFile( u32 fstIdx, u32 *size = NULL ) const; + + // gets a file and copies it into a newly memalign()'d buffer + //! if the data looks ASH or LZ77 compressed, it is decompressed + virtual u8* GetFileAllocated( const char *path, u32 *size = NULL ) const; + virtual u8* GetFileAllocated( const std::string &path, u32 *size = NULL ) const; + virtual u8* GetFileAllocated( u32 fstIdx, u32 *size = NULL ) const; + + virtual u32 FileDescriptor( const char *path ) const; + virtual u32 FileDescriptor( const std::string &path ) const{ return FileDescriptor( path.c_str() ); } + virtual u8* GetFileFromFd( u32 fd, u32 *size = NULL )const; + + +protected: + struct U8Header + { + u32 magic; + u32 rootNodeOffset; + u32 headerSize; + u32 dataOffset; + u8 zeroes[16]; + } __attribute__((packed)); + + struct FstEntry + { + u8 filetype; + char name_offset[3]; + u32 fileoffset; + u32 filelen; + } __attribute__((packed)); + + FstEntry *fst; + char *name_table; + u8* data; + + u32 NextEntryInFolder( u32 current, u32 directory ) const ; + s32 EntryFromPath( const char *path, int d = 0 ) const ; + char *FstName( const FstEntry *entry ) const; + u8 *DecompressCopy( const u8 * stuff, u32 len, u32 *size ) const; + + static int strlen_slash( const char *s ); + + static int strcasecmp_slash( const char *s1, const char *s2 ); + + // lightweight toupper - because U8 archives are case-insinsitive + static char toupper( char c ) + { + if( c <= 'z' && c >= 'a' ) + return c - 0x20; + return c; + } + + // looks in some common offsets for a U8 tag + static const u8* FindU8Tag( const u8* stuff, u32 len ); +}; + +// class to access files from an archive that is saved on the nand +// GetFile() will return NULL, use GetfileAllocated() +class U8NandArchive : public U8Archive +{ +public: + U8NandArchive( const char* nandPath ); + ~U8NandArchive(); + + bool SetFile( const char* nandPath ); + + + // not implimented in this subclass... + void SetData( const u8 *stuff, u32 len ){} + u8* GetFile( const char *path, u32 *size = NULL ) const { return NULL; } + u8* GetFile( const std::string &path, u32 *size = NULL ) const { return NULL; } + u8* GetFileFromFd( u32 fd, u32 *size = NULL )const { return NULL; } + + + // gets a file and copies it into a newly memalign()'d buffer + //! if the data looks ASH or LZ77 compressed, it is decompressed + u8* GetFileAllocated( const char *path, u32 *size = NULL ) const; + +private: + s32 fd; + + // where the U8 header starts within the file + u32 dataOffset; + void CloseFile() + { + if( fd >= 0 ) + { + ISFS_Close( fd ); + fd = -1; + } + } +}; + +#endif // U8ARCHIVE_H diff --git a/source/utils/ash.cpp b/source/utils/ash.cpp new file mode 100644 index 00000000..53f099b6 --- /dev/null +++ b/source/utils/ash.cpp @@ -0,0 +1,455 @@ +/**************************************************************************** + * Copyright (C) 2012 giantpune + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include "ash.h" +#include "gecko.h" + +#include +#include +//#include +#include +#include + + + +bool IsAshCompressed( const u8 *stuff, u32 len ) +{ + return ( len > 0x10 && + ((*(u32*)( stuff )) & 0xFFFFFF00 ) == 0x41534800 ); +} + +u8* DecompressAsh( const u8 *stuff, u32 &len ) +{ + if( !IsAshCompressed( stuff, len ) ) + { + return NULL; + } + + unsigned int r[32]; + unsigned int count = 0; + unsigned int t; + + r[4] = (u32)stuff; //in + + r[5] = 0x415348; + r[6] = 0x415348; + + r[5] = s32(*(unsigned int *)(r[4]+4)); + r[5] = r[5] & 0x00FFFFFF; + + u32 size = r[5]; + //gprintf("Decompressed size: %d\n", size); + u8* buf1 = (u8*)memalign( 32, size ); + if( !buf1 ) + { + gprintf( "ASH: no memory\n" ); + return NULL; + } + r[3] = (u32)buf1; //out + memset( (void*)buf1, 0, size ); + //printf("r[3] :%08X\n", r[3]); + + //printf("\n\n"); + + r[24] = 0x10; + r[28] = s32(*(unsigned int *)(r[4]+8)); + r[25] = 0; + r[29] = 0; + r[26] = s32(*(unsigned int *)(r[4]+0xC)); + r[30] = s32(*(unsigned int *)(r[4]+r[28])); + r[28] = r[28] + 4; + //r[8] = 0x8108<<16; + //HACK, pointer to RAM + u8* workingBuffer = (u8*)memalign( 32, 0x100000 ); + if( !workingBuffer ) + { + gprintf( "ASH: no memory 2\n" ); + free( buf1 ); + return NULL; + } + r[8] = (u32)workingBuffer; + memset( (void*)workingBuffer, 0, 0x100000 ); + //printf("r[8] :%08X\n", r[8]); + + r[8] = r[8]; + r[9] = r[8] + 0x07FE; + r[10] = r[9] + 0x07FE; + r[11] = r[10] + 0x1FFE; + r[31] = r[11] + 0x1FFE; + r[23] = 0x200; + r[22] = 0x200; + r[27] = 0; + +loc_81332124: + + if( r[25] != 0x1F ) + goto loc_81332140; + + r[0] = r[26] >> 31; + r[26]= s32(*(unsigned int *)(r[4] + r[24])); + r[25]= 0; + r[24]= r[24] + 4; + goto loc_8133214C; + +loc_81332140: + + r[0] = r[26] >> 31; + r[25]= r[25] + 1; + r[26]= r[26] << 1; + +loc_8133214C: + + if( r[0] == 0 ) + goto loc_81332174; + + r[0] = r[23] | 0x8000; + *(unsigned short *)(r[31]) = s16(r[0]); + r[0] = r[23] | 0x4000; + *(unsigned short *)(r[31]+2) = s16(r[0]); + + r[31] = r[31] + 4; + r[27] = r[27] + 2; + r[23] = r[23] + 1; + r[22] = r[22] + 1; + + goto loc_81332124; + +loc_81332174: + + r[12] = 9; + r[21] = r[25] + r[12]; + t = r[21]; + if( r[21] > 0x20 ) + goto loc_813321AC; + + r[21] = (~(r[12] - 0x20))+1; + r[6] = r[26] >> r[21]; + if( t == 0x20 ) + goto loc_8133219C; + + r[26] = r[26] << r[12]; + r[25] = r[25] + r[12]; + goto loc_813321D0; + +loc_8133219C: + + r[26]= s32(*(unsigned int *)(r[4] + r[24])); + r[25]= 0; + r[24]= r[24] + 4; + goto loc_813321D0; + +loc_813321AC: + + r[0] = (~(r[12] - 0x20))+1; + r[6] = r[26] >> r[0]; + r[26]= s32(*(unsigned int *)(r[4] + r[24])); + r[0] = (~(r[21] - 0x40))+1; + r[24]= r[24] + 4; + r[0] = r[26] >> r[0]; + r[6] = r[6] | r[0]; + r[25] = r[21] - 0x20; + r[26] = r[26] << r[25]; + +loc_813321D0: + + r[12]= s16(*(unsigned short *)(r[31] - 2)); + r[31] -= 2; + r[27]= r[27] - 1; + r[0] = r[12] & 0x8000; + r[12]= (r[12] & 0x1FFF) << 1; + if( r[0] == 0 ) + goto loc_813321F8; + + *(unsigned short *)(r[9]+r[12]) = s16(r[6]); + r[6] = (r[12] & 0x3FFF)>>1; // extrwi %r6, %r12, 14,17 + if( r[27] != 0 ) + goto loc_813321D0; + + goto loc_81332204; + +loc_813321F8: + + *(unsigned short *)(r[8]+r[12]) = s16(r[6]); + r[23] = r[22]; + goto loc_81332124; + +loc_81332204: + + r[23] = 0x800; + r[22] = 0x800; + +loc_8133220C: + + if( r[29] != 0x1F ) + goto loc_81332228; + + r[0] = r[30] >> 31; + r[30]= s32(*(unsigned int *)(r[4] + r[28])); + r[29]= 0; + r[28]= r[28] + 4; + goto loc_81332234; + +loc_81332228: + + r[0] = r[30] >> 31; + r[29]= r[29] + 1; + r[30]= r[30] << 1; + +loc_81332234: + + if( r[0] == 0 ) + goto loc_8133225C; + + r[0] = r[23] | 0x8000; + *(unsigned short *)(r[31]) = s16(r[0]); + r[0] = r[23] | 0x4000; + *(unsigned short *)(r[31]+2) = s16(r[0]); + + r[31] = r[31] + 4; + r[27] = r[27] + 2; + r[23] = r[23] + 1; + r[22] = r[22] + 1; + + goto loc_8133220C; + +loc_8133225C: + + r[12] = 0xB; + r[21] = r[29] + r[12]; + t = r[21]; + if( r[21] > 0x20 ) + goto loc_81332294; + + r[21] = (~(r[12] - 0x20))+1; + r[7] = r[30] >> r[21]; + if( t == 0x20 ) + goto loc_81332284; + + r[30] = r[30] << r[12]; + r[29] = r[29] + r[12]; + goto loc_813322B8; + +loc_81332284: + + r[30]= s32(*(unsigned int *)(r[4] + r[28])); + r[29]= 0; + r[28]= r[28] + 4; + goto loc_813322B8; + +loc_81332294: + + r[0] = (~(r[12] - 0x20))+1; + r[7] = r[30] >> r[0]; + r[30]= s32(*(unsigned int *)(r[4] + r[28])); + r[0] = (~(r[21] - 0x40))+1; + r[28]= r[28] + 4; + r[0] = r[30] >> r[0]; + r[7] = r[7] | r[0]; + r[29]= r[21] - 0x20; + r[30]= r[30] << r[29]; + +loc_813322B8: + + r[12]= s16(*(unsigned short *)(r[31] - 2)); + r[31] -= 2; + r[27]= r[27] - 1; + r[0] = r[12] & 0x8000; + r[12]= (r[12] & 0x1FFF) << 1; + if( r[0] == 0 ) + goto loc_813322E0; + + *(unsigned short *)(r[11]+r[12]) = s16(r[7]); + r[7] = (r[12] & 0x3FFF)>>1; // extrwi %r7, %r12, 14,17 + if( r[27] != 0 ) + goto loc_813322B8; + + goto loc_813322EC; + +loc_813322E0: + + *(unsigned short *)(r[10]+r[12]) = s16(r[7]); + r[23] = r[22]; + goto loc_8133220C; + +loc_813322EC: + + r[0] = r[5]; + +loc_813322F0: + + r[12]= r[6]; + +loc_813322F4: + + if( r[12] < 0x200 ) + goto loc_8133233C; + + if( r[25] != 0x1F ) + goto loc_81332318; + + r[31] = r[26] >> 31; + r[26] = s32(*(unsigned int *)(r[4] + r[24])); + r[24] = r[24] + 4; + r[25] = 0; + goto loc_81332324; + +loc_81332318: + + r[31] = r[26] >> 31; + r[25] = r[25] + 1; + r[26] = r[26] << 1; + +loc_81332324: + + r[27] = r[12] << 1; + if( r[31] != 0 ) + goto loc_81332334; + + r[12] = s16(*(unsigned short *)(r[8] + r[27])); + goto loc_813322F4; + +loc_81332334: + + r[12] = s16(*(unsigned short *)(r[9] + r[27])); + goto loc_813322F4; + +loc_8133233C: + + if( r[12] >= 0x100 ) + goto loc_8133235C; + + *(unsigned char *)(r[3]) = r[12]; + r[3] = r[3] + 1; + r[5] = r[5] - 1; + if( r[5] != 0 ) + goto loc_813322F0; + + goto loc_81332434; + +loc_8133235C: + + r[23] = r[7]; + +loc_81332360: + + if( r[23] < 0x800 ) + goto loc_813323A8; + + if( r[29] != 0x1F ) + goto loc_81332384; + + r[31] = r[30] >> 31; + r[30] = s32(*(unsigned int *)(r[4] + r[28])); + r[28] = r[28] + 4; + r[29] = 0; + goto loc_81332390; + +loc_81332384: + + r[31] = r[30] >> 31; + r[29] = r[29] + 1; + r[30] = r[30] << 1; + +loc_81332390: + + r[27] = r[23] << 1; + if( r[31] != 0 ) + goto loc_813323A0; + + r[23] = s16(*(unsigned short *)(r[10] + r[27])); + goto loc_81332360; + +loc_813323A0: + + r[23] = s16(*(unsigned short *)(r[11] + r[27])); + goto loc_81332360; + +loc_813323A8: + + r[12] = r[12] - 0xFD; + r[23] = ~r[23] + r[3] + 1; + r[5] = ~r[12] + r[5] + 1; + r[31] = r[12] >> 3; + + if( r[31] == 0 ) + goto loc_81332414; + + count = r[31]; + +loc_813323C0: + + r[31] = *(unsigned char *)(r[23] - 1); + *(unsigned char *)(r[3]) = r[31]; + + r[31] = *(unsigned char *)(r[23]); + *(unsigned char *)(r[3]+1) = r[31]; + + r[31] = *(unsigned char *)(r[23] + 1); + *(unsigned char *)(r[3]+2) = r[31]; + + r[31] = *(unsigned char *)(r[23] + 2); + *(unsigned char *)(r[3]+3) = r[31]; + + r[31] = *(unsigned char *)(r[23] + 3); + *(unsigned char *)(r[3]+4) = r[31]; + + r[31] = *(unsigned char *)(r[23] + 4); + *(unsigned char *)(r[3]+5) = r[31]; + + r[31] = *(unsigned char *)(r[23] + 5); + *(unsigned char *)(r[3]+6) = r[31]; + + r[31] = *(unsigned char *)(r[23] + 6); + *(unsigned char *)(r[3]+7) = r[31]; + + r[23] = r[23] + 8; + r[3] = r[3] + 8; + + if( --count ) + goto loc_813323C0; + + r[12] = r[12] & 7; + if( r[12] == 0 ) + goto loc_8133242C; + +loc_81332414: + + count = r[12]; + +loc_81332418: + + r[31] = *(unsigned char *)(r[23] - 1); + r[23] = r[23] + 1; + *(unsigned char *)(r[3]) = r[31]; + r[3] = r[3] + 1; + + if( --count ) + goto loc_81332418; + +loc_8133242C: + + if( r[5] != 0 ) + goto loc_813322F0; + +loc_81332434: + + r[3] = r[0]; + len = r[3]; + + //gprintf("Decompressed %d bytes\n", r[3]); + free( workingBuffer ); + return buf1; +} diff --git a/source/utils/ash.h b/source/utils/ash.h new file mode 100644 index 00000000..43614c64 --- /dev/null +++ b/source/utils/ash.h @@ -0,0 +1,30 @@ +/**************************************************************************** + * Copyright (C) 2012 giantpune + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef ASH_H +#define ASH_H + +#include + +// check if data is ash compressed +bool IsAshCompressed( const u8 *stuff, u32 len ); + +// decompress ash compressed data +//! len is the size of the compressed data, and is set to the size of the decompressed data +//! this allocates memory with memalign, free it when you are done with it + +u8* DecompressAsh( const u8 *stuff, u32 &len ); +#endif // ASH_H diff --git a/source/utils/gx_addons.c b/source/utils/gx_addons.c new file mode 100644 index 00000000..ce7402ae --- /dev/null +++ b/source/utils/gx_addons.c @@ -0,0 +1,76 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#include + +void GX_Project(f32 mx, f32 my, f32 mz, Mtx mv, const f32 *projection, + const f32 *viewport, f32 *sx, f32 *sy, f32 *sz) +{ + float x, y, z, w; + + guVector vec = (guVector) { mx, my, mz }; + guVector vecRes; + guVecMultiply(mv, &vec, &vecRes); + + if(projection[0] == GX_PERSPECTIVE) + { + x = (vecRes.x * projection[1]) + (vecRes.z * projection[2]); + y = (vecRes.y * projection[3]) + (vecRes.z * projection[4]); + z = (vecRes.z * projection[5]) + projection[6]; + w = -1.0f / vecRes.z; + } + else + { + x = (vecRes.x * projection[1]) + projection[2]; + y = (vecRes.y * projection[3]) + projection[4]; + z = (vecRes.z * projection[5]) + projection[6]; + w = 1.0f; + } + + *sx = viewport[0] + (w * x * viewport[2] + viewport[2]) * 0.5f; + *sy = viewport[1] - (w * y * viewport[3] - viewport[3]) * 0.5f; + *sz = viewport[5] + (w * z * (viewport[5] - viewport[4])); +} + +void GX_GetProjectionv( f32* ptr, Mtx44 p, u8 type) +{ + ptr[0] = (f32)type; + ptr[1] = p[0][0]; + ptr[3] = p[1][1]; + ptr[5] = p[2][2]; + ptr[6] = p[2][3]; + + if(type == GX_PERSPECTIVE) + { + ptr[2] = p[0][2]; + ptr[4] = p[1][2]; + } + else + { + ptr[2] = p[0][3]; + ptr[4] = p[1][3]; + } +} + +void GX_GetViewportv( f32* ptr, GXRModeObj *vmode ) +{ + ptr[0] = 0.0f; + ptr[1] = 0.0f; + ptr[2] = vmode->fbWidth; + ptr[3] = vmode->efbHeight; + ptr[4] = 0.0f; + ptr[5] = 1.0f; +} diff --git a/source/utils/gx_addons.h b/source/utils/gx_addons.h new file mode 100644 index 00000000..89afbf3a --- /dev/null +++ b/source/utils/gx_addons.h @@ -0,0 +1,35 @@ +/**************************************************************************** + * Copyright (C) 2012 Dimok + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + ****************************************************************************/ +#ifndef GX_ADDONS_H_ +#define GX_ADDONS_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void GX_Project(f32 mx, f32 my, f32 mz, Mtx mv, const f32 *projection, + const f32 *viewport, f32 *sx, f32 *sy, f32 *sz); +void GX_GetProjectionv( f32* ptr, Mtx44 p, u8 type); +void GX_GetViewportv( f32* ptr, GXRModeObj *vmode ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/utils/lz77.c b/source/utils/lz77.c index 3750c43a..cf2846f0 100755 --- a/source/utils/lz77.c +++ b/source/utils/lz77.c @@ -20,15 +20,14 @@ #include #include "lz77.h" +#include "tools.h" -#define ALIGN32(x) (((x) + 31) & ~31) - -u32 packBytes(int a, int b, int c, int d) +static inline u32 packBytes(int a, int b, int c, int d) { return (d << 24) | (c << 16) | (b << 8) | (a); } -s32 __decompressLZ77_11(u8 *in, u32 inputLen, u8 **output, u32 *outputLen) +s32 __decompressLZ77_11( const u8 *in, u32 inputLen, u8 **output, u32 *outputLen) { int x, y; @@ -48,10 +47,7 @@ s32 __decompressLZ77_11(u8 *in, u32 inputLen, u8 **output, u32 *outputLen) out = memalign(32, ALIGN32(decompressedSize)); if (out == NULL) - { - printf("Out of memory\n"); return -1; - } while (compressedPos < inputLen && decompressedPos < decompressedSize) { @@ -117,7 +113,7 @@ s32 __decompressLZ77_11(u8 *in, u32 inputLen, u8 **output, u32 *outputLen) return 0; } -s32 __decompressLZ77_10(u8 *in, u32 inputLen, u8 **output, u32 *outputLen) +s32 __decompressLZ77_10( const u8 *in, u32 inputLen, u8 **output, u32 *outputLen) { int x, y; @@ -131,10 +127,7 @@ s32 __decompressLZ77_10(u8 *in, u32 inputLen, u8 **output, u32 *outputLen) out = memalign(32, ALIGN32(decompressedSize)); if (out == NULL) - { - printf("Out of memory\n"); return -1; - } compressedPos += 0x4; @@ -179,17 +172,15 @@ s32 __decompressLZ77_10(u8 *in, u32 inputLen, u8 **output, u32 *outputLen) return 0; } -int isLZ77compressed(u8 *buffer) +int isLZ77compressed( const u8 *buffer) { if ((buffer[0] == LZ77_0x10_FLAG) || (buffer[0] == LZ77_0x11_FLAG)) - { return 1; - } return 0; } -int decompressLZ77content(u8 *buffer, u32 length, u8 **output, u32 *outputLen) +int decompressLZ77content(const u8 *buffer, u32 length, u8 **output, u32 *outputLen) { int ret; switch (buffer[0]) diff --git a/source/utils/lz77.h b/source/utils/lz77.h index ebaf0b5e..185ca548 100755 --- a/source/utils/lz77.h +++ b/source/utils/lz77.h @@ -1,34 +1,33 @@ -/******************************************************************************* - * lz77.h - * - * Copyright (c) 2009 The Lemon Man - * Copyright (c) 2009 Nicksasa - * Copyright (c) 2009 WiiPower - * - * Distributed under the terms of the GNU General Public License (v2) - * See http://www.gnu.org/licenses/gpl-2.0.txt for more info. - * - * Description: - * ----------- - * - ******************************************************************************/ - -#ifndef _LZ77_MODULE -#define _LZ77_MODULE - -#define LZ77_0x10_FLAG 0x10 -#define LZ77_0x11_FLAG 0x11 - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -int isLZ77compressed(u8 *buffer); -int decompressLZ77content(u8 *buffer, u32 length, u8 **output, u32 *outputLen); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif - +/******************************************************************************* + * lz77.h + * + * Copyright (c) 2009 The Lemon Man + * Copyright (c) 2009 Nicksasa + * Copyright (c) 2009 WiiPower + * + * Distributed under the terms of the GNU General Public License (v2) + * See http://www.gnu.org/licenses/gpl-2.0.txt for more info. + * + * Description: + * ----------- + * + ******************************************************************************/ +#ifndef _LZ77_MODULE +#define _LZ77_MODULE + +#define LZ77_0x10_FLAG 0x10 +#define LZ77_0x11_FLAG 0x11 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int isLZ77compressed( const u8 *buffer); +int decompressLZ77content( const u8 *buffer, u32 length, u8 **output, u32 *outputLen); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + diff --git a/source/utils/timer.h b/source/utils/timer.h index c640472e..f250f638 100644 --- a/source/utils/timer.h +++ b/source/utils/timer.h @@ -54,11 +54,11 @@ void ConvertNTFSDate(u64 ulNTFSDate, TimeStruct * ptm); class Timer { public: - Timer() { starttick = gettime(); }; - ~Timer() { }; - float elapsed() { return (float) (gettime()-starttick)/(1000.0f*TB_TIMER_CLOCK); }; - float elapsed_millisecs() { return 1000.0f*elapsed(); }; - void reset() { starttick = gettime(); }; + Timer() { starttick = gettime(); } + ~Timer() { } + float elapsed() { return (float) (gettime()-starttick)/(1000.0f*TB_TIMER_CLOCK); } + u32 elapsed_millisecs() { return (u32) ((gettime()-starttick)/TB_TIMER_CLOCK); } + void reset() { starttick = gettime(); } protected: u64 starttick; }; diff --git a/source/utils/wifi_gecko.c b/source/utils/wifi_gecko.c index 49ac770c..3a237a42 100644 --- a/source/utils/wifi_gecko.c +++ b/source/utils/wifi_gecko.c @@ -30,8 +30,8 @@ #include #include -#define DESTINATION_IP "192.168.178.3" -#define DESTINATION_PORT 4405 +#define DESTINATION_IP "192.168.178.3" +#define DESTINATION_PORT 4405 static int connection = -1; @@ -45,10 +45,10 @@ void WifiGecko_Close() int WifiGecko_Connect() { - if(!(connection < 0)) + if(connection >= 0) return connection; - connection = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + connection = net_socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (connection < 0) return connection; diff --git a/source/video.cpp b/source/video.cpp index be845d36..839755bd 100644 --- a/source/video.cpp +++ b/source/video.cpp @@ -17,16 +17,17 @@ #include "GUI/gui.h" #include "ImageOperations/TextureConverter.h" #include "ImageOperations/ImageWrite.h" +#include "settings/CSettings.h" #include "input.h" #include "gecko.h" -#define DEFAULT_FIFO_SIZE 256 * 1024 +#define GP_FIFO_SIZE (256 * 1024 * 3) static unsigned int *xfb[2] = { NULL, NULL }; // Double buffered static int whichfb = 0; // Switch -static GXRModeObj *vmode; // Menu video mode -static unsigned char gp_fifo[DEFAULT_FIFO_SIZE] ATTRIBUTE_ALIGN ( 32 ); -static Mtx GXmodelView2D; -static Mtx44 projection; +static unsigned char *gp_fifo = NULL; +Mtx44 FSProjection2D; +Mtx FSModelView2D; +GXRModeObj *vmode; // Menu video mode int screenheight = 480; int screenwidth = 640; u32 frameCount = 0; @@ -78,8 +79,11 @@ static void ResetVideo_Menu() GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); - GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); - GX_SetZMode(GX_FALSE, GX_LEQUAL, GX_TRUE); + + for(u32 i = 0; i < 8; i++) + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0+i, GX_TEX_ST, GX_F32, 0); + + GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); GX_SetNumChans(1); GX_SetNumTexGens(1); @@ -87,15 +91,14 @@ static void ResetVideo_Menu() GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); - guMtxIdentity(GXmodelView2D); - guMtxTransApply(GXmodelView2D, GXmodelView2D, 0.0F, 0.0F, -9900.0F); - GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0); + guMtxIdentity(FSModelView2D); + guMtxTransApply(FSModelView2D, FSModelView2D, 0.0F, 0.0F, -9900.0F); - guOrtho(projection, 0, screenheight-1, 0, screenwidth-1, 0, 10000); - GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); + guOrtho(FSProjection2D, 0, screenheight-1, 0, screenwidth-1, 0, 10000); GX_SetViewport(0.0f, 0.0f, vmode->fbWidth, vmode->efbHeight, 0.0f, 1.0f); GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + GX_SetColorUpdate(GX_TRUE); GX_SetAlphaUpdate(GX_TRUE); } @@ -111,11 +114,15 @@ void InitVideo() VIDEO_Init(); vmode = VIDEO_GetPreferredMode(NULL); // get default video mode - if (CONF_GetAspectRatio() == CONF_ASPECT_16_9) + vmode->viWidth = Settings.widescreen ? 708 : 640; + + if (Settings.PAL50) { - // widescreen fix - vmode->viWidth = VI_MAX_WIDTH_PAL - 12; - vmode->viXOrigin = ((VI_MAX_WIDTH_PAL - vmode->viWidth) / 2) + 2; + vmode->viXOrigin = (VI_MAX_WIDTH_PAL - vmode->viWidth) / 2; + } + else + { + vmode->viXOrigin = (VI_MAX_WIDTH_NTSC - vmode->viWidth) / 2; } VIDEO_Configure(vmode); @@ -134,12 +141,14 @@ void InitVideo() VIDEO_Flush(); VIDEO_WaitVSync(); - if (vmode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync(); + if (vmode->viTVMode & VI_NON_INTERLACE) + VIDEO_WaitVSync(); // Initialize GX GXColor background = { 0, 0, 0, 0xff }; - memset (&gp_fifo, 0, DEFAULT_FIFO_SIZE); - GX_Init (&gp_fifo, DEFAULT_FIFO_SIZE); + gp_fifo = (u8 *) memalign(32, GP_FIFO_SIZE); + memset (gp_fifo, 0, GP_FIFO_SIZE); + GX_Init (gp_fifo, GP_FIFO_SIZE); GX_SetCopyClear (background, 0x00ffffff); GX_SetDispCopyGamma (GX_GM_1_0); GX_SetCullMode (GX_CULL_NONE); @@ -154,24 +163,9 @@ void InitVideo() ***************************************************************************/ void AdjustOverscan(int x, int y) { - guOrtho(projection, y, screenheight-1 - y, x, screenwidth-1 - x, 0, 10000); + guOrtho(FSProjection2D, y, screenheight-1 - y, x, screenwidth-1 - x, 0, 10000); } -void VIDEO_SetWidescreen(bool widescreen) -{ - if (widescreen) - { - // widescreen fix - vmode->viWidth = VI_MAX_WIDTH_PAL - 12; - vmode->viXOrigin = ((VI_MAX_WIDTH_PAL - vmode->viWidth) / 2) + 2; - } - else - { - VIDEO_GetPreferredMode(NULL); - } - - VIDEO_Configure(vmode); -} /**************************************************************************** * StopGX * @@ -197,8 +191,6 @@ void StopGX() void Menu_Render() { whichfb ^= 1; // flip framebuffer - GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE); - GX_SetColorUpdate(GX_TRUE); GX_CopyDisp(xfb[whichfb], GX_TRUE); GX_DrawDone(); VIDEO_SetNextFramebuffer(xfb[whichfb]); @@ -217,12 +209,14 @@ void Menu_DrawImg(f32 xpos, f32 ypos, f32 zpos, f32 width, f32 height, u8 data[] { if (data == NULL) return; - GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); + GX_LoadProjectionMtx(FSProjection2D, GX_ORTHOGRAPHIC); GXTexObj texObj; GX_InitTexObj(&texObj, data, width, height, GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); GX_LoadTexObj(&texObj, GX_TEXMAP0); + GX_ClearVtxDesc(); + GX_InvVtxCache(); GX_InvalidateTexAll(); GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); @@ -239,7 +233,7 @@ void Menu_DrawImg(f32 xpos, f32 ypos, f32 zpos, f32 width, f32 height, u8 data[] guMtxConcat(m1, m2, m); guMtxTransApply(m, m, xpos + width + 0.5f, ypos + height + 0.5f, zpos); - guMtxConcat(GXmodelView2D, m, mv); + guMtxConcat(FSModelView2D, m, mv); GX_LoadPosMtxImm(mv, GX_PNMTX0); GX_Begin(GX_QUADS, GX_VTXFMT0, 4); @@ -260,8 +254,6 @@ void Menu_DrawImg(f32 xpos, f32 ypos, f32 zpos, f32 width, f32 height, u8 data[] GX_TexCoord2f32(0, 1); GX_End(); - - GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0); } /**************************************************************************** @@ -271,8 +263,12 @@ void Menu_DrawImg(f32 xpos, f32 ypos, f32 zpos, f32 width, f32 height, u8 data[] ***************************************************************************/ void Menu_DrawRectangle(f32 x, f32 y, f32 width, f32 height, GXColor color, u8 filled) { + GX_LoadProjectionMtx(FSProjection2D, GX_ORTHOGRAPHIC); + GX_LoadPosMtxImm(FSModelView2D, GX_PNMTX0); + GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR); - GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); + GX_ClearVtxDesc(); + GX_InvVtxCache(); GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); @@ -310,12 +306,14 @@ void Menu_DrawDiskCover(f32 xpos, f32 ypos, f32 zpos, u16 width, u16 height, u16 { if (data == NULL) return; - GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); + GX_LoadProjectionMtx(FSProjection2D, GX_ORTHOGRAPHIC); GXTexObj texObj; GX_InitTexObj(&texObj, data, width, height, GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); GX_LoadTexObj(&texObj, GX_TEXMAP0); + GX_ClearVtxDesc(); + GX_InvVtxCache(); GX_InvalidateTexAll(); GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); @@ -348,7 +346,7 @@ void Menu_DrawDiskCover(f32 xpos, f32 ypos, f32 zpos, u16 width, u16 height, u16 else guMtxTransApply(m, m, xpos + width + 0.5, ypos + height + 0.5, zpos); - guMtxConcat(GXmodelView2D, m, mv); + guMtxConcat(FSModelView2D, m, mv); GX_LoadPosMtxImm(mv, GX_PNMTX0); GX_Begin(GX_QUADS, GX_VTXFMT0, 4); @@ -390,15 +388,16 @@ void Menu_DrawDiskCover(f32 xpos, f32 ypos, f32 zpos, u16 width, u16 height, u16 } GX_End(); - GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0); } void Menu_DrawTPLImg(f32 xpos, f32 ypos, f32 zpos, f32 width, f32 height, GXTexObj *texObj, f32 degrees, f32 scaleX, f32 scaleY, u8 alpha, int XX1, int YY1, int XX2, int YY2, int XX3, int YY3, int XX4, int YY4) { - GX_LoadProjectionMtx(projection, GX_ORTHOGRAPHIC); + GX_LoadProjectionMtx(FSProjection2D, GX_ORTHOGRAPHIC); GX_LoadTexObj(texObj, GX_TEXMAP0); + GX_ClearVtxDesc(); + GX_InvVtxCache(); GX_InvalidateTexAll(); GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); @@ -415,7 +414,7 @@ void Menu_DrawTPLImg(f32 xpos, f32 ypos, f32 zpos, f32 width, f32 height, GXTexO guMtxConcat(m1, m2, m); guMtxTransApply(m, m, xpos + width + 0.5, ypos + height + 0.5, zpos); - guMtxConcat(GXmodelView2D, m, mv); + guMtxConcat(FSModelView2D, m, mv); GX_LoadPosMtxImm(mv, GX_PNMTX0); GX_Begin(GX_QUADS, GX_VTXFMT0, 4); @@ -436,8 +435,6 @@ void Menu_DrawTPLImg(f32 xpos, f32 ypos, f32 zpos, f32 width, f32 height, GXTexO GX_TexCoord2f32(0, 1); GX_End(); - - GX_LoadPosMtxImm(GXmodelView2D, GX_PNMTX0); } /**************************************************************************** * TakeScreenshot @@ -455,13 +452,60 @@ s32 TakeScreenshot(const char *path) memcpy(buffer, xfb[whichfb], size); - gdImagePtr gdImg; - if(YCbYCrToGD(buffer, vmode->fbWidth, vmode->xfbHeight, &gdImg)) - { - WriteGDImage(path,gdImg, IMAGE_PNG, 0); - gdImageDestroy(gdImg); - } + gdImagePtr gdImg = 0; + YCbYCrToGD(buffer, vmode->fbWidth, vmode->xfbHeight, &gdImg); + free(buffer); + if(gdImg == 0) + return -1; + + if(Settings.widescreen) + { + gdImagePtr dst = gdImageCreateTrueColor(768, screenheight); + if(dst == 0) + { + gdImageDestroy(gdImg); + return -1; + } + gdImageCopyResampled(dst, gdImg, 0, 0, 0, 0, 768, screenheight, screenwidth, screenheight); + gdImageDestroy(gdImg); + gdImg = dst; + } + + WriteGDImage(path,gdImg, IMAGE_PNG, 0); + gdImageDestroy(gdImg); + return 1; } + +/**************************************************************************** + * Restore GX to 2D mode drawing + ***************************************************************************/ +void ReSetup_GX(void) +{ + // channel control + GX_SetNumChans(1); + GX_SetChanCtrl(GX_COLOR0A0,GX_DISABLE,GX_SRC_REG,GX_SRC_VTX,GX_LIGHTNULL,GX_DF_NONE,GX_AF_NONE); + + // texture gen. + GX_SetNumTexGens(1); + GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + // texture environment + GX_SetNumTevStages(1); + GX_SetNumIndStages(0); + GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE); + GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GX_SetTevSwapMode(GX_TEVSTAGE0, GX_TEV_SWAP0, GX_TEV_SWAP0); + GX_SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_1_4); + GX_SetTevKAlphaSel(GX_TEVSTAGE0, GX_TEV_KASEL_1); + GX_SetTevDirect(GX_TEVSTAGE0); + // swap table + GX_SetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA); + GX_SetTevSwapModeTable(GX_TEV_SWAP1, GX_CH_RED, GX_CH_RED, GX_CH_RED, GX_CH_ALPHA); + GX_SetTevSwapModeTable(GX_TEV_SWAP2, GX_CH_GREEN, GX_CH_GREEN, GX_CH_GREEN, GX_CH_ALPHA); + GX_SetTevSwapModeTable(GX_TEV_SWAP3, GX_CH_BLUE, GX_CH_BLUE, GX_CH_BLUE, GX_CH_ALPHA); + // alpha compare and blend mode + GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_AND, GX_ALWAYS, 0); + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_SET); +} diff --git a/source/video.h b/source/video.h index 9ec1cf11..918ec4b8 100644 --- a/source/video.h +++ b/source/video.h @@ -22,7 +22,11 @@ void Menu_DrawRectangle(f32 x, f32 y, f32 width, f32 height, GXColor color, u8 f s32 TakeScreenshot(const char *path); void VIDEO_SetWidescreen(bool widescreen); void AdjustOverscan(int x, int y); +void ReSetup_GX(void); +extern GXRModeObj *vmode; +extern Mtx44 FSProjection2D; +extern Mtx FSModelView2D; extern int screenheight; extern int screenwidth; extern u32 frameCount; diff --git a/source/wad/nandtitle.h b/source/wad/nandtitle.h index 9e2e91a5..0fca9283 100644 --- a/source/wad/nandtitle.h +++ b/source/wad/nandtitle.h @@ -96,6 +96,8 @@ class NandTitle u64 operator[](u32 i) { return At(i); } bool GetName(u64 tid, int language, wchar_t* name); + + tmd* GetTMD(u64 tid); static int LoadFileFromNand(const char *filepath, u8 **outbuffer, u32 *outfilesize); static int ExtractFile(const char *nandPath, const char *filepath); @@ -105,7 +107,6 @@ class NandTitle std::vector titleIds; std::map NameList; - tmd* GetTMD(u64 tid); u32 currentIndex; u32 currentType; diff --git a/source/xml/GameTDB.cpp b/source/xml/GameTDB.cpp index d0206acc..b94a6f49 100644 --- a/source/xml/GameTDB.cpp +++ b/source/xml/GameTDB.cpp @@ -1160,12 +1160,12 @@ int GameTDB::GetAccessoirList(const char * id, vector & acc_list) for(const char * ptr = ControlsNode; *ptr != '"' && *ptr != '\0'; ptr++) acc_list[list_num].Name.push_back(*ptr); - acc_list[list_num].Required = false; + acc_list[list_num].Required = false; char * requiredField = strstr(ControlsNode, "required=\""); if(requiredField && strncmp(requiredField + strlen("required=\""), "true", 4) == 0) { - acc_list[list_num].Required = true; + acc_list[list_num].Required = true; } ControlsNode = strstr(ControlsNode, "