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, "