From fa11a745d642e6843034fb4ac68df179e273e096 Mon Sep 17 00:00:00 2001 From: dimok321 <15055714+dimok789@users.noreply.github.com> Date: Sat, 13 Nov 2010 22:34:53 +0000 Subject: [PATCH] A lot of changes with this rev *Rewrote the whole Settings.cpp into 11 classes. Each settings menu has it's own class now *Reworked the whole sound system. Supported formats AIF/MP3/OGG/BNS/WAV now with no file size limit (streaming). *Changed button click/over sounds to wav from raw pcm *Lot's of bug fixes --- Makefile | 11 +- gui.pnproj | 2 +- gui.pnps | 2 +- source/FileOperations/File.cpp | 145 + source/FileOperations/File.hpp | 30 + source/SoundOperations/AifDecoder.cpp | 214 ++ source/SoundOperations/AifDecoder.hpp | 50 + source/SoundOperations/BNSDecoder.cpp | 363 ++ source/SoundOperations/BNSDecoder.hpp | 59 + source/SoundOperations/BufferCircle.cpp | 144 + source/SoundOperations/BufferCircle.hpp | 92 + source/SoundOperations/Mp3Decoder.cpp | 216 ++ source/SoundOperations/Mp3Decoder.hpp | 51 + source/SoundOperations/OggDecoder.cpp | 144 + source/SoundOperations/OggDecoder.hpp | 45 + source/SoundOperations/SoundDecoder.cpp | 156 + source/SoundOperations/SoundDecoder.hpp | 89 + source/SoundOperations/SoundHandler.cpp | 271 ++ source/SoundOperations/SoundHandler.hpp | 68 + source/SoundOperations/WavDecoder.cpp | 155 + source/SoundOperations/WavDecoder.hpp | 75 + .../gui_bgm.cpp | 460 ++- .../{libwiigui => SoundOperations}/gui_bgm.h | 84 +- source/SoundOperations/gui_sound.cpp | 380 ++ source/SoundOperations/gui_sound.h | 80 + source/bannersound.cpp | 57 +- source/cheats/cheatmenu.cpp | 8 +- source/fatmounter.c | 1 + source/filelist.h | 12 +- source/homebrewboot/HomebrewBrowse.cpp | 41 +- source/libs/libwbfs/libwbfs.c | 1654 ++++----- source/libs/libwbfs/libwbfs.h | 2 +- source/libs/libwbfs/libwbfs_os.h | 64 +- source/libwiigui/gui.h | 71 +- source/libwiigui/gui_button.cpp | 21 +- source/libwiigui/gui_customoptionbrowser.cpp | 65 +- source/libwiigui/gui_customoptionbrowser.h | 20 +- source/libwiigui/gui_filebrowser.cpp | 9 +- source/libwiigui/gui_gamebrowser.cpp | 3 +- source/libwiigui/gui_gamebrowser.h | 129 +- source/libwiigui/gui_gamecarousel.cpp | 5 - source/libwiigui/gui_gamecarousel.h | 106 +- source/libwiigui/gui_gamegrid.cpp | 5 - source/libwiigui/gui_gamegrid.h | 2 - source/libwiigui/gui_keyboard.cpp | 9 +- source/libwiigui/gui_numpad.cpp | 406 ++- source/libwiigui/gui_optionbrowser.cpp | 4 - source/libwiigui/gui_searchbar.cpp | 12 +- source/libwiigui/gui_searchbar.h | 2 - source/libwiigui/gui_sound.cpp | 446 --- source/libwiigui/gui_sound_decoder.h | 114 - source/libwiigui/gui_sound_decoder_aiff.cpp | 158 - source/libwiigui/gui_sound_decoder_bns.cpp | 257 -- source/libwiigui/gui_sound_decoder_mpg.cpp | 213 -- source/libwiigui/gui_sound_decoder_ogg.cpp | 285 -- source/libwiigui/gui_sound_decoder_wav.cpp | 237 -- source/main.cpp | 2 +- source/menu.cpp | 13 +- source/menu.h | 3 - source/menu/MountGamePartition.cpp | 2 +- source/menu/menu_disclist.cpp | 41 +- source/menu/menu_install.cpp | 2 - source/menu/menu_partition_selection.cpp | 8 +- source/menu/menus.h | 1 - source/prompts/DiscBrowser.cpp | 33 +- source/prompts/DiscBrowser.h | 5 +- source/prompts/PromptWindows.cpp | 156 +- source/prompts/TitleBrowser.cpp | 13 +- source/prompts/filebrowser.cpp | 21 +- source/prompts/gameinfo.cpp | 5 +- source/settings/Settings.cpp | 3052 +---------------- source/settings/SettingsPrompts.cpp | 25 +- source/settings/menus/CustomPathsSM.cpp | 252 ++ source/settings/menus/CustomPathsSM.hpp | 43 + source/settings/menus/FlyingButtonsMenu.cpp | 462 +++ source/settings/menus/FlyingButtonsMenu.hpp | 103 + source/settings/menus/GUISettingsMenu.cpp | 334 ++ source/settings/menus/GUISettingsMenu.hpp | 45 + source/settings/menus/GameLoadSM.cpp | 327 ++ source/settings/menus/GameLoadSM.hpp | 45 + source/settings/menus/GameSettingsMenu.cpp | 121 + source/settings/menus/GameSettingsMenu.hpp | 44 + source/settings/menus/GlobalSettings.cpp | 200 ++ source/settings/menus/GlobalSettings.hpp | 43 + source/settings/menus/IndGameLoadSM.cpp | 409 +++ source/settings/menus/IndGameLoadSM.hpp | 51 + source/settings/menus/ParentalControlSM.cpp | 181 + source/settings/menus/ParentalControlSM.hpp | 42 + source/settings/menus/SettingsMenu.cpp | 125 + source/settings/menus/SettingsMenu.hpp | 72 + source/settings/menus/SoundSettingsMenu.cpp | 180 + source/settings/menus/SoundSettingsMenu.hpp | 42 + source/settings/menus/UninstallSM.cpp | 175 + source/settings/menus/UninstallSM.hpp | 44 + source/sounds/button_click.pcm | Bin 7204 -> 0 bytes source/sounds/button_click.wav | Bin 0 -> 4848 bytes source/sounds/button_click2.pcm | Bin 152064 -> 0 bytes source/sounds/button_click2.wav | Bin 0 -> 101424 bytes source/sounds/button_over.pcm | Bin 32256 -> 0 bytes source/sounds/button_over.wav | Bin 0 -> 21552 bytes source/sys.cpp | 5 + source/themes/Theme_Downloader.cpp | 40 +- source/utils/timer.c | 119 + source/utils/timer.h | 68 + source/utils/uncompress.c | 185 + source/utils/uncompress.h | 55 + source/wad/wad.cpp | 16 +- 107 files changed, 8292 insertions(+), 6722 deletions(-) create mode 100644 source/FileOperations/File.cpp create mode 100644 source/FileOperations/File.hpp create mode 100644 source/SoundOperations/AifDecoder.cpp create mode 100644 source/SoundOperations/AifDecoder.hpp create mode 100644 source/SoundOperations/BNSDecoder.cpp create mode 100644 source/SoundOperations/BNSDecoder.hpp create mode 100644 source/SoundOperations/BufferCircle.cpp create mode 100644 source/SoundOperations/BufferCircle.hpp create mode 100644 source/SoundOperations/Mp3Decoder.cpp create mode 100644 source/SoundOperations/Mp3Decoder.hpp create mode 100644 source/SoundOperations/OggDecoder.cpp create mode 100644 source/SoundOperations/OggDecoder.hpp create mode 100644 source/SoundOperations/SoundDecoder.cpp create mode 100644 source/SoundOperations/SoundDecoder.hpp create mode 100644 source/SoundOperations/SoundHandler.cpp create mode 100644 source/SoundOperations/SoundHandler.hpp create mode 100644 source/SoundOperations/WavDecoder.cpp create mode 100644 source/SoundOperations/WavDecoder.hpp rename source/{libwiigui => SoundOperations}/gui_bgm.cpp (87%) rename source/{libwiigui => SoundOperations}/gui_bgm.h (86%) create mode 100644 source/SoundOperations/gui_sound.cpp create mode 100644 source/SoundOperations/gui_sound.h delete mode 100644 source/libwiigui/gui_sound.cpp delete mode 100644 source/libwiigui/gui_sound_decoder.h delete mode 100644 source/libwiigui/gui_sound_decoder_aiff.cpp delete mode 100644 source/libwiigui/gui_sound_decoder_bns.cpp delete mode 100644 source/libwiigui/gui_sound_decoder_mpg.cpp delete mode 100644 source/libwiigui/gui_sound_decoder_ogg.cpp delete mode 100644 source/libwiigui/gui_sound_decoder_wav.cpp create mode 100644 source/settings/menus/CustomPathsSM.cpp create mode 100644 source/settings/menus/CustomPathsSM.hpp create mode 100644 source/settings/menus/FlyingButtonsMenu.cpp create mode 100644 source/settings/menus/FlyingButtonsMenu.hpp create mode 100644 source/settings/menus/GUISettingsMenu.cpp create mode 100644 source/settings/menus/GUISettingsMenu.hpp create mode 100644 source/settings/menus/GameLoadSM.cpp create mode 100644 source/settings/menus/GameLoadSM.hpp create mode 100644 source/settings/menus/GameSettingsMenu.cpp create mode 100644 source/settings/menus/GameSettingsMenu.hpp create mode 100644 source/settings/menus/GlobalSettings.cpp create mode 100644 source/settings/menus/GlobalSettings.hpp create mode 100644 source/settings/menus/IndGameLoadSM.cpp create mode 100644 source/settings/menus/IndGameLoadSM.hpp create mode 100644 source/settings/menus/ParentalControlSM.cpp create mode 100644 source/settings/menus/ParentalControlSM.hpp create mode 100644 source/settings/menus/SettingsMenu.cpp create mode 100644 source/settings/menus/SettingsMenu.hpp create mode 100644 source/settings/menus/SoundSettingsMenu.cpp create mode 100644 source/settings/menus/SoundSettingsMenu.hpp create mode 100644 source/settings/menus/UninstallSM.cpp create mode 100644 source/settings/menus/UninstallSM.hpp delete mode 100644 source/sounds/button_click.pcm create mode 100644 source/sounds/button_click.wav delete mode 100644 source/sounds/button_click2.pcm create mode 100644 source/sounds/button_click2.wav delete mode 100644 source/sounds/button_over.pcm create mode 100644 source/sounds/button_over.wav create mode 100644 source/utils/timer.c create mode 100644 source/utils/timer.h create mode 100644 source/utils/uncompress.c create mode 100644 source/utils/uncompress.h diff --git a/Makefile b/Makefile index 6e979142..68ee12a0 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,7 @@ SOURCES := source \ source/xml \ source/network \ source/settings \ + source/settings/menus \ source/prompts \ source/wad \ source/banner \ @@ -43,6 +44,7 @@ SOURCES := source \ source/memory \ source/FileOperations \ source/ImageOperations \ + source/SoundOperations \ source/utils \ source/utils/minizip \ source/usbloader/wbfs @@ -53,7 +55,7 @@ INCLUDES := source # options for code generation #--------------------------------------------------------------------------------- -CFLAGS = -g -O4 -Wall $(MACHDEP) $(INCLUDE) -DHAVE_CONFIG_H +CFLAGS = -g -O4 -Wall -Wno-multichar $(MACHDEP) $(INCLUDE) -DHAVE_CONFIG_H CXXFLAGS = -Xassembler -aln=$@.lst $(CFLAGS) LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map,--section-start,.init=0x80B00000,-wrap,malloc,-wrap,free,-wrap,memalign,-wrap,calloc,-wrap,realloc,-wrap,malloc_usable_size -include $(PROJECTDIR)/Make.config @@ -93,6 +95,7 @@ TTFFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.ttf))) PNGFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.png))) OGGFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.ogg))) PCMFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.pcm))) +WAVFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.wav))) DOLFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.dol))) MP3FILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.mp3))) @@ -110,7 +113,7 @@ export OFILES := $(addsuffix .o,$(BINFILES)) \ $(sFILES:.s=.o) $(SFILES:.S=.o) \ $(TTFFILES:.ttf=.ttf.o) $(PNGFILES:.png=.png.o) $(addsuffix .o,$(DOLFILES))\ $(OGGFILES:.ogg=.ogg.o) $(PCMFILES:.pcm=.pcm.o) $(MP3FILES:.mp3=.mp3.o) \ - $(addsuffix .o,$(ELFFILES)) $(CURDIR)/data/magic_patcher.o + $(WAVFILES:.wav=.wav.o) $(addsuffix .o,$(ELFFILES)) $(CURDIR)/data/magic_patcher.o #--------------------------------------------------------------------------------- # build a list of include paths @@ -215,6 +218,10 @@ language: $(wildcard $(PROJECTDIR)/Languages/*.lang) %.pcm.o : %.pcm @echo $(notdir $<) @bin2s -a 32 $< | $(AS) -o $(@) + +%.wav.o : %.wav + @echo $(notdir $<) + @bin2s -a 32 $< | $(AS) -o $(@) %.mp3.o : %.mp3 @echo $(notdir $<) diff --git a/gui.pnproj b/gui.pnproj index 1cf7bd39..621e2f15 100644 --- a/gui.pnproj +++ b/gui.pnproj @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/gui.pnps b/gui.pnps index cbff6d41..ce6765fe 100644 --- a/gui.pnps +++ b/gui.pnps @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/source/FileOperations/File.cpp b/source/FileOperations/File.cpp new file mode 100644 index 00000000..ce68add3 --- /dev/null +++ b/source/FileOperations/File.cpp @@ -0,0 +1,145 @@ +#include +#include "File.hpp" + +CFile::CFile() +{ + file_fd = NULL; + mem_file = NULL; + filesize = 0; + Pos = 0; +} + +CFile::CFile(const char * filepath, const char * mode) +{ + file_fd = NULL; + open(filepath, mode); +} + +CFile::CFile(const u8 * mem, int size) +{ + file_fd = NULL; + open(mem, size); +} + +CFile::~CFile() +{ + close(); +} + +int CFile::open(const char * filepath, const char * mode) +{ + close(); + + file_fd = fopen(filepath, mode); + if(!file_fd) + return -1; + + fseek(file_fd, 0, SEEK_END); + filesize = ftell(file_fd); + rewind(); + + return 0; +} + +int CFile::open(const u8 * mem, int size) +{ + close(); + + mem_file = mem; + filesize = size; + + return 0; +} + +void CFile::close() +{ + if(file_fd) + fclose(file_fd); + + file_fd = NULL; + mem_file = NULL; + filesize = 0; + Pos = 0; +} + +int CFile::read(u8 * ptr, size_t size) +{ + if(file_fd) + { + int ret = fread(ptr, 1, size, file_fd); + if(ret > 0) + Pos += ret; + return ret; + } + + int readsize = size; + + if(readsize > (long int) filesize-Pos) + readsize = filesize-Pos; + + if(readsize <= 0) + return readsize; + + if(mem_file != NULL) + { + memcpy(ptr, mem_file+Pos, readsize); + Pos += readsize; + return readsize; + } + + return -1; +} + +int CFile::write(const u8 * ptr, size_t size) +{ + if(size < 0) + return size; + + if(file_fd) + { + int ret = fwrite(ptr, 1, size, file_fd); + if(ret > 0) + Pos += ret; + return ret; + } + + return -1; +} + +int CFile::seek(long int offset, int origin) +{ + int ret = 0; + + if(origin == SEEK_SET) + { + Pos = offset; + } + else if(origin == SEEK_CUR) + { + Pos += offset; + } + else if(origin == SEEK_END) + { + Pos = filesize+offset; + } + if(Pos < 0) + { + Pos = 0; + return -1; + } + + if(file_fd) + ret = fseek(file_fd, Pos, SEEK_SET); + + if(mem_file != NULL) + { + if(Pos > (long int) filesize) + { + Pos = filesize; + return -1; + } + } + + return ret; +} + diff --git a/source/FileOperations/File.hpp b/source/FileOperations/File.hpp new file mode 100644 index 00000000..b5e1af75 --- /dev/null +++ b/source/FileOperations/File.hpp @@ -0,0 +1,30 @@ +#ifndef FILE_HPP_ +#define FILE_HPP_ + +#include +#include + +class CFile +{ + public: + CFile(); + CFile(const char * filepath, const char * mode); + CFile(const u8 * memory, int memsize); + ~CFile(); + int open(const char * filepath, const char * mode); + int open(const u8 * memory, int memsize); + void close(); + int read(u8 * ptr, size_t size); + int write(const u8 * ptr, size_t size); + int seek(long int offset, int origin); + long int tell() { return Pos; }; + long int size() { return filesize; }; + void rewind() { seek(0, SEEK_SET); }; + protected: + FILE * file_fd; + const u8 * mem_file; + u64 filesize; + long int Pos; +}; + +#endif diff --git a/source/SoundOperations/AifDecoder.cpp b/source/SoundOperations/AifDecoder.cpp new file mode 100644 index 00000000..3fa2f2b8 --- /dev/null +++ b/source/SoundOperations/AifDecoder.cpp @@ -0,0 +1,214 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include +#include "AifDecoder.hpp" + +typedef struct +{ + u32 fccCOMM; + u32 size; + u16 channels; + u8 frames[4]; + u16 bps; + u8 freq[10]; +} SAIFFCommChunk; + +typedef struct +{ + u32 fccSSND; + u32 size; + u32 offset; + u32 blockSize; +} SAIFFSSndChunk; + +// ------ +// Copyright (C) 1988-1991 Apple Computer, Inc. +#ifndef HUGE_VAL +# define HUGE_VAL HUGE +#endif + +# define UnsignedToFloat(u) (((double)((long)(u - 2147483647L - 1))) + 2147483648.0) + +static double ConvertFromIeeeExtended(const unsigned char* bytes) +{ + double f; + int expon; + unsigned long hiMant, loMant; + + expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); + hiMant = ((unsigned long)(bytes[2] & 0xFF) << 24) + | ((unsigned long)(bytes[3] & 0xFF) << 16) + | ((unsigned long)(bytes[4] & 0xFF) << 8) + | ((unsigned long)(bytes[5] & 0xFF)); + loMant = ((unsigned long)(bytes[6] & 0xFF) << 24) + | ((unsigned long)(bytes[7] & 0xFF) << 16) + | ((unsigned long)(bytes[8] & 0xFF) << 8) + | ((unsigned long)(bytes[9] & 0xFF)); + + if (expon == 0 && hiMant == 0 && loMant == 0) { + f = 0; + } + else { + if (expon == 0x7FFF) { + f = HUGE_VAL; + } + else { + expon -= 16383; + f = ldexp(UnsignedToFloat(hiMant), expon-=31); + f += ldexp(UnsignedToFloat(loMant), expon-=32); + } + } + + if (bytes[0] & 0x80) + return -f; + else + return f; +} + +AifDecoder::AifDecoder(const char * filepath) + : SoundDecoder(filepath) +{ + SoundType = SOUND_AIF; + + if(!file_fd) + return; + + OpenFile(); +} + +AifDecoder::AifDecoder(const u8 * snd, int len) + : SoundDecoder(snd, len) +{ + SoundType = SOUND_AIF; + + if(!file_fd) + return; + + OpenFile(); +} + +AifDecoder::~AifDecoder() +{ +} + +void AifDecoder::OpenFile() +{ + SWaveHdr Header; + file_fd->read((u8 *) &Header, sizeof(SWaveHdr)); + + if (Header.magicRIFF != 'FORM') + { + CloseFile(); + return; + } + else if(Header.magicWAVE != 'AIFF') + { + CloseFile(); + return; + } + + SWaveChunk WaveChunk; + do + { + int ret = file_fd->read((u8 *) &WaveChunk, sizeof(SWaveChunk)); + if(ret <= 0) + { + CloseFile(); + return; + } + } + while(WaveChunk.magicDATA != 'COMM'); + + DataOffset = file_fd->tell()+WaveChunk.size; + + SAIFFCommChunk CommHdr; + file_fd->seek(file_fd->tell()-sizeof(SWaveChunk), SEEK_SET); + file_fd->read((u8 *) &CommHdr, sizeof(SAIFFCommChunk)); + + if(CommHdr.fccCOMM != 'COMM') + { + CloseFile(); + return; + } + + file_fd->seek(DataOffset, SEEK_SET); + + SAIFFSSndChunk SSndChunk; + file_fd->read((u8 *) &SSndChunk, sizeof(SAIFFSSndChunk)); + + if(SSndChunk.fccSSND != 'SSND') + { + CloseFile(); + return; + } + + DataOffset += sizeof(SAIFFSSndChunk); + DataSize = SSndChunk.size-8; + SampleRate = (u32) ConvertFromIeeeExtended(CommHdr.freq); + Format = VOICE_STEREO_16BIT; + + if(CommHdr.channels == 1 && CommHdr.bps == 8) + Format = VOICE_MONO_8BIT; + else if (CommHdr.channels == 1 && CommHdr.bps == 16) + Format = VOICE_MONO_16BIT; + else if (CommHdr.channels == 2 && CommHdr.bps == 8) + Format = VOICE_STEREO_8BIT; + else if (CommHdr.channels == 2 && CommHdr.bps == 16) + Format = VOICE_STEREO_16BIT; + + Decode(); +} + +void AifDecoder::CloseFile() +{ + if(file_fd) + delete file_fd; + + file_fd = NULL; +} + +int AifDecoder::Read(u8 * buffer, int buffer_size, int pos) +{ + if(!file_fd) + return -1; + + if(CurPos >= (int) DataSize) + return 0; + + file_fd->seek(DataOffset+CurPos, SEEK_SET); + + if(buffer_size > (int) DataSize-CurPos) + buffer_size = DataSize-CurPos; + + int read = file_fd->read(buffer, buffer_size); + if(read > 0) + { + CurPos += read; + } + + return read; +} diff --git a/source/SoundOperations/AifDecoder.hpp b/source/SoundOperations/AifDecoder.hpp new file mode 100644 index 00000000..f9c6fd91 --- /dev/null +++ b/source/SoundOperations/AifDecoder.hpp @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef AIFDECODER_HPP_ +#define AIFDECODER_HPP_ + +#include "SoundDecoder.hpp" +#include "WavDecoder.hpp" + +class AifDecoder : public SoundDecoder +{ + public: + AifDecoder(const char * filepath); + AifDecoder(const u8 * snd, int len); + ~AifDecoder(); + int GetFormat() { return Format; }; + int GetSampleRate() { return SampleRate; }; + int Read(u8 * buffer, int buffer_size, int pos); + protected: + void OpenFile(); + void CloseFile(); + u32 DataOffset; + u32 DataSize; + u32 SampleRate; + u8 Format; +}; + +#endif diff --git a/source/SoundOperations/BNSDecoder.cpp b/source/SoundOperations/BNSDecoder.cpp new file mode 100644 index 00000000..e79a2442 --- /dev/null +++ b/source/SoundOperations/BNSDecoder.cpp @@ -0,0 +1,363 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include +#include +#include +#include "BNSDecoder.hpp" + +BNSDecoder::BNSDecoder(const char * filepath) + : SoundDecoder(filepath) +{ + SoundType = SOUND_BNS; + memset(&SoundData, 0, sizeof(SoundBlock)); + + if(!file_fd) + return; + + OpenFile(); +} + +BNSDecoder::BNSDecoder(const u8 * snd, int len) + : SoundDecoder(snd, len) +{ + SoundType = SOUND_BNS; + memset(&SoundData, 0, sizeof(SoundBlock)); + + if(!file_fd) + return; + + OpenFile(); +} + +BNSDecoder::~BNSDecoder() +{ + ExitRequested = true; + while(Decoding) + usleep(100); + + if(SoundData.buffer != NULL) + free(SoundData.buffer); + + SoundData.buffer = NULL; +} + +void BNSDecoder::OpenFile() +{ + u8 * tempbuff = new (std::nothrow) u8[file_fd->size()]; + if(!tempbuff) + { + CloseFile(); + return; + } + + int done = 0; + + while(done < file_fd->size()) + { + int read = file_fd->read(tempbuff, file_fd->size()); + if(read > 0) + done += read; + else + { + CloseFile(); + return; + } + } + + SoundData = DecodefromBNS(tempbuff, done); + if(SoundData.buffer == NULL) + { + CloseFile(); + return; + } + + delete [] tempbuff; + tempbuff = NULL; + + Decode(); +} + +void BNSDecoder::CloseFile() +{ + if(file_fd) + delete file_fd; + + file_fd = NULL; +} + +int BNSDecoder::Read(u8 * buffer, int buffer_size, int pos) +{ + if(!SoundData.buffer) + return -1; + + if(SoundData.loopFlag) + { + int factor = SoundData.format == VOICE_STEREO_16BIT ? 4 : 2; + if(CurPos >= (int) SoundData.loopEnd*factor) + CurPos = SoundData.loopStart*factor; + + if(buffer_size > (int) SoundData.loopEnd*factor-CurPos) + buffer_size = SoundData.loopEnd*factor-CurPos; + } + else + { + if(CurPos >= (int) SoundData.size) + return 0; + + if(buffer_size > (int) SoundData.size-CurPos) + buffer_size = SoundData.size-CurPos; + } + + memcpy(buffer, SoundData.buffer+CurPos, buffer_size); + CurPos += buffer_size; + + return buffer_size; +} + +struct BNSHeader +{ + u32 fccBNS; + u32 magic; + u32 size; + u16 unk1; + u16 unk2; + u32 infoOffset; + u32 infoSize; + u32 dataOffset; + u32 dataSize; +} __attribute__((packed)); + +struct BNSInfo +{ + u32 fccINFO; + u32 size; + u8 codecNum; + u8 loopFlag; + u8 chanCount; + u8 zero; + u16 freq; + u8 pad1[2]; + u32 loopStart; + u32 loopEnd; + u32 offsetToChanStarts; + u8 pad2[4]; + u32 chan1StartOffset; + u32 chan2StartOffset; + u32 chan1Start; + u32 coeff1Offset; + u8 pad3[4]; + u32 chan2Start; + u32 coeff2Offset; + u8 pad4[4]; + s16 coefficients1[8][2]; + u16 chan1Gain; + u16 chan1PredictiveScale; + s16 chan1PrevSamples[2]; + u16 chan1LoopPredictiveScale; + s16 chan1LoopPrevSamples[2]; + u16 chan1LoopPadding; + s16 coefficients2[8][2]; + u16 chan2Gain; + u16 chan2PredictiveScale; + s16 chan2PrevSamples[2]; + u16 chan2LoopPredictiveScale; + s16 chan2LoopPrevSamples[2]; + u16 chan2LoopPadding; +} __attribute__((packed)); + +struct BNSData +{ + u32 fccDATA; + u32 size; + u8 data; +} __attribute__((packed)); + +struct ADPCMByte +{ + s8 sample1 : 4; + s8 sample2 : 4; +} __attribute__((packed)); + +struct BNSADPCMBlock +{ + u8 pad : 1; + u8 coeffIndex : 3; + u8 lshift : 4; + ADPCMByte samples[7]; +} __attribute__((packed)); + +struct BNSDecObj +{ + s16 prevSamples[2]; + s16 coeff[8][2]; +}; + +static void loadBNSInfo(BNSInfo &bnsInfo, const u8 *buffer) +{ + const u8 *ptr = buffer + 8; + bnsInfo = *(const BNSInfo *)buffer; + if (bnsInfo.offsetToChanStarts == 0x18 && bnsInfo.chan1StartOffset == 0x20 && bnsInfo.chan2StartOffset == 0x2C + && bnsInfo.coeff1Offset == 0x38 && bnsInfo.coeff2Offset == 0x68) + return; + bnsInfo.chan1StartOffset = *(const u32 *)(ptr + bnsInfo.offsetToChanStarts); + bnsInfo.chan1Start = *(const u32 *)(ptr + bnsInfo.chan1StartOffset); + bnsInfo.coeff1Offset = *(const u32 *)(ptr + bnsInfo.chan1StartOffset + 4); + if ((u8 *)bnsInfo.coefficients1 != ptr + bnsInfo.coeff1Offset) + memcpy(bnsInfo.coefficients1, ptr + bnsInfo.coeff1Offset, (u8 *)bnsInfo.coefficients2 - (u8 *)&bnsInfo.coefficients1); + if (bnsInfo.chanCount == 2) + { + bnsInfo.chan2StartOffset = *(const u32 *)(ptr + bnsInfo.offsetToChanStarts + 4); + bnsInfo.chan2Start = *(const u32 *)(ptr + bnsInfo.chan2StartOffset); + bnsInfo.coeff2Offset = *(const u32 *)(ptr + bnsInfo.chan2StartOffset + 4); + if ((u8 *)bnsInfo.coefficients2 != ptr + bnsInfo.coeff2Offset) + memcpy(bnsInfo.coefficients2, ptr + bnsInfo.coeff2Offset, (u8 *)bnsInfo.coefficients2 - (u8 *)&bnsInfo.coefficients1); + } +} + +static void decodeADPCMBlock(s16 *buffer, const BNSADPCMBlock &block, BNSDecObj &bnsDec) +{ + int h1 = bnsDec.prevSamples[0]; + int h2 = bnsDec.prevSamples[1]; + int c1 = bnsDec.coeff[block.coeffIndex][0]; + int c2 = bnsDec.coeff[block.coeffIndex][1]; + for (int i = 0; i < 14; ++i) + { + int nibSample = ((i & 1) == 0) ? block.samples[i / 2].sample1 : block.samples[i / 2].sample2; + int sampleDeltaHP = (nibSample << block.lshift) << 11; + int predictedSampleHP = c1 * h1 + c2 * h2; + int sampleHP = predictedSampleHP + sampleDeltaHP; + buffer[i] = std::min(std::max(-32768, (sampleHP + 1024) >> 11), 32767); + h2 = h1; + h1 = buffer[i]; + } + bnsDec.prevSamples[0] = h1; + bnsDec.prevSamples[1] = h2; +} + +static u8 * decodeBNS(u32 &size, const BNSInfo &bnsInfo, const BNSData &bnsData) +{ + static s16 smplBlock[14]; + BNSDecObj decObj; + int numBlocks = (bnsData.size - 8) / 8; + int numSamples = numBlocks * 14; + const BNSADPCMBlock *inputBuf = (const BNSADPCMBlock *)&bnsData.data; + u8 * buffer = (u8 *) malloc(numSamples * sizeof (s16)); + s16 *outputBuf; + + if (!buffer) + return buffer; + memcpy(decObj.coeff, bnsInfo.coefficients1, sizeof decObj.coeff); + memcpy(decObj.prevSamples, bnsInfo.chan1PrevSamples, sizeof decObj.prevSamples); + outputBuf = (s16 *)buffer; + if (bnsInfo.chanCount == 1) + for (int i = 0; i < numBlocks; ++i) + { + decodeADPCMBlock(smplBlock, inputBuf[i], decObj); + memcpy(outputBuf, smplBlock, sizeof smplBlock); + outputBuf += 14; + } + else + { + numBlocks /= 2; + for (int i = 0; i < numBlocks; ++i) + { + decodeADPCMBlock(smplBlock, inputBuf[i], decObj); + for (int j = 0; j < 14; ++j) + outputBuf[j * 2] = smplBlock[j]; + outputBuf += 2 * 14; + } + outputBuf = (s16 *)buffer + 1; + memcpy(decObj.coeff, bnsInfo.coefficients2, sizeof decObj.coeff); + memcpy(decObj.prevSamples, bnsInfo.chan2PrevSamples, sizeof decObj.prevSamples); + for (int i = 0; i < numBlocks; ++i) + { + decodeADPCMBlock(smplBlock, inputBuf[numBlocks + i], decObj); + for (int j = 0; j < 14; ++j) + outputBuf[j * 2] = smplBlock[j]; + outputBuf += 2 * 14; + } + } + size = numSamples * sizeof (s16); + return buffer; +} + +SoundBlock DecodefromBNS(const u8 *buffer, u32 size) +{ + SoundBlock OutBlock; + memset(&OutBlock, 0, sizeof(SoundBlock)); + + const BNSHeader &hdr = *(BNSHeader *)buffer; + if (size < sizeof hdr) + return OutBlock; + if (hdr.fccBNS != 'BNS ') + return OutBlock; + // Find info and data + BNSInfo infoChunk; + loadBNSInfo(infoChunk, buffer + hdr.infoOffset); + const BNSData &dataChunk = *(const BNSData *)(buffer + hdr.dataOffset); + // Check sizes + if (size < hdr.size || size < hdr.infoOffset + hdr.infoSize || size < hdr.dataOffset + hdr.dataSize + || hdr.infoSize < 0x60 || hdr.dataSize < sizeof dataChunk + || infoChunk.size != hdr.infoSize || dataChunk.size != hdr.dataSize) + return OutBlock; + // Check format + if (infoChunk.codecNum != 0) // Only codec i've found : 0 = ADPCM. Maybe there's also 1 and 2 for PCM 8 or 16 bits ? + return OutBlock; + u8 format = (u8)-1; + if (infoChunk.chanCount == 1 && infoChunk.codecNum == 0) + format = VOICE_MONO_16BIT; + else if (infoChunk.chanCount == 2 && infoChunk.codecNum == 0) + format = VOICE_STEREO_16BIT; + if (format == (u8)-1) + return OutBlock; + u32 freq = (u32) infoChunk.freq; + u32 length = 0; + // Copy data + if (infoChunk.codecNum == 0) + { + OutBlock.buffer = decodeBNS(length, infoChunk, dataChunk); + if (!OutBlock.buffer) + return OutBlock; + } + else + { + OutBlock.buffer = (u8*) malloc(dataChunk.size); + if (!OutBlock.buffer) + return OutBlock; + memcpy(OutBlock.buffer, &dataChunk.data, dataChunk.size); + length = dataChunk.size; + } + + OutBlock.frequency = freq; + OutBlock.format = format; + OutBlock.size = length; + OutBlock.loopStart = infoChunk.loopStart; + OutBlock.loopEnd = infoChunk.loopEnd; + OutBlock.loopFlag = infoChunk.loopFlag; + + return OutBlock; +} diff --git a/source/SoundOperations/BNSDecoder.hpp b/source/SoundOperations/BNSDecoder.hpp new file mode 100644 index 00000000..4c9d76c3 --- /dev/null +++ b/source/SoundOperations/BNSDecoder.hpp @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef BNSDECODER_HPP_ +#define BNSDECODER_HPP_ + +#include "SoundDecoder.hpp" + +typedef struct _SoundBlock +{ + u8 * buffer; + u32 size; + u8 format; + u32 frequency; + u32 loopStart; + u32 loopEnd; + u8 loopFlag; +} SoundBlock; + +class BNSDecoder : public SoundDecoder +{ + public: + BNSDecoder(const char * filepath); + BNSDecoder(const u8 * snd, int len); + ~BNSDecoder(); + int GetFormat() { return SoundData.format; }; + int GetSampleRate() { return SoundData.frequency; }; + int Read(u8 * buffer, int buffer_size, int pos); + protected: + void OpenFile(); + void CloseFile(); + SoundBlock SoundData; +}; + +SoundBlock DecodefromBNS(const u8 *buffer, u32 size); + +#endif diff --git a/source/SoundOperations/BufferCircle.cpp b/source/SoundOperations/BufferCircle.cpp new file mode 100644 index 00000000..a8ff1e44 --- /dev/null +++ b/source/SoundOperations/BufferCircle.cpp @@ -0,0 +1,144 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include "BufferCircle.hpp" + +#define ALIGN32(x) (((x) + 31) & ~31) + +BufferCircle::BufferCircle() +{ + which = 0; + BufferBlockSize = 0; +} + +BufferCircle::~BufferCircle() +{ + FreeBuffer(); + SoundBuffer.clear(); + BufferSize.clear(); + BufferReady.clear(); +} + +void BufferCircle::SetBufferBlockSize(int size) +{ + if(size < 0) + return; + + BufferBlockSize = size; + + for(int i = 0; i < Size(); i++) + { + if(SoundBuffer[i] != NULL) + free(SoundBuffer[i]); + + SoundBuffer[i] = (u8 *) memalign(32, ALIGN32(BufferBlockSize)); + BufferSize[i] = 0; + BufferReady[i] = false; + } +} + +void BufferCircle::Resize(int size) +{ + while(size < Size()) + RemoveBuffer(Size()-1); + + int oldSize = Size(); + + SoundBuffer.resize(size); + BufferSize.resize(size); + BufferReady.resize(size); + + for(int i = oldSize; i < Size(); i++) + { + if(BufferBlockSize > 0) + SoundBuffer[i] = (u8 *) memalign(32, ALIGN32(BufferBlockSize)); + else + SoundBuffer[i] = NULL; + BufferSize[i] = 0; + BufferReady[i] = false; + } +} + +void BufferCircle::RemoveBuffer(int pos) +{ + if(!Valid(pos)) + return; + + if(SoundBuffer[pos] != NULL) + free(SoundBuffer[pos]); + + SoundBuffer.erase(SoundBuffer.begin()+pos); + BufferSize.erase(BufferSize.begin()+pos); + BufferReady.erase(BufferReady.begin()+pos); +} + +void BufferCircle::ClearBuffer() +{ + for(int i = 0; i < Size(); i++) + { + BufferSize[i] = 0; + BufferReady[i] = false; + } + which = 0; +} + +void BufferCircle::FreeBuffer() +{ + for(int i = 0; i < Size(); i++) + { + if(SoundBuffer[i] != NULL) + free(SoundBuffer[i]); + + SoundBuffer[i] = NULL; + BufferSize[i] = 0; + BufferReady[i] = false; + } +} + +void BufferCircle::LoadNext() +{ + int pos = (which+Size()-1) % Size(); + BufferReady[pos] = false; + BufferSize[pos] = 0; + + which = (which+1) % Size(); +} + +void BufferCircle::SetBufferReady(int pos, bool state) +{ + if(!Valid(pos)) + return; + + BufferReady[pos] = state; +} + +void BufferCircle::SetBufferSize(int pos, int size) +{ + if(!Valid(pos)) + return; + + BufferSize[pos] = size; +} diff --git a/source/SoundOperations/BufferCircle.hpp b/source/SoundOperations/BufferCircle.hpp new file mode 100644 index 00000000..56ab0898 --- /dev/null +++ b/source/SoundOperations/BufferCircle.hpp @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef BUFFER_CIRCLE_HPP_ +#define BUFFER_CIRCLE_HPP_ + +#include +#include + +class BufferCircle +{ + public: + //!> Constructor + BufferCircle(); + //!> Destructor + ~BufferCircle(); + //!> Set circle size + void Resize(int size); + //!> Get the circle size + int Size() { return SoundBuffer.size(); }; + //!> Set/resize the buffer size + void SetBufferBlockSize(int size); + //!> Remove a buffer + void RemoveBuffer(int pos); + //!> Set all buffers clear + void ClearBuffer(); + //!> Free all buffers + void FreeBuffer(); + //!> Switch to next buffer + void LoadNext(); + //!> Get the current buffer + u8 * GetBuffer() { if(!Valid(which)) return 0; return SoundBuffer[which]; }; + //!> Get a buffer at a position + u8 * GetBuffer(int pos) { if(!Valid(pos)) return NULL; else return SoundBuffer[pos]; }; + //!> Get next buffer + u8 * GetNextBuffer() { if(Size() <= 0) return 0; else return SoundBuffer[(which+1) % Size()]; }; + //!> Get previous buffer + u8 * GetLastBuffer() { if(Size() <= 0) return 0; else return SoundBuffer[(which+Size()-1) % Size()]; }; + //!> Get current buffer size + u32 GetBufferSize() { if(!Valid(which)) return 0; else return BufferSize[which]; }; + //!> Get buffer size at position + u32 GetBufferSize(int pos) { if(!Valid(pos)) return 0; else return BufferSize[pos]; }; + //!> Get previous buffer size + u32 GetLastBufferSize() { if(Size() <= 0) return 0; else return BufferSize[(which+Size()-1) % Size()]; }; + //!> Is current buffer ready + bool IsBufferReady() { if(!Valid(which)) return false; else return BufferReady[which]; }; + //!> Is a buffer at a position ready + bool IsBufferReady(int pos) { if(!Valid(pos)) return false; else return BufferReady[pos]; }; + //!> Is next buffer ready + bool IsNextBufferReady() { if(Size() <= 0) return false; else return BufferReady[(which+1) % Size()]; }; + //!> Is last buffer ready + bool IsLastBufferReady() { if(Size() <= 0) return false; else return BufferReady[(which+Size()-1) % Size()]; }; + //!> Set a buffer at a position to a ready state + void SetBufferReady(int pos, bool st); + //!> Set the buffersize at a position + void SetBufferSize(int pos, int size); + //!> Get the current position in the circle + u16 Which() { return which; }; + protected: + //!> Check if the position is a valid position in the vector + bool Valid(int pos) { return !(which < 0 || which >= Size()); }; + + u16 which; + u32 BufferBlockSize; + std::vector SoundBuffer; + std::vector BufferSize; + std::vector BufferReady; +}; + +#endif diff --git a/source/SoundOperations/Mp3Decoder.cpp b/source/SoundOperations/Mp3Decoder.cpp new file mode 100644 index 00000000..13dd0ae7 --- /dev/null +++ b/source/SoundOperations/Mp3Decoder.cpp @@ -0,0 +1,216 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include "Mp3Decoder.hpp" + +Mp3Decoder::Mp3Decoder(const char * filepath) + : SoundDecoder(filepath) +{ + SoundType = SOUND_MP3; + ReadBuffer = NULL; + mad_timer_reset(&Timer); + mad_stream_init(&Stream); + mad_frame_init(&Frame); + mad_synth_init(&Synth); + + if(!file_fd) + return; + + OpenFile(); +} + +Mp3Decoder::Mp3Decoder(const u8 * snd, int len) + : SoundDecoder(snd, len) +{ + SoundType = SOUND_MP3; + ReadBuffer = NULL; + mad_timer_reset(&Timer); + mad_stream_init(&Stream); + mad_frame_init(&Frame); + mad_synth_init(&Synth); + + if(!file_fd) + return; + + OpenFile(); +} + +Mp3Decoder::~Mp3Decoder() +{ + ExitRequested = true; + while(Decoding) + usleep(100); + + mad_synth_finish(&Synth); + mad_frame_finish(&Frame); + mad_stream_finish(&Stream); + + if(ReadBuffer) + free(ReadBuffer); + ReadBuffer = NULL; +} + +void Mp3Decoder::OpenFile() +{ + GuardPtr = NULL; + ReadBuffer = (u8 *) memalign(32, SoundBlockSize*SoundBlocks); + if(!ReadBuffer) + { + if(file_fd) + delete file_fd; + file_fd = NULL; + return; + } + + u8 dummybuff[4096]; + int ret = Read((u8 *) &dummybuff, 4096, 0); + if(ret <= 0) + { + if(file_fd) + delete file_fd; + file_fd = NULL; + return; + } + + SampleRate = (u32) Frame.header.samplerate; + Format = ((MAD_NCHANNELS(&Frame.header) == 2) ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT); + Rewind(); + Decode(); +} + +int Mp3Decoder::Rewind() +{ + mad_synth_finish(&Synth); + mad_frame_finish(&Frame); + mad_stream_finish(&Stream); + mad_timer_reset(&Timer); + mad_stream_init(&Stream); + mad_frame_init(&Frame); + mad_synth_init(&Synth); + SynthPos = 0; + GuardPtr = NULL; + + if(!file_fd) + return -1; + + return SoundDecoder::Rewind(); +} + +static inline s16 FixedToShort(mad_fixed_t Fixed) +{ + /* Clipping */ + if(Fixed>=MAD_F_ONE) + return(SHRT_MAX); + if(Fixed<=-MAD_F_ONE) + return(-SHRT_MAX); + + Fixed=Fixed>>(MAD_F_FRACBITS-15); + return((s16)Fixed); +} + +int Mp3Decoder::Read(u8 * buffer, int buffer_size, int pos) +{ + if(!file_fd) + return -1; + + if(Format == VOICE_STEREO_16BIT) + buffer_size &= ~0x0003; + else + buffer_size &= ~0x0001; + + u8 * write_pos = buffer; + u8 * write_end = buffer+buffer_size; + + while(1) + { + while(SynthPos < Synth.pcm.length) + { + if(write_pos >= write_end) + return write_pos-buffer; + + *((s16 *) write_pos) = FixedToShort(Synth.pcm.samples[0][SynthPos]); + write_pos += 2; + + if(MAD_NCHANNELS(&Frame.header) == 2) + { + *((s16 *) write_pos) = FixedToShort(Synth.pcm.samples[1][SynthPos]); + write_pos += 2; + } + SynthPos++; + } + + if(Stream.buffer == NULL || Stream.error == MAD_ERROR_BUFLEN) + { + u8 * ReadStart = ReadBuffer; + int ReadSize = SoundBlockSize*SoundBlocks; + int Remaining = 0; + + if(Stream.next_frame != NULL) + { + Remaining = Stream.bufend - Stream.next_frame; + memmove(ReadBuffer, Stream.next_frame, Remaining); + ReadStart += Remaining; + ReadSize -= Remaining; + } + + ReadSize = file_fd->read(ReadStart, ReadSize); + if(ReadSize <= 0) + { + GuardPtr = ReadStart; + memset(GuardPtr, 0, MAD_BUFFER_GUARD); + ReadSize = MAD_BUFFER_GUARD; + } + + CurPos += ReadSize; + mad_stream_buffer(&Stream, ReadBuffer, Remaining+ReadSize); + } + + if(mad_frame_decode(&Frame,&Stream)) + { + if(MAD_RECOVERABLE(Stream.error)) + { + if(Stream.error != MAD_ERROR_LOSTSYNC || !GuardPtr) + continue; + } + else + { + if(Stream.error != MAD_ERROR_BUFLEN) + return -1; + else if(Stream.error == MAD_ERROR_BUFLEN && GuardPtr) + return -1; + } + } + + mad_timer_add(&Timer,Frame.header.duration); + mad_synth_frame(&Synth,&Frame); + SynthPos = 0; + } +} diff --git a/source/SoundOperations/Mp3Decoder.hpp b/source/SoundOperations/Mp3Decoder.hpp new file mode 100644 index 00000000..a622f1f3 --- /dev/null +++ b/source/SoundOperations/Mp3Decoder.hpp @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include + +#include "SoundDecoder.hpp" + +class Mp3Decoder : public SoundDecoder +{ + public: + Mp3Decoder(const char * filepath); + Mp3Decoder(const u8 * sound, int len); + ~Mp3Decoder(); + int GetFormat() { return Format; }; + int GetSampleRate() { return SampleRate; }; + int Rewind(); + int Read(u8 * buffer, int buffer_size, int pos); + protected: + void OpenFile(); + struct mad_stream Stream; + struct mad_frame Frame; + struct mad_synth Synth; + mad_timer_t Timer; + u8 * GuardPtr; + u8 * ReadBuffer; + u8 Format; + u32 SampleRate; + u32 SynthPos; +}; diff --git a/source/SoundOperations/OggDecoder.cpp b/source/SoundOperations/OggDecoder.cpp new file mode 100644 index 00000000..904270ac --- /dev/null +++ b/source/SoundOperations/OggDecoder.cpp @@ -0,0 +1,144 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include +#include "OggDecoder.hpp" + +extern "C" int ogg_read(void * punt, int bytes, int blocks, int *f) +{ + return ((CFile *) f)->read((u8 *) punt, bytes*blocks); +} + +extern "C" int ogg_seek(int *f, ogg_int64_t offset, int mode) +{ + return ((CFile *) f)->seek((u64) offset, mode); +} + +extern "C" int ogg_close(int *f) +{ + ((CFile *) f)->close(); + return 0; +} + +extern "C" long ogg_tell(int *f) +{ + return (long) ((CFile *) f)->tell(); +} + +static ov_callbacks callbacks = { + (size_t (*)(void *, size_t, size_t, void *)) ogg_read, + (int (*)(void *, ogg_int64_t, int)) ogg_seek, + (int (*)(void *)) ogg_close, + (long (*)(void *)) ogg_tell +}; + +OggDecoder::OggDecoder(const char * filepath) + : SoundDecoder(filepath) +{ + SoundType = SOUND_OGG; + + if(!file_fd) + return; + + OpenFile(); +} + +OggDecoder::OggDecoder(const u8 * snd, int len) + : SoundDecoder(snd, len) +{ + SoundType = SOUND_OGG; + + if(!file_fd) + return; + + OpenFile(); +} + +OggDecoder::~OggDecoder() +{ + ExitRequested = true; + while(Decoding) + usleep(100); + + if(file_fd) + ov_clear(&ogg_file); +} + +void OggDecoder::OpenFile() +{ + if (ov_open_callbacks(file_fd, &ogg_file, NULL, 0, callbacks) < 0) + { + delete file_fd; + file_fd = NULL; + return; + } + + ogg_info = ov_info(&ogg_file, -1); + Decode(); +} + +int OggDecoder::GetFormat() +{ + if(!file_fd) + return VOICE_STEREO_16BIT; + + return ((ogg_info->channels == 2) ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT); +} + +int OggDecoder::GetSampleRate() +{ + if(!file_fd) + return 0; + + return (int) ogg_info->rate; +} + +int OggDecoder::Rewind() +{ + if(!file_fd) + return -1; + + int ret = ov_time_seek(&ogg_file, 0); + CurPos = 0; + EndOfFile = false; + + return ret; +} + +int OggDecoder::Read(u8 * buffer, int buffer_size, int pos) +{ + if(!file_fd) + return -1; + + int bitstream = 0; + + int read = ov_read(&ogg_file, (char *) buffer, buffer_size, &bitstream); + + if(read > 0) + CurPos += read; + + return read; +} diff --git a/source/SoundOperations/OggDecoder.hpp b/source/SoundOperations/OggDecoder.hpp new file mode 100644 index 00000000..49de225e --- /dev/null +++ b/source/SoundOperations/OggDecoder.hpp @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include + +#include "SoundDecoder.hpp" + +class OggDecoder : public SoundDecoder +{ + public: + OggDecoder(const char * filepath); + OggDecoder(const u8 * snd, int len); + ~OggDecoder(); + int GetFormat(); + int GetSampleRate(); + int Rewind(); + int Read(u8 * buffer, int buffer_size, int pos); + protected: + void OpenFile(); + OggVorbis_File ogg_file; + vorbis_info *ogg_info; +}; diff --git a/source/SoundOperations/SoundDecoder.cpp b/source/SoundOperations/SoundDecoder.cpp new file mode 100644 index 00000000..f4bc6ecb --- /dev/null +++ b/source/SoundOperations/SoundDecoder.cpp @@ -0,0 +1,156 @@ +/*************************************************************************** + * Copyright (C) 2010 + * by Dimok + * + * 3Band resampling thanks to libmad + * + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include +#include +#include +#include "SoundDecoder.hpp" +#include "main.h" + +SoundDecoder::SoundDecoder() +{ + file_fd = NULL; + Init(); +} + +SoundDecoder::SoundDecoder(const char * filepath) +{ + file_fd = new CFile(filepath, "rb"); + Init(); +} + +SoundDecoder::SoundDecoder(const u8 * buffer, int size) +{ + file_fd = new CFile(buffer, size); + Init(); +} + +SoundDecoder::~SoundDecoder() +{ + ExitRequested = true; + while(Decoding) + usleep(100); + + if(file_fd) + delete file_fd; + file_fd = NULL; +} + +void SoundDecoder::Init() +{ + SoundType = SOUND_RAW; + SoundBlocks = 8; + SoundBlockSize = 8192; + CurPos = 0; + Loop = false; + EndOfFile = false; + Decoding = false; + ExitRequested = false; + SoundBuffer.SetBufferBlockSize(SoundBlockSize); + SoundBuffer.Resize(SoundBlocks); +} + +int SoundDecoder::Rewind() +{ + CurPos = 0; + EndOfFile = false; + file_fd->rewind(); + + return 0; +} + +int SoundDecoder::Read(u8 * buffer, int buffer_size, int pos) +{ + int ret = file_fd->read(buffer, buffer_size); + CurPos += ret; + + return ret; +} + +void SoundDecoder::Decode() +{ + if(!file_fd || ExitRequested || EndOfFile) + return; + + u16 newWhich = SoundBuffer.Which(); + u16 i = 0; + for (i = 0; i < SoundBuffer.Size()-2; i++) + { + if(!SoundBuffer.IsBufferReady(newWhich)) + break; + + newWhich = (newWhich+1) % SoundBuffer.Size(); + } + + if(i == SoundBuffer.Size()-2) + return; + + Decoding = true; + + int done = 0; + u8 * write_buf = SoundBuffer.GetBuffer(newWhich); + if(!write_buf) + { + ExitRequested = true; + Decoding = false; + return; + } + + while(done < SoundBlockSize) + { + int ret = Read(&write_buf[done], SoundBlockSize-done, Tell()); + + if(ret <= 0) + { + if(Loop) + { + Rewind(); + continue; + } + else + { + EndOfFile = true; + break; + } + } + + done += ret; + } + + if(done > 0) + { + SoundBuffer.SetBufferSize(newWhich, done); + SoundBuffer.SetBufferReady(newWhich, true); + } + + if(!SoundBuffer.IsBufferReady((newWhich+1) % SoundBuffer.Size())) + Decode(); + + Decoding = false; +} + diff --git a/source/SoundOperations/SoundDecoder.hpp b/source/SoundOperations/SoundDecoder.hpp new file mode 100644 index 00000000..b9e71ad4 --- /dev/null +++ b/source/SoundOperations/SoundDecoder.hpp @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef SOUND_DECODER_HPP +#define SOUND_DECODER_HPP + +#include +#include +#include +#include "utils/timer.h" +#include "FileOperations/File.hpp" +#include "BufferCircle.hpp" + +enum +{ + SOUND_RAW = 0, + SOUND_MP3, + SOUND_OGG, + SOUND_WAV, + SOUND_BNS, + SOUND_AIF +}; + +class SoundDecoder +{ + public: + SoundDecoder(); + SoundDecoder(const char * filepath); + SoundDecoder(const u8 * buffer, int size); + ~SoundDecoder(); + virtual int Read(u8 * buffer, int buffer_size, int pos); + virtual int Tell() { return CurPos; }; + virtual int Seek(int pos) { CurPos = pos; return file_fd->seek(CurPos, SEEK_SET); }; + virtual int Rewind(); + virtual int GetFormat() { return VOICE_STEREO_16BIT; }; + virtual int GetSampleRate() { return 48000; }; + virtual void Decode(); + virtual u32 GetBufferSize() { return SoundBuffer.GetBufferSize(); }; + virtual u8 * GetBuffer() { return SoundBuffer.GetBuffer(); }; + virtual u8 * GetNextBuffer() { return SoundBuffer.GetNextBuffer(); }; + virtual u8 * GetLastBuffer() { return SoundBuffer.GetLastBuffer(); }; + virtual void LoadNext() { SoundBuffer.LoadNext(); }; + virtual bool IsBufferReady() { return SoundBuffer.IsBufferReady(); }; + virtual bool IsNextBufferReady() { return SoundBuffer.IsNextBufferReady(); }; + virtual bool IsLastBufferReady() { return SoundBuffer.IsLastBufferReady(); }; + virtual bool IsEOF() { return EndOfFile; }; + virtual void SetLoop(bool l) { Loop = l; }; + virtual u8 GetSoundType() { return SoundType; }; + virtual void ClearBuffer() { SoundBuffer.ClearBuffer(); }; + virtual bool IsStereo() { return (GetFormat() == VOICE_STEREO_16BIT || GetFormat() == VOICE_STEREO_8BIT); }; + virtual bool Is16Bit() { return (GetFormat() == VOICE_STEREO_16BIT || GetFormat() == VOICE_MONO_16BIT); }; + protected: + void Init(); + + CFile * file_fd; + BufferCircle SoundBuffer; + u8 SoundType; + u16 SoundBlocks; + int SoundBlockSize; + int CurPos; + bool Loop; + bool EndOfFile; + bool Decoding; + bool ExitRequested; +}; + +#endif diff --git a/source/SoundOperations/SoundHandler.cpp b/source/SoundOperations/SoundHandler.cpp new file mode 100644 index 00000000..a4548886 --- /dev/null +++ b/source/SoundOperations/SoundHandler.cpp @@ -0,0 +1,271 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include +#include "SoundHandler.hpp" +#include "Mp3Decoder.hpp" +#include "OggDecoder.hpp" +#include "WavDecoder.hpp" +#include "AifDecoder.hpp" +#include "BNSDecoder.hpp" + +SoundHandler * SoundHandler::instance = NULL; + +SoundHandler::SoundHandler() +{ + Decoding = false; + ExitRequested = false; + for(u32 i = 0; i < MAX_DECODERS; ++i) + DecoderList[i] = NULL; + + ThreadStack = (u8 *) memalign(32, 32768); + if(!ThreadStack) + return; + + LWP_CreateThread(&SoundThread, UpdateThread, this, ThreadStack, 32768, 80); +} + +SoundHandler::~SoundHandler() +{ + ExitRequested = true; + ThreadSignal(); + LWP_JoinThread(SoundThread, NULL); + SoundThread = LWP_THREAD_NULL; + if(ThreadStack) + free(ThreadStack); + + ClearDecoderList(); +} + +SoundHandler * SoundHandler::Instance() +{ + if (instance == NULL) + { + instance = new SoundHandler(); + } + return instance; +} + +void SoundHandler::DestroyInstance() +{ + if(instance) + { + delete instance; + } + instance = NULL; +} + +void SoundHandler::AddDecoder(int voice, const char * filepath) +{ + if(voice < 0 || voice >= MAX_DECODERS) + return; + + if(DecoderList[voice] != NULL) + RemoveDecoder(voice); + + DecoderList[voice] = GetSoundDecoder(filepath); +} + +void SoundHandler::AddDecoder(int voice, const u8 * snd, int len) +{ + if(voice < 0 || voice >= MAX_DECODERS) + return; + + if(DecoderList[voice] != NULL) + RemoveDecoder(voice); + + DecoderList[voice] = GetSoundDecoder(snd, len); +} + +void SoundHandler::RemoveDecoder(int voice) +{ + if(voice < 0 || voice >= MAX_DECODERS) + return; + + if(DecoderList[voice] != NULL) + { + if(DecoderList[voice]->GetSoundType() == SOUND_OGG) delete ((OggDecoder *) DecoderList[voice]); + else if(DecoderList[voice]->GetSoundType() == SOUND_MP3) delete ((Mp3Decoder *) DecoderList[voice]); + else if(DecoderList[voice]->GetSoundType() == SOUND_WAV) delete ((WavDecoder *) DecoderList[voice]); + else if(DecoderList[voice]->GetSoundType() == SOUND_AIF) delete ((AifDecoder *) DecoderList[voice]); + else if(DecoderList[voice]->GetSoundType() == SOUND_BNS) delete ((BNSDecoder *) DecoderList[voice]); + else delete DecoderList[voice]; + } + + DecoderList[voice] = NULL; +} + +void SoundHandler::ClearDecoderList() +{ + for(u32 i = 0; i < MAX_DECODERS; ++i) + RemoveDecoder(i); +} + +static inline bool CheckMP3Signature(const u8 * buffer) +{ + const char MP3_Magic[][3] = + { + {'I', 'D', '3'}, //'ID3' + {0xff, 0xfe}, //'MPEG ADTS, layer III, v1.0 [protected]', 'mp3', 'audio/mpeg'), + {0xff, 0xff}, //'MPEG ADTS, layer III, v1.0', 'mp3', 'audio/mpeg'), + {0xff, 0xfa}, //'MPEG ADTS, layer III, v1.0 [protected]', 'mp3', 'audio/mpeg'), + {0xff, 0xfb}, //'MPEG ADTS, layer III, v1.0', 'mp3', 'audio/mpeg'), + {0xff, 0xf2}, //'MPEG ADTS, layer III, v2.0 [protected]', 'mp3', 'audio/mpeg'), + {0xff, 0xf3}, //'MPEG ADTS, layer III, v2.0', 'mp3', 'audio/mpeg'), + {0xff, 0xf4}, //'MPEG ADTS, layer III, v2.0 [protected]', 'mp3', 'audio/mpeg'), + {0xff, 0xf5}, //'MPEG ADTS, layer III, v2.0', 'mp3', 'audio/mpeg'), + {0xff, 0xf6}, //'MPEG ADTS, layer III, v2.0 [protected]', 'mp3', 'audio/mpeg'), + {0xff, 0xf7}, //'MPEG ADTS, layer III, v2.0', 'mp3', 'audio/mpeg'), + {0xff, 0xe2}, //'MPEG ADTS, layer III, v2.5 [protected]', 'mp3', 'audio/mpeg'), + {0xff, 0xe3}, //'MPEG ADTS, layer III, v2.5', 'mp3', 'audio/mpeg'), + }; + + if(buffer[0] == MP3_Magic[0][0] && buffer[1] == MP3_Magic[0][1] && + buffer[2] == MP3_Magic[0][2]) + { + return true; + } + + for(int i = 1; i < 13; i++) + { + if(buffer[0] == MP3_Magic[i][0] && buffer[1] == MP3_Magic[i][1]) + return true; + } + + return false; +} + +SoundDecoder * SoundHandler::GetSoundDecoder(const char * filepath) +{ + u32 magic; + CFile f(filepath, "rb"); + if(f.size() == 0) + return NULL; + + do + { + f.read((u8 *) &magic, 1); + } + while(((u8 *) &magic)[0] == 0 && f.tell() < f.size()); + + if(f.tell() == f.size()) + return NULL; + + f.seek(f.tell()-1, SEEK_SET); + f.read((u8 *) &magic, 4); + f.close(); + + if(magic == 'OggS') + { + return new OggDecoder(filepath); + } + else if(magic == 'RIFF') + { + return new WavDecoder(filepath); + } + else if(magic == 'BNS ') + { + return new BNSDecoder(filepath); + } + else if(magic == 'FORM') + { + return new AifDecoder(filepath); + } + else if(CheckMP3Signature((u8 *) &magic) == true) + { + return new Mp3Decoder(filepath); + } + + return new SoundDecoder(filepath); +} + +SoundDecoder * SoundHandler::GetSoundDecoder(const u8 * sound, int length) +{ + const u8 * check = sound; + int counter = 0; + + while(check[0] == 0 && counter < length) + { + check++; + counter++; + } + + if(counter >= length) + return NULL; + + u32 * magic = (u32 *) check; + + if(magic[0] == 'OggS') + { + return new OggDecoder(sound, length); + } + else if(magic[0] == 'RIFF') + { + return new WavDecoder(sound, length); + } + else if(magic[0] == 'BNS ') + { + return new BNSDecoder(sound, length); + } + else if(magic[0] == 'FORM') + { + return new AifDecoder(sound, length); + } + else if(CheckMP3Signature(check) == true) + { + return new Mp3Decoder(sound, length); + } + + return new SoundDecoder(sound, length); +} + +void * SoundHandler::UpdateThread(void *arg) +{ + ((SoundHandler *) arg)->InternalSoundUpdates(); + return NULL; +} + +void SoundHandler::InternalSoundUpdates() +{ + u16 i = 0; + LWP_InitQueue(&ThreadQueue); + while (!ExitRequested) + { + LWP_ThreadSleep(ThreadQueue); + + for(i = 0; i < MAX_DECODERS; ++i) + { + if(DecoderList[i] == NULL) + continue; + + Decoding = true; + DecoderList[i]->Decode(); + } + Decoding = false; + } + LWP_CloseQueue(ThreadQueue); + ThreadQueue = LWP_TQUEUE_NULL; +} diff --git a/source/SoundOperations/SoundHandler.hpp b/source/SoundOperations/SoundHandler.hpp new file mode 100644 index 00000000..b10953ec --- /dev/null +++ b/source/SoundOperations/SoundHandler.hpp @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef SOUNDHANDLER_H_ +#define SOUNDHANDLER_H_ + +#include +#include +#include "SoundDecoder.hpp" + +#define MAX_DECODERS 16 + +class SoundHandler +{ + public: + static SoundHandler * Instance(); + static void DestroyInstance(); + + void AddDecoder(int voice, const char * filepath); + void AddDecoder(int voice, const u8 * snd, int len); + void RemoveDecoder(int voice); + void DestroyDecoder(SoundDecoder * decoder); + + SoundDecoder * Decoder(int i) { return ((i < 0 || i >= MAX_DECODERS) ? NULL : DecoderList[i]); }; + void ThreadSignal() { LWP_ThreadSignal(ThreadQueue); }; + bool IsDecoding() { return Decoding; }; + protected: + SoundHandler(); + ~SoundHandler(); + static void * UpdateThread(void *arg); + void InternalSoundUpdates(); + void ClearDecoderList(); + SoundDecoder * GetSoundDecoder(const char * filepath); + SoundDecoder * GetSoundDecoder(const u8 * sound, int length); + + static SoundHandler * instance; + u8 * ThreadStack; + lwp_t SoundThread; + lwpq_t ThreadQueue; + bool Decoding; + bool ExitRequested; + + SoundDecoder * DecoderList[MAX_DECODERS]; +}; + +#endif diff --git a/source/SoundOperations/WavDecoder.cpp b/source/SoundOperations/WavDecoder.cpp new file mode 100644 index 00000000..3f37b95b --- /dev/null +++ b/source/SoundOperations/WavDecoder.cpp @@ -0,0 +1,155 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include "WavDecoder.hpp" +#include "utils/uncompress.h" + +WavDecoder::WavDecoder(const char * filepath) + : SoundDecoder(filepath) +{ + SoundType = SOUND_WAV; + SampleRate = 48000; + Format = VOICE_STEREO_16BIT; + + if(!file_fd) + return; + + OpenFile(); +} + +WavDecoder::WavDecoder(const u8 * snd, int len) + : SoundDecoder(snd, len) +{ + SoundType = SOUND_WAV; + SampleRate = 48000; + Format = VOICE_STEREO_16BIT; + + if(!file_fd) + return; + + OpenFile(); +} + +WavDecoder::~WavDecoder() +{ +} + +void WavDecoder::OpenFile() +{ + SWaveHdr Header; + SWaveFmtChunk FmtChunk; + memset(&Header, 0, sizeof(SWaveHdr)); + memset(&FmtChunk, 0, sizeof(SWaveFmtChunk)); + + file_fd->read((u8 *) &Header, sizeof(SWaveHdr)); + file_fd->read((u8 *) &FmtChunk, sizeof(SWaveFmtChunk)); + + if (Header.magicRIFF != 'RIFF') + { + CloseFile(); + return; + } + else if(Header.magicWAVE != 'WAVE') + { + CloseFile(); + return; + } + else if(FmtChunk.magicFMT != 'fmt ') + { + CloseFile(); + return; + } + + DataOffset = sizeof(SWaveHdr)+le32(FmtChunk.size)+8; + file_fd->seek(DataOffset, SEEK_SET); + SWaveChunk DataChunk; + file_fd->read((u8 *) &DataChunk, sizeof(SWaveChunk)); + + if(DataChunk.magicDATA == 'fact') + { + DataOffset += 8+le32(DataChunk.size); + file_fd->seek(DataOffset, SEEK_SET); + file_fd->read((u8 *) &DataChunk, sizeof(SWaveChunk)); + } + if(DataChunk.magicDATA != 'data') + { + CloseFile(); + return; + } + + DataOffset += 8; + DataSize = le32(DataChunk.size); + Is16Bit = (le16(FmtChunk.bps) == 16); + SampleRate = le32(FmtChunk.freq); + + if (le16(FmtChunk.channels) == 1 && le16(FmtChunk.bps) == 8 && le16(FmtChunk.alignment) <= 1) + Format = VOICE_MONO_8BIT; + else if (le16(FmtChunk.channels) == 1 && le16(FmtChunk.bps) == 16 && le16(FmtChunk.alignment) <= 2) + Format = VOICE_MONO_16BIT; + else if (le16(FmtChunk.channels) == 2 && le16(FmtChunk.bps) == 8 && le16(FmtChunk.alignment) <= 2) + Format = VOICE_STEREO_8BIT; + else if (le16(FmtChunk.channels) == 2 && le16(FmtChunk.bps) == 16 && le16(FmtChunk.alignment) <= 4) + Format = VOICE_STEREO_16BIT; + + Decode(); +} + +void WavDecoder::CloseFile() +{ + if(file_fd) + delete file_fd; + + file_fd = NULL; +} + +int WavDecoder::Read(u8 * buffer, int buffer_size, int pos) +{ + if(!file_fd) + return -1; + + if(CurPos >= (int) DataSize) + return 0; + + file_fd->seek(DataOffset+CurPos, SEEK_SET); + + if(buffer_size > (int) DataSize-CurPos) + buffer_size = DataSize-CurPos; + + int read = file_fd->read(buffer, buffer_size); + if(read > 0) + { + if (Is16Bit) + { + read &= ~0x0001; + + for (u32 i = 0; i < (u32) (read / sizeof (u16)); ++i) + ((u16 *) buffer)[i] = le16(((u16 *) buffer)[i]); + } + CurPos += read; + } + + return read; +} diff --git a/source/SoundOperations/WavDecoder.hpp b/source/SoundOperations/WavDecoder.hpp new file mode 100644 index 00000000..4681bf2b --- /dev/null +++ b/source/SoundOperations/WavDecoder.hpp @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef WAVDECODER_HPP_ +#define WAVDECODER_HPP_ + +#include "SoundDecoder.hpp" + +typedef struct +{ + u32 magicRIFF; + u32 size; + u32 magicWAVE; +} SWaveHdr; + +typedef struct +{ + u32 magicFMT; + u32 size; + u16 format; + u16 channels; + u32 freq; + u32 avgBps; + u16 alignment; + u16 bps; +} SWaveFmtChunk; + +typedef struct +{ + u32 magicDATA; + u32 size; +} SWaveChunk; + +class WavDecoder : public SoundDecoder +{ + public: + WavDecoder(const char * filepath); + WavDecoder(const u8 * snd, int len); + ~WavDecoder(); + int GetFormat() { return Format; }; + int GetSampleRate() { return SampleRate; }; + int Read(u8 * buffer, int buffer_size, int pos); + protected: + void OpenFile(); + void CloseFile(); + u32 DataOffset; + u32 DataSize; + u32 SampleRate; + u8 Format; + bool Is16Bit; +}; + +#endif diff --git a/source/libwiigui/gui_bgm.cpp b/source/SoundOperations/gui_bgm.cpp similarity index 87% rename from source/libwiigui/gui_bgm.cpp rename to source/SoundOperations/gui_bgm.cpp index 98c89533..c56bd4fd 100644 --- a/source/libwiigui/gui_bgm.cpp +++ b/source/SoundOperations/gui_bgm.cpp @@ -1,236 +1,224 @@ -/**************************************************************************** - * SettingsPrompts - * USB Loader GX 2009 - * - * Backgroundmusic - ***************************************************************************/ -#include -#include "gui_bgm.h" -#include "menu.h" - -GuiBGM::GuiBGM(const u8 *s, int l, int v) : - GuiSound(s, l, v) -{ - loop = 0; - loopMode = ONCE; - currentPath = NULL; - currentPlaying = 0; - - //shouldn't be needed but - //fixes some kind of weird bug in ogg system - GuiSound::Load(s, l, v); -} - -GuiBGM::~GuiBGM() -{ - if (currentPath) delete[] currentPath; - - ClearList(); -} -; - -void GuiBGM::SetLoop(bool l) -{ -} - -void GuiBGM::SetLoop(int l) -{ - loop = false; - loopMode = ONCE; - - if (l == LOOP) - { - loop = true; - } - else loopMode = l; -} - -bool GuiBGM::Load(const char *path) -{ - if (!path) - { - LoadStandard(); - return false; - } - if (strcmp(path, "") == 0) - { - LoadStandard(); - return false; - } - - if (!GuiSound::Load(path)) - { - LoadStandard(); - return false; - } - - return ParsePath(path); -} - -bool GuiBGM::LoadStandard() -{ - ClearList(); - if (currentPath) - { - delete[] currentPath; - currentPath = NULL; - } - - strcpy(Settings.ogg_path, ""); - - bool ret = GuiSound::Load(bg_music_ogg, bg_music_ogg_size, true); - - if (ret) Play(); - - return ret; -} - -bool GuiBGM::ParsePath(const char * folderpath) -{ - ClearList(); - - if (currentPath) delete[] currentPath; - - currentPath = new char[strlen(folderpath) + 1]; - sprintf(currentPath, "%s", folderpath); - - char * isdirpath = strrchr(folderpath, '.'); - if (isdirpath) - { - char * pathptr = strrchr(currentPath, '/'); - if (pathptr) - { - pathptr++; - pathptr[0] = 0; - } - } - - char * LoadedFilename = strrchr(folderpath, '/') + 1; - - char filename[1024]; - struct stat st; - - DIR_ITER * dir = diropen(currentPath); - if (dir == NULL) - { - LoadStandard(); - return false; - } - u32 counter = 0; - - while (dirnext(dir, filename, &st) == 0) - { - char * fileext = strrchr(filename, '.'); - if (fileext) - { - if (strcasecmp(fileext, ".mp3") == 0 || strcasecmp(fileext, ".ogg") == 0 || strcasecmp(fileext, ".wav") - == 0) - { - AddEntrie(filename); - - if (strcmp(LoadedFilename, filename) == 0) currentPlaying = counter; - - counter++; - } - } - } - - dirclose(dir); - - snprintf(Settings.ogg_path, sizeof(Settings.ogg_path), "%s", folderpath); - - return true; -} - -void GuiBGM::AddEntrie(const char * filename) -{ - if (!filename) return; - - char * NewEntrie = new char[strlen(filename) + 1]; - sprintf(NewEntrie, "%s", filename); - - PlayList.push_back(NewEntrie); -} - -void GuiBGM::ClearList() -{ - for (u32 i = 0; i < PlayList.size(); i++) - { - if (PlayList.at(i) != NULL) - { - delete[] PlayList.at(i); - PlayList.at(i) = NULL; - } - } - - PlayList.clear(); -} - -bool GuiBGM::PlayNext() -{ - if (!currentPath) return false; - - currentPlaying++; - if (currentPlaying >= (int) PlayList.size()) currentPlaying = 0; - - snprintf(Settings.ogg_path, sizeof(Settings.ogg_path), "%s%s", currentPath, PlayList.at(currentPlaying)); - - if (!GuiSound::Load(Settings.ogg_path)) return false; - - Play(); - - return true; -} - -bool GuiBGM::PlayPrevious() -{ - if (!currentPath) return false; - - currentPlaying--; - if (currentPlaying < 0) currentPlaying = PlayList.size() - 1; - - snprintf(Settings.ogg_path, sizeof(Settings.ogg_path), "%s%s", currentPath, PlayList.at(currentPlaying)); - - if (!GuiSound::Load(Settings.ogg_path)) return false; - - Play(); - - return true; -} - -bool GuiBGM::PlayRandom() -{ - if (!currentPath) return false; - - srand(time(NULL)); - - currentPlaying = rand() % PlayList.size(); - - //just in case - if (currentPlaying < 0) - currentPlaying = PlayList.size() - 1; - else if (currentPlaying >= (int) PlayList.size()) currentPlaying = 0; - - snprintf(Settings.ogg_path, sizeof(Settings.ogg_path), "%s%s", currentPath, PlayList.at(currentPlaying)); - - if (!GuiSound::Load(Settings.ogg_path)) return false; - - Play(); - - return true; -} - -void GuiBGM::UpdateState() -{ - if (!IsPlaying()) - { - if (loopMode == DIR_LOOP) - { - PlayNext(); - } - else if (loopMode == RANDOM_BGM) - { - PlayRandom(); - } - } -} +/**************************************************************************** + * SettingsPrompts + * USB Loader GX 2009 + * + * Backgroundmusic + ***************************************************************************/ +#include +#include "gui_bgm.h" +#include "menu.h" + +GuiBGM::GuiBGM(const u8 *s, int l, int v) : + GuiSound(s, l, v, false, 0) +{ + loop = 0; + loopMode = ONCE; + currentPath = NULL; + currentPlaying = 0; + voice = 0; +} + +GuiBGM::~GuiBGM() +{ + if (currentPath) delete[] currentPath; + + ClearList(); +} +; + +void GuiBGM::SetLoop(u8 l) +{ + loopMode = l; + + GuiSound::SetLoop(l == LOOP); +} + +bool GuiBGM::Load(const char *path) +{ + if (!path) + { + LoadStandard(); + return false; + } + if (strcmp(path, "") == 0) + { + LoadStandard(); + return false; + } + + if (!GuiSound::Load(path)) + { + LoadStandard(); + return false; + } + + return ParsePath(path); +} + +bool GuiBGM::LoadStandard() +{ + ClearList(); + if (currentPath) + { + delete[] currentPath; + currentPath = NULL; + } + + strcpy(Settings.ogg_path, ""); + + bool ret = GuiSound::Load(bg_music_ogg, bg_music_ogg_size, false); + + if (ret) Play(); + + return ret; +} + +bool GuiBGM::ParsePath(const char * folderpath) +{ + ClearList(); + + if (currentPath) delete[] currentPath; + + currentPath = new char[strlen(folderpath) + 1]; + sprintf(currentPath, "%s", folderpath); + + char * isdirpath = strrchr(folderpath, '.'); + if (isdirpath) + { + char * pathptr = strrchr(currentPath, '/'); + if (pathptr) + { + pathptr++; + pathptr[0] = 0; + } + } + + char * LoadedFilename = strrchr(folderpath, '/') + 1; + + char filename[1024]; + struct stat st; + + DIR_ITER * dir = diropen(currentPath); + if (dir == NULL) + { + LoadStandard(); + return false; + } + u32 counter = 0; + + while (dirnext(dir, filename, &st) == 0) + { + char * fileext = strrchr(filename, '.'); + if (fileext) + { + if (strcasecmp(fileext, ".mp3") == 0 || strcasecmp(fileext, ".ogg") == 0 || strcasecmp(fileext, ".wav") + == 0) + { + AddEntrie(filename); + + if (strcmp(LoadedFilename, filename) == 0) currentPlaying = counter; + + counter++; + } + } + } + + dirclose(dir); + + snprintf(Settings.ogg_path, sizeof(Settings.ogg_path), "%s", folderpath); + + return true; +} + +void GuiBGM::AddEntrie(const char * filename) +{ + if (!filename) return; + + char * NewEntrie = new char[strlen(filename) + 1]; + sprintf(NewEntrie, "%s", filename); + + PlayList.push_back(NewEntrie); +} + +void GuiBGM::ClearList() +{ + for (u32 i = 0; i < PlayList.size(); i++) + { + if (PlayList.at(i) != NULL) + { + delete[] PlayList.at(i); + PlayList.at(i) = NULL; + } + } + + PlayList.clear(); +} + +bool GuiBGM::PlayNext() +{ + if (!currentPath) return false; + + currentPlaying++; + if (currentPlaying >= (int) PlayList.size()) currentPlaying = 0; + + snprintf(Settings.ogg_path, sizeof(Settings.ogg_path), "%s%s", currentPath, PlayList.at(currentPlaying)); + + if (!GuiSound::Load(Settings.ogg_path)) return false; + + Play(); + + return true; +} + +bool GuiBGM::PlayPrevious() +{ + if (!currentPath) return false; + + currentPlaying--; + if (currentPlaying < 0) currentPlaying = PlayList.size() - 1; + + snprintf(Settings.ogg_path, sizeof(Settings.ogg_path), "%s%s", currentPath, PlayList.at(currentPlaying)); + + if (!GuiSound::Load(Settings.ogg_path)) return false; + + Play(); + + return true; +} + +bool GuiBGM::PlayRandom() +{ + if (!currentPath) return false; + + srand(time(NULL)); + + currentPlaying = rand() % PlayList.size(); + + //just in case + if (currentPlaying < 0) + currentPlaying = PlayList.size() - 1; + else if (currentPlaying >= (int) PlayList.size()) currentPlaying = 0; + + snprintf(Settings.ogg_path, sizeof(Settings.ogg_path), "%s%s", currentPath, PlayList.at(currentPlaying)); + + if (!GuiSound::Load(Settings.ogg_path)) return false; + + Play(); + + return true; +} + +void GuiBGM::UpdateState() +{ + if (!IsPlaying()) + { + if (loopMode == DIR_LOOP) + { + PlayNext(); + } + else if (loopMode == RANDOM_BGM) + { + PlayRandom(); + } + } +} diff --git a/source/libwiigui/gui_bgm.h b/source/SoundOperations/gui_bgm.h similarity index 86% rename from source/libwiigui/gui_bgm.h rename to source/SoundOperations/gui_bgm.h index 8fe076d1..741ce36a 100644 --- a/source/libwiigui/gui_bgm.h +++ b/source/SoundOperations/gui_bgm.h @@ -1,42 +1,42 @@ -/**************************************************************************** - * SettingsPrompts - * USB Loader GX 2009 - * - * Backgroundmusic - ***************************************************************************/ - -#ifndef _BGM_H_ -#define _BGM_H_ - -#include "libwiigui/gui.h" - -enum -{ - ONCE = 0, LOOP, RANDOM_BGM, DIR_LOOP -}; - -class GuiBGM: public GuiSound -{ - public: - GuiBGM(const u8 *s, int l, int v); - ~GuiBGM(); - bool Load(const char *path); - bool LoadStandard(); - bool ParsePath(const char * folderpath); - bool PlayNext(); - bool PlayPrevious(); - bool PlayRandom(); - void SetLoop(bool l); - void SetLoop(int l); - void UpdateState(); - protected: - void AddEntrie(const char * filename); - void ClearList(); - - int currentPlaying; - int loopMode; - char * currentPath; - std::vector PlayList; -}; - -#endif +/**************************************************************************** + * SettingsPrompts + * USB Loader GX 2009 + * + * Backgroundmusic + ***************************************************************************/ + +#ifndef _BGM_H_ +#define _BGM_H_ + +#include +#include "gui_sound.h" + +enum +{ + ONCE = 0, LOOP, RANDOM_BGM, DIR_LOOP +}; + +class GuiBGM: public GuiSound +{ + public: + GuiBGM(const u8 *s, int l, int v); + ~GuiBGM(); + bool Load(const char *path); + bool LoadStandard(); + bool ParsePath(const char * folderpath); + bool PlayNext(); + bool PlayPrevious(); + bool PlayRandom(); + void SetLoop(u8 l); + void UpdateState(); + protected: + void AddEntrie(const char * filename); + void ClearList(); + + int currentPlaying; + int loopMode; + char * currentPath; + std::vector PlayList; +}; + +#endif diff --git a/source/SoundOperations/gui_sound.cpp b/source/SoundOperations/gui_sound.cpp new file mode 100644 index 00000000..350ecaaf --- /dev/null +++ b/source/SoundOperations/gui_sound.cpp @@ -0,0 +1,380 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include "libwiigui/gui.h" +#include "utils/uncompress.h" +#include "FileOperations/fileops.h" +#include "SoundHandler.hpp" +#include "WavDecoder.hpp" + +#define MAX_SND_VOICES 16 + +static bool VoiceUsed[MAX_SND_VOICES] = +{ + true, false, false, false, false, false, + false, false, false, false, false, false, + false, false, false, false +}; + +static inline int GetFirstUnusedVoice() +{ + for(int i = 1; i < MAX_SND_VOICES; i++) + { + if(VoiceUsed[i] == false) + return i; + } + + return -1; +} + +extern "C" void SoundCallback(s32 voice) +{ + SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice); + if(!decoder) + return; + + if(decoder->IsBufferReady()) + { + if(ASND_AddVoice(voice, decoder->GetBuffer(), decoder->GetBufferSize()) == SND_OK) + { + decoder->LoadNext(); + SoundHandler::Instance()->ThreadSignal(); + } + } + else if(decoder->IsEOF()) + { + ASND_StopVoice(voice); + //if(voice == 0) + //MusicPlayer::Instance()->SetPlaybackFinished(true); //see if next music must be played + } + else + { + SoundHandler::Instance()->ThreadSignal(); + } +} + +GuiSound::GuiSound(const char * filepath) +{ + sound = NULL; + length = 0; + voice = GetFirstUnusedVoice(); + if(voice > 0) + VoiceUsed[voice] = true; + + volume = 255; + SoundEffectLength = 0; + loop = false; + allocated = false; + Load(filepath); +} + +GuiSound::GuiSound(const u8 * snd, s32 len, int vol, bool isallocated, int v) +{ + sound = NULL; + length = 0; + if(v < 0) + voice = GetFirstUnusedVoice(); + else + voice = v; + + if(voice > 0) + VoiceUsed[voice] = true; + + volume = vol; + SoundEffectLength = 0; + loop = false; + allocated = false; + Load(snd, len, isallocated); +} + +GuiSound::~GuiSound() +{ + FreeMemory(); + if(voice > 0) + VoiceUsed[voice] = false; +} + +void GuiSound::FreeMemory() +{ + this->Stop(); + + SoundHandler::Instance()->RemoveDecoder(voice); + + if(allocated && sound != NULL) + { + free(sound); + sound = NULL; + allocated = false; + } + + SoundEffectLength = 0; +} + +bool GuiSound::Load(const char * filepath) +{ + FreeMemory(); + + 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; + u64 filesize = 0; + LoadFileToMem(filepath, &snd, &filesize); + return Load(snd, filesize, true); + } + + SoundHandler::Instance()->AddDecoder(voice, filepath); + + SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice); + if(!decoder) + return false; + + if(!decoder->IsBufferReady()) + { + SoundHandler::Instance()->RemoveDecoder(voice); + return false; + } + + SetLoop(loop); + + return true; +} + +bool GuiSound::Load(const u8 * snd, s32 len, bool isallocated) +{ + 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; + } + + SoundHandler::Instance()->AddDecoder(voice, sound, length); + + SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice); + if(!decoder) + return false; + + if(!decoder->IsBufferReady()) + { + SoundHandler::Instance()->RemoveDecoder(voice); + return false; + } + + SetLoop(loop); + + return true; +} + +bool GuiSound::LoadSoundEffect(const u8 * snd, s32 len) +{ + WavDecoder decoder(snd, len); + decoder.Rewind(); + + u32 done = 0; + sound = (u8 *) malloc(4096); + memset(sound, 0, 4096); + + while(1) + { + u8 * tmpsnd = (u8 *) realloc(sound, done+4096); + if(!tmpsnd) + { + free(sound); + sound = NULL; + return false; + } + + sound = tmpsnd; + + int read = decoder.Read(sound+done, 4096, done); + if(read <= 0) + break; + + done += read; + } + + sound = (u8 *) realloc(sound, done); + SoundEffectLength = done; + allocated = true; + + return true; +} + +void GuiSound::Play() +{ + if(SoundEffectLength > 0) + { + ASND_StopVoice(voice); + ASND_SetVoice(voice, VOICE_STEREO_16BIT, 32000, 0, sound, SoundEffectLength, volume, volume, NULL); + return; + } + + if(IsPlaying()) + return; + + if(voice < 0 || voice >= 16) + return; + + SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice); + if(!decoder) + return; + + if(decoder->IsEOF()) + { + ASND_StopVoice(voice); + decoder->ClearBuffer(); + decoder->Rewind(); + decoder->Decode(); + } + + u8 * curbuffer = decoder->GetBuffer(); + int bufsize = decoder->GetBufferSize(); + decoder->LoadNext(); + SoundHandler::Instance()->ThreadSignal(); + + ASND_SetVoice(voice, decoder->GetFormat(), decoder->GetSampleRate(), 0, curbuffer, bufsize, volume, volume, SoundCallback); +} + +void GuiSound::Stop() +{ + if(voice < 0 || voice >= 16) + return; + + ASND_StopVoice(voice); + + SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice); + if(!decoder) + return; + + decoder->ClearBuffer(); + Rewind(); + SoundHandler::Instance()->ThreadSignal(); +} + +void GuiSound::Pause() +{ + if(voice < 0 || voice >= 16) + return; + + ASND_StopVoice(voice); +} + +void GuiSound::Resume() +{ + Play(); +} + +bool GuiSound::IsPlaying() +{ + if(voice < 0 || voice >= 16) + return false; + + int result = ASND_StatusVoice(voice); + + if(result == SND_WORKING || result == SND_WAITING) + return true; + + return false; +} + +void GuiSound::SetVolume(int vol) +{ + if(voice < 0 || voice >= 16) + return; + + if(vol < 0) + return; + + volume = 255*(vol/100.0); + ASND_ChangeVolumeVoice(voice, volume, volume); +} + +void GuiSound::SetLoop(u8 l) +{ + loop = l; + + SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice); + if(!decoder) + return; + + decoder->SetLoop(l == 1); +} + +void GuiSound::Rewind() +{ + SoundDecoder * decoder = SoundHandler::Instance()->Decoder(voice); + if(!decoder) + return; + + 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 new file mode 100644 index 00000000..1101b16b --- /dev/null +++ b/source/SoundOperations/gui_sound.h @@ -0,0 +1,80 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef GUI_SOUND_H_ +#define GUI_SOUND_H_ + +#include + +//!Sound conversion and playback. A wrapper for other sound libraries - ASND, libmad, ltremor, etc +class GuiSound +{ + public: + //!Constructor + //!\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); + //!Destructor + ~GuiSound(); + //!Load a file and replace the old one + bool Load(const char * filepath); + //!Load a file and replace the old one + bool Load(const u8 * sound, int filesize, bool allocated = true); + //!For quick playback of the internal soundeffects + bool LoadSoundEffect(const u8 * snd, s32 len); + //!Start sound playback + void Play(); + //!Stop sound playback + void Stop(); + //!Pause sound playback + void Pause(); + //!Resume sound playback + void Resume(); + //!Checks if the sound is currently playing + //!\return true if sound is playing, false otherwise + bool IsPlaying(); + //!Rewind the music + void Rewind(); + //!Set sound volume + //!\param v Sound volume (0-100) + void SetVolume(int v); + //!\param l Loop (true to loop) + 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(); + u8 * sound; //!< Pointer to the sound data + int length; //!< Length of sound data + s32 voice; //!< Currently assigned ASND voice channel + 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/bannersound.cpp b/source/bannersound.cpp index 48d11de2..8a6939ff 100644 --- a/source/bannersound.cpp +++ b/source/bannersound.cpp @@ -9,6 +9,7 @@ #include "libs/libwbfs/libwbfs.h" #include "language/gettext.h" #include "bannersound.h" +#include "utils/uncompress.h" struct IMD5Header { @@ -67,58 +68,6 @@ static char *u8Filename(const U8Entry *fst, int i) return (char *) (fst + fst[0].numEntries) + fst[i].nameOffset; } -inline u32 le32(u32 i) -{ - return ((i & 0xFF) << 24) | ((i & 0xFF00) << 8) | ((i & 0xFF0000) >> 8) | ((i & 0xFF000000) >> 24); -} - -inline u16 le16(u16 i) -{ - return ((i & 0xFF) << 8) | ((i & 0xFF00) >> 8); -} - -static u8 *uncompressLZ77(const u8 *inBuf, u32 inLength, u32 &size) -{ - u8 *buffer = NULL; - if (inLength <= 0x8 || *((const u32 *) inBuf) != 0x4C5A3737 /*"LZ77"*/|| inBuf[4] != 0x10) return NULL; - u32 uncSize = le32(((const u32 *) inBuf)[1] << 8); - - const u8 *inBufEnd = inBuf + inLength; - inBuf += 8; - buffer = new (std::nothrow) u8[uncSize]; - if (!buffer) return buffer; - - u8 *bufCur = buffer; - u8 *bufEnd = buffer + uncSize; - - while (bufCur < bufEnd && inBuf < inBufEnd) - { - u8 flags = *inBuf; - ++inBuf; - for (int i = 0; i < 8 && bufCur < bufEnd && inBuf < inBufEnd; ++i) - { - if ((flags & 0x80) != 0) - { - const LZ77Info &info = *(const LZ77Info *) inBuf; - inBuf += sizeof(LZ77Info); - int length = info.length + 3; - if (bufCur - info.offset - 1 < buffer || bufCur + length > bufEnd) return buffer; - memcpy(bufCur, bufCur - info.offset - 1, length); - bufCur += length; - } - else - { - *bufCur = *inBuf; - ++inBuf; - ++bufCur; - } - flags <<= 1; - } - } - size = uncSize; - return buffer; -} - const u8 *LoadBannerSound(const u8 *discid, u32 *size) { if (!discid) return NULL; @@ -181,7 +130,7 @@ const u8 *LoadBannerSound(const u8 *discid, u32 *size) if (*((u32*) soundChunk) == 0x4C5A3737 /*"LZ77"*/) { u32 uncSize = 0; - u8 * uncompressed_data = uncompressLZ77(soundChunk, soundChunkSize, uncSize); + u8 * uncompressed_data = uncompressLZ77(soundChunk, soundChunkSize, &uncSize); if (!uncompressed_data) { // WindowPrompt(tr("Can't decompress LZ77"), 0, tr("OK")); @@ -192,7 +141,7 @@ const u8 *LoadBannerSound(const u8 *discid, u32 *size) free(opening_bnr); return uncompressed_data; } - u8 *out = new (std::nothrow) u8[soundChunkSize]; + u8 *out = (u8 *) malloc(soundChunkSize); if (out) { memcpy(out, soundChunk, soundChunkSize); diff --git a/source/cheats/cheatmenu.cpp b/source/cheats/cheatmenu.cpp index 81684d4e..c4964e3e 100644 --- a/source/cheats/cheatmenu.cpp +++ b/source/cheats/cheatmenu.cpp @@ -30,10 +30,6 @@ int CheatMenu(const char * gameID) bool exit = false; int ret = 1; - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData settingsbg(Resources::GetFile("settings_background.png"), Resources::GetFileSize("settings_background.png")); GuiImage settingsbackground(&settingsbg); @@ -46,14 +42,14 @@ int CheatMenu(const char * gameID) GuiText backBtnTxt(tr( "Back" ), 22, Theme.prompttext); backBtnTxt.SetMaxWidth(btnOutline.GetWidth() - 30); GuiImage backBtnImg(&btnOutline); - GuiButton backBtn(&backBtnImg, &backBtnImg, 2, 3, -140, 400, &trigA, NULL, btnClick2, 1); + GuiButton backBtn(&backBtnImg, &backBtnImg, 2, 3, -140, 400, &trigA, NULL, btnSoundClick2, 1); backBtn.SetLabel(&backBtnTxt); backBtn.SetTrigger(&trigB); GuiText createBtnTxt(tr( "Create" ), 22, Theme.prompttext); createBtnTxt.SetMaxWidth(btnOutline.GetWidth() - 30); GuiImage createBtnImg(&btnOutline); - GuiButton createBtn(&createBtnImg, &createBtnImg, 2, 3, 160, 400, &trigA, NULL, btnClick2, 1); + GuiButton createBtn(&createBtnImg, &createBtnImg, 2, 3, 160, 400, &trigA, NULL, btnSoundClick2, 1); createBtn.SetLabel(&createBtnTxt); char txtfilename[55]; diff --git a/source/fatmounter.c b/source/fatmounter.c index b134ef77..39212be0 100644 --- a/source/fatmounter.c +++ b/source/fatmounter.c @@ -82,6 +82,7 @@ void USBDevice_deInit() fatUnmount("USB:/"); //only shutdown libogc usb and not the cios one __io_usbstorage.shutdown(); + __io_usbstorage2.shutdown(); fat_usb_mount = 0; fat_usb_sec = 0; diff --git a/source/filelist.h b/source/filelist.h index 4c6e9a47..6009cb67 100644 --- a/source/filelist.h +++ b/source/filelist.h @@ -71,14 +71,14 @@ extern const u32 credits_button_png_size; extern const u8 credits_button_over_png[]; extern const u32 credits_button_over_png_size; -extern const u8 button_over_pcm[]; -extern const u32 button_over_pcm_size; +extern const u8 button_over_wav[]; +extern const u32 button_over_wav_size; -extern const u8 button_click_pcm[]; -extern const u32 button_click_pcm_size; +extern const u8 button_click_wav[]; +extern const u32 button_click_wav_size; -extern const u8 button_click2_pcm[]; -extern const u32 button_click2_pcm_size; +extern const u8 button_click2_wav[]; +extern const u32 button_click2_wav_size; extern const u8 tooltip_left_png[]; extern const u32 tooltip_left_png_size; diff --git a/source/homebrewboot/HomebrewBrowse.cpp b/source/homebrewboot/HomebrewBrowse.cpp index f380055a..7d823952 100644 --- a/source/homebrewboot/HomebrewBrowse.cpp +++ b/source/homebrewboot/HomebrewBrowse.cpp @@ -208,13 +208,6 @@ int MenuHomebrewBrowse() int slidedirection = FADE; - /*** Sound Variables ***/ - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiSound btnClick1(button_click_pcm, button_click_pcm_size, Settings.sfxvolume); - /*** Image Variables ***/ GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); @@ -273,7 +266,7 @@ int MenuHomebrewBrowse() backBtnTxt.SetWidescreen(Settings.widescreen); backBtnImg.SetWidescreen(Settings.widescreen); } - GuiButton backBtn(&backBtnImg, &backBtnImg, 2, 3, -180, 400, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton backBtn(&backBtnImg, &backBtnImg, 2, 3, -180, 400, &trigA, btnSoundOver, btnSoundClick2, 1); backBtn.SetLabel(&backBtnTxt); backBtn.SetTrigger(&trigB); @@ -285,8 +278,8 @@ int MenuHomebrewBrowse() GoLeftBtn.SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); GoLeftBtn.SetPosition(25, -25); GoLeftBtn.SetImage(&GoLeftImg); - GoLeftBtn.SetSoundOver(&btnSoundOver); - GoLeftBtn.SetSoundClick(btnClick2); + GoLeftBtn.SetSoundOver(btnSoundOver); + GoLeftBtn.SetSoundClick(btnSoundClick2); GoLeftBtn.SetEffectGrow(); GoLeftBtn.SetTrigger(&trigA); GoLeftBtn.SetTrigger(&trigL); @@ -297,8 +290,8 @@ int MenuHomebrewBrowse() GoRightBtn.SetAlignment(ALIGN_RIGHT, ALIGN_MIDDLE); GoRightBtn.SetPosition(-25, -25); GoRightBtn.SetImage(&GoRightImg); - GoRightBtn.SetSoundOver(&btnSoundOver); - GoRightBtn.SetSoundClick(btnClick2); + GoRightBtn.SetSoundOver(btnSoundOver); + GoRightBtn.SetSoundClick(btnSoundClick2); GoRightBtn.SetEffectGrow(); GoRightBtn.SetTrigger(&trigA); GoRightBtn.SetTrigger(&trigR); @@ -332,8 +325,8 @@ int MenuHomebrewBrowse() MainButton1.SetLabel(&MainButton1Txt); MainButton1.SetLabel(&MainButton1DescTxt, 1); MainButton1.SetLabelOver(&MainButton1DescOverTxt, 1); - MainButton1.SetSoundOver(&btnSoundOver); - MainButton1.SetSoundClick(&btnClick1); + MainButton1.SetSoundOver(btnSoundOver); + MainButton1.SetSoundClick(btnSoundClick); MainButton1.SetEffectGrow(); MainButton1.SetTrigger(&trigA); @@ -359,8 +352,8 @@ int MenuHomebrewBrowse() MainButton2.SetLabel(&MainButton2Txt); MainButton2.SetLabel(&MainButton2DescTxt, 1); MainButton2.SetLabelOver(&MainButton2DescOverTxt, 1); - MainButton2.SetSoundOver(&btnSoundOver); - MainButton2.SetSoundClick(&btnClick1); + MainButton2.SetSoundOver(btnSoundOver); + MainButton2.SetSoundClick(btnSoundClick); MainButton2.SetEffectGrow(); MainButton2.SetTrigger(&trigA); @@ -386,8 +379,8 @@ int MenuHomebrewBrowse() MainButton3.SetLabel(&MainButton3Txt); MainButton3.SetLabel(&MainButton3DescTxt, 1); MainButton3.SetLabelOver(&MainButton3DescOverTxt, 1); - MainButton3.SetSoundOver(&btnSoundOver); - MainButton3.SetSoundClick(&btnClick1); + MainButton3.SetSoundOver(btnSoundOver); + MainButton3.SetSoundClick(btnSoundClick); MainButton3.SetEffectGrow(); MainButton3.SetTrigger(&trigA); @@ -416,8 +409,8 @@ int MenuHomebrewBrowse() MainButton4.SetLabel(&MainButton4Txt); MainButton4.SetLabel(&MainButton4DescTxt, 1); MainButton4.SetLabelOver(&MainButton4DescOverTxt, 1); - MainButton4.SetSoundOver(&btnSoundOver); - MainButton4.SetSoundClick(&btnClick1); + MainButton4.SetSoundOver(btnSoundOver); + MainButton4.SetSoundClick(btnSoundClick); MainButton4.SetEffectGrow(); MainButton4.SetTrigger(&trigA); @@ -429,8 +422,8 @@ int MenuHomebrewBrowse() GuiButton wifiBtn(wifiImg.GetWidth(), wifiImg.GetHeight()); wifiBtn.SetImage(&wifiImg); wifiBtn.SetPosition(500, 400); - wifiBtn.SetSoundOver(&btnSoundOver); - wifiBtn.SetSoundClick(&btnClick1); + wifiBtn.SetSoundOver(btnSoundOver); + wifiBtn.SetSoundClick(btnSoundClick); wifiBtn.SetEffectGrow(); wifiBtn.SetAlpha(80); wifiBtn.SetTrigger(&trigA); @@ -441,8 +434,8 @@ int MenuHomebrewBrowse() channelBtn.SetAlignment(ALIGN_LEFT, ALIGN_TOP); channelBtn.SetPosition(440, 400); channelBtn.SetImage(&channelBtnImg); - channelBtn.SetSoundOver(&btnSoundOver); - channelBtn.SetSoundClick(btnClick2); + channelBtn.SetSoundOver(btnSoundOver); + channelBtn.SetSoundClick(btnSoundClick2); channelBtn.SetEffectGrow(); channelBtn.SetTrigger(&trigA); diff --git a/source/libs/libwbfs/libwbfs.c b/source/libs/libwbfs/libwbfs.c index 120be9ea..57a4df82 100644 --- a/source/libs/libwbfs/libwbfs.c +++ b/source/libs/libwbfs/libwbfs.c @@ -1,827 +1,827 @@ -// Copyright 2009 Kwiirk -// Licensed under the terms of the GNU GPL, version 2 -// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt - -// Modified by oggzee - -#include "libwbfs.h" - -#define likely(x) __builtin_expect(!!(x), 1) -#define unlikely(x) __builtin_expect(!!(x), 0) - -#define ERROR(x) do {wbfs_error(x);goto error;}while(0) -#define ALIGN_LBA(x) (((x)+p->hd_sec_sz-1)&(~(p->hd_sec_sz-1))) - -wbfs_t wbfs_iso_file; - -static int force_mode = 0; - -void wbfs_set_force_mode(int force) -{ - force_mode = force; -} - -static u8 size_to_shift(u32 size) -{ - u8 ret = 0; - while (size) - { - ret++; - size >>= 1; - } - return ret - 1; -} -#define read_le32_unaligned(x) ((x)[0]|((x)[1]<<8)|((x)[2]<<16)|((x)[3]<<24)) - -wbfs_t*wbfs_open_hd(rw_sector_callback_t read_hdsector, rw_sector_callback_t write_hdsector, void *callback_data, - int hd_sector_size, int num_hd_sector __attribute( ( unused ) ), int reset) -{ - int i = num_hd_sector, ret; - u8 *ptr, *tmp_buffer = wbfs_ioalloc( hd_sector_size ); - u8 part_table[16 * 4]; - ret = read_hdsector(callback_data, 0, 1, tmp_buffer); - if (ret) return 0; - //find wbfs partition - wbfs_memcpy( part_table, tmp_buffer + 0x1be, 16*4 ); - ptr = part_table; - for (i = 0; i < 4; i++, ptr += 16) - { - u32 part_lba = read_le32_unaligned( ptr + 0x8 ); - wbfs_head_t *head = (wbfs_head_t *) tmp_buffer; - ret = read_hdsector(callback_data, part_lba, 1, tmp_buffer); - // verify there is the magic. - if (head->magic == wbfs_htonl( WBFS_MAGIC )) - { - wbfs_t*p = wbfs_open_partition(read_hdsector, write_hdsector, callback_data, hd_sector_size, 0, part_lba, - reset); - wbfs_iofree( tmp_buffer ); - return p; - } - } - wbfs_iofree( tmp_buffer ); - if (reset)// XXX make a empty hd partition.. - { - } - return 0; -} -wbfs_t*wbfs_open_partition(rw_sector_callback_t read_hdsector, rw_sector_callback_t write_hdsector, - void *callback_data, int hd_sector_size, int num_hd_sector, u32 part_lba, int reset) -{ - wbfs_t *p = wbfs_malloc( sizeof( wbfs_t ) ); - - wbfs_head_t *head = wbfs_ioalloc( hd_sector_size ? hd_sector_size : 512 ); - - //constants, but put here for consistancy - p->wii_sec_sz = 0x8000; - p->wii_sec_sz_s = size_to_shift(0x8000); - p->n_wii_sec = (num_hd_sector / 0x8000) * hd_sector_size; - p->n_wii_sec_per_disc = 143432 * 2;//support for double layers discs.. - p->head = head; - p->part_lba = part_lba; - // init the partition - if (reset) - { - u8 sz_s; - wbfs_memset( head, 0, hd_sector_size ); - head->magic = wbfs_htonl( WBFS_MAGIC ); - head->hd_sec_sz_s = size_to_shift(hd_sector_size); - head->n_hd_sec = wbfs_htonl( num_hd_sector ); - // choose minimum wblk_sz that fits this partition size - for (sz_s = 6; sz_s < 11; sz_s++) - { - // ensure that wbfs_sec_sz is big enough to address every blocks using 16 bits - if (p->n_wii_sec < ((1U << 16) * (1 << sz_s))) break; - } - head->wbfs_sec_sz_s = sz_s + p->wii_sec_sz_s; - } - else read_hdsector(callback_data, p->part_lba, 1, head); - if (head->magic != wbfs_htonl( WBFS_MAGIC )) - ERROR( "bad magic" ); - if (!force_mode && hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size)) - ERROR( "hd sector size doesn't match" ); - if (!force_mode && num_hd_sector && head->n_hd_sec != wbfs_htonl( num_hd_sector )) - ERROR( "hd num sector doesn't match" ); - p->hd_sec_sz = 1 << head->hd_sec_sz_s; - p->hd_sec_sz_s = head->hd_sec_sz_s; - p->n_hd_sec = wbfs_ntohl( head->n_hd_sec ); - - p->n_wii_sec = (p->n_hd_sec / p->wii_sec_sz) * (p->hd_sec_sz); - - p->wbfs_sec_sz_s = head->wbfs_sec_sz_s; - p->wbfs_sec_sz = 1 << p->wbfs_sec_sz_s; - p->n_wbfs_sec = p->n_wii_sec >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s); - p->n_wbfs_sec_per_disc = p->n_wii_sec_per_disc >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s); - p->disc_info_sz = ALIGN_LBA( sizeof( wbfs_disc_info_t ) + p->n_wbfs_sec_per_disc * 2 ); - - //printf("hd_sector_size %X wii_sector size %X wbfs sector_size %X\n",p->hd_sec_sz,p->wii_sec_sz,p->wbfs_sec_sz); - p->read_hdsector = read_hdsector; - p->write_hdsector = write_hdsector; - p->callback_data = callback_data; - - p->freeblks_lba = (p->wbfs_sec_sz - p->n_wbfs_sec / 8) >> p->hd_sec_sz_s; - - if (!reset) - p->freeblks = 0; // will alloc and read only if needed - else - { - // init with all free blocks - p->freeblks = wbfs_ioalloc( ALIGN_LBA( p->n_wbfs_sec / 8 ) ); - wbfs_memset( p->freeblks, 0xff, p->n_wbfs_sec / 8 ); - } - p->max_disc = (p->freeblks_lba - 1) / (p->disc_info_sz >> p->hd_sec_sz_s); - if (p->max_disc > p->hd_sec_sz - sizeof(wbfs_head_t)) p->max_disc = p->hd_sec_sz - sizeof(wbfs_head_t); - - p->tmp_buffer = wbfs_ioalloc( p->hd_sec_sz ); - p->n_disc_open = 0; - return p; - error: wbfs_free( p ); - wbfs_iofree( head ); - return 0; - -} - -void wbfs_sync(wbfs_t*p) -{ - // copy back descriptors - if (p->write_hdsector) - { - p->write_hdsector(p->callback_data, p->part_lba + 0, 1, p->head); - - if (p->freeblks) p->write_hdsector(p->callback_data, p->part_lba + p->freeblks_lba, - ALIGN_LBA( p->n_wbfs_sec / 8 ) >> p->hd_sec_sz_s, p->freeblks); - } -} - -void wbfs_close(wbfs_t*p) -{ - wbfs_sync(p); - - if (p->n_disc_open) - ERROR( "trying to close wbfs while discs still open" ); - - wbfs_iofree( p->head ); - wbfs_iofree( p->tmp_buffer ); - if (p->freeblks) wbfs_iofree( p->freeblks ); - - wbfs_free( p ); - - error: return; -} - -wbfs_disc_t *wbfs_open_disc(wbfs_t* p, u8 *discid) -{ - u32 i; - int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s; - wbfs_disc_t *d = 0; - for (i = 0; i < p->max_disc; i++) - { - if (p->head->disc_table[i]) - { - p->read_hdsector(p->callback_data, p->part_lba + 1 + i * disc_info_sz_lba, 1, p->tmp_buffer); - if (wbfs_memcmp( discid, p->tmp_buffer, 6 ) == 0) - { - d = wbfs_malloc( sizeof( *d ) ); - if (!d) - ERROR( "allocating memory" ); - d->p = p; - d->i = i; - d->header = wbfs_ioalloc( p->disc_info_sz ); - if (!d->header) - ERROR( "allocating memory" ); - p->read_hdsector(p->callback_data, p->part_lba + 1 + i * disc_info_sz_lba, disc_info_sz_lba, d->header); - p->n_disc_open++; - // for(i=0;in_wbfs_sec_per_disc;i++) - // printf("%d,",wbfs_ntohs(d->header->wlba_table[i])); - return d; - } - } - } - return 0; - error: if (d) wbfs_iofree( d ); - return 0; - -} -void wbfs_close_disc(wbfs_disc_t*d) -{ - d->p->n_disc_open--; - wbfs_iofree( d->header ); - wbfs_free( d ); -} -// offset is pointing 32bit words to address the whole dvd, although len is in bytes -int wbfs_disc_read(wbfs_disc_t*d, u32 offset, u32 len, u8 *data) -{ - if (d->p == &wbfs_iso_file) - { - return wbfs_iso_file_read(d, offset, data, len); - } - - wbfs_t *p = d->p; - u16 wlba = offset >> (p->wbfs_sec_sz_s - 2); - u32 iwlba_shift = p->wbfs_sec_sz_s - p->hd_sec_sz_s; - u32 lba_mask = (p->wbfs_sec_sz - 1) >> (p->hd_sec_sz_s); - u32 lba = (offset >> (p->hd_sec_sz_s - 2)) & lba_mask; - u32 off = offset & ((p->hd_sec_sz >> 2) - 1); - u16 iwlba = wbfs_ntohs( d->header->wlba_table[wlba] ); - u32 len_copied; - int err = 0; - u8 *ptr = data; - if (unlikely( iwlba == 0 )) return 1; - if (unlikely( off )) - { - off *= 4; - err = p->read_hdsector(p->callback_data, p->part_lba + (iwlba << iwlba_shift) + lba, 1, p->tmp_buffer); - if (err) return err; - len_copied = p->hd_sec_sz - off; - if (likely( len < len_copied )) len_copied = len; - wbfs_memcpy( ptr, p->tmp_buffer + off, len_copied ); - len -= len_copied; - ptr += len_copied; - lba++; - if (unlikely( lba > lba_mask && len )) - { - lba = 0; - iwlba = wbfs_ntohs( d->header->wlba_table[++wlba] ); - if (unlikely( iwlba == 0 )) return 1; - } - } - while (likely( len >= p->hd_sec_sz )) - { - u32 nlb = len >> (p->hd_sec_sz_s); - - if (unlikely( lba + nlb > p->wbfs_sec_sz )) // dont cross wbfs sectors.. - nlb = p->wbfs_sec_sz - lba; - err = p->read_hdsector(p->callback_data, p->part_lba + (iwlba << iwlba_shift) + lba, nlb, ptr); - if (err) return err; - len -= nlb << p->hd_sec_sz_s; - ptr += nlb << p->hd_sec_sz_s; - lba += nlb; - if (unlikely( lba > lba_mask && len )) - { - lba = 0; - iwlba = wbfs_ntohs( d->header->wlba_table[++wlba] ); - if (unlikely( iwlba == 0 )) return 1; - } - } - if (unlikely( len )) - { - err = p->read_hdsector(p->callback_data, p->part_lba + (iwlba << iwlba_shift) + lba, 1, p->tmp_buffer); - if (err) return err; - wbfs_memcpy( ptr, p->tmp_buffer, len ); - } - return 0; -} - -// disc listing -u32 wbfs_count_discs(wbfs_t*p) -{ - u32 i, count = 0; - for (i = 0; i < p->max_disc; i++) - if (p->head->disc_table[i]) count++; - return count; - -} - -u32 wbfs_sector_used(wbfs_t *p, wbfs_disc_info_t *di) -{ - u32 tot_blk = 0, j; - for (j = 0; j < p->n_wbfs_sec_per_disc; j++) - if (wbfs_ntohs( di->wlba_table[j] )) tot_blk++; - return tot_blk; -} - -u32 wbfs_sector_used2(wbfs_t *p, wbfs_disc_info_t *di, u32 *last_blk) -{ - u32 tot_blk = 0, j; - for (j = 0; j < p->n_wbfs_sec_per_disc; j++) - if (wbfs_ntohs( di->wlba_table[j] )) - { - if (last_blk) *last_blk = j; - tot_blk++; - } - return tot_blk; -} - -u32 wbfs_get_disc_info(wbfs_t*p, u32 index, u8 *header, int header_size, u32 *size)//size in 32 bit -{ - u32 i, count = 0; - if (!p) return 1; - int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s; - - for (i = 0; i < p->max_disc; i++) - if (p->head->disc_table[i]) - { - if (count++ == index) - { - p->read_hdsector(p->callback_data, p->part_lba + 1 + i * disc_info_sz_lba, 1, p->tmp_buffer); - if (header_size > (int) p->hd_sec_sz) header_size = p->hd_sec_sz; - u32 magic = wbfs_ntohl( *( u32* )( p->tmp_buffer + 24 ) ); - if (magic != 0x5D1C9EA3) - { - p->head->disc_table[i] = 0; - return 1; - } - memcpy(header, p->tmp_buffer, header_size); - if (size) - { - u8 *header = wbfs_ioalloc( p->disc_info_sz ); - p->read_hdsector(p->callback_data, p->part_lba + 1 + i * disc_info_sz_lba, disc_info_sz_lba, header); - u32 sec_used = wbfs_sector_used(p, (wbfs_disc_info_t *) header); - wbfs_iofree( header ); - *size = sec_used << (p->wbfs_sec_sz_s - 2); - } - return 0; - } - } - return 1; -} - -static void load_freeblocks(wbfs_t*p) -{ - if (p->freeblks) return; - // XXX should handle malloc error.. - p->freeblks = wbfs_ioalloc( ALIGN_LBA( p->n_wbfs_sec / 8 ) ); - p->read_hdsector(p->callback_data, p->part_lba + p->freeblks_lba, ALIGN_LBA( p->n_wbfs_sec / 8 ) >> p->hd_sec_sz_s, - p->freeblks); - -} -u32 wbfs_count_usedblocks(wbfs_t*p) -{ - u32 i, j, count = 0; - load_freeblocks(p); - for (i = 0; i < p->n_wbfs_sec / (8 * 4); i++) - { - u32 v = wbfs_ntohl( p->freeblks[i] ); - if (v == ~0U) - count += 32; - else if (v != 0) for (j = 0; j < 32; j++) - if (v & (1 << j)) count++; - } - return count; -} - -// write access - - -//static -int block_used(u8 *used, u32 i, u32 wblk_sz) -{ - u32 k; - i *= wblk_sz; - for (k = 0; k < wblk_sz; k++) - if (i + k < 143432 * 2 && used[i + k]) return 1; - return 0; -} - -static u32 alloc_block(wbfs_t*p) -{ - u32 i, j; - for (i = 0; i < p->n_wbfs_sec / (8 * 4); i++) - { - u32 v = wbfs_ntohl( p->freeblks[i] ); - if (v != 0) - { - for (j = 0; j < 32; j++) - if (v & (1 << j)) - { - p->freeblks[i] = wbfs_htonl( v & ~( 1 << j ) ); - return (i * 32) + j + 1; - } - } - } - return ~0; -} -static void free_block(wbfs_t *p, int bl) -{ - int i = (bl - 1) / (32); - int j = (bl - 1) & 31; - u32 v = wbfs_ntohl( p->freeblks[i] ); - p->freeblks[i] = wbfs_htonl( v | 1 << j ); -} - -u32 wbfs_add_disc(wbfs_t*p, read_wiidisc_callback_t read_src_wii_disc, void *callback_data, - progress_callback_t spinner, partition_selector_t sel, int copy_1_1) -{ - int i, discn; - u32 tot, cur; - u32 wii_sec_per_wbfs_sect = 1 << (p->wbfs_sec_sz_s - p->wii_sec_sz_s); - wiidisc_t *d = 0; - u8 *used = 0; - wbfs_disc_info_t *info = 0; - u8* copy_buffer = 0; - int retval = -1; - int num_wbfs_sect_to_copy; - u32 last_used; - used = wbfs_malloc( p->n_wii_sec_per_disc ); - - if (!used) - ERROR( "unable to alloc memory" ); - // copy_1_1 needs disk usage for layers detection - //if(!copy_1_1) - { - d = wd_open_disc(read_src_wii_disc, callback_data); - if (!d) - ERROR( "unable to open wii disc" ); - wd_build_disc_usage(d, sel, used); - wd_close_disc(d); - d = 0; - } - - for (i = 0; i < p->max_disc; i++)// find a free slot. - if (p->head->disc_table[i] == 0) break; - if (i == p->max_disc) - ERROR( "no space left on device (table full)" ); - p->head->disc_table[i] = 1; - discn = i; - load_freeblocks(p); - - // build disc info - info = wbfs_ioalloc( p->disc_info_sz ); - read_src_wii_disc(callback_data, 0, 0x100, info->disc_header_copy); - - copy_buffer = wbfs_ioalloc( p->wii_sec_sz ); - if (!copy_buffer) - ERROR( "alloc memory" ); - tot = 0; - cur = 0; - num_wbfs_sect_to_copy = p->n_wbfs_sec_per_disc; - // count total number of sectors to write - last_used = 0; - for (i = 0; i < num_wbfs_sect_to_copy; i++) - { - if (block_used(used, i, wii_sec_per_wbfs_sect)) - { - tot += wii_sec_per_wbfs_sect; - last_used = i; - } - } - if (copy_1_1) - { - // detect single or dual layer - if ((last_used + 1) > (p->n_wbfs_sec_per_disc / 2)) - { - // dual layer - num_wbfs_sect_to_copy = p->n_wbfs_sec_per_disc; - } - else - { - // single layer - num_wbfs_sect_to_copy = p->n_wbfs_sec_per_disc / 2; - } - tot = num_wbfs_sect_to_copy * wii_sec_per_wbfs_sect; - } - /* - // num of hd sectors to copy could be specified directly - if (copy_1_1 > 1) { - u32 hd_sec_per_wii_sec = p->wii_sec_sz / p->hd_sec_sz; - num_wbfs_sect_to_copy = copy_1_1 / hd_sec_per_wii_sec / wii_sec_per_wbfs_sect; - tot = num_wbfs_sect_to_copy * wii_sec_per_wbfs_sect; - }*/ - int ret = 0; - if (spinner) spinner(0, tot); - for (i = 0; i < num_wbfs_sect_to_copy; i++) - { - u16 bl = 0; - if (copy_1_1 || block_used(used, i, wii_sec_per_wbfs_sect)) - { - u16 j; - - bl = alloc_block(p); - if (bl == 0xffff) - ERROR( "no space left on device (disc full)" ); - for (j = 0; j < wii_sec_per_wbfs_sect; j++) - { - u32 offset = (i * (p->wbfs_sec_sz >> 2)) + (j * (p->wii_sec_sz >> 2)); - - ret = read_src_wii_disc(callback_data, offset, p->wii_sec_sz, copy_buffer); - if (ret) - { - if (copy_1_1 && i > p->n_wbfs_sec_per_disc / 2) - { - // end of dual layer data - if (j > 0) - { - info->wlba_table[i] = wbfs_htons( bl ); - } - spinner(tot, tot); - break; - } - //ERROR("read error"); - printf("\rWARNING: read (%u) error (%d)\n", offset, ret); - } - - //fix the partition table - if (offset == (0x40000 >> 2)) wd_fix_partition_table(d, sel, copy_buffer); - p->write_hdsector(p->callback_data, p->part_lba + bl * (p->wbfs_sec_sz / p->hd_sec_sz) + j - * (p->wii_sec_sz / p->hd_sec_sz), p->wii_sec_sz / p->hd_sec_sz, copy_buffer); - cur++; - if (spinner) spinner(cur, tot); - } - } - if (ret) break; - info->wlba_table[i] = wbfs_htons( bl ); - wbfs_sync(p); - } - // write disc info - int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s; - p->write_hdsector(p->callback_data, p->part_lba + 1 + discn * disc_info_sz_lba, disc_info_sz_lba, info); - wbfs_sync(p); - retval = 0; - error: if (d) wd_close_disc(d); - if (used) wbfs_free( used ); - if (info) wbfs_iofree( info ); - if (copy_buffer) wbfs_iofree( copy_buffer ); - // init with all free blocks - - return retval; -} - -u32 wbfs_rm_disc(wbfs_t*p, u8* discid) -{ - wbfs_disc_t *d = wbfs_open_disc(p, discid); - int i; - int discn = 0; - int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s; - if (!d) return 1; - load_freeblocks(p); - discn = d->i; - for (i = 0; i < p->n_wbfs_sec_per_disc; i++) - { - u32 iwlba = wbfs_ntohs( d->header->wlba_table[i] ); - if (iwlba) free_block(p, iwlba); - } - memset(d->header, 0, p->disc_info_sz); - p->write_hdsector(p->callback_data, p->part_lba + 1 + discn * disc_info_sz_lba, disc_info_sz_lba, d->header); - p->head->disc_table[discn] = 0; - wbfs_close_disc(d); - wbfs_sync(p); - return 0; -} - -u32 wbfs_ren_disc(wbfs_t*p, u8* discid, u8* newname) -{ - wbfs_disc_t *d = wbfs_open_disc(p, discid); - int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s; - - if (!d) return 1; - - memset(d->header->disc_header_copy + 0x20, 0, 0x40); - strncpy((char *) d->header->disc_header_copy + 0x20, (char *) newname, 0x39); - - p->write_hdsector(p->callback_data, p->part_lba + 1 + d->i * disc_info_sz_lba, disc_info_sz_lba, d->header); - wbfs_close_disc(d); - return 0; -} - -u32 wbfs_rID_disc(wbfs_t*p, u8* discid, u8* newID) -{ - wbfs_disc_t *d = wbfs_open_disc(p, discid); - int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s; - - if (!d) return 1; - - memset(d->header->disc_header_copy, 0, 0x10); - strncpy((char *) d->header->disc_header_copy, (char *) newID, 0x9); - - p->write_hdsector(p->callback_data, p->part_lba + 1 + d->i * disc_info_sz_lba, disc_info_sz_lba, d->header); - wbfs_close_disc(d); - return 0; -} - -// trim the file-system to its minimum size -u32 wbfs_trim(wbfs_t*p) -{ - u32 maxbl; - load_freeblocks(p); - maxbl = alloc_block(p); - p->n_hd_sec = maxbl << (p->wbfs_sec_sz_s - p->hd_sec_sz_s); - p->head->n_hd_sec = wbfs_htonl( p->n_hd_sec ); - // make all block full - memset(p->freeblks, 0, p->n_wbfs_sec / 8); - wbfs_sync(p); - // os layer will truncate the file. - return maxbl; -} - -// data extraction -u32 wbfs_extract_disc(wbfs_disc_t*d, rw_sector_callback_t write_dst_wii_sector, void *callback_data, - progress_callback_t spinner) -{ - wbfs_t *p = d->p; - u8* copy_buffer = 0; - int i; - int src_wbs_nlb = p->wbfs_sec_sz / p->hd_sec_sz; - int dst_wbs_nlb = p->wbfs_sec_sz / p->wii_sec_sz; - copy_buffer = wbfs_ioalloc( p->wbfs_sec_sz ); - if (!copy_buffer) - ERROR( "alloc memory" ); - - for (i = 0; i < p->n_wbfs_sec_per_disc; i++) - { - u32 iwlba = wbfs_ntohs( d->header->wlba_table[i] ); - if (iwlba) - { - - if (spinner) spinner(i, p->n_wbfs_sec_per_disc); - p->read_hdsector(p->callback_data, p->part_lba + iwlba * src_wbs_nlb, src_wbs_nlb, copy_buffer); - write_dst_wii_sector(callback_data, i * dst_wbs_nlb, dst_wbs_nlb, copy_buffer); - } - } - wbfs_iofree( copy_buffer ); - return 0; - error: return 1; -} - -float wbfs_estimate_disc(wbfs_t *p, read_wiidisc_callback_t read_src_wii_disc, void *callback_data, - partition_selector_t sel) -{ - u8 *b; - int i; - u32 tot; - u32 wii_sec_per_wbfs_sect = 1 << (p->wbfs_sec_sz_s - p->wii_sec_sz_s); - wiidisc_t *d = 0; - u8 *used = 0; - wbfs_disc_info_t *info = 0; - - tot = 0; - - used = wbfs_malloc( p->n_wii_sec_per_disc ); - if (!used) - { - ERROR( "unable to alloc memory" ); - } - - d = wd_open_disc(read_src_wii_disc, callback_data); - if (!d) - { - ERROR( "unable to open wii disc" ); - } - - wd_build_disc_usage(d, sel, used); - wd_close_disc(d); - d = 0; - - info = wbfs_ioalloc( p->disc_info_sz ); - b = (u8 *) info; - read_src_wii_disc(callback_data, 0, 0x100, info->disc_header_copy); - - //fprintf(stderr, "estimating %c%c%c%c%c%c %s...\n",b[0], b[1], b[2], b[3], b[4], b[5], b + 0x20); - - for (i = 0; i < p->n_wbfs_sec_per_disc; i++) - { - if (block_used(used, i, wii_sec_per_wbfs_sect)) - { - tot++; - } - } - //memcpy(header, b,0x100); - - error: if (d) wd_close_disc(d); - - if (used) wbfs_free( used ); - - if (info) wbfs_iofree( info ); - - return tot * (((p->wbfs_sec_sz * 1.0) / p->hd_sec_sz) * 512); -} -u32 wbfs_size_disc(wbfs_t*p, read_wiidisc_callback_t read_src_wii_disc, void *callback_data, partition_selector_t sel, - u32 *comp_size, u32 *real_size) -{ - int i; - u32 tot = 0, last = 0; - u32 wii_sec_per_wbfs_sect = 1 << (p->wbfs_sec_sz_s - p->wii_sec_sz_s); - wiidisc_t *d = 0; - u8 *used = 0; - used = wbfs_malloc( p->n_wii_sec_per_disc ); - if (!used) - ERROR( "unable to alloc memory" ); - d = wd_open_disc(read_src_wii_disc, callback_data); - if (!d) - ERROR( "unable to open wii disc" ); - wd_build_disc_usage(d, sel, used); - wd_close_disc(d); - d = 0; - - // count total number to write for spinner - for (i = 0; i < p->n_wbfs_sec_per_disc; i++) - { - if (block_used(used, i, wii_sec_per_wbfs_sect)) - { - tot += wii_sec_per_wbfs_sect; - last = i * wii_sec_per_wbfs_sect; - } - } - - error: if (d) wd_close_disc(d); - if (used) wbfs_free( used ); - - *comp_size = tot; - *real_size = last; - - return 0; -} - -// offset is pointing 32bit words to address the whole dvd, although len is in bytes -//int wbfs_disc_read(wbfs_disc_t*d,u32 offset, u8 *data, u32 len) - -// offset points 32bit words, count counts bytes -//int (*read_wiidisc_callback_t)(void*fp,u32 offset,u32 count,void*iobuf); - -// connect wiidisc to wbfs_disc -int read_wiidisc_wbfsdisc(void*fp, u32 offset, u32 count, void*iobuf) -{ - return wbfs_disc_read((wbfs_disc_t*) fp, offset, count, iobuf); -} - -int wbfs_extract_file(wbfs_disc_t*d, char *path, void **data) -{ - wiidisc_t *wd = 0; - int ret = 0; - - wd = wd_open_disc(read_wiidisc_wbfsdisc, d); - if (!wd) - { - ERROR( "opening wbfs disc" ); - return -1; - } - wd->extracted_size = 0; - *data = wd_extract_file(wd, ONLY_GAME_PARTITION, path); - ret = wd->extracted_size; - if (!*data) - { - //ERROR("file not found"); - ret = -1; - } - wd_close_disc(wd); - error: return ret; -} - -int wbfs_get_fragments(wbfs_disc_t *d, _frag_append_t append_fragment, void *callback_data) -{ - if (!d) return -1; - wbfs_t *p = d->p; - int src_wbs_nlb = p->wbfs_sec_sz / p->hd_sec_sz; - int i, ret, last = 0; - for (i = 0; i < p->n_wbfs_sec_per_disc; i++) - { - u32 iwlba = wbfs_ntohs( d->header->wlba_table[i] ); - if (iwlba) - { - ret = append_fragment(callback_data, i * src_wbs_nlb, // offset - p->part_lba + iwlba * src_wbs_nlb, // sector - src_wbs_nlb); // count - if (ret) return ret; // error - last = i; - } - } - if (last < p->n_wbfs_sec_per_disc / 2) - { - last = p->n_wbfs_sec_per_disc / 2; - } - u32 size = last * src_wbs_nlb; - append_fragment(callback_data, size, 0, 0); // set size - return 0; -} - -// wrapper for reading .iso files using wbfs apis - -#include -#include - -// offset is pointing 32bit words to address the whole dvd, although len is in bytes -int wbfs_iso_file_read(wbfs_disc_t*d, u32 offset, u8 *data, u32 len) -{ - if (!d || d->p != &wbfs_iso_file) return -1; - int fd = (int) d->header; - off_t off = ((u64) offset) << 2; - off_t ret_off; - int ret; - ret_off = lseek(fd, off, SEEK_SET); - if (ret_off != off) return -1; - ret = read(fd, data, len); - if (ret != len) return -2; - return 0; -} - -u32 wbfs_disc_sector_used(wbfs_disc_t *d, u32 *num_blk) -{ - if (d->p == &wbfs_iso_file) - { - int fd = (int) d->header; - struct stat st; - if (fstat(fd, &st) == -1) return 0; - if (num_blk) - { - *num_blk = (st.st_size >> 9); // in 512 units - } - return st.st_blocks; // in 512 units (can be sparse) - } - u32 last_blk = 0; - u32 ret; - ret = wbfs_sector_used2(d->p, d->header, &last_blk); - if (num_blk) - { - *num_blk = last_blk + 1; - } - return ret; -} - +// Copyright 2009 Kwiirk +// Licensed under the terms of the GNU GPL, version 2 +// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +// Modified by oggzee + +#include "libwbfs.h" + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#define ERROR(x) do {wbfs_error(x);goto error;}while(0) +#define ALIGN_LBA(x) (((x)+p->hd_sec_sz-1)&(~(p->hd_sec_sz-1))) + +wbfs_t wbfs_iso_file; + +static int force_mode = 0; + +void wbfs_set_force_mode(int force) +{ + force_mode = force; +} + +static u8 size_to_shift(u32 size) +{ + u8 ret = 0; + while (size) + { + ret++; + size >>= 1; + } + return ret - 1; +} +#define read_le32_unaligned(x) ((x)[0]|((x)[1]<<8)|((x)[2]<<16)|((x)[3]<<24)) + +wbfs_t*wbfs_open_hd(rw_sector_callback_t read_hdsector, rw_sector_callback_t write_hdsector, void *callback_data, + int hd_sector_size, int num_hd_sector __attribute( ( unused ) ), int reset) +{ + int i = num_hd_sector, ret; + u8 *ptr, *tmp_buffer = wbfs_ioalloc( hd_sector_size ); + u8 part_table[16 * 4]; + ret = read_hdsector(callback_data, 0, 1, tmp_buffer); + if (ret) return 0; + //find wbfs partition + wbfs_memcpy( part_table, tmp_buffer + 0x1be, 16*4 ); + ptr = part_table; + for (i = 0; i < 4; i++, ptr += 16) + { + u32 part_lba = read_le32_unaligned( ptr + 0x8 ); + wbfs_head_t *head = (wbfs_head_t *) tmp_buffer; + ret = read_hdsector(callback_data, part_lba, 1, tmp_buffer); + // verify there is the magic. + if (head->magic == wbfs_htonl( WBFS_MAGIC )) + { + wbfs_t*p = wbfs_open_partition(read_hdsector, write_hdsector, callback_data, hd_sector_size, 0, part_lba, + reset); + wbfs_iofree( tmp_buffer ); + return p; + } + } + wbfs_iofree( tmp_buffer ); + if (reset)// XXX make a empty hd partition.. + { + } + return 0; +} +wbfs_t*wbfs_open_partition(rw_sector_callback_t read_hdsector, rw_sector_callback_t write_hdsector, + void *callback_data, int hd_sector_size, int num_hd_sector, u32 part_lba, int reset) +{ + wbfs_t *p = wbfs_malloc( sizeof( wbfs_t ) ); + + wbfs_head_t *head = wbfs_ioalloc( hd_sector_size ? hd_sector_size : 512 ); + + //constants, but put here for consistancy + p->wii_sec_sz = 0x8000; + p->wii_sec_sz_s = size_to_shift(0x8000); + p->n_wii_sec = (num_hd_sector / 0x8000) * hd_sector_size; + p->n_wii_sec_per_disc = 143432 * 2;//support for double layers discs.. + p->head = head; + p->part_lba = part_lba; + // init the partition + if (reset) + { + u8 sz_s; + wbfs_memset( head, 0, hd_sector_size ); + head->magic = wbfs_htonl( WBFS_MAGIC ); + head->hd_sec_sz_s = size_to_shift(hd_sector_size); + head->n_hd_sec = wbfs_htonl( num_hd_sector ); + // choose minimum wblk_sz that fits this partition size + for (sz_s = 6; sz_s < 11; sz_s++) + { + // ensure that wbfs_sec_sz is big enough to address every blocks using 16 bits + if (p->n_wii_sec < ((1U << 16) * (1 << sz_s))) break; + } + head->wbfs_sec_sz_s = sz_s + p->wii_sec_sz_s; + } + else read_hdsector(callback_data, p->part_lba, 1, head); + if (head->magic != wbfs_htonl( WBFS_MAGIC )) + ERROR( "bad magic" ); + if (!force_mode && hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size)) + ERROR( "hd sector size doesn't match" ); + if (!force_mode && num_hd_sector && head->n_hd_sec != wbfs_htonl( num_hd_sector )) + ERROR( "hd num sector doesn't match" ); + p->hd_sec_sz = 1 << head->hd_sec_sz_s; + p->hd_sec_sz_s = head->hd_sec_sz_s; + p->n_hd_sec = wbfs_ntohl( head->n_hd_sec ); + + p->n_wii_sec = (p->n_hd_sec / p->wii_sec_sz) * (p->hd_sec_sz); + + p->wbfs_sec_sz_s = head->wbfs_sec_sz_s; + p->wbfs_sec_sz = 1 << p->wbfs_sec_sz_s; + p->n_wbfs_sec = p->n_wii_sec >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s); + p->n_wbfs_sec_per_disc = p->n_wii_sec_per_disc >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s); + p->disc_info_sz = ALIGN_LBA( sizeof( wbfs_disc_info_t ) + p->n_wbfs_sec_per_disc * 2 ); + + //printf("hd_sector_size %X wii_sector size %X wbfs sector_size %X\n",p->hd_sec_sz,p->wii_sec_sz,p->wbfs_sec_sz); + p->read_hdsector = read_hdsector; + p->write_hdsector = write_hdsector; + p->callback_data = callback_data; + + p->freeblks_lba = (p->wbfs_sec_sz - p->n_wbfs_sec / 8) >> p->hd_sec_sz_s; + + if (!reset) + p->freeblks = 0; // will alloc and read only if needed + else + { + // init with all free blocks + p->freeblks = wbfs_ioalloc( ALIGN_LBA( p->n_wbfs_sec / 8 ) ); + wbfs_memset( p->freeblks, 0xff, p->n_wbfs_sec / 8 ); + } + p->max_disc = (p->freeblks_lba - 1) / (p->disc_info_sz >> p->hd_sec_sz_s); + if (p->max_disc > p->hd_sec_sz - sizeof(wbfs_head_t)) p->max_disc = p->hd_sec_sz - sizeof(wbfs_head_t); + + p->tmp_buffer = wbfs_ioalloc( p->hd_sec_sz ); + p->n_disc_open = 0; + return p; + error: wbfs_free( p ); + wbfs_iofree( head ); + return 0; + +} + +void wbfs_sync(wbfs_t*p) +{ + // copy back descriptors + if (p->write_hdsector) + { + p->write_hdsector(p->callback_data, p->part_lba + 0, 1, p->head); + + if (p->freeblks) p->write_hdsector(p->callback_data, p->part_lba + p->freeblks_lba, + ALIGN_LBA( p->n_wbfs_sec / 8 ) >> p->hd_sec_sz_s, p->freeblks); + } +} + +void wbfs_close(wbfs_t*p) +{ + wbfs_sync(p); + + if (p->n_disc_open) + ERROR( "trying to close wbfs while discs still open" ); + + wbfs_iofree( p->head ); + wbfs_iofree( p->tmp_buffer ); + if (p->freeblks) wbfs_iofree( p->freeblks ); + + wbfs_free( p ); + + error: return; +} + +wbfs_disc_t *wbfs_open_disc(wbfs_t* p, u8 *discid) +{ + u32 i; + int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s; + wbfs_disc_t *d = 0; + for (i = 0; i < p->max_disc; i++) + { + if (p->head->disc_table[i]) + { + p->read_hdsector(p->callback_data, p->part_lba + 1 + i * disc_info_sz_lba, 1, p->tmp_buffer); + if (wbfs_memcmp( discid, p->tmp_buffer, 6 ) == 0) + { + d = wbfs_malloc( sizeof( *d ) ); + if (!d) + ERROR( "allocating memory" ); + d->p = p; + d->i = i; + d->header = wbfs_ioalloc( p->disc_info_sz ); + if (!d->header) + ERROR( "allocating memory" ); + p->read_hdsector(p->callback_data, p->part_lba + 1 + i * disc_info_sz_lba, disc_info_sz_lba, d->header); + p->n_disc_open++; + // for(i=0;in_wbfs_sec_per_disc;i++) + // printf("%d,",wbfs_ntohs(d->header->wlba_table[i])); + return d; + } + } + } + return 0; + error: if (d) wbfs_iofree( d ); + return 0; + +} +void wbfs_close_disc(wbfs_disc_t*d) +{ + d->p->n_disc_open--; + wbfs_iofree( d->header ); + wbfs_free( d ); +} +// offset is pointing 32bit words to address the whole dvd, although len is in bytes +int wbfs_disc_read(wbfs_disc_t*d, u32 offset, u32 len, u8 *data) +{ + if (d->p == &wbfs_iso_file) + { + return wbfs_iso_file_read(d, offset, data, len); + } + + wbfs_t *p = d->p; + u16 wlba = offset >> (p->wbfs_sec_sz_s - 2); + u32 iwlba_shift = p->wbfs_sec_sz_s - p->hd_sec_sz_s; + u32 lba_mask = (p->wbfs_sec_sz - 1) >> (p->hd_sec_sz_s); + u32 lba = (offset >> (p->hd_sec_sz_s - 2)) & lba_mask; + u32 off = offset & ((p->hd_sec_sz >> 2) - 1); + u16 iwlba = wbfs_ntohs( d->header->wlba_table[wlba] ); + u32 len_copied; + int err = 0; + u8 *ptr = data; + if (unlikely( iwlba == 0 )) return 1; + if (unlikely( off )) + { + off *= 4; + err = p->read_hdsector(p->callback_data, p->part_lba + (iwlba << iwlba_shift) + lba, 1, p->tmp_buffer); + if (err) return err; + len_copied = p->hd_sec_sz - off; + if (likely( len < len_copied )) len_copied = len; + wbfs_memcpy( ptr, p->tmp_buffer + off, len_copied ); + len -= len_copied; + ptr += len_copied; + lba++; + if (unlikely( lba > lba_mask && len )) + { + lba = 0; + iwlba = wbfs_ntohs( d->header->wlba_table[++wlba] ); + if (unlikely( iwlba == 0 )) return 1; + } + } + while (likely( len >= p->hd_sec_sz )) + { + u32 nlb = len >> (p->hd_sec_sz_s); + + if (unlikely( lba + nlb > p->wbfs_sec_sz )) // dont cross wbfs sectors.. + nlb = p->wbfs_sec_sz - lba; + err = p->read_hdsector(p->callback_data, p->part_lba + (iwlba << iwlba_shift) + lba, nlb, ptr); + if (err) return err; + len -= nlb << p->hd_sec_sz_s; + ptr += nlb << p->hd_sec_sz_s; + lba += nlb; + if (unlikely( lba > lba_mask && len )) + { + lba = 0; + iwlba = wbfs_ntohs( d->header->wlba_table[++wlba] ); + if (unlikely( iwlba == 0 )) return 1; + } + } + if (unlikely( len )) + { + err = p->read_hdsector(p->callback_data, p->part_lba + (iwlba << iwlba_shift) + lba, 1, p->tmp_buffer); + if (err) return err; + wbfs_memcpy( ptr, p->tmp_buffer, len ); + } + return 0; +} + +// disc listing +u32 wbfs_count_discs(wbfs_t*p) +{ + u32 i, count = 0; + for (i = 0; i < p->max_disc; i++) + if (p->head->disc_table[i]) count++; + return count; + +} + +u32 wbfs_sector_used(wbfs_t *p, wbfs_disc_info_t *di) +{ + u32 tot_blk = 0, j; + for (j = 0; j < p->n_wbfs_sec_per_disc; j++) + if (wbfs_ntohs( di->wlba_table[j] )) tot_blk++; + return tot_blk; +} + +u32 wbfs_sector_used2(wbfs_t *p, wbfs_disc_info_t *di, u32 *last_blk) +{ + u32 tot_blk = 0, j; + for (j = 0; j < p->n_wbfs_sec_per_disc; j++) + if (wbfs_ntohs( di->wlba_table[j] )) + { + if (last_blk) *last_blk = j; + tot_blk++; + } + return tot_blk; +} + +u32 wbfs_get_disc_info(wbfs_t*p, u32 index, u8 *header, int header_size, u32 *size)//size in 32 bit +{ + u32 i, count = 0; + if (!p) return 1; + int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s; + + for (i = 0; i < p->max_disc; i++) + if (p->head->disc_table[i]) + { + if (count++ == index) + { + p->read_hdsector(p->callback_data, p->part_lba + 1 + i * disc_info_sz_lba, 1, p->tmp_buffer); + if (header_size > (int) p->hd_sec_sz) header_size = p->hd_sec_sz; + u32 magic = wbfs_ntohl( *( u32* )( p->tmp_buffer + 24 ) ); + if (magic != 0x5D1C9EA3) + { + p->head->disc_table[i] = 0; + return 1; + } + memcpy(header, p->tmp_buffer, header_size); + if (size) + { + u8 *header = wbfs_ioalloc( p->disc_info_sz ); + p->read_hdsector(p->callback_data, p->part_lba + 1 + i * disc_info_sz_lba, disc_info_sz_lba, header); + u32 sec_used = wbfs_sector_used(p, (wbfs_disc_info_t *) header); + wbfs_iofree( header ); + *size = sec_used << (p->wbfs_sec_sz_s - 2); + } + return 0; + } + } + return 1; +} + +static void load_freeblocks(wbfs_t*p) +{ + if (p->freeblks) return; + // XXX should handle malloc error.. + p->freeblks = wbfs_ioalloc( ALIGN_LBA( p->n_wbfs_sec / 8 ) ); + p->read_hdsector(p->callback_data, p->part_lba + p->freeblks_lba, ALIGN_LBA( p->n_wbfs_sec / 8 ) >> p->hd_sec_sz_s, + p->freeblks); + +} +u32 wbfs_count_usedblocks(wbfs_t*p) +{ + u32 i, j, count = 0; + load_freeblocks(p); + for (i = 0; i < p->n_wbfs_sec / (8 * 4); i++) + { + u32 v = wbfs_ntohl( p->freeblks[i] ); + if (v == ~0U) + count += 32; + else if (v != 0) for (j = 0; j < 32; j++) + if (v & (1 << j)) count++; + } + return count; +} + +// write access + + +//static +int block_used(u8 *used, u32 i, u32 wblk_sz) +{ + u32 k; + i *= wblk_sz; + for (k = 0; k < wblk_sz; k++) + if (i + k < 143432 * 2 && used[i + k]) return 1; + return 0; +} + +static u32 alloc_block(wbfs_t*p) +{ + u32 i, j; + for (i = 0; i < p->n_wbfs_sec / (8 * 4); i++) + { + u32 v = wbfs_ntohl( p->freeblks[i] ); + if (v != 0) + { + for (j = 0; j < 32; j++) + if (v & (1 << j)) + { + p->freeblks[i] = wbfs_htonl( v & ~( 1 << j ) ); + return (i * 32) + j + 1; + } + } + } + return ~0; +} +static void free_block(wbfs_t *p, int bl) +{ + int i = (bl - 1) / (32); + int j = (bl - 1) & 31; + u32 v = wbfs_ntohl( p->freeblks[i] ); + p->freeblks[i] = wbfs_htonl( v | 1 << j ); +} + +s32 wbfs_add_disc(wbfs_t*p, read_wiidisc_callback_t read_src_wii_disc, void *callback_data, + progress_callback_t spinner, partition_selector_t sel, int copy_1_1) +{ + int i, discn; + u32 tot, cur; + u32 wii_sec_per_wbfs_sect = 1 << (p->wbfs_sec_sz_s - p->wii_sec_sz_s); + wiidisc_t *d = 0; + u8 *used = 0; + wbfs_disc_info_t *info = 0; + u8* copy_buffer = 0; + int retval = -1; + int num_wbfs_sect_to_copy; + u32 last_used; + used = wbfs_malloc( p->n_wii_sec_per_disc ); + + if (!used) + ERROR( "unable to alloc memory" ); + // copy_1_1 needs disk usage for layers detection + //if(!copy_1_1) + { + d = wd_open_disc(read_src_wii_disc, callback_data); + if (!d) + ERROR( "unable to open wii disc" ); + wd_build_disc_usage(d, sel, used); + wd_close_disc(d); + d = 0; + } + + for (i = 0; i < p->max_disc; i++)// find a free slot. + if (p->head->disc_table[i] == 0) break; + if (i == p->max_disc) + ERROR( "no space left on device (table full)" ); + p->head->disc_table[i] = 1; + discn = i; + load_freeblocks(p); + + // build disc info + info = wbfs_ioalloc( p->disc_info_sz ); + read_src_wii_disc(callback_data, 0, 0x100, info->disc_header_copy); + + copy_buffer = wbfs_ioalloc( p->wii_sec_sz ); + if (!copy_buffer) + ERROR( "alloc memory" ); + tot = 0; + cur = 0; + num_wbfs_sect_to_copy = p->n_wbfs_sec_per_disc; + // count total number of sectors to write + last_used = 0; + for (i = 0; i < num_wbfs_sect_to_copy; i++) + { + if (block_used(used, i, wii_sec_per_wbfs_sect)) + { + tot += wii_sec_per_wbfs_sect; + last_used = i; + } + } + if (copy_1_1) + { + // detect single or dual layer + if ((last_used + 1) > (p->n_wbfs_sec_per_disc / 2)) + { + // dual layer + num_wbfs_sect_to_copy = p->n_wbfs_sec_per_disc; + } + else + { + // single layer + num_wbfs_sect_to_copy = p->n_wbfs_sec_per_disc / 2; + } + tot = num_wbfs_sect_to_copy * wii_sec_per_wbfs_sect; + } + /* + // num of hd sectors to copy could be specified directly + if (copy_1_1 > 1) { + u32 hd_sec_per_wii_sec = p->wii_sec_sz / p->hd_sec_sz; + num_wbfs_sect_to_copy = copy_1_1 / hd_sec_per_wii_sec / wii_sec_per_wbfs_sect; + tot = num_wbfs_sect_to_copy * wii_sec_per_wbfs_sect; + }*/ + int ret = 0; + if (spinner) spinner(0, tot); + for (i = 0; i < num_wbfs_sect_to_copy; i++) + { + u16 bl = 0; + if (copy_1_1 || block_used(used, i, wii_sec_per_wbfs_sect)) + { + u16 j; + + bl = alloc_block(p); + if (bl == 0xffff) + ERROR( "no space left on device (disc full)" ); + for (j = 0; j < wii_sec_per_wbfs_sect; j++) + { + u32 offset = (i * (p->wbfs_sec_sz >> 2)) + (j * (p->wii_sec_sz >> 2)); + + ret = read_src_wii_disc(callback_data, offset, p->wii_sec_sz, copy_buffer); + if (ret) + { + if (copy_1_1 && i > p->n_wbfs_sec_per_disc / 2) + { + // end of dual layer data + if (j > 0) + { + info->wlba_table[i] = wbfs_htons( bl ); + } + spinner(tot, tot); + break; + } + //ERROR("read error"); + printf("\rWARNING: read (%u) error (%d)\n", offset, ret); + } + + //fix the partition table + if (offset == (0x40000 >> 2)) wd_fix_partition_table(d, sel, copy_buffer); + p->write_hdsector(p->callback_data, p->part_lba + bl * (p->wbfs_sec_sz / p->hd_sec_sz) + j + * (p->wii_sec_sz / p->hd_sec_sz), p->wii_sec_sz / p->hd_sec_sz, copy_buffer); + cur++; + if (spinner) spinner(cur, tot); + } + } + if (ret) break; + info->wlba_table[i] = wbfs_htons( bl ); + wbfs_sync(p); + } + // write disc info + int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s; + p->write_hdsector(p->callback_data, p->part_lba + 1 + discn * disc_info_sz_lba, disc_info_sz_lba, info); + wbfs_sync(p); + retval = 0; + error: if (d) wd_close_disc(d); + if (used) wbfs_free( used ); + if (info) wbfs_iofree( info ); + if (copy_buffer) wbfs_iofree( copy_buffer ); + // init with all free blocks + + return retval; +} + +u32 wbfs_rm_disc(wbfs_t*p, u8* discid) +{ + wbfs_disc_t *d = wbfs_open_disc(p, discid); + int i; + int discn = 0; + int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s; + if (!d) return 1; + load_freeblocks(p); + discn = d->i; + for (i = 0; i < p->n_wbfs_sec_per_disc; i++) + { + u32 iwlba = wbfs_ntohs( d->header->wlba_table[i] ); + if (iwlba) free_block(p, iwlba); + } + memset(d->header, 0, p->disc_info_sz); + p->write_hdsector(p->callback_data, p->part_lba + 1 + discn * disc_info_sz_lba, disc_info_sz_lba, d->header); + p->head->disc_table[discn] = 0; + wbfs_close_disc(d); + wbfs_sync(p); + return 0; +} + +u32 wbfs_ren_disc(wbfs_t*p, u8* discid, u8* newname) +{ + wbfs_disc_t *d = wbfs_open_disc(p, discid); + int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s; + + if (!d) return 1; + + memset(d->header->disc_header_copy + 0x20, 0, 0x40); + strncpy((char *) d->header->disc_header_copy + 0x20, (char *) newname, 0x39); + + p->write_hdsector(p->callback_data, p->part_lba + 1 + d->i * disc_info_sz_lba, disc_info_sz_lba, d->header); + wbfs_close_disc(d); + return 0; +} + +u32 wbfs_rID_disc(wbfs_t*p, u8* discid, u8* newID) +{ + wbfs_disc_t *d = wbfs_open_disc(p, discid); + int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s; + + if (!d) return 1; + + memset(d->header->disc_header_copy, 0, 0x10); + strncpy((char *) d->header->disc_header_copy, (char *) newID, 0x9); + + p->write_hdsector(p->callback_data, p->part_lba + 1 + d->i * disc_info_sz_lba, disc_info_sz_lba, d->header); + wbfs_close_disc(d); + return 0; +} + +// trim the file-system to its minimum size +u32 wbfs_trim(wbfs_t*p) +{ + u32 maxbl; + load_freeblocks(p); + maxbl = alloc_block(p); + p->n_hd_sec = maxbl << (p->wbfs_sec_sz_s - p->hd_sec_sz_s); + p->head->n_hd_sec = wbfs_htonl( p->n_hd_sec ); + // make all block full + memset(p->freeblks, 0, p->n_wbfs_sec / 8); + wbfs_sync(p); + // os layer will truncate the file. + return maxbl; +} + +// data extraction +u32 wbfs_extract_disc(wbfs_disc_t*d, rw_sector_callback_t write_dst_wii_sector, void *callback_data, + progress_callback_t spinner) +{ + wbfs_t *p = d->p; + u8* copy_buffer = 0; + int i; + int src_wbs_nlb = p->wbfs_sec_sz / p->hd_sec_sz; + int dst_wbs_nlb = p->wbfs_sec_sz / p->wii_sec_sz; + copy_buffer = wbfs_ioalloc( p->wbfs_sec_sz ); + if (!copy_buffer) + ERROR( "alloc memory" ); + + for (i = 0; i < p->n_wbfs_sec_per_disc; i++) + { + u32 iwlba = wbfs_ntohs( d->header->wlba_table[i] ); + if (iwlba) + { + + if (spinner) spinner(i, p->n_wbfs_sec_per_disc); + p->read_hdsector(p->callback_data, p->part_lba + iwlba * src_wbs_nlb, src_wbs_nlb, copy_buffer); + write_dst_wii_sector(callback_data, i * dst_wbs_nlb, dst_wbs_nlb, copy_buffer); + } + } + wbfs_iofree( copy_buffer ); + return 0; + error: return 1; +} + +float wbfs_estimate_disc(wbfs_t *p, read_wiidisc_callback_t read_src_wii_disc, void *callback_data, + partition_selector_t sel) +{ + u8 *b; + int i; + u32 tot; + u32 wii_sec_per_wbfs_sect = 1 << (p->wbfs_sec_sz_s - p->wii_sec_sz_s); + wiidisc_t *d = 0; + u8 *used = 0; + wbfs_disc_info_t *info = 0; + + tot = 0; + + used = wbfs_malloc( p->n_wii_sec_per_disc ); + if (!used) + { + ERROR( "unable to alloc memory" ); + } + + d = wd_open_disc(read_src_wii_disc, callback_data); + if (!d) + { + ERROR( "unable to open wii disc" ); + } + + wd_build_disc_usage(d, sel, used); + wd_close_disc(d); + d = 0; + + info = wbfs_ioalloc( p->disc_info_sz ); + b = (u8 *) info; + read_src_wii_disc(callback_data, 0, 0x100, info->disc_header_copy); + + //fprintf(stderr, "estimating %c%c%c%c%c%c %s...\n",b[0], b[1], b[2], b[3], b[4], b[5], b + 0x20); + + for (i = 0; i < p->n_wbfs_sec_per_disc; i++) + { + if (block_used(used, i, wii_sec_per_wbfs_sect)) + { + tot++; + } + } + //memcpy(header, b,0x100); + + error: if (d) wd_close_disc(d); + + if (used) wbfs_free( used ); + + if (info) wbfs_iofree( info ); + + return tot * (((p->wbfs_sec_sz * 1.0) / p->hd_sec_sz) * 512); +} +u32 wbfs_size_disc(wbfs_t*p, read_wiidisc_callback_t read_src_wii_disc, void *callback_data, partition_selector_t sel, + u32 *comp_size, u32 *real_size) +{ + int i; + u32 tot = 0, last = 0; + u32 wii_sec_per_wbfs_sect = 1 << (p->wbfs_sec_sz_s - p->wii_sec_sz_s); + wiidisc_t *d = 0; + u8 *used = 0; + used = wbfs_malloc( p->n_wii_sec_per_disc ); + if (!used) + ERROR( "unable to alloc memory" ); + d = wd_open_disc(read_src_wii_disc, callback_data); + if (!d) + ERROR( "unable to open wii disc" ); + wd_build_disc_usage(d, sel, used); + wd_close_disc(d); + d = 0; + + // count total number to write for spinner + for (i = 0; i < p->n_wbfs_sec_per_disc; i++) + { + if (block_used(used, i, wii_sec_per_wbfs_sect)) + { + tot += wii_sec_per_wbfs_sect; + last = i * wii_sec_per_wbfs_sect; + } + } + + error: if (d) wd_close_disc(d); + if (used) wbfs_free( used ); + + *comp_size = tot; + *real_size = last; + + return 0; +} + +// offset is pointing 32bit words to address the whole dvd, although len is in bytes +//int wbfs_disc_read(wbfs_disc_t*d,u32 offset, u8 *data, u32 len) + +// offset points 32bit words, count counts bytes +//int (*read_wiidisc_callback_t)(void*fp,u32 offset,u32 count,void*iobuf); + +// connect wiidisc to wbfs_disc +int read_wiidisc_wbfsdisc(void*fp, u32 offset, u32 count, void*iobuf) +{ + return wbfs_disc_read((wbfs_disc_t*) fp, offset, count, iobuf); +} + +int wbfs_extract_file(wbfs_disc_t*d, char *path, void **data) +{ + wiidisc_t *wd = 0; + int ret = 0; + + wd = wd_open_disc(read_wiidisc_wbfsdisc, d); + if (!wd) + { + ERROR( "opening wbfs disc" ); + return -1; + } + wd->extracted_size = 0; + *data = wd_extract_file(wd, ONLY_GAME_PARTITION, path); + ret = wd->extracted_size; + if (!*data) + { + //ERROR("file not found"); + ret = -1; + } + wd_close_disc(wd); + error: return ret; +} + +int wbfs_get_fragments(wbfs_disc_t *d, _frag_append_t append_fragment, void *callback_data) +{ + if (!d) return -1; + wbfs_t *p = d->p; + int src_wbs_nlb = p->wbfs_sec_sz / p->hd_sec_sz; + int i, ret, last = 0; + for (i = 0; i < p->n_wbfs_sec_per_disc; i++) + { + u32 iwlba = wbfs_ntohs( d->header->wlba_table[i] ); + if (iwlba) + { + ret = append_fragment(callback_data, i * src_wbs_nlb, // offset + p->part_lba + iwlba * src_wbs_nlb, // sector + src_wbs_nlb); // count + if (ret) return ret; // error + last = i; + } + } + if (last < p->n_wbfs_sec_per_disc / 2) + { + last = p->n_wbfs_sec_per_disc / 2; + } + u32 size = last * src_wbs_nlb; + append_fragment(callback_data, size, 0, 0); // set size + return 0; +} + +// wrapper for reading .iso files using wbfs apis + +#include +#include + +// offset is pointing 32bit words to address the whole dvd, although len is in bytes +int wbfs_iso_file_read(wbfs_disc_t*d, u32 offset, u8 *data, u32 len) +{ + if (!d || d->p != &wbfs_iso_file) return -1; + int fd = (int) d->header; + off_t off = ((u64) offset) << 2; + off_t ret_off; + int ret; + ret_off = lseek(fd, off, SEEK_SET); + if (ret_off != off) return -1; + ret = read(fd, data, len); + if (ret != len) return -2; + return 0; +} + +u32 wbfs_disc_sector_used(wbfs_disc_t *d, u32 *num_blk) +{ + if (d->p == &wbfs_iso_file) + { + int fd = (int) d->header; + struct stat st; + if (fstat(fd, &st) == -1) return 0; + if (num_blk) + { + *num_blk = (st.st_size >> 9); // in 512 units + } + return st.st_blocks; // in 512 units (can be sparse) + } + u32 last_blk = 0; + u32 ret; + ret = wbfs_sector_used2(d->p, d->header, &last_blk); + if (num_blk) + { + *num_blk = last_blk + 1; + } + return ret; +} + diff --git a/source/libs/libwbfs/libwbfs.h b/source/libs/libwbfs/libwbfs.h index 8921773a..c115e5c0 100644 --- a/source/libs/libwbfs/libwbfs.h +++ b/source/libs/libwbfs/libwbfs.h @@ -185,7 +185,7 @@ extern "C" @sel: selects which partitions to copy. @copy_1_1: makes a 1:1 copy, whenever a game would not use the wii disc format, and some data is hidden outside the filesystem. */ - u32 wbfs_add_disc(wbfs_t*p, read_wiidisc_callback_t read_src_wii_disc, void *callback_data, + s32 wbfs_add_disc(wbfs_t*p, read_wiidisc_callback_t read_src_wii_disc, void *callback_data, progress_callback_t spinner, partition_selector_t sel, int copy_1_1); /*! remove a wiidvd inside a partition */ diff --git a/source/libs/libwbfs/libwbfs_os.h b/source/libs/libwbfs/libwbfs_os.h index f7670525..f0931e5e 100644 --- a/source/libs/libwbfs/libwbfs_os.h +++ b/source/libs/libwbfs/libwbfs_os.h @@ -1,32 +1,32 @@ -#ifndef LIBWBFS_GLUE_H -#define LIBWBFS_GLUE_H - -#include - -#define debug_printf(fmt, ...); - -#include -#define wbfs_fatal(x) do { printf("\nwbfs panic: %s\n\n",x); while(1); } while(0) -#define wbfs_error(x) do { printf("\nwbfs error: %s\n\n",x); } while(0) - -#include -#include - -#define wbfs_malloc(x) malloc(x) -#define wbfs_free(x) free(x) -#define wbfs_ioalloc(x) memalign(32, ((x) + 31) & ~31) -#define wbfs_iofree(x) free(x) -#define wbfs_be16(x) (*((u16*)(x))) -#define wbfs_be32(x) (*((u32*)(x))) -#define wbfs_ntohl(x) (x) -#define wbfs_htonl(x) (x) -#define wbfs_ntohs(x) (x) -#define wbfs_htons(x) (x) - -#include - -#define wbfs_memcmp(x,y,z) memcmp(x,y,z) -#define wbfs_memcpy(x,y,z) memcpy(x,y,z) -#define wbfs_memset(x,y,z) memset(x,y,z) - -#endif +#ifndef LIBWBFS_GLUE_H +#define LIBWBFS_GLUE_H + +#include + +#define debug_printf(fmt, ...); + +#include +#define wbfs_fatal(x) do { printf("\nwbfs panic: %s\n\n",x); return; } while(0) +#define wbfs_error(x) do { printf("\nwbfs error: %s\n\n",x); } while(0) + +#include +#include + +#define wbfs_malloc(x) malloc(x) +#define wbfs_free(x) free(x) +#define wbfs_ioalloc(x) memalign(32, ((x) + 31) & ~31) +#define wbfs_iofree(x) free(x) +#define wbfs_be16(x) (*((u16*)(x))) +#define wbfs_be32(x) (*((u32*)(x))) +#define wbfs_ntohl(x) (x) +#define wbfs_htonl(x) (x) +#define wbfs_ntohs(x) (x) +#define wbfs_htons(x) (x) + +#include + +#define wbfs_memcmp(x,y,z) memcmp(x,y,z) +#define wbfs_memcpy(x,y,z) memcpy(x,y,z) +#define wbfs_memset(x,y,z) memset(x,y,z) + +#endif diff --git a/source/libwiigui/gui.h b/source/libwiigui/gui.h index f1e8f74b..196524fc 100644 --- a/source/libwiigui/gui.h +++ b/source/libwiigui/gui.h @@ -46,8 +46,15 @@ #include "filelist.h" #include "input.h" #include "OptionList.hpp" +#include "SoundOperations/gui_sound.h" +#include "SoundOperations/gui_bgm.h" +//! Frequently used variables extern FreeTypeGX *fontSystem; +extern GuiSound *btnSoundClick; +extern GuiSound *btnSoundClick2; +extern GuiSound *btnSoundOver; +extern GuiBGM *bgMusic; #define SCROLL_INITIAL_DELAY 20 #define SCROLL_LOOP_DELAY 3 @@ -108,70 +115,6 @@ typedef struct _paddata #define EFFECT_ROCK_VERTICLE 1024 #define EFFECT_GOROUND 2048 -#define MAX_SND_VOICES 16 - -class GuiSoundDecoder; -class GuiSound -{ - public: - //!Constructor - //!\param s Pointer to the sound data - //!\param l Length of sound data - //!\param v Sound volume (0-100) - //!\param r RAW PCM Sound, when no decoder is found then try to play as raw-pcm - //!\param a true--> Pointer to the sound data is allocated with new u8[...] - //!\ GuiSound will be destroy the buffer if it no more needed - //!\ false-> sound data buffer has to live just as long as GuiSound - GuiSound(const u8 *s, int l, int v = 100, bool r = true, bool a = false); - //!Constructor - //!\param p Path to the sound data - //!\param v Sound volume (0-100) - GuiSound(const char *p, int v = 100); - //!Load - stop playing and load the new sound data - //! if load not failed replace the current with new sound data - //! otherwise the current date will not changed - //!\params same as by Constructors - //!\return true ok / false = failed - bool Load(const u8 *s, int l, bool r = false, bool a = false); - bool Load(const char *p); - //!Destructor - ~GuiSound(); - - //!Start sound playback - void Play(); - //!Stop sound playback - void Stop(); - //!Pause sound playback - void Pause(); - //!Resume sound playback - void Resume(); - //!Checks if the sound is currently playing - //!\return true if sound is playing, false otherwise - bool IsPlaying(); - //!Set sound volume - //!\param v Sound volume (0-100) - void SetVolume(int v); - //!Set the sound to loop playback (only applies to OGG) - //!\param l Loop (true to loop) - void SetLoop(bool l); - //!Get the playing time in ms for that moment (only applies to OGG) - protected: - s32 voice; // used asnd-voice - u8 *play_buffer[3]; // trpple-playbuffer - int buffer_nr; // current playbuffer - int buffer_pos; // current idx to write in buffer - bool buffer_ready; // buffer is filled and ready - bool buffer_eof; // no mor datas - will stop playing - bool loop; // play looped - s32 volume; // volume - GuiSoundDecoder *decoder; - - void DecoderCallback(); - void PlayerCallback(); - friend void *GuiSoundDecoderThread(void *args); - friend void GuiSoundPlayerCallback(s32 Voice); -}; - //!Menu input trigger management. Determine if action is neccessary based on input data by comparing controller input data to a specific trigger element. class GuiTrigger { diff --git a/source/libwiigui/gui_button.cpp b/source/libwiigui/gui_button.cpp index 59bccea8..2d4ade53 100644 --- a/source/libwiigui/gui_button.cpp +++ b/source/libwiigui/gui_button.cpp @@ -51,10 +51,10 @@ GuiButton::GuiButton(int w, int h) GuiButton::GuiButton(GuiImage* img, GuiImage* imgOver, int hor, int vert, int x, int y, GuiTrigger* trig, GuiSound* sndOver, GuiSound* sndClick, u8 grow) { - width = img->GetWidth(); - height = img->GetHeight(); + width = img ? img->GetWidth() : 0; + height = img ? img->GetHeight() : 0; image = img; - image->SetParent(this); + if(image) image->SetParent(this); imageOver = imgOver; if (imageOver) imageOver->SetParent(this); imageHold = NULL; @@ -97,10 +97,10 @@ GuiButton::GuiButton(GuiImage* img, GuiImage* imgOver, int hor, int vert, int x, GuiButton::GuiButton(GuiImage* img, GuiImage* imgOver, int hor, int vert, int x, int y, GuiTrigger* trig, GuiSound* sndOver, GuiSound* sndClick, u8 grow, GuiTooltip* tt, int ttx, int tty, int h_align, int v_align) { - width = img->GetWidth(); - height = img->GetHeight(); + width = img ? img->GetWidth() : 0; + height = img ? img->GetHeight() : 0; image = img; - image->SetParent(this); + if(image) image->SetParent(this); imageOver = imgOver; if (imageOver) imageOver->SetParent(this); imageHold = NULL; @@ -139,9 +139,12 @@ GuiButton::GuiButton(GuiImage* img, GuiImage* imgOver, int hor, int vert, int x, } toolTip = tt; - toolTip->SetParent(this); - toolTip->SetAlignment(h_align, v_align); - toolTip->SetPosition(ttx, tty); + if(toolTip) + { + toolTip->SetParent(this); + toolTip->SetAlignment(h_align, v_align); + toolTip->SetPosition(ttx, tty); + } time1 = time2 = 0; } diff --git a/source/libwiigui/gui_customoptionbrowser.cpp b/source/libwiigui/gui_customoptionbrowser.cpp index a3542c1e..0527717a 100644 --- a/source/libwiigui/gui_customoptionbrowser.cpp +++ b/source/libwiigui/gui_customoptionbrowser.cpp @@ -13,6 +13,7 @@ #include "../settings/CSettings.h" #include "gui_customoptionbrowser.h" #include "themes/CTheme.h" +#include "menu.h" #include @@ -26,7 +27,6 @@ GuiCustomOptionBrowser::GuiCustomOptionBrowser(int w, int h, OptionList * l, con width = w; height = h; options = l; - size = PAGESIZE; scrollbaron = scrollon; selectable = true; listOffset = this->FindMenuItem(-1, 1); @@ -38,7 +38,6 @@ GuiCustomOptionBrowser::GuiCustomOptionBrowser(int w, int h, OptionList * l, con trigA->SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); trigHeldA = new GuiTrigger; trigHeldA->SetHeldTrigger(-1, WPAD_BUTTON_A, PAD_BUTTON_A); - btnSoundClick = new GuiSound(button_click_pcm, button_click_pcm_size, Settings.sfxvolume); bgOptions = Resources::GetImageData(custombg); @@ -104,14 +103,7 @@ GuiCustomOptionBrowser::GuiCustomOptionBrowser(int w, int h, OptionList * l, con scrollbarBoxBtn->SetHoldable(true); scrollbarBoxBtn->SetTrigger(trigHeldA); - optionIndex = new int[size]; - optionVal = new GuiText *[size]; - optionValOver = new GuiText *[size]; - optionBtn = new GuiButton *[size]; - optionTxt = new GuiText *[size]; - optionBg = new GuiImage *[size]; - - for (int i = 0; i < size; i++) + for (int i = 0; i < PAGESIZE; i++) { optionTxt[i] = new GuiText(options->GetName(i), 20, Theme.settingstext); optionTxt[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); @@ -170,9 +162,8 @@ GuiCustomOptionBrowser::~GuiCustomOptionBrowser() delete trigA; delete trigHeldA; - delete btnSoundClick; - for (int i = 0; i < size; i++) + for (int i = 0; i < PAGESIZE; i++) { delete optionTxt[i]; delete optionVal[i]; @@ -180,20 +171,13 @@ GuiCustomOptionBrowser::~GuiCustomOptionBrowser() delete optionBg[i]; delete optionBtn[i]; } - delete[] optionIndex; - delete[] optionVal; - delete[] optionValOver; - delete[] optionBtn; - delete[] optionTxt; - delete[] optionBg; } void GuiCustomOptionBrowser::SetFocus(int f) { - LOCK( this ); focus = f; - for (int i = 0; i < size; i++) + for (int i = 0; i < PAGESIZE; i++) optionBtn[i]->ResetState(); if (f == 1) optionBtn[selectedItem]->SetState(STATE_SELECTED); @@ -201,14 +185,13 @@ void GuiCustomOptionBrowser::SetFocus(int f) void GuiCustomOptionBrowser::ResetState() { - LOCK( this ); if (state != STATE_DISABLED) { state = STATE_DEFAULT; stateChan = -1; } - for (int i = 0; i < size; i++) + for (int i = 0; i < PAGESIZE; i++) { optionBtn[i]->ResetState(); } @@ -217,7 +200,7 @@ void GuiCustomOptionBrowser::ResetState() int GuiCustomOptionBrowser::GetClickedOption() { int found = -1; - for (int i = 0; i < size; i++) + for (int i = 0; i < PAGESIZE; i++) { if (optionBtn[i]->GetState() == STATE_CLICKED) { @@ -232,7 +215,7 @@ int GuiCustomOptionBrowser::GetClickedOption() int GuiCustomOptionBrowser::GetSelectedOption() { int found = -1; - for (int i = 0; i < size; i++) + for (int i = 0; i < PAGESIZE; i++) { if (optionBtn[i]->GetState() == STATE_SELECTED) { @@ -245,7 +228,7 @@ int GuiCustomOptionBrowser::GetSelectedOption() void GuiCustomOptionBrowser::SetClickable(bool enable) { - for (int i = 0; i < size; i++) + for (int i = 0; i < PAGESIZE; i++) { optionBtn[i]->SetClickable(enable); } @@ -284,14 +267,13 @@ int GuiCustomOptionBrowser::FindMenuItem(int currentItem, int direction) */ void GuiCustomOptionBrowser::Draw() { - LOCK( this ); if (!this->IsVisible()) return; bgOptionsImg->Draw(); int next = listOffset; - for (int i = 0; i < size; i++) + for (int i = 0; i < PAGESIZE; i++) { if (next >= 0) { @@ -313,12 +295,12 @@ void GuiCustomOptionBrowser::Draw() void GuiCustomOptionBrowser::UpdateListEntries() { - scrollbaron = options->GetLength() > size; + scrollbaron = options->GetLength() > PAGESIZE; if (listOffset < 0) listOffset = this->FindMenuItem(-1, 1); int next = listOffset; int maxNameWidth = 0; - for (int i = 0; i < size; i++) + for (int i = 0; i < PAGESIZE; i++) { if (next >= 0) { @@ -343,7 +325,7 @@ void GuiCustomOptionBrowser::UpdateListEntries() } } if (coL2 < (24 + maxNameWidth + 16)) coL2 = 24 + maxNameWidth + 16; - for (int i = 0; i < size; i++) + for (int i = 0; i < PAGESIZE; i++) { if (optionBtn[i]->GetState() != STATE_DISABLED) { @@ -358,7 +340,6 @@ void GuiCustomOptionBrowser::UpdateListEntries() void GuiCustomOptionBrowser::Update(GuiTrigger * t) { - LOCK( this ); int next, prev, lang = options->GetLength(); if (state == STATE_DISABLED || !t) return; @@ -384,7 +365,7 @@ void GuiCustomOptionBrowser::Update(GuiTrigger * t) if (buttonshold != WPAD_BUTTON_UP && buttonshold != WPAD_BUTTON_DOWN) { - for (int i = 0; i < size; i++) + for (int i = 0; i < PAGESIZE; i++) { if (next >= 0) next = this->FindMenuItem(next, 1); @@ -418,7 +399,7 @@ void GuiCustomOptionBrowser::Update(GuiTrigger * t) if (next >= 0) { - if (selectedItem == size - 1) + if (selectedItem == PAGESIZE - 1) { // move list down by 1 listOffset = this->FindMenuItem(listOffset, 1); @@ -460,7 +441,7 @@ void GuiCustomOptionBrowser::Update(GuiTrigger * t) if (next >= 0) { - if (selectedItem == size - 1) + if (selectedItem == PAGESIZE - 1) { // move list down by 1 listOffset = this->FindMenuItem(listOffset, 1); @@ -506,7 +487,7 @@ void GuiCustomOptionBrowser::Update(GuiTrigger * t) } if (scrollbarBoxBtn->GetState() == STATE_HELD && scrollbarBoxBtn->GetStateChan() == t->chan && t->wpad.ir.valid - && options->GetLength() > size) + && options->GetLength() > PAGESIZE) { scrollbarBoxBtn->SetPosition(width / 2 - 18 + 7, 0); @@ -519,10 +500,10 @@ void GuiCustomOptionBrowser::Update(GuiTrigger * t) listOffset = 0; selectedItem = 0; } - else if (listOffset + size >= lang) + else if (listOffset + PAGESIZE >= lang) { - listOffset = lang - size; - selectedItem = size - 1; + listOffset = lang - PAGESIZE; + selectedItem = PAGESIZE - 1; } } int positionbar = 237 * (listOffset + selectedItem) / lang; @@ -532,17 +513,17 @@ void GuiCustomOptionBrowser::Update(GuiTrigger * t) if (t->Right()) { - if (listOffset < lang && lang > size) + if (listOffset < lang && lang > PAGESIZE) { - listOffset = listOffset + size; - if (listOffset + size >= lang) listOffset = lang - size; + listOffset = listOffset + PAGESIZE; + if (listOffset + PAGESIZE >= lang) listOffset = lang - PAGESIZE; } } else if (t->Left()) { if (listOffset > 0) { - listOffset = listOffset - size; + listOffset = listOffset - PAGESIZE; if (listOffset < 0) listOffset = 0; } } diff --git a/source/libwiigui/gui_customoptionbrowser.h b/source/libwiigui/gui_customoptionbrowser.h index f5bd304b..f6064d94 100644 --- a/source/libwiigui/gui_customoptionbrowser.h +++ b/source/libwiigui/gui_customoptionbrowser.h @@ -1,4 +1,8 @@ +#ifndef GUI_CUSTOMBROWSER_H_ +#define GUI_CUSTOMBROWSER_H_ + #include "gui.h" +#include //!Display a list of menu options class GuiCustomOptionBrowser: public GuiElement @@ -20,17 +24,16 @@ class GuiCustomOptionBrowser: public GuiElement void UpdateListEntries(); int selectedItem; int listOffset; - int size; int coL2; int scrollbaron; OptionList * options; - int * optionIndex; - GuiButton ** optionBtn; - GuiText ** optionTxt; - GuiText ** optionVal; - GuiText ** optionValOver; - GuiImage ** optionBg; + int optionIndex[PAGESIZE]; + GuiButton * optionBtn[PAGESIZE]; + GuiText * optionTxt[PAGESIZE]; + GuiText * optionVal[PAGESIZE]; + GuiText * optionValOver[PAGESIZE]; + GuiImage * optionBg[PAGESIZE]; GuiButton * arrowUpBtn; GuiButton * arrowDownBtn; @@ -55,7 +58,8 @@ class GuiCustomOptionBrowser: public GuiElement GuiImageData * scrollbarBox; GuiImageData * scrollbarBoxOver; - GuiSound * btnSoundClick; GuiTrigger * trigA; GuiTrigger * trigHeldA; }; + +#endif diff --git a/source/libwiigui/gui_filebrowser.cpp b/source/libwiigui/gui_filebrowser.cpp index 1e71fbdc..d2a5e07c 100644 --- a/source/libwiigui/gui_filebrowser.cpp +++ b/source/libwiigui/gui_filebrowser.cpp @@ -32,18 +32,15 @@ GuiFileBrowser::GuiFileBrowser(int w, int h) trigHeldA = new GuiTrigger; trigHeldA->SetHeldTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); - btnSoundOver = new GuiSound(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - btnSoundClick = new GuiSound(button_click_pcm, button_click_pcm_size, Settings.sfxvolume); - bgFileSelection = new GuiImageData(Resources::GetFile("bg_browser.png"), Resources::GetFileSize("bg_browser.png")); bgFileSelectionImg = new GuiImage(bgFileSelection); bgFileSelectionImg->SetParent(this); bgFileSelectionImg->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); bgFileSelectionEntry = Resources::GetImageData("bg_browser_selection.png"); - + fileFolder = Resources::GetImageData("icon_folder.png"); - + scrollbar = Resources::GetImageData("scrollbar.png"); scrollbarImg = new GuiImage(scrollbar); scrollbarImg->SetParent(this); @@ -160,8 +157,6 @@ GuiFileBrowser::~GuiFileBrowser() delete arrowUp; delete scrollbarBox; - delete btnSoundOver; - delete btnSoundClick; delete trigHeldA; delete trigA; diff --git a/source/libwiigui/gui_gamebrowser.cpp b/source/libwiigui/gui_gamebrowser.cpp index 9ab6c889..f6320859 100644 --- a/source/libwiigui/gui_gamebrowser.cpp +++ b/source/libwiigui/gui_gamebrowser.cpp @@ -17,6 +17,7 @@ #include "settings/GameTitles.h" #include "usbloader/GameList.h" #include "themes/CTheme.h" +#include "menu.h" #include #include @@ -41,7 +42,6 @@ GuiGameBrowser::GuiGameBrowser(int w, int h, int selected, int offset) trigA->SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); trigHeldA = new GuiTrigger; trigHeldA->SetHeldTrigger(-1, WPAD_BUTTON_A, PAD_BUTTON_A); - btnSoundClick = new GuiSound(button_click_pcm, button_click_pcm_size, Settings.sfxvolume); bgGames = Resources::GetImageData("bg_options.png"); newGames = Resources::GetImageData("new.png"); @@ -182,7 +182,6 @@ GuiGameBrowser::~GuiGameBrowser() delete trigA; delete trigHeldA; - delete btnSoundClick; for (int i = 0; i < pagesize; i++) { diff --git a/source/libwiigui/gui_gamebrowser.h b/source/libwiigui/gui_gamebrowser.h index c8370dc9..3918f239 100644 --- a/source/libwiigui/gui_gamebrowser.h +++ b/source/libwiigui/gui_gamebrowser.h @@ -1,65 +1,64 @@ -#ifndef _GUIGAMEBROWSER_H_ -#define _GUIGAMEBROWSER_H_ - -#include "gui.h" -#include "../usbloader/disc.h" - -class GuiGameBrowser: public GuiElement -{ - public: - GuiGameBrowser(int w, int h, int selected = 0, int offset = 0); - ~GuiGameBrowser(); - int FindMenuItem(int c, int d); - int GetClickedOption(); - int GetSelectedOption(); - void ResetState(); - void SetFocus(int f); - void Draw(); - void Update(GuiTrigger * t); - int GetOffset(); - void Reload(); - //GuiText * optionVal[PAGESIZE]; - protected: - void UpdateListEntries(); - int selectedItem; - int listOffset; - int scrollbaron; - int pagesize; - int maxTextWidth; - - int * gameIndex; - GuiButton ** game; - GuiText ** gameTxt; - GuiText ** gameTxtOver; - GuiImage ** gameBg; - GuiImage ** newImg; - - GuiButton * arrowUpBtn; - GuiButton * arrowDownBtn; - GuiButton * scrollbarBoxBtn; - - GuiImage * bgGameImg; - GuiImage * scrollbarImg; - GuiImage * arrowDownImg; - GuiImage * arrowDownOverImg; - GuiImage * arrowUpImg; - GuiImage * arrowUpOverImg; - GuiImage * scrollbarBoxImg; - GuiImage * scrollbarBoxOverImg; - - GuiImageData * bgGames; - GuiImageData * bgGamesEntry; - GuiImageData * newGames; - GuiImageData * scrollbar; - GuiImageData * arrowDown; - GuiImageData * arrowDownOver; - GuiImageData * arrowUp; - GuiImageData * arrowUpOver; - GuiImageData * scrollbarBox; - GuiImageData * scrollbarBoxOver; - - GuiSound * btnSoundClick; - GuiTrigger * trigA; - GuiTrigger * trigHeldA; -}; -#endif +#ifndef _GUIGAMEBROWSER_H_ +#define _GUIGAMEBROWSER_H_ + +#include "gui.h" +#include "../usbloader/disc.h" + +class GuiGameBrowser: public GuiElement +{ + public: + GuiGameBrowser(int w, int h, int selected = 0, int offset = 0); + ~GuiGameBrowser(); + int FindMenuItem(int c, int d); + int GetClickedOption(); + int GetSelectedOption(); + void ResetState(); + void SetFocus(int f); + void Draw(); + void Update(GuiTrigger * t); + int GetOffset(); + void Reload(); + //GuiText * optionVal[PAGESIZE]; + protected: + void UpdateListEntries(); + int selectedItem; + int listOffset; + int scrollbaron; + int pagesize; + int maxTextWidth; + + int * gameIndex; + GuiButton ** game; + GuiText ** gameTxt; + GuiText ** gameTxtOver; + GuiImage ** gameBg; + GuiImage ** newImg; + + GuiButton * arrowUpBtn; + GuiButton * arrowDownBtn; + GuiButton * scrollbarBoxBtn; + + GuiImage * bgGameImg; + GuiImage * scrollbarImg; + GuiImage * arrowDownImg; + GuiImage * arrowDownOverImg; + GuiImage * arrowUpImg; + GuiImage * arrowUpOverImg; + GuiImage * scrollbarBoxImg; + GuiImage * scrollbarBoxOverImg; + + GuiImageData * bgGames; + GuiImageData * bgGamesEntry; + GuiImageData * newGames; + GuiImageData * scrollbar; + GuiImageData * arrowDown; + GuiImageData * arrowDownOver; + GuiImageData * arrowUp; + GuiImageData * arrowUpOver; + GuiImageData * scrollbarBox; + GuiImageData * scrollbarBoxOver; + + GuiTrigger * trigA; + GuiTrigger * trigHeldA; +}; +#endif diff --git a/source/libwiigui/gui_gamecarousel.cpp b/source/libwiigui/gui_gamecarousel.cpp index d333fa2f..2c2db0cc 100644 --- a/source/libwiigui/gui_gamecarousel.cpp +++ b/source/libwiigui/gui_gamecarousel.cpp @@ -70,9 +70,6 @@ GuiGameCarousel::GuiGameCarousel(int w, int h, const char *themePath, const u8 * trigMinus = new GuiTrigger; trigMinus->SetButtonOnlyTrigger(-1, WPAD_BUTTON_MINUS | WPAD_CLASSIC_BUTTON_MINUS, 0); - btnSoundClick = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - btnSoundOver = new GuiSound(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - imgLeft = Resources::GetImageData("startgame_arrow_left.png"); imgRight = Resources::GetImageData("startgame_arrow_right.png"); @@ -164,8 +161,6 @@ GuiGameCarousel::~GuiGameCarousel() delete trigR; delete trigPlus; delete trigMinus; - delete btnSoundClick; - delete btnSoundOver; delete gamename; for (int i = 0; i < pagesize; i++) diff --git a/source/libwiigui/gui_gamecarousel.h b/source/libwiigui/gui_gamecarousel.h index bd8db075..ae654ac5 100644 --- a/source/libwiigui/gui_gamecarousel.h +++ b/source/libwiigui/gui_gamecarousel.h @@ -1,54 +1,52 @@ -#ifndef _GUIGAMECAROUSEL_H_ -#define _GUIGAMECAROUSEL_H_ - -#include "gui.h" -#include "../usbloader/disc.h" -class GuiImageAsync; -class GuiGameCarousel: public GuiElement -{ - public: - GuiGameCarousel(int w, int h, const char *themePath, const u8 *imagebg, int imagebgsize, int selected = 0, int offset = 0); - ~GuiGameCarousel(); - int FindMenuItem(int c, int d); - int GetClickedOption(); - int GetSelectedOption(); - void ResetState(); - void SetFocus(int f); - void Draw(); - void Update(GuiTrigger * t); - int GetOffset(); - void Reload(); - //GuiText * optionVal[PAGESIZE]; - protected: - GuiImageData noCover; - int selectedItem; - int listOffset; - int scrollbaron; - int pagesize; - int speed; - int clickedItem; - - int * gameIndex; - GuiButton ** game; - GuiImageAsync ** coverImg; - - GuiText * gamename; - - GuiButton * btnRight; - GuiButton * btnLeft; - - GuiImage * btnLeftImg; - GuiImage * btnRightImg; - - GuiImageData * imgLeft; - GuiImageData * imgRight; - - GuiSound * btnSoundOver; - GuiSound * btnSoundClick; - GuiTrigger * trigA; - GuiTrigger * trigL; - GuiTrigger * trigR; - GuiTrigger * trigPlus; - GuiTrigger * trigMinus; -}; -#endif +#ifndef _GUIGAMECAROUSEL_H_ +#define _GUIGAMECAROUSEL_H_ + +#include "gui.h" +#include "../usbloader/disc.h" +class GuiImageAsync; +class GuiGameCarousel: public GuiElement +{ + public: + GuiGameCarousel(int w, int h, const char *themePath, const u8 *imagebg, int imagebgsize, int selected = 0, int offset = 0); + ~GuiGameCarousel(); + int FindMenuItem(int c, int d); + int GetClickedOption(); + int GetSelectedOption(); + void ResetState(); + void SetFocus(int f); + void Draw(); + void Update(GuiTrigger * t); + int GetOffset(); + void Reload(); + //GuiText * optionVal[PAGESIZE]; + protected: + GuiImageData noCover; + int selectedItem; + int listOffset; + int scrollbaron; + int pagesize; + int speed; + int clickedItem; + + int * gameIndex; + GuiButton ** game; + GuiImageAsync ** coverImg; + + GuiText * gamename; + + GuiButton * btnRight; + GuiButton * btnLeft; + + GuiImage * btnLeftImg; + GuiImage * btnRightImg; + + GuiImageData * imgLeft; + GuiImageData * imgRight; + + GuiTrigger * trigA; + GuiTrigger * trigL; + GuiTrigger * trigR; + GuiTrigger * trigPlus; + GuiTrigger * trigMinus; +}; +#endif diff --git a/source/libwiigui/gui_gamegrid.cpp b/source/libwiigui/gui_gamegrid.cpp index bf04ef8d..c4c33f2c 100644 --- a/source/libwiigui/gui_gamegrid.cpp +++ b/source/libwiigui/gui_gamegrid.cpp @@ -238,9 +238,6 @@ GuiGameGrid::GuiGameGrid(int w, int h, const char *themePath, const u8 *imagebg, trigMinus = new GuiTrigger; trigMinus->SetButtonOnlyTrigger(-1, WPAD_BUTTON_MINUS | WPAD_CLASSIC_BUTTON_MINUS, 0); - btnSoundClick = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - btnSoundOver = new GuiSound(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - int btnHeight = (int) lround(sqrt(RADIUS * RADIUS - 90000) - RADIUS - 50); // Button Left @@ -303,8 +300,6 @@ GuiGameGrid::~GuiGameGrid() delete trigMinus; delete trig1; delete trig2; - delete btnSoundClick; - delete btnSoundOver; for (int i = pagesize - 1; i >= 0; i--) { diff --git a/source/libwiigui/gui_gamegrid.h b/source/libwiigui/gui_gamegrid.h index 0dd739df..86a0cc9b 100644 --- a/source/libwiigui/gui_gamegrid.h +++ b/source/libwiigui/gui_gamegrid.h @@ -45,8 +45,6 @@ class GuiGameGrid: public GuiElement GuiImageData * imgLeft; GuiImageData * imgRight; - GuiSound * btnSoundOver; - GuiSound * btnSoundClick; GuiTrigger * trigA; GuiTrigger * trigL; GuiTrigger * trigR; diff --git a/source/libwiigui/gui_keyboard.cpp b/source/libwiigui/gui_keyboard.cpp index 59ced7df..ce4e80c1 100644 --- a/source/libwiigui/gui_keyboard.cpp +++ b/source/libwiigui/gui_keyboard.cpp @@ -13,6 +13,7 @@ #include "../settings/CSettings.h" #include #include +#include "menu.h" /** * Constructor for the GuiKeyboard class. */ @@ -136,8 +137,6 @@ GuiKeyboard::GuiKeyboard(char * t, u32 max, int min, int lang) keyLarge = new GuiImageData(keyboard_largekey_over_png, keyboard_largekey_over_png_size); keyLargeOver = new GuiImageData(keyboard_largekey_over_png, keyboard_largekey_over_png_size); - keySoundOver = new GuiSound(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - keySoundClick = new GuiSound(button_click_pcm, button_click_pcm_size, Settings.sfxvolume); trigA = new GuiTrigger; trigA->SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); trigB = new GuiTrigger; @@ -161,11 +160,9 @@ GuiKeyboard::GuiKeyboard(char * t, u32 max, int min, int lang) keyBackText = new GuiText("Back", 20, ( GXColor ) { 0, 0, 0, 0xff}); } - //GuiButton(GuiImage* img, GuiImage* imgOver, int hor, int vert, int x, int y, GuiTrigger* trig, GuiSound* sndOver, GuiSound* sndClick, u8 grow); - //keyBack = new GuiButton(keyMedium->GetWidth(), keyMedium->GetHeight()); keyBack = new GuiButton(keyBackImg, keyBackOverImg, 0, 3, 11 * 42 + 40 + eurocheck, 0 * 42 + 120, trigA, - keySoundOver, keySoundClick, 1); + btnSoundOver, btnSoundClick, 1); //keyBack->SetImage(keyBackImg); //keyBack->SetImageOver(keyBackOverImg); keyBack->SetLabel(keyBackText); @@ -366,8 +363,6 @@ GuiKeyboard::~GuiKeyboard() delete keyMediumOver; delete keyLarge; delete keyLargeOver; - delete keySoundOver; - delete keySoundClick; delete trigA; delete trigB; diff --git a/source/libwiigui/gui_numpad.cpp b/source/libwiigui/gui_numpad.cpp index 83dbd9db..a6596376 100644 --- a/source/libwiigui/gui_numpad.cpp +++ b/source/libwiigui/gui_numpad.cpp @@ -1,205 +1,201 @@ -/**************************************************************************** - * USB Loader GX - * - * r-win 2009 - * - * gui_numpad.cpp - * - * GUI class definitions - ***************************************************************************/ - -#include "gui.h" -#include "../main.h" -#include "../settings/CSettings.h" -#include -#include -/** - * Constructor for the GuiNumpad class. - */ - -#define SAFEFREE(p) if(p){free(p);p=NULL;} - -GuiNumpad::GuiNumpad(char * t, u32 max) -{ - width = 400; - height = 370; - selectable = true; - focus = 0; // allow focus - alignmentHor = ALIGN_CENTRE; - alignmentVert = ALIGN_MIDDLE; - kbtextmaxlen = max > sizeof(kbtextstr) ? sizeof(kbtextstr) : max; // limit max up to sizeof(kbtextstr) - // strlcpy(kbtextstr, t, kbtextmaxlen); - strncpy(kbtextstr, t, kbtextmaxlen); // strncpy is needed to fill the rest with \0 - kbtextstr[sizeof(kbtextstr) - 1] = 0; // terminate with \0 - - char thekeys[11] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '\0', '0' }; - memcpy(keys, thekeys, sizeof(thekeys)); - - keyTextbox = new GuiImageData(keyboard_textbox_png, keyboard_textbox_png_size); - keyTextboxImg = new GuiImage(keyTextbox); - keyTextboxImg->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - keyTextboxImg->SetPosition(0, 40);//(0,0); - this->Append(keyTextboxImg); - - kbText = new GuiText(kbtextstr, 20, ( GXColor ) - { 0, 0, 0, 0xff}); - kbText->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - kbText->SetPosition(0, 53);//(0, 13); - kbText->SetPassChar('*'); - this->Append(kbText); - - keyMedium = new GuiImageData(keyboard_mediumkey_over_png, keyboard_mediumkey_over_png_size); - keyMediumOver = new GuiImageData(keyboard_mediumkey_over_png, keyboard_mediumkey_over_png_size); - - keySoundOver = new GuiSound(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - keySoundClick = new GuiSound(button_click_pcm, button_click_pcm_size, Settings.sfxvolume); - 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); - - keyBackImg = new GuiImage(keyMedium); - keyBackOverImg = new GuiImage(keyMediumOver); - keyBackText = new GuiText("Back", 20, ( GXColor ) - { 0, 0, 0, 0xff}); - - keyBack = new GuiButton(keyBackImg, keyBackOverImg, ALIGN_CENTRE, ALIGN_MIDDLE, 90, 80, trigA, keySoundOver, - keySoundClick, 1); - keyBack->SetLabel(keyBackText); - keyBack->SetTrigger(trigB); - this->Append(keyBack); - - keyClearImg = new GuiImage(keyMedium); - keyClearOverImg = new GuiImage(keyMediumOver); - keyClearText = new GuiText("Clear", 20, ( GXColor ) - { 0, 0, 0, 0xff}); - keyClear = new GuiButton(keyClearImg, keyClearOverImg, ALIGN_CENTRE, ALIGN_MIDDLE, -90, 80, trigA, keySoundOver, - keySoundClick, 1); - keyClear->SetLabel(keyClearText); - this->Append(keyClear); - - char txt[2] = { 0, 0 }; - for (int i = 0; i < 11; i++) - { - if (keys[i] != '\0') - { - int col = i % 3; - int row = i / 3; - - keyImg[i] = new GuiImage(keyMedium); - keyImgOver[i] = new GuiImage(keyMediumOver); - txt[0] = keys[i]; - keyTxt[i] = new GuiText(txt, 20, ( GXColor ) - { 0, 0, 0, 0xff}); - keyTxt[i]->SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); - keyTxt[i]->SetPosition(0, -10); - keyBtn[i] = new GuiButton(keyImg[i], keyImgOver[i], ALIGN_CENTRE, ALIGN_MIDDLE, -90 + 90 * col, -70 + 50 - * row, trigA, keySoundOver, keySoundClick, 1); - keyBtn[i]->SetLabel(keyTxt[i]); - - this->Append(keyBtn[i]); - } - } -} - -/** - * Destructor for the GuiKeyboard class. - */ -GuiNumpad::~GuiNumpad() -{ - SAFEFREE( kbText ) - SAFEFREE( keyTextbox ) - SAFEFREE( keyTextboxImg ) - SAFEFREE( keyBackText ) - SAFEFREE( keyBackImg ) - SAFEFREE( keyBackOverImg ) - SAFEFREE( keyBack ) - SAFEFREE( keyClear ) - SAFEFREE( keyClearImg ) - SAFEFREE( keyClearOverImg ) - SAFEFREE( keyClearText ) - SAFEFREE( keyMedium ) - SAFEFREE( keyMediumOver ) - SAFEFREE( keySoundOver ) - SAFEFREE( keySoundClick ) - SAFEFREE( trigA ) - SAFEFREE( trigB ) - - for (int i = 0; i < 11; i++) - { - if (keys[i] != '\0') - { - SAFEFREE( keyImg[i] ) - SAFEFREE( keyImgOver[i] ) - SAFEFREE( keyTxt[i] ) - SAFEFREE( keyBtn[i] ) - } - } -} - -void GuiNumpad::Update(GuiTrigger * t) -{ - LOCK( this ); - if (_elements.size() == 0 || (state == STATE_DISABLED && parentElement)) return; - - for (u8 i = 0; i < _elements.size(); i++) - { - try - { - _elements.at(i)->Update(t); - } - catch (const std::exception& e) - { - } - } - - if (keyBack->GetState() == STATE_CLICKED) - { - if (strlen(kbtextstr) > 0) - { - kbtextstr[strlen(kbtextstr) - 1] = 0; - kbText->SetText(kbtextstr); - } - keyBack->SetState(STATE_SELECTED, t->chan); - } - else if (keyClear->GetState() == STATE_CLICKED) - { - memset(kbtextstr, 0, sizeof(kbtextstr)); - kbText->SetText(kbtextstr); - keyClear->SetState(STATE_SELECTED, t->chan); - } - - char txt[2] = { 0, 0 }; - for (int i = 0; i < 11; i++) - { - if (keys[i] != '\0') - { - if (keyBtn[i]->GetState() == STATE_CLICKED) - { - 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); - } - } - } - - kbText->SetPosition(0, 53); - - this->ToggleFocus(t); - - if (focus) // only send actions to this window if it's in focus - { - // pad/joystick navigation - if (t->Right()) - this->MoveSelectionHor(1); - else if (t->Left()) - this->MoveSelectionHor(-1); - else if (t->Down()) - this->MoveSelectionVert(1); - else if (t->Up()) this->MoveSelectionVert(-1); - } -} +/**************************************************************************** + * USB Loader GX + * + * r-win 2009 + * + * gui_numpad.cpp + * + * GUI class definitions + ***************************************************************************/ + +#include "gui.h" +#include "../main.h" +#include "../settings/CSettings.h" +#include +#include +/** + * Constructor for the GuiNumpad class. + */ + +#define SAFEFREE(p) if(p){free(p);p=NULL;} + +GuiNumpad::GuiNumpad(char * t, u32 max) +{ + width = 400; + height = 370; + selectable = true; + focus = 0; // allow focus + alignmentHor = ALIGN_CENTRE; + alignmentVert = ALIGN_MIDDLE; + kbtextmaxlen = max > sizeof(kbtextstr) ? sizeof(kbtextstr) : max; // limit max up to sizeof(kbtextstr) + // strlcpy(kbtextstr, t, kbtextmaxlen); + strncpy(kbtextstr, t, kbtextmaxlen); // strncpy is needed to fill the rest with \0 + kbtextstr[sizeof(kbtextstr) - 1] = 0; // terminate with \0 + + char thekeys[11] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '\0', '0' }; + memcpy(keys, thekeys, sizeof(thekeys)); + + keyTextbox = new GuiImageData(keyboard_textbox_png, keyboard_textbox_png_size); + keyTextboxImg = new GuiImage(keyTextbox); + keyTextboxImg->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + keyTextboxImg->SetPosition(0, 40);//(0,0); + this->Append(keyTextboxImg); + + kbText = new GuiText(kbtextstr, 20, ( GXColor ) + { 0, 0, 0, 0xff}); + kbText->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + kbText->SetPosition(0, 53);//(0, 13); + kbText->SetPassChar('*'); + this->Append(kbText); + + keyMedium = new GuiImageData(keyboard_mediumkey_over_png, keyboard_mediumkey_over_png_size); + keyMediumOver = new GuiImageData(keyboard_mediumkey_over_png, keyboard_mediumkey_over_png_size); + + 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); + + keyBackImg = new GuiImage(keyMedium); + keyBackOverImg = new GuiImage(keyMediumOver); + keyBackText = new GuiText("Back", 20, ( GXColor ) + { 0, 0, 0, 0xff}); + + keyBack = new GuiButton(keyBackImg, keyBackOverImg, ALIGN_CENTRE, ALIGN_MIDDLE, 90, 80, trigA, keySoundOver, + keySoundClick, 1); + keyBack->SetLabel(keyBackText); + keyBack->SetTrigger(trigB); + this->Append(keyBack); + + keyClearImg = new GuiImage(keyMedium); + keyClearOverImg = new GuiImage(keyMediumOver); + keyClearText = new GuiText("Clear", 20, ( GXColor ) + { 0, 0, 0, 0xff}); + keyClear = new GuiButton(keyClearImg, keyClearOverImg, ALIGN_CENTRE, ALIGN_MIDDLE, -90, 80, trigA, keySoundOver, + keySoundClick, 1); + keyClear->SetLabel(keyClearText); + this->Append(keyClear); + + char txt[2] = { 0, 0 }; + for (int i = 0; i < 11; i++) + { + if (keys[i] != '\0') + { + int col = i % 3; + int row = i / 3; + + keyImg[i] = new GuiImage(keyMedium); + keyImgOver[i] = new GuiImage(keyMediumOver); + txt[0] = keys[i]; + keyTxt[i] = new GuiText(txt, 20, ( GXColor ) + { 0, 0, 0, 0xff}); + keyTxt[i]->SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + keyTxt[i]->SetPosition(0, -10); + keyBtn[i] = new GuiButton(keyImg[i], keyImgOver[i], ALIGN_CENTRE, ALIGN_MIDDLE, -90 + 90 * col, -70 + 50 + * row, trigA, keySoundOver, keySoundClick, 1); + keyBtn[i]->SetLabel(keyTxt[i]); + + this->Append(keyBtn[i]); + } + } +} + +/** + * Destructor for the GuiKeyboard class. + */ +GuiNumpad::~GuiNumpad() +{ + SAFEFREE( kbText ) + SAFEFREE( keyTextbox ) + SAFEFREE( keyTextboxImg ) + SAFEFREE( keyBackText ) + SAFEFREE( keyBackImg ) + SAFEFREE( keyBackOverImg ) + SAFEFREE( keyBack ) + SAFEFREE( keyClear ) + SAFEFREE( keyClearImg ) + SAFEFREE( keyClearOverImg ) + SAFEFREE( keyClearText ) + SAFEFREE( keyMedium ) + SAFEFREE( keyMediumOver ) + SAFEFREE( trigA ) + SAFEFREE( trigB ) + + for (int i = 0; i < 11; i++) + { + if (keys[i] != '\0') + { + SAFEFREE( keyImg[i] ) + SAFEFREE( keyImgOver[i] ) + SAFEFREE( keyTxt[i] ) + SAFEFREE( keyBtn[i] ) + } + } +} + +void GuiNumpad::Update(GuiTrigger * t) +{ + LOCK( this ); + if (_elements.size() == 0 || (state == STATE_DISABLED && parentElement)) return; + + for (u8 i = 0; i < _elements.size(); i++) + { + try + { + _elements.at(i)->Update(t); + } + catch (const std::exception& e) + { + } + } + + if (keyBack->GetState() == STATE_CLICKED) + { + if (strlen(kbtextstr) > 0) + { + kbtextstr[strlen(kbtextstr) - 1] = 0; + kbText->SetText(kbtextstr); + } + keyBack->SetState(STATE_SELECTED, t->chan); + } + else if (keyClear->GetState() == STATE_CLICKED) + { + memset(kbtextstr, 0, sizeof(kbtextstr)); + kbText->SetText(kbtextstr); + keyClear->SetState(STATE_SELECTED, t->chan); + } + + char txt[2] = { 0, 0 }; + for (int i = 0; i < 11; i++) + { + if (keys[i] != '\0') + { + if (keyBtn[i]->GetState() == STATE_CLICKED) + { + 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); + } + } + } + + kbText->SetPosition(0, 53); + + this->ToggleFocus(t); + + if (focus) // only send actions to this window if it's in focus + { + // pad/joystick navigation + if (t->Right()) + this->MoveSelectionHor(1); + else if (t->Left()) + this->MoveSelectionHor(-1); + else if (t->Down()) + this->MoveSelectionVert(1); + else if (t->Up()) this->MoveSelectionVert(-1); + } +} diff --git a/source/libwiigui/gui_optionbrowser.cpp b/source/libwiigui/gui_optionbrowser.cpp index 0f5dbea3..32086efb 100644 --- a/source/libwiigui/gui_optionbrowser.cpp +++ b/source/libwiigui/gui_optionbrowser.cpp @@ -38,8 +38,6 @@ GuiOptionBrowser::GuiOptionBrowser(int w, int h, OptionList * l, const char *ima trigHeldA = new GuiTrigger; trigHeldA->SetHeldTrigger(-1, WPAD_BUTTON_A, PAD_BUTTON_A); - btnSoundClick = new GuiSound(button_click_pcm, button_click_pcm_size, Settings.sfxvolume); - bgOptions = Resources::GetImageData(imagebg); bgOptionsImg = new GuiImage(bgOptions); bgOptionsImg->SetParent(this); @@ -152,7 +150,6 @@ GuiOptionBrowser::GuiOptionBrowser(int w, int h, OptionList * l, const char *ima trigA->SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); trigHeldA = new GuiTrigger; trigHeldA->SetHeldTrigger(-1, WPAD_BUTTON_A, PAD_BUTTON_A); - btnSoundClick = new GuiSound(button_click_pcm, button_click_pcm_size, Settings.sfxvolume); bgOptions = Resources::GetImageData(imagebg); @@ -276,7 +273,6 @@ GuiOptionBrowser::~GuiOptionBrowser() loaded = 0; delete trigA; - delete btnSoundClick; // delete optionBg; for (int i = 0; i < PAGESIZE; i++) diff --git a/source/libwiigui/gui_searchbar.cpp b/source/libwiigui/gui_searchbar.cpp index f05afafb..0f17ac26 100644 --- a/source/libwiigui/gui_searchbar.cpp +++ b/source/libwiigui/gui_searchbar.cpp @@ -31,9 +31,7 @@ class cSearchButton GuiSearchBar::GuiSearchBar(const wchar_t *SearchChars) : inSide(0), text((char *) NULL, 22, ( GXColor ) - { 0, 0, 0, 255}), buttons(0), keyImageData(keyboard_key_png, keyboard_key_png_size), keyOverImageData(keyboard_key_over_png, keyboard_key_over_png_size), sndOver( - button_over_pcm, button_over_pcm_size, Settings.sfxvolume), sndClick(button_click_pcm, - button_click_pcm_size, Settings.sfxvolume) + { 0, 0, 0, 255}), buttons(0), keyImageData(keyboard_key_png, keyboard_key_png_size), keyOverImageData(keyboard_key_over_png, keyboard_key_over_png_size) { trig.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); @@ -57,7 +55,7 @@ GuiSearchBar::GuiSearchBar(const wchar_t *SearchChars) : if (x == 0) y++; charstr[0] = SearchChars[i]; buttons[i] = new cSearchButton(charstr, &keyImageData, &keyOverImageData, x_start + x * 42, y_start - 42 + y - * 42, &trig, &sndOver, &sndClick); + * 42, &trig, btnSoundOver, btnSoundClick); this->Append(&(buttons[i]->button)); } height = 10 + 42 + y * 42 + 10; @@ -73,16 +71,14 @@ GuiSearchBar::GuiSearchBar(const wchar_t *SearchChars) : BacspaceBtnImg_Over = new GuiImage(imgBacspaceBtn); BacspaceBtnImg = new GuiImage(BacspaceBtnImg_Over); BacspaceBtnImg->SetGrayscale(); - BacspaceBtn = new GuiButton(BacspaceBtnImg, BacspaceBtnImg_Over, ALIGN_RIGHT, ALIGN_TOP, -52, 10, &trig, &sndOver, - &sndClick, 1); + BacspaceBtn = new GuiButton(BacspaceBtnImg, BacspaceBtnImg_Over, ALIGN_RIGHT, ALIGN_TOP, -52, 10, &trig, btnSoundOver, btnSoundClick, 1); this->Append(BacspaceBtn); imgClearBtn = Resources::GetImageData("keyboard_clear_over.png"); ClearBtnImg_Over = new GuiImage(imgClearBtn); ClearBtnImg = new GuiImage(ClearBtnImg_Over); ClearBtnImg->SetGrayscale(); - ClearBtn = new GuiButton(ClearBtnImg, ClearBtnImg_Over, ALIGN_RIGHT, ALIGN_TOP, -10, 10, &trig, &sndOver, - &sndClick, 1); + ClearBtn = new GuiButton(ClearBtnImg, ClearBtnImg_Over, ALIGN_RIGHT, ALIGN_TOP, -10, 10, &trig, btnSoundOver, btnSoundClick, 1); this->Append(ClearBtn); // SetPosition(100,100); diff --git a/source/libwiigui/gui_searchbar.h b/source/libwiigui/gui_searchbar.h index f3cd7d01..23bbb82e 100644 --- a/source/libwiigui/gui_searchbar.h +++ b/source/libwiigui/gui_searchbar.h @@ -30,7 +30,5 @@ class GuiSearchBar: public GuiWindow GuiImageData keyImageData; GuiImageData keyOverImageData; GuiTrigger trig; - GuiSound sndOver; - GuiSound sndClick; }; diff --git a/source/libwiigui/gui_sound.cpp b/source/libwiigui/gui_sound.cpp deleted file mode 100644 index 2442fed2..00000000 --- a/source/libwiigui/gui_sound.cpp +++ /dev/null @@ -1,446 +0,0 @@ -/**************************************************************************** - * libwiigui - * - * Tantric 2009 - * - * gui_sound.cpp - * - * decoder modification by ardi 2009 - * - * GUI class definitions - ***************************************************************************/ - -#include "gui.h" -#include -#include "gecko.h" - -#include "gui_sound_decoder.h" - -#define BUFFER_SIZE 8192 - -/*************************************************************** - * - * D E C O D E R – L I S T - * - * - ***************************************************************/ - -GuiSoundDecoder::DecoderListEntry *GuiSoundDecoder::DecoderList = NULL; -GuiSoundDecoder::DecoderListEntry &GuiSoundDecoder::RegisterDecoder(DecoderListEntry &Decoder, - GuiSoundDecoderCreate fnc) -{ - if (Decoder.fnc != fnc) - { - Decoder.fnc = fnc; - Decoder.next = DecoderList; - DecoderList = &Decoder; - } - return Decoder; -} -GuiSoundDecoder *GuiSoundDecoder::GetDecoder(const u8 * snd, u32 len, bool snd_is_allocated) -{ - for (DecoderListEntry *de = DecoderList; de; de = de->next) - { - GuiSoundDecoder *d = NULL; - try - { - d = de->fnc(snd, len, snd_is_allocated); - } - catch (const char *error) - { - gprintf("%s", error); - } - catch (...) - { - } - if (d) return d; - } - return NULL; -} - -/*************************************************************** - * - * D E C O D E R – T H R E A D - * - * - ***************************************************************/ - -static GuiSound *GuiSoundPlayer[16] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL }; - -static lwp_t GuiSoundDecoderThreadHandle = LWP_THREAD_NULL; -static bool GuiSoundDecoderThreadRunning = false; -static bool GuiSoundDecoderDataRquested = false; - -void *GuiSoundDecoderThread(void *args) -{ - GuiSoundDecoderThreadRunning = true; - do - { - if (GuiSoundDecoderDataRquested) - { - GuiSoundDecoderDataRquested = false; - GuiSound **players = GuiSoundPlayer; - for (int i = 0; i < 16; ++i, ++players) - { - GuiSound *player = *players; - if (player) player->DecoderCallback(); - } - } - if (!GuiSoundDecoderDataRquested) usleep(50); - } while (GuiSoundDecoderThreadRunning); - return 0; -} - -/*************************************************************** - * - * A S N D – C A L L B A C K - * - * - ***************************************************************/ - -void GuiSoundPlayerCallback(s32 Voice) -{ - if (Voice >= 0 && Voice < 16 && GuiSoundPlayer[Voice]) - { - GuiSoundPlayer[Voice]->PlayerCallback(); - GuiSoundDecoderDataRquested = true; - } -} - -/*************************************************************** - * - * R A W - D E C O D E R - * Decoder for Raw-PCM-Datas (16bit Stereo 48kHz) - * - ***************************************************************/ -class GuiSoundDecoderRAW: public GuiSoundDecoder -{ - protected: - GuiSoundDecoderRAW(const u8 * snd, u32 len, bool snd_is_allocated) - { - pcm_start = snd; - is_allocated = snd_is_allocated; - pcm_end = pcm_start + len; - pos = pcm_start; - is_running = false; - - } - public: - ~GuiSoundDecoderRAW() - { - while (is_running) - usleep(50); - if (is_allocated) delete[] pcm_start; - } - static GuiSoundDecoder *Create(const u8 * snd, u32 len, bool snd_is_allocated) - { - try - { - return new GuiSoundDecoderRAW(snd, len, snd_is_allocated); - } - catch (...) - { - } - return NULL; - } - s32 GetFormat() - { - return VOICE_STEREO_16BIT; - } - s32 GetSampleRate() - { - return 48000; - } - /* Read reads data from stream to buffer - return: >0 = readed bytes; - 0 = EOF; - <0 = Error; - */ - int Read(u8 * buffer, int buffer_size) - { - if (pos >= pcm_end) return 0; // EOF - - is_running = true; - if (pos + buffer_size > pcm_end) buffer_size = pcm_end - pos; - memcpy(buffer, pos, buffer_size); - pos += buffer_size; - is_running = false; - return buffer_size; - } - int Rewind() - { - pos = pcm_start; - return 0; - } - private: - const u8 *pcm_start; - const u8 *pcm_end; - bool is_allocated; - const u8 *pos; - bool is_running; - -}; - -/*************************************************************** - * - * G u i S o u n d - * - * - ***************************************************************/ -#define GuiSoundBufferReady 0x01 -#define GuiSoundBufferEOF 0x02 -#define GuiSoundFinish 0x04 -static int GuiSoundCount = 0; -/** - * Constructor for the GuiSound class. - */ -GuiSound::GuiSound(const u8 *s, int l, int v/*=100*/, bool r/*=true*/, bool a/*=false*/) -{ - if (GuiSoundCount++ == 0 || GuiSoundDecoderThreadHandle == LWP_THREAD_NULL) - { - LWP_CreateThread(&GuiSoundDecoderThreadHandle, GuiSoundDecoderThread, NULL, NULL, 32768, 80); - } - voice = -1; - play_buffer[0] = (u8*) memalign(32, BUFFER_SIZE * 3); // tripple-buffer first is played - play_buffer[1] = play_buffer[0] + BUFFER_SIZE; // second is waiting - play_buffer[2] = play_buffer[1] + BUFFER_SIZE; // third is decoding - buffer_nr = 0; // current playbuffer - buffer_pos = 0; // current idx to write in buffer - buffer_ready = false; - buffer_eof = false; - loop = false; // play looped - volume = v; // volume - decoder = NULL; - if (play_buffer[0]) // playbuffer ok - Load(s, l, r, a); -} -bool GuiSound::Load(const u8 *s, int l, bool r/*=false*/, bool a/*=false*/) -{ - Stop(); - if (!play_buffer[0]) return false; - GuiSoundDecoder *newDecoder = GuiSoundDecoder::GetDecoder(s, l, a); - if (!newDecoder && r) newDecoder = GuiSoundDecoderRAW::Create(s, l, a); - if (newDecoder) - { - delete decoder; - decoder = newDecoder; - return true; - } - else if (a) delete[] s; - return false; -} -GuiSound::GuiSound(const char *p, int v/*=100*/) -{ - if (GuiSoundCount++ == 0 || GuiSoundDecoderThreadHandle == LWP_THREAD_NULL) - { - LWP_CreateThread(&GuiSoundDecoderThreadHandle, GuiSoundDecoderThread, NULL, NULL, 32 * 1024, 80); - } - voice = -1; - play_buffer[0] = (u8*) memalign(32, BUFFER_SIZE * 3); // tripple-buffer first is played - play_buffer[1] = play_buffer[0] + BUFFER_SIZE; // second is waiting - play_buffer[2] = play_buffer[1] + BUFFER_SIZE; // third is decoding - buffer_nr = 0; // current playbuffer - buffer_pos = 0; // current idx to write in buffer - buffer_ready = false; - buffer_eof = false; - loop = false; // play looped - volume = v; // volume - decoder = NULL; - if (play_buffer[0]) // playbuffer ok - Load(p); -} -bool GuiSound::Load(const char *p) -{ - Stop(); // stop playing - if (!play_buffer[0]) return false; - - bool ret = false; - voice = -2; // -2 marks loading from file - u32 filesize = 0; - u8 *buffer = NULL; - size_t result; - - FILE * pFile = fopen(p, "rb"); - if (pFile) - { - // get file size: - fseek(pFile, 0, SEEK_END); - filesize = ftell(pFile); - fseek(pFile, 0, SEEK_SET); - - // allocate memory to contain the whole file: - buffer = new (std::nothrow) u8[filesize]; - if (buffer) - { - // copy the file into the buffer: - result = fread(buffer, 1, filesize, pFile); - if (result == filesize) - ret = Load(buffer, filesize, false, true); - else delete[] buffer; - } - fclose(pFile); - } - return ret; -} - -/** - * Destructor for the GuiSound class. - */ -GuiSound::~GuiSound() -{ - if (!loop) while (voice >= 0) - usleep(50); - Stop(); - if (--GuiSoundCount == 0 && GuiSoundDecoderThreadHandle != LWP_THREAD_NULL) - { - GuiSoundDecoderThreadRunning = false; - LWP_JoinThread(GuiSoundDecoderThreadHandle, NULL); - GuiSoundDecoderThreadHandle = LWP_THREAD_NULL; - } - delete decoder; - free(play_buffer[0]); -} - -void GuiSound::Play() -{ - Stop(); // stop playing if it played - if (!play_buffer[0]) return; - if (!decoder) return; // no decoder or no play_buffer -> no playing - // initialize the buffer - buffer_nr = 0; // allways starts with buffer 0 - buffer_pos = 0; // reset position - buffer_ready = false; - buffer_eof = false; - decoder->Rewind(); // play from begin - DecoderCallback(); // fill first buffer; - if (!buffer_ready || buffer_eof) // if first buffer not ready -> no play - return; - voice = ASND_GetFirstUnusedVoice(); - if (voice >= 0) - { - s32 vol = (255 * volume) / 100; - s32 format = decoder->GetFormat(); - s32 samplerate = decoder->GetSampleRate(); - s32 first_pos = buffer_pos; - // switch to next buffer - buffer_nr = 1; - buffer_pos = 0; - buffer_ready = false; - buffer_eof = false; - DecoderCallback(); // fill second buffer; - GuiSoundPlayer[voice] = this; // activate Callbacks for this voice - // Play the voice - ASND_SetVoice(voice, format, samplerate, 0, play_buffer[0], first_pos, vol, vol, GuiSoundPlayerCallback); - } -} -/* - int GuiSound::PlayOggFile(char * path) - { - if(Load(path)) - Play(); - return 1; - } - */ -void GuiSound::Stop() -{ - if (voice < 0) return; - GuiSoundPlayer[voice] = NULL; // disable Callbacks - SND_StopVoice(voice); - voice = -1; -} - -void GuiSound::Pause() -{ - if (voice < 0) return; - ASND_PauseVoice(voice, 1); -} - -void GuiSound::Resume() -{ - if (voice < 0) return; - ASND_PauseVoice(voice, 0); -} - -bool GuiSound::IsPlaying() -{ - return voice >= 0; -} - -void GuiSound::SetVolume(int vol) -{ - volume = vol; - if (voice < 0) return; - int newvol = 255 * (volume / 100.0); - ASND_ChangeVolumeVoice(voice, newvol, newvol); -} - -void GuiSound::SetLoop(bool l) -{ - loop = l; -} - -void GuiSound::DecoderCallback() -{ - if (buffer_ready || buffer_eof) // if buffer ready or EOF -> nothing - return; - bool error = false; - while (buffer_pos < BUFFER_SIZE) - { - int ret = decoder->Read(&play_buffer[buffer_nr][buffer_pos], BUFFER_SIZE - buffer_pos); - if (ret > 0) - buffer_pos += ret; // ok -> fill the buffer more - else if (ret == 0) // EOF from decoder - { - if (loop) - decoder->Rewind(); // if loop -> rewind and fill the buffer more - else if (buffer_pos) - break; // has data in buffer -> play the buffer - else buffer_eof = true; // no data in buffer -> return EOF - return; - } - else if (ret < 0) // an ERROR - { - if (buffer_pos) - break; // has data in buffer -> play the buffer - else if (loop) - { - if (!error) // if no prev error - { - decoder->Rewind(); // if loop -> rewind - error = true; // set error-state - continue; // and fill the buffer more - } - buffer_eof = true; // has prev error -> error in first block -> return EOF - return; - } - else - { - buffer_eof = true; // no loop -> return EOF - return; - } - } - error = false; // clear error-state - } - buffer_ready = true; -} -void GuiSound::PlayerCallback() -{ - if (buffer_eof) // if EOF - { - if (ASND_TestPointer(voice, play_buffer[(buffer_nr + 2) % 3]) == 0) // test prev. Buffer - Stop(); - } - else if (buffer_ready) // if buffer ready - { - if (ASND_AddVoice(voice, play_buffer[buffer_nr], buffer_pos) == SND_OK) // add buffer - { - // next buffer - buffer_nr = (buffer_nr + 1) % 3; - buffer_pos = 0; - buffer_ready = false; - buffer_eof = false; - } - } -} - diff --git a/source/libwiigui/gui_sound_decoder.h b/source/libwiigui/gui_sound_decoder.h deleted file mode 100644 index fd81babd..00000000 --- a/source/libwiigui/gui_sound_decoder.h +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************** - * libwiigui - * - * Tantric 2009 - * - * gui_sound_decoder.h - * - * by ardi 2009 - * - * GUI class definitions - ***************************************************************************/ - -#ifndef GUI_SOUND_DECODER_H -#define GUI_SOUND_DECODER_H - -#include - -#define REGISTER_GUI_SOUND_DECODER(decoder) GuiSoundDecoder::DecoderListEntry decoder##_l = GuiSoundDecoder::RegisterDecoder(decoder##_l, decoder::Create) -class GuiSoundDecoder; -typedef GuiSoundDecoder *(*GuiSoundDecoderCreate)(const u8 * snd, u32 len, bool snd_is_allocated); - -class GuiSoundDecoder -{ - protected: - GuiSoundDecoder() - { - } - ; // Constructors must protected so it can create only with Init(...); - public: - virtual ~GuiSoundDecoder() - { - } - ; - // begin API - // --------- - // each Decoder must have an own static Create(...) fnc - // static GuiSoundDecoder *Create(const u8 * snd, u32 len, bool snd_is_allocated); - virtual s32 GetFormat() = 0; - virtual s32 GetSampleRate() = 0; - /* Read reads data from stream to buffer - return: >0 = readed bytes; - 0 = EOF; - <0 = Error; - */ - virtual int Read(u8 * buffer, int buffer_size) = 0; - // set the stream to the start - virtual int Rewind() = 0; - // ------- - // end API - - - struct DecoderListEntry - { - GuiSoundDecoderCreate fnc; - DecoderListEntry *next; - }; - static DecoderListEntry &RegisterDecoder(DecoderListEntry &Decoder, GuiSoundDecoderCreate fnc); - static GuiSoundDecoder *GetDecoder(const u8 * snd, u32 len, bool snd_is_allocated); - private: - static DecoderListEntry *DecoderList; - GuiSoundDecoder(GuiSoundDecoder&); // no copy -}; - -#define BIG_ENDIAN_HOST 1 // Wii PPC is a Big-Endian-Host -#if BIG_ENDIAN_HOST - -inline uint16_t be16(const uint8_t *p8) -{ - return *((uint16_t*) p8); -} -inline uint32_t be32(const uint8_t *p8) -{ - return *((uint32_t*) p8); -} -inline uint16_t le16(const uint8_t *p8) -{ - uint16_t ret = p8[1] << 8 | p8[0]; - return ret; -} -inline uint32_t le32(const uint8_t *p8) -{ - uint32_t ret = p8[3] << 24 | p8[2] << 16 | p8[1] << 8 | p8[0]; - return ret; -} - -#elif LITTLE_ENDIAN_HOST -inline uint16_t be16( const uint8_t *p8 ) -{ - uint16_t ret = p8[0] << 8 | p8[1]; - return ret; -} -inline uint32_t be32( const uint8_t *p8 ) -{ - uint32_t ret = p8[0] << 24 | p8[1] << 16 | p8[2] << 8 | p8[3]; - return ret; -} -inline uint16_t le16( const uint8_t *p8 ) -{ - return *( ( uint16_t* )p8 ); -} -inline uint32_t le32( const uint8_t *p8 ) -{ - return *( ( uint32_t* )p8 ); -} -#else -#error "BIG_ENDIAN_HOST or LITTLE_ENDIAN_HOST not setted" -#endif /* XXX_ENDIAN_HOST */ - -#define be16inc(p8) (p8+=2, be16(p8-2)) -#define le16inc(p8) (p8+=2, le16(p8-2)) -#define be32inc(p8) (p8+=4, be32(p8-4)) -#define le32inc(p8) (p8+=4, le32(p8-4)) - -#endif /* GUI_SOUND_DECODER_H */ diff --git a/source/libwiigui/gui_sound_decoder_aiff.cpp b/source/libwiigui/gui_sound_decoder_aiff.cpp deleted file mode 100644 index 1811f7c8..00000000 --- a/source/libwiigui/gui_sound_decoder_aiff.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/**************************************************************************** - * libwiigui - * - * ardi 2009 - * - * gui_sound_plugin_aif.cpp - * - * GUI class definitions - ***************************************************************************/ - -#include -#include -#include -#include -#include - -#include "gui_sound_decoder.h" - -// ------ -// Copyright (C) 1988-1991 Apple Computer, Inc. -#ifndef HUGE_VAL -# define HUGE_VAL HUGE -#endif - -# define UnsignedToFloat(u) (((double)((long)(u - 2147483647L - 1))) + 2147483648.0) - -static double ConvertFromIeeeExtended(const u8* bytes) -{ - double f; - int expon; - u32 hiMant, loMant; - - expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); - hiMant = ((unsigned long) (bytes[2] & 0xFF) << 24) | ((unsigned long) (bytes[3] & 0xFF) << 16) - | ((unsigned long) (bytes[4] & 0xFF) << 8) | ((unsigned long) (bytes[5] & 0xFF)); - loMant = ((unsigned long) (bytes[6] & 0xFF) << 24) | ((unsigned long) (bytes[7] & 0xFF) << 16) - | ((unsigned long) (bytes[8] & 0xFF) << 8) | ((unsigned long) (bytes[9] & 0xFF)); - - if (expon == 0 && hiMant == 0 && loMant == 0) - f = 0; - else - { - if (expon == 0x7FFF) - f = HUGE_VAL; - else - { - expon -= 16383; - f = ldexp(UnsignedToFloat( hiMant ), expon -= 31); - f += ldexp(UnsignedToFloat( loMant ), expon -= 32); - } - } - - if (bytes[0] & 0x80) - return -f; - else return f; -} -// ------ - -class GuiSoundDecoderAIFF: public GuiSoundDecoder -{ - protected: - GuiSoundDecoderAIFF(const u8 * snd, u32 len, bool snd_is_allocated) - { - sound = snd; - length = len; - is_allocated = snd_is_allocated; - is_running = false; - - const u8 *in_ptr = sound; - - if (be32inc( in_ptr ) != 0x464F524D /*'FORM'*/) throw("No FORM chunk"); - if (be32inc( in_ptr ) + 8 != len) throw("wrong Size"); - if (be32inc( in_ptr ) != 0x41494646 /*'AIFF'*/) throw("No AIFF chunk"); - - while (in_ptr + 8 < sound + len) - { - u32 chunk_id = be32inc( in_ptr ); - u32 chunk_size = be32inc( in_ptr ); - const u8 *chunk_start = in_ptr; - switch (chunk_id) - { - case 0x434F4D4D /*'COMM'*/: - channelCount = be16inc( in_ptr ); - in_ptr += 4; // skip numSampleFrames - bytePerSample = (be16inc( in_ptr ) + 7) / 8; - if (bytePerSample < 1 && bytePerSample > 2) throw("wrong bits per Sample"); - sampleRate = ConvertFromIeeeExtended(in_ptr); - break; - case 0x53534E44 /*'SSND'*/: - pcm_start = in_ptr + 8; - pcm_end = chunk_start + chunk_size; - break; - } - in_ptr = chunk_start + chunk_size; - } - currentPos = pcm_start; - } - public: - ~GuiSoundDecoderAIFF() - { - while (is_running) - usleep(50); - if (is_allocated) delete[] sound; - } - static GuiSoundDecoder *Create(const u8 * snd, u32 len, bool snd_is_allocated) - { - if (snd && len > 12 && snd[0] == 'F' && snd[1] == 'O' && snd[2] == 'R' && snd[3] == 'M' && snd[8] == 'A' - && snd[9] == 'I' && snd[10] == 'F' && snd[11] == 'F') return new GuiSoundDecoderAIFF(snd, len, - snd_is_allocated); - return NULL; - } - s32 GetFormat() - { - if (bytePerSample == 2) - return channelCount == 2 ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT; - else return channelCount == 2 ? VOICE_STEREO_8BIT : VOICE_MONO_8BIT; - } - s32 GetSampleRate() - { - return sampleRate; - } - /* Read reads data from stream to buffer - return: >0 = readed bytes; - 0 = EOF; - <0 = Error; - */ - int Read(u8 * buffer, int buffer_size) - { - if (currentPos >= pcm_end) return 0; // EOF - - is_running = true; - if (currentPos + buffer_size > pcm_end) buffer_size = pcm_end - currentPos; - memcpy(buffer, currentPos, buffer_size); - currentPos += buffer_size; - is_running = false; - return buffer_size; - } - int Rewind() - { - while (is_running) - usleep(50); - currentPos = pcm_start; - return 0; - } - private: - const u8 *sound; - u32 length; - bool is_allocated; - bool is_running; - - u32 sampleRate; - u16 channelCount; - u16 bytePerSample; - const u8 *pcm_start; - const u8 *pcm_end; - const u8 *currentPos; -}; -REGISTER_GUI_SOUND_DECODER( GuiSoundDecoderAIFF ); diff --git a/source/libwiigui/gui_sound_decoder_bns.cpp b/source/libwiigui/gui_sound_decoder_bns.cpp deleted file mode 100644 index ac7f872d..00000000 --- a/source/libwiigui/gui_sound_decoder_bns.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/**************************************************************************** - * libwiigui - * - * Tantric 2009 - * - * gui_sound_plugin_bns.cpp - * - * by ardi 2009 - * - * Decoder for Wii bns-sound - * - * GUI class definitions - ***************************************************************************/ - -#include -#include - -#include "gui_sound_decoder.h" -#define BIG_ENDIAN_HOST 1 - -class chanel_t -{ - public: - void Reset() - { - currentPos = startPos; - hist1 = hist2 = 0; - } - int DecodeNextBlock() - { - int Offset = 0; - if (currentPos == loopStart) - { - loop_hist1 = hist1; - loop_hist2 = hist2; - } - if (loop && currentPos >= endPos) - { - currentPos = loopStart; - hist1 = loop_hist1; - hist2 = loop_hist2; - Offset = loopOffset; - - } - - if (currentPos + 8 <= endPos) - { - u16 index = (*currentPos >> 4) & 0x07; - s32 scale = 1 << (*currentPos++ & 0x0F); - for (int i = 0; i < 14; i += 2) - { - nibbles[i] = ((s8) *currentPos) >> 4; - nibbles[i + 1] = ((s8) ((*currentPos++) << 4)) >> 4; - } - for (int i = 0; i < 14; ++i) - { - s32 sample = (scale * nibbles[i]) << 11; - sample += coEfficients[index * 2] * hist1; - sample += coEfficients[index * 2 + 1] * hist2; - sample += 1024; - sample = sample >> 11; - if (sample > 32767) - sample = 32767; - else if (sample < -32768) sample = -32768; - pcm[i] = sample; - - hist2 = hist1; - hist1 = sample; - } - return Offset; - } - return -1; - } - - const u8* startPos; - const u8* endPos; - const u8* currentPos; - s16 coEfficients[16]; - s16 nibbles[14]; - s16 pcm[14]; - s16 hist1; - s16 hist2; - bool loop; - const u8* loopStart; - u16 loopOffset; - s16 loop_hist1; - s16 loop_hist2; -}; - -class GuiSoundDecoderBNS: public GuiSoundDecoder -{ - protected: - GuiSoundDecoderBNS(const u8 * snd, u32 len, bool snd_is_allocated) - { - sound = snd; - is_running = false; - is_allocated = snd_is_allocated; - - const u8 *in_ptr = sound; - - ///////////////// - // READ HEADER // - ///////////////// - if (be32inc( in_ptr ) != 0x424E5320 /*'BNS '*/) throw("Not a BNS"); - - in_ptr += 4; // skip 4 byte - - u32 bnssize = be32inc( in_ptr ); - if (bnssize != len) throw("Wrong size"); - - in_ptr += 4; // skip unknown1 - - const u8* infoStart = sound + be32inc( in_ptr ); - in_ptr += 4; // skip const u8* infoEnd = infoStart + be32inc(in_ptr); - - channel[0].startPos = sound + be32inc( in_ptr ) + 8; - channel[0].endPos = channel[0].startPos + be32inc( in_ptr ) - 8; - - /////////////// - // READ INFO // - /////////////// - in_ptr = infoStart + 8; // skip 'INFO' and Infosize - - in_ptr++; // skip u8 codeType = *in_ptr++; - - channel[0].loop = channel[1].loop = *in_ptr++; // u8 loopFlag; - - channelCount = *in_ptr++; - - in_ptr++; // skip unknown byte - - sampleRate = be16inc( in_ptr ); - - in_ptr += 2; // skip unknown word - - u32 loopStart = be32inc( in_ptr ); - channel[0].loopStart = channel[0].startPos + ((loopStart / 14) * 8);//LoopPos to BlockStart - channel[1].loopStart = channel[1].startPos + ((loopStart / 14) * 8); - channel[0].loopOffset = channel[1].loopOffset = loopStart % 14; - - in_ptr += 4; // skip u32 SampleCount = be32inc(in_ptr); - - in_ptr += 24; // skip unknown Bytes - - if (channelCount == 2) - { - in_ptr += 4; // skip unknown long - u32 ChannelSplit = be32inc( in_ptr ); - - in_ptr += 8; // skip 2x unknown long - - channel[1].endPos = channel[0].endPos; - channel[0].endPos = channel[1].startPos = channel[0].startPos + ChannelSplit; - - channel[1].loopStart = channel[1].startPos + (channel[0].loopStart - channel[0].startPos); - } - for (int a = 0; a < 16; a++) - { - channel[0].coEfficients[a] = (s16) be16inc( in_ptr ); - } - if (channelCount == 2) - { - in_ptr += 16; // skip 16 byte - for (int a = 0; a < 16; a++) - { - channel[1].coEfficients[a] = (s16) be16inc( in_ptr ); - } - } - channel[0].Reset(); - channel[1].Reset(); - currentBlockPos = 14; - } - public: - ~GuiSoundDecoderBNS() - { - while (is_running) - usleep(50); - if (is_allocated) delete[] sound; - } - static GuiSoundDecoder *Create(const u8 * snd, u32 len, bool snd_is_allocated) - { - if (snd && len > 4 && snd[0] == 'B' && snd[1] == 'N' && snd[2] == 'S' && snd[3] == ' ') return new GuiSoundDecoderBNS( - snd, len, snd_is_allocated); - return NULL; - } - s32 GetFormat() - { - return channelCount == 2 ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT; - } - s32 GetSampleRate() - { - return sampleRate; - } - /* Read reads data from stream to buffer - return: >0 = readed bytes; - 0 = EOF; - <0 = Error; - */ - int Read(u8 * buffer, int buffer_size) - { - is_running = true; - u8 *write_pos = buffer; - u8 *write_end = buffer + buffer_size; - - for (;;) - { - if (currentBlockPos >= 14) - { - int Offset = channel[0].DecodeNextBlock(); - if (Offset < 0 || (channelCount == 2 && channel[1].DecodeNextBlock() < 0)) - { - is_running = false; - return write_pos - buffer; - } - currentBlockPos = Offset; - } - for (; currentBlockPos < 14; ++currentBlockPos) - { - if (write_pos >= write_end) - { - is_running = false; - return write_pos - buffer; - } - *((s16*) write_pos) = channel[0].pcm[currentBlockPos]; - write_pos += 2; - if (channelCount == 2) // stereo - { - *((s16*) write_pos) = channel[1].pcm[currentBlockPos]; - write_pos += 2; - } - } - } - is_running = false; - return 0; - } - int Rewind() - { - channel[0].Reset(); - channel[1].Reset(); - currentBlockPos = 14; - return 0; - } - private: - const u8 *sound; - bool is_allocated; - bool is_running; - chanel_t channel[2]; - u16 currentBlockPos; - u16 channelCount; - u32 sampleRate; - // u16 loopOffset; - // u16 bytePerSample; - // const u8 *soundDataStart; - // const u8 *soundDataEnd; - // u32 soundDataLen; -}; -REGISTER_GUI_SOUND_DECODER( GuiSoundDecoderBNS ); diff --git a/source/libwiigui/gui_sound_decoder_mpg.cpp b/source/libwiigui/gui_sound_decoder_mpg.cpp deleted file mode 100644 index 40cb7512..00000000 --- a/source/libwiigui/gui_sound_decoder_mpg.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/**************************************************************************** - * libwiigui - * - * Tantric 2009 - * - * gui_sound_plugin_mpg.cpp - * - * by ardi 2009 - * - * Decoder for MPEG-Audio Mpeg-1/-2 Layer I,II and III with libmad - * - * GUI class definitions - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include - -#include "gui_sound_decoder.h" - -static inline s16 FixedToShort(mad_fixed_t Fixed) -{ - /* Clipping */ - if (Fixed >= MAD_F_ONE) return (SHRT_MAX); - if (Fixed <= -MAD_F_ONE) return (-SHRT_MAX); - - Fixed = Fixed >> (MAD_F_FRACBITS - 15); - return ((s16) Fixed); -} - -#define ADMA_BUFFERSIZE (8192) -#define DATABUFFER_SIZE (32768) -// http://www.fr-an.de/fragen/v06/02_01.htm - - -class GuiSoundDecoderMPG: public GuiSoundDecoder -{ - protected: - GuiSoundDecoderMPG(const u8 * snd, u32 len, bool snd_is_allocated) - { - sound = snd; - length = len; - is_allocated = snd_is_allocated; - // Init mad-structures - mad_stream_init(&madStream); - mad_stream_buffer(&madStream, sound, length); - mad_frame_init(&madFrame); - mad_synth_init(&madSynth); - madSynthPcmPos = 0; - mad_timer_reset( &madTimer ); - guardBuffer = NULL; - is_running = false; - - // decode first Frame - if (DecodeFirstFrame()) - { - mad_synth_finish( &madSynth ); - mad_frame_finish(&madFrame); - mad_stream_finish(&madStream); - throw("Stream Error"); - } - } - public: - ~GuiSoundDecoderMPG() - { - while (is_running) - usleep(50); - mad_synth_finish( &madSynth ); - mad_frame_finish(&madFrame); - mad_stream_finish(&madStream); - delete[] guardBuffer; - if (is_allocated) delete[] sound; - } - static GuiSoundDecoder *Create(const u8 * snd, u32 len, bool snd_is_allocated) - { - struct mad_stream madStream; - struct mad_header madHeader; - mad_stream_init(&madStream); - mad_stream_buffer(&madStream, snd, len); - mad_header_init(&madHeader); - s32 ret = mad_header_decode(&madHeader, &madStream); - if (ret == 0 || madStream.error == MAD_ERROR_LOSTSYNC) // LOSTSYNC in first call is ok - { - int i; - for (i = 0; i < 4 && mad_header_decode(&madHeader, &madStream) == 0; i++) - ; - if (i == 4) - { - mad_header_finish( &madHeader ); - mad_stream_finish(&madStream); - return new GuiSoundDecoderMPG(snd, len, snd_is_allocated); - } - } mad_header_finish( &madHeader ); - mad_stream_finish(&madStream); - return NULL; - } - s32 GetFormat() - { - return MAD_NCHANNELS( &madFrame.header ) == 2 ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT; - } - s32 GetSampleRate() - { - return madFrame.header.samplerate; - } - /* Read reads data from stream to buffer - return: >0 = readed bytes; - 0 = EOF; - <0 = Error; - */ - int Read(u8 * buffer, int buffer_size) - { - is_running = true; - if (MAD_NCHANNELS( &madFrame.header ) == 2) // stereo - buffer_size &= ~0x0003; // make size to a kind of 4 - else buffer_size &= ~0x0001; // make size to a kind of 2 - u8 *write_pos = buffer; - u8 *write_end = buffer + buffer_size; - - for (;;) - { - for (; madSynthPcmPos < madSynth.pcm.length; ++madSynthPcmPos) - { - if (write_pos >= write_end) - { - is_running = false; - return write_pos - buffer; - } - *((s16*) write_pos) = FixedToShort(madSynth.pcm.samples[0][madSynthPcmPos]); - write_pos += 2; - if (MAD_NCHANNELS( &madFrame.header ) == 2) // stereo - { - *((s16*) write_pos) = FixedToShort(madSynth.pcm.samples[1][madSynthPcmPos]); - write_pos += 2; - } - } - - madStream.error = MAD_ERROR_NONE; - if (mad_frame_decode(&madFrame, &madStream)) - { - if (MAD_RECOVERABLE( madStream.error )) - { - if (madStream.error != MAD_ERROR_LOSTSYNC || !guardBuffer) continue; - } - else if (madStream.error == MAD_ERROR_BUFLEN) - { - if (!guardBuffer) - { - u32 guardLen = (madStream.bufend - madStream.next_frame); - guardBuffer = new (std::nothrow) u8[guardLen + MAD_BUFFER_GUARD]; - if (guardBuffer) - { - memcpy(guardBuffer, madStream.next_frame, guardLen); - memset(guardBuffer + guardLen, 0, MAD_BUFFER_GUARD); - mad_stream_buffer(&madStream, guardBuffer, guardLen + MAD_BUFFER_GUARD); - continue; - } - } - } - break; - } - mad_timer_add(&madTimer, madFrame.header.duration); - mad_synth_frame(&madSynth, &madFrame); - madSynthPcmPos = 0; - } - is_running = false; - return write_pos - buffer; - } - int Rewind() - { - while (is_running) - usleep(50); - delete[] guardBuffer; - guardBuffer = NULL; - mad_stream_buffer(&madStream, sound, length); - mad_synth_finish( &madSynth ); - mad_synth_init(&madSynth); - madSynthPcmPos = 0; - mad_timer_reset( &madTimer ); - // decode first Frame - return DecodeFirstFrame(); - } - private: - int DecodeFirstFrame() - { - for (;;) - { - madStream.error = MAD_ERROR_NONE; - if (mad_frame_decode(&madFrame, &madStream)) - { - if (MAD_RECOVERABLE( madStream.error )) - continue; - else return -1; - } - mad_timer_add(&madTimer, madFrame.header.duration); - mad_synth_frame(&madSynth, &madFrame); - return 0; - } - } - const u8 *sound; - u32 length; - bool is_allocated; - struct mad_stream madStream; - struct mad_frame madFrame; - struct mad_synth madSynth; - u16 madSynthPcmPos; - mad_timer_t madTimer; - u8 *guardBuffer; - bool is_running; -}; -REGISTER_GUI_SOUND_DECODER( GuiSoundDecoderMPG ); diff --git a/source/libwiigui/gui_sound_decoder_ogg.cpp b/source/libwiigui/gui_sound_decoder_ogg.cpp deleted file mode 100644 index 5cf4c76b..00000000 --- a/source/libwiigui/gui_sound_decoder_ogg.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/**************************************************************************** - * libwiigui - * - * Tantric 2009 - * - * gui_sound_plugin_ogg.cpp - * - * by ardi 2009 - * - * Decoder for ogg-vorbis with libtremor - * - * GUI class definitions - ***************************************************************************/ - -#include -#include -#include -#include -#include - -#include "gui_sound_decoder.h" - -/* functions to read the Ogg file from memory */ - -static struct -{ - char *mem; - int size; - int pos; -} file[4]; - -static int f_read(void * punt, int bytes, int blocks, int *f) -{ - int b; - int c = 0; - int d; - - if (bytes * blocks <= 0) return 0; - - blocks *= bytes; - - while (blocks > 0) - { - b = blocks; - if (b > 4096) b = 4096; - - d = (*f) - 0x666; - if ((unsigned) (d) <= (0x669 - 0x666)) - { - if (file[d].size == 0) return -1; - if ((file[d].pos + b) > file[d].size) b = file[d].size - file[d].pos; - if (b > 0) - { - memcpy(punt, file[d].mem + file[d].pos, b); - file[d].pos += b; - } - } - else b = read(*f, ((char *) punt) + c, b); - - if (b <= 0) - { - return c / bytes; - } - c += b; - blocks -= b; - } - return c / bytes; -} - -static int f_seek(int *f, ogg_int64_t offset, int mode) -{ - if (f == NULL) return (-1); - - int k; - mode &= 3; - - int d = (*f) - 0x666; - if ((unsigned) (d) <= (0x669 - 0x666)) - { - k = 0; - - if (file[d].size == 0) return -1; - - if (mode == 0) - { - if ((offset) >= file[d].size) - { - file[d].pos = file[d].size; - k = -1; - } - else if ((offset) < 0) - { - file[d].pos = 0; - k = -1; - } - else file[d].pos = offset; - } - else if (mode == 1) - { - if ((file[d].pos + offset) >= file[d].size) - { - file[d].pos = file[d].size; - k = -1; - } - else if ((file[d].pos + offset) < 0) - { - file[d].pos = 0; - k = -1; - } - else file[d].pos += offset; - } - else if (mode == 2) - { - - if ((file[d].size + offset) >= file[d].size) - { - file[d].pos = file[d].size; - k = -1; - } - else if ((file[d].size + offset) < 0) - { - file[d].pos = 0; - k = -1; - } - else file[d].pos = file[d].size + offset; - } - - } - else k = lseek(*f, (int) offset, mode); - - if (k < 0) - k = -1; - else k = 0; - return k; -} - -static int f_close(int *f) -{ - int d = (*f) - 0x666; - if ((unsigned) (d) <= (0x669 - 0x666)) - { - file[d].size = 0; - file[d].pos = 0; - if (file[d].mem) - { - file[d].mem = (char *) 0; - } - return 0; - } - else return close(*f); - return 0; -} - -static long f_tell(int *f) -{ - int k; - - int d = (*f) - 0x666; - if ((unsigned) (d) <= (0x669 - 0x666)) - { - k = file[d].pos; - } - else k = lseek(*f, 0, 1); - - return (long) k; -} - -static int mem_open(char * ogg, int size) -{ - static int one = 1; - int n; - if (one) - { - one = 0; - - file[0].size = 0; - file[1].size = 0; - file[2].size = 0; - file[3].size = 0; - file[0].mem = ogg; - file[0].size = size; - file[0].pos = 0; - return (0x666); - } - - for (n = 0; n < 4; n++) - { - if (file[n].size == 0) - { - file[n].mem = ogg; - file[n].size = size; - file[n].pos = 0; - return (0x666 + n); - } - } - return -1; -} - -static int mem_close(int fd) -{ - if ((unsigned) ((fd) - 0x666) <= (0x669 - 0x666)) // it is a memory file descriptor? - { - fd -= 0x666; - file[fd].size = 0; - return 0; - } - else return f_close(&fd); -} - -static ov_callbacks callbacks = { (size_t(*)(void *, size_t, size_t, void *)) f_read, - (int(*)(void *, ogg_int64_t, int)) f_seek, (int(*)(void *)) f_close, (long(*)(void *)) f_tell }; - -class GuiSoundDecoderOGG: public GuiSoundDecoder -{ - protected: - GuiSoundDecoderOGG(const u8 * snd, u32 len, bool snd_is_allocated) - { - sound = snd; - is_allocated = snd_is_allocated; - ogg_fd = mem_open((char *) snd, len); - if (ogg_fd < 0) throw("mem open failed"); - - if (ov_open_callbacks((void*) &ogg_fd, &ogg_file, NULL, 0, callbacks) < 0) - { - mem_close(ogg_fd); - throw("ogg open failed"); - } - ogg_info = ov_info(&ogg_file, -1); - bitstream = 0; - is_running = false; - } - public: - ~GuiSoundDecoderOGG() - { - while (is_running) - usleep(50); - ov_clear(&ogg_file); - if (is_allocated) delete[] sound; - } - static GuiSoundDecoder *Create(const u8 * snd, u32 len, bool snd_is_allocated) - { - if (snd && len > 4 && snd[0] == 'O' && snd[1] == 'g' && snd[2] == 'g' && snd[3] == 'S') return new GuiSoundDecoderOGG( - snd, len, snd_is_allocated); - return NULL; - } - s32 GetFormat() - { - return ogg_info->channels == 2 ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT; - } - s32 GetSampleRate() - { - return ogg_info->rate; - } - /* Read reads data from stream to buffer - return: >0 = readed bytes; - 0 = EOF; - <0 = Error; - */ - int Read(u8 * buffer, int buffer_size) - { - is_running = true; - int ret = ov_read(&ogg_file, (char *) buffer, buffer_size, &bitstream); - if (ret < 0) - { - /* error in the stream. Not a problem, just reporting it in - case we (the app) cares. In this case, we don't. */ - if (ret != OV_HOLE) ret = 0; // we says EOF - } - is_running = false; - return ret; - } - int Rewind() - { - return ov_time_seek(&ogg_file, 0); - } - private: - const u8 *sound; - bool is_allocated; - int ogg_fd; - OggVorbis_File ogg_file; - vorbis_info *ogg_info; - int bitstream; - bool is_running; -}; -REGISTER_GUI_SOUND_DECODER( GuiSoundDecoderOGG ); diff --git a/source/libwiigui/gui_sound_decoder_wav.cpp b/source/libwiigui/gui_sound_decoder_wav.cpp deleted file mode 100644 index 30f4d462..00000000 --- a/source/libwiigui/gui_sound_decoder_wav.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/**************************************************************************** - * libwiigui - * - * Tantric 2009 - * - * gui_sound_plugin_wav.cpp - * - * by ardi 2009 - * - * Decoder for WAVE PCM - * - * GUI class definitions - ***************************************************************************/ - -#include -#include -#include -#include - -#include "gui_sound_decoder.h" - -typedef struct -{ - u32 cueID; - u32 len; - u32 loops; -} plst_t; -typedef struct -{ - const u8 *start; - const u8 *end; - u32 loops; -} playlist_t; - -class GuiSoundDecoderWAV: public GuiSoundDecoder -{ - protected: - GuiSoundDecoderWAV(const u8 * snd, u32 len, bool snd_is_allocated) - { - sound = snd; - is_running = false; - is_allocated = snd_is_allocated; - - const u8 *in_ptr = sound; - - if (be32inc( in_ptr ) != 0x52494646 /*'RIFF' (WAV)*/) throw("Not a WAV"); - - u32 riffsize = le32inc( in_ptr ); - if (riffsize > (len - 8)) throw("Wrong size"); - - if (be32inc( in_ptr ) != 0x57415645 /*'WAVE'*/) throw("No WAVE-Tag"); - - if (be32inc( in_ptr ) != 0x666D7420 /*'fmt '*/) throw("No fmt-Tag"); - - u32 fmtLen = le32inc( in_ptr ); - - if (le16inc( in_ptr ) != 1) throw("Not PCM data"); - - channelCount = le16inc( in_ptr ); - if (channelCount < 1 || channelCount > 2) throw("only mono or stereo"); - - sampleRate = le32inc( in_ptr ); - - in_ptr += 6; // skip and - - bytePerSample = (le16inc( in_ptr ) + 7) / 8; - if (bytePerSample < 1 || bytePerSample > 2) throw("only 1-16 bit/Sample"); - - in_ptr += fmtLen - 16; - - if (be32inc( in_ptr ) != 0x64617461 /*'data'*/) throw("No data-Tag"); - - soundDataStart = in_ptr + 4; - soundDataEnd = soundDataStart + le32(in_ptr); - - in_ptr = soundDataEnd; - - std::map cue; - std::vector plst; - - if (((u32) in_ptr) & 0x0001UL) in_ptr++; - while ((in_ptr + 4) < (sound + riffsize)) - { - u32 tag = be32inc( in_ptr ); - switch (tag) - { - case 0x63756520 /*'cue '*/: - in_ptr += 4; // skip size - for (u32 count = le32inc( in_ptr ); count > 0; count--) - { - u32 ID = be32inc( in_ptr ); - in_ptr += 4; // skip dwPosition - if (be32inc( in_ptr ) == 0x64617461 /*'data'*/) - { - in_ptr += 8; // skip chunkStart - dwBlockStart - cue[ID] = le32inc( in_ptr ); - } - else in_ptr += 12; // skip chunkStart - SammpleOffset - } - break; - case 0x706C7374 /*' plst'*/: - in_ptr += 4; // skip size - for (u32 count = le32inc( in_ptr ); count > 0; count--) - plst.push_back(( plst_t ) - { le32inc( in_ptr ), le32inc( in_ptr ), le32inc( in_ptr )}); - break; - default: - in_ptr -= 2; - break; - } - } - for (std::vector::iterator i = plst.begin(); i != plst.end(); ++i) - { - const u8 *start = soundDataStart + cue[i->cueID]; - const u8 *end = soundDataStart + (i->len * bytePerSample * channelCount); - u32 loops = i->loops; - playlist.push_back(( playlist_t ) - { start, end, loops}); - } - if (playlist.size() == 0) - { - playlist.push_back(( playlist_t ) - { soundDataStart, soundDataEnd, 1}); - } - Rewind(); - } - public: - ~GuiSoundDecoderWAV() - { - while (is_running) - usleep(50); - if (is_allocated) delete[] sound; - } - static GuiSoundDecoder *Create(const u8 * snd, u32 len, bool snd_is_allocated) - { - if (snd && len > 4 && snd[0] == 'R' && snd[1] == 'I' && snd[2] == 'F' && snd[3] == 'F' && snd[8] == 'W' - && snd[9] == 'A' && snd[10] == 'V' && snd[11] == 'E') return new GuiSoundDecoderWAV(snd, len, - snd_is_allocated); - return NULL; - } - s32 GetFormat() - { - if (bytePerSample == 2) - return channelCount == 2 ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT; - else return channelCount == 2 ? VOICE_STEREO_8BIT : VOICE_MONO_8BIT; - } - s32 GetSampleRate() - { - return sampleRate; - } - /* Read reads data from stream to buffer - return: >0 = readed bytes; - 0 = EOF; - <0 = Error; - */ - int Read(u8 * buffer, int buffer_size) - { - is_running = true; - u8 *write_pos = buffer; - u8 *write_end = buffer + buffer_size; - - for (;;) - { - while (currentPos < currentEnd) - { - if (write_pos >= write_end) - { - is_running = false; - return write_pos - buffer; - } - if (bytePerSample == 2) - { - *((s16*) write_pos) = le16inc( currentPos ); - write_pos += 2; - if (channelCount == 2) // stereo - { - *((s16*) write_pos) = le16inc( currentPos ); - write_pos += 2; - } - } - else - { - *write_pos++ = *currentPos++; - if (channelCount == 2) // stereo - *write_pos++ = *currentPos++; - } - } - if (currentLoops > 1) - { - currentLoops--; - currentPos = currentStart; - continue; - } - if (currentPlaylist != playlist.end()) currentPlaylist++; - if (currentPlaylist != playlist.end()) - { - currentStart = currentPos = currentPlaylist->start; - currentEnd = currentPlaylist->end; - currentLoops = currentPlaylist->loops; - continue; - } - else - { - is_running = false; - return write_pos - buffer; - } - } - is_running = false; - return 0; - } - int Rewind() - { - currentPlaylist = playlist.begin(); - currentStart = currentPos = currentPlaylist->start; - currentEnd = currentPlaylist->end; - currentLoops = currentPlaylist->loops; - return 0; - } - private: - const u8 *sound; - bool is_allocated; - bool is_running; - - u16 channelCount; - u32 sampleRate; - u16 bytePerSample; - const u8 *soundDataStart; - const u8 *soundDataEnd; - std::vector playlist; - std::vector::iterator currentPlaylist; - const u8 *currentStart; - const u8 *currentEnd; - u32 currentLoops; - const u8 *currentPos; - -}; -REGISTER_GUI_SOUND_DECODER( GuiSoundDecoderWAV ); diff --git a/source/main.cpp b/source/main.cpp index a41f852a..d68857cd 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -85,7 +85,7 @@ int main(int argc, char *argv[]) if (IosLoader::LoadAppCios() < 0) { printf("\n\tWARNING!\n"); - printf("\tUSB Loader GX needs unstubbed cIOS 222 v4 or 249 v9+\n\n"); + printf("\tUSB Loader GX needs unstubbed cIOS 222 v4+ or 249 v9+\n\n"); printf( "\tWe cannot determine the versions on your system,\n\tsince you have no patched ios 36 or 236 installed.\n"); diff --git a/source/menu.cpp b/source/menu.cpp index 2633d655..c8b62721 100644 --- a/source/menu.cpp +++ b/source/menu.cpp @@ -43,7 +43,9 @@ GuiImageData * pointer[4]; GuiImage * bgImg = NULL; GuiImageData * background = NULL; GuiBGM * bgMusic = NULL; -GuiSound *btnClick2 = NULL; +GuiSound *btnSoundClick = NULL; +GuiSound *btnSoundClick2 = NULL; +GuiSound *btnSoundOver = NULL; int currentMenu; u8 mountMethod = 0; @@ -265,6 +267,10 @@ int MainMenu(int menu) if (Settings.autonetwork) ResumeNetworkThread(); + btnSoundClick = new GuiSound(button_click_wav, button_click_wav_size, Settings.sfxvolume); + btnSoundClick2 = new GuiSound(button_click2_wav, button_click2_wav_size, Settings.sfxvolume); + btnSoundOver = new GuiSound(button_over_wav, button_over_wav_size, Settings.sfxvolume); + pointer[0] = Resources::GetImageData("player1_point.png"); pointer[1] = Resources::GetImageData("player2_point.png"); pointer[2] = Resources::GetImageData("player3_point.png"); @@ -279,10 +285,15 @@ int MainMenu(int menu) ResumeGui(); + gprintf("Vor bgm\n"); bgMusic = new GuiBGM(bg_music_ogg, bg_music_ogg_size, Settings.volume); + gprintf("new bgm\n"); bgMusic->SetLoop(Settings.musicloopmode); //loop music + gprintf("SetLoop\n"); bgMusic->Load(Settings.ogg_path); + gprintf("Load\n"); bgMusic->Play(); + gprintf("Nach bgm\n"); MountGamePartition(); diff --git a/source/menu.h b/source/menu.h index 28948bed..d79f0b05 100644 --- a/source/menu.h +++ b/source/menu.h @@ -11,7 +11,6 @@ #include #include "libwiigui/gui.h" -#include "libwiigui/gui_bgm.h" #include "settings/CSettings.h" #include "main.h" @@ -37,8 +36,6 @@ void ResumeGui(); void HaltGui(); GuiImageData *LoadCoverImage(struct discHdr *header, bool Prefere3D = true, bool noCover = true); -extern GuiSound *btnClick2; -extern GuiBGM *bgMusic; extern GuiImageData *pointer[4]; extern GuiImageData *background; extern GuiImage *bgImg; diff --git a/source/menu/MountGamePartition.cpp b/source/menu/MountGamePartition.cpp index bfb6349c..fa906388 100644 --- a/source/menu/MountGamePartition.cpp +++ b/source/menu/MountGamePartition.cpp @@ -161,7 +161,7 @@ int MountGamePartition(bool ShowGUI) // open database if needed, load titles if needed if (CheckFile(Settings.titlestxt_path)) - OpenXMLDatabase(Settings.titlestxt_path, Settings.db_language, Settings.db_JPtoEN, true, Settings.titlesOverride == 1 ? true : false, true); + OpenXMLDatabase(Settings.titlestxt_path, Settings.db_language, Settings.db_JPtoEN, true, Settings.titlesOverride, true); return ret; } diff --git a/source/menu/menu_disclist.cpp b/source/menu/menu_disclist.cpp index 24ffbc89..fb548de9 100644 --- a/source/menu/menu_disclist.cpp +++ b/source/menu/menu_disclist.cpp @@ -96,11 +96,6 @@ int MenuDiscList() nolist = 1; } - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnInstall(Resources::GetFile("button_install.png"), Resources::GetFileSize("button_install.png")); GuiImageData btnInstallOver(Resources::GetFile("button_install_over.png"), Resources::GetFileSize("button_install_over.png")); @@ -197,7 +192,7 @@ int MenuDiscList() installBtnImgOver.SetWidescreen(Settings.widescreen); GuiButton installBtn(&installBtnImg, &installBtnImgOver, ALIGN_LEFT, ALIGN_TOP, Theme.install_x, Theme.install_y, - &trigA, &btnSoundOver, btnClick2, 1, &installBtnTT, 24, -30, 0, 5); + &trigA, btnSoundOver, btnSoundClick2, 1, &installBtnTT, 24, -30, 0, 5); GuiTooltip settingsBtnTT(tr( "Settings" )); if (Settings.wsprompt) settingsBtnTT.SetWidescreen(Settings.widescreen); @@ -207,7 +202,7 @@ int MenuDiscList() GuiImage settingsBtnImgOver(&btnSettingsOver); settingsBtnImgOver.SetWidescreen(Settings.widescreen); GuiButton settingsBtn(&settingsBtnImg, &settingsBtnImgOver, 0, 3, Theme.setting_x, Theme.setting_y, &trigA, - &btnSoundOver, btnClick2, 1, &settingsBtnTT, 65, -30, 0, 5); + btnSoundOver, btnSoundClick2, 1, &settingsBtnTT, 65, -30, 0, 5); GuiTooltip homeBtnTT(tr( "Back to HBC or Wii Menu" )); if (Settings.wsprompt) homeBtnTT.SetWidescreen(Settings.widescreen); @@ -216,7 +211,7 @@ int MenuDiscList() homeBtnImg.SetWidescreen(Settings.widescreen); GuiImage homeBtnImgOver(&btnhomeOver); homeBtnImgOver.SetWidescreen(Settings.widescreen); - GuiButton homeBtn(&homeBtnImg, &homeBtnImgOver, 0, 3, Theme.home_x, Theme.home_y, &trigA, &btnSoundOver, btnClick2, + GuiButton homeBtn(&homeBtnImg, &homeBtnImgOver, 0, 3, Theme.home_x, Theme.home_y, &trigA, btnSoundOver, btnSoundClick2, 1, &homeBtnTT, 15, -30, 1, 5); homeBtn.RemoveSoundClick(); homeBtn.SetTrigger(&trigHome); @@ -229,7 +224,7 @@ int MenuDiscList() poweroffBtnImg.SetWidescreen(Settings.widescreen); poweroffBtnImgOver.SetWidescreen(Settings.widescreen); GuiButton poweroffBtn(&poweroffBtnImg, &poweroffBtnImgOver, 0, 3, Theme.power_x, Theme.power_y, &trigA, - &btnSoundOver, btnClick2, 1, &poweroffBtnTT, -10, -30, 1, 5); + btnSoundOver, btnSoundClick2, 1, &poweroffBtnTT, -10, -30, 1, 5); GuiTooltip sdcardBtnTT(tr( "Reload SD" )); if (Settings.wsprompt) sdcardBtnTT.SetWidescreen(Settings.widescreen); @@ -238,12 +233,12 @@ int MenuDiscList() GuiImage sdcardImgOver(&btnsdcardOver); sdcardImg.SetWidescreen(Settings.widescreen); sdcardImgOver.SetWidescreen(Settings.widescreen); - GuiButton sdcardBtn(&sdcardImg, &sdcardImgOver, 0, 3, Theme.sdcard_x, Theme.sdcard_y, &trigA, &btnSoundOver, - btnClick2, 1, &sdcardBtnTT, 15, -30, 0, 5); + GuiButton sdcardBtn(&sdcardImg, &sdcardImgOver, 0, 3, Theme.sdcard_x, Theme.sdcard_y, &trigA, btnSoundOver, + btnSoundClick2, 1, &sdcardBtnTT, 15, -30, 0, 5); GuiButton gameInfo(0, 0); gameInfo.SetTrigger(&trig2); - gameInfo.SetSoundClick(btnClick2); + gameInfo.SetSoundClick(btnSoundClick2); GuiTooltip favoriteBtnTT(tr( "Display favorites only" )); if (Settings.wsprompt) favoriteBtnTT.SetWidescreen(Settings.widescreen); @@ -253,7 +248,7 @@ int MenuDiscList() GuiImage favoriteBtnImg_g(&imgfavIcon_gray); favoriteBtnImg_g.SetWidescreen(Settings.widescreen); GuiButton favoriteBtn(&favoriteBtnImg_g, &favoriteBtnImg_g, ALIGN_LEFT, ALIGN_TOP, Theme.gamelist_favorite_x, - Theme.gamelist_favorite_y, &trigA, &btnSoundOver, btnClick2, 1, &favoriteBtnTT, -15, 52, 0, 3); + Theme.gamelist_favorite_y, &trigA, btnSoundOver, btnSoundClick2, 1, &favoriteBtnTT, -15, 52, 0, 3); favoriteBtn.SetAlpha(180); GuiTooltip searchBtnTT(tr( "Set Search-Filter" )); @@ -264,7 +259,7 @@ int MenuDiscList() GuiImage searchBtnImg_g(&imgsearchIcon_gray); searchBtnImg_g.SetWidescreen(Settings.widescreen); GuiButton searchBtn(&searchBtnImg_g, &searchBtnImg_g, ALIGN_LEFT, ALIGN_TOP, Theme.gamelist_search_x, - Theme.gamelist_search_y, &trigA, &btnSoundOver, btnClick2, 1, &searchBtnTT, -15, 52, 0, 3); + Theme.gamelist_search_y, &trigA, btnSoundOver, btnSoundClick2, 1, &searchBtnTT, -15, 52, 0, 3); searchBtn.SetAlpha(180); const char * sortTTText = NULL; @@ -292,7 +287,7 @@ int MenuDiscList() GuiImage sortBtnImg(sortImgData); sortBtnImg.SetWidescreen(Settings.widescreen); - GuiButton sortBtn(&sortBtnImg, &sortBtnImg, ALIGN_LEFT, ALIGN_TOP, Theme.gamelist_abc_x, Theme.gamelist_abc_y, &trigA, &btnSoundOver, btnClick2, 1, &sortBtnTT, -15, 52, 0, 3); + GuiButton sortBtn(&sortBtnImg, &sortBtnImg, ALIGN_LEFT, ALIGN_TOP, Theme.gamelist_abc_x, Theme.gamelist_abc_y, &trigA, btnSoundOver, btnSoundClick2, 1, &sortBtnTT, -15, 52, 0, 3); GuiTooltip listBtnTT(tr( "Display as a list" )); if (Settings.wsprompt) listBtnTT.SetWidescreen(Settings.widescreen); @@ -302,7 +297,7 @@ int MenuDiscList() GuiImage listBtnImg_g(&imgarrangeList_gray); listBtnImg_g.SetWidescreen(Settings.widescreen); GuiButton listBtn(&listBtnImg_g, &listBtnImg_g, ALIGN_LEFT, ALIGN_TOP, Theme.gamelist_list_x, - Theme.gamelist_list_y, &trigA, &btnSoundOver, btnClick2, 1, &listBtnTT, 15, 52, 1, 3); + Theme.gamelist_list_y, &trigA, btnSoundOver, btnSoundClick2, 1, &listBtnTT, 15, 52, 1, 3); listBtn.SetAlpha(180); GuiTooltip gridBtnTT(tr( "Display as a grid" )); @@ -313,7 +308,7 @@ int MenuDiscList() GuiImage gridBtnImg_g(&imgarrangeGrid_gray); gridBtnImg_g.SetWidescreen(Settings.widescreen); GuiButton gridBtn(&gridBtnImg_g, &gridBtnImg_g, ALIGN_LEFT, ALIGN_TOP, Theme.gamelist_grid_x, - Theme.gamelist_grid_y, &trigA, &btnSoundOver, btnClick2, 1, &gridBtnTT, 15, 52, 1, 3); + Theme.gamelist_grid_y, &trigA, btnSoundOver, btnSoundClick2, 1, &gridBtnTT, 15, 52, 1, 3); gridBtn.SetAlpha(180); GuiTooltip carouselBtnTT(tr( "Display as a carousel" )); @@ -324,7 +319,7 @@ int MenuDiscList() GuiImage carouselBtnImg_g(&imgarrangeCarousel_gray); carouselBtnImg_g.SetWidescreen(Settings.widescreen); GuiButton carouselBtn(&carouselBtnImg_g, &carouselBtnImg_g, ALIGN_LEFT, ALIGN_TOP, Theme.gamelist_carousel_x, - Theme.gamelist_carousel_y, &trigA, &btnSoundOver, btnClick2, 1, &carouselBtnTT, 15, 52, 1, 3); + Theme.gamelist_carousel_y, &trigA, btnSoundOver, btnSoundClick2, 1, &carouselBtnTT, 15, 52, 1, 3); carouselBtn.SetAlpha(180); bool canUnlock = (Settings.parentalcontrol == 0 && Settings.Parental.enabled == 1); @@ -337,7 +332,7 @@ int MenuDiscList() GuiImage lockBtnImg_g(&imgLock_gray); lockBtnImg_g.SetWidescreen(Settings.widescreen); GuiButton lockBtn(&lockBtnImg_g, &lockBtnImg_g, ALIGN_LEFT, ALIGN_TOP, Theme.gamelist_lock_x, - Theme.gamelist_lock_y, &trigA, &btnSoundOver, btnClick2, 1, &lockBtnTT, 15, 52, 1, 3); + Theme.gamelist_lock_y, &trigA, btnSoundOver, btnSoundClick2, 1, &lockBtnTT, 15, 52, 1, 3); lockBtn.SetAlpha(180); GuiTooltip unlockBtnTT(tr( "Enable Parental Control" )); @@ -363,7 +358,7 @@ int MenuDiscList() GuiImage dvdBtnImg_g(dvdBtnImg); dvdBtnImg_g.SetWidescreen(Settings.widescreen); GuiButton dvdBtn(&dvdBtnImg_g, &dvdBtnImg_g, ALIGN_LEFT, ALIGN_TOP, Theme.gamelist_dvd_x, Theme.gamelist_dvd_y, - &trigA, &btnSoundOver, btnClick2, 1, &dvdBtnTT, 15, 52, 1, 3); + &trigA, btnSoundOver, btnSoundClick2, 1, &dvdBtnTT, 15, 52, 1, 3); dvdBtn.SetAlpha(180); GuiTooltip homebrewBtnTT(tr( "Homebrew Launcher" )); @@ -374,7 +369,7 @@ int MenuDiscList() homebrewImg.SetWidescreen(Settings.widescreen); homebrewImgOver.SetWidescreen(Settings.widescreen); GuiButton homebrewBtn(&homebrewImg, &homebrewImgOver, ALIGN_LEFT, ALIGN_TOP, Theme.homebrew_x, Theme.homebrew_y, - &trigA, &btnSoundOver, btnClick2, 1, &homebrewBtnTT, 15, -30, 1, 5); + &trigA, btnSoundOver, btnSoundClick2, 1, &homebrewBtnTT, 15, -30, 1, 5); if (Settings.GameSort & SORT_FAVORITE) { @@ -461,12 +456,12 @@ int MenuDiscList() if (Settings.godmode == 1 && mountMethod != 3) //only make the button have trigger & tooltip if in godmode { - DownloadBtn.SetSoundOver(&btnSoundOver); + DownloadBtn.SetSoundOver(btnSoundOver); DownloadBtn.SetTrigger(&trigA); DownloadBtn.SetTrigger(&trig1); DownloadBtn.SetToolTip(&DownloadBtnTT, 205, -30); - idBtn.SetSoundOver(&btnSoundOver); + idBtn.SetSoundOver(btnSoundOver); idBtn.SetTrigger(&trigA); idBtn.SetToolTip(&IDBtnTT, 205, -30); diff --git a/source/menu/menu_install.cpp b/source/menu/menu_install.cpp index 04d1192d..e6444e8c 100644 --- a/source/menu/menu_install.cpp +++ b/source/menu/menu_install.cpp @@ -25,8 +25,6 @@ int MenuInstall() int ret, choice = 0; char name[200]; - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - GuiImageData battery(Resources::GetFile("battery.png"), Resources::GetFileSize("battery.png")); GuiImageData batteryBar(Resources::GetFile("battery_bar.png"), Resources::GetFileSize("battery_bar.png")); GuiImageData batteryRed(Resources::GetFile("battery_red.png"), Resources::GetFileSize("battery_red.png")); diff --git a/source/menu/menu_partition_selection.cpp b/source/menu/menu_partition_selection.cpp index d63f8772..1728a606 100644 --- a/source/menu/menu_partition_selection.cpp +++ b/source/menu/menu_partition_selection.cpp @@ -43,10 +43,6 @@ int SelectPartitionMenu() counter++; } - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnpwroff(Resources::GetFile("wiimote_poweroff.png"), Resources::GetFileSize("wiimote_poweroff.png")); GuiImageData btnpwroffOver(Resources::GetFile("wiimote_poweroff_over.png"), Resources::GetFileSize("wiimote_poweroff_over.png")); GuiImageData btnhome(Resources::GetFile("menu_button.png"), Resources::GetFileSize("menu_button.png")); @@ -66,12 +62,12 @@ int SelectPartitionMenu() poweroffBtnImg.SetWidescreen(Settings.widescreen); poweroffBtnImgOver.SetWidescreen(Settings.widescreen); GuiButton poweroffBtn(&poweroffBtnImg, &poweroffBtnImgOver, 0, 3, Theme.power_x, Theme.power_y, &trigA, - &btnSoundOver, btnClick2, 1); + btnSoundOver, btnSoundClick2, 1); GuiImage exitBtnImg(&btnhome); GuiImage exitBtnImgOver(&btnhomeOver); exitBtnImg.SetWidescreen(Settings.widescreen); exitBtnImgOver.SetWidescreen(Settings.widescreen); - GuiButton exitBtn(&exitBtnImg, &exitBtnImgOver, 0, 3, Theme.home_x, Theme.home_y, &trigA, &btnSoundOver, btnClick2, + GuiButton exitBtn(&exitBtnImg, &exitBtnImgOver, 0, 3, Theme.home_x, Theme.home_y, &trigA, btnSoundOver, btnSoundClick2, 1); exitBtn.SetTrigger(&trigHome); diff --git a/source/menu/menus.h b/source/menu/menus.h index 4ea83c37..3cac187c 100644 --- a/source/menu/menus.h +++ b/source/menu/menus.h @@ -2,7 +2,6 @@ #define _MENUS_H #include "libwiigui/gui.h" -#include "libwiigui/gui_bgm.h" #include "language/gettext.h" #include "prompts/PromptWindows.h" #include "menu.h" diff --git a/source/prompts/DiscBrowser.cpp b/source/prompts/DiscBrowser.cpp index 5176939f..b205de15 100644 --- a/source/prompts/DiscBrowser.cpp +++ b/source/prompts/DiscBrowser.cpp @@ -36,7 +36,7 @@ extern u8 reset; /******************************************************************************** *Disk Browser *********************************************************************************/ -int DiscBrowse(struct discHdr * header, char * alternatedname, int alternatedname_size) +int DiscBrowse(const char * GameID, char * alternatedname, int alternatedname_size) { gprintf("\nDiscBrowser() started"); bool exit = false; @@ -44,7 +44,7 @@ int DiscBrowse(struct discHdr * header, char * alternatedname, int alternatednam HaltGui(); - wbfs_disc_t *disc = WBFS_OpenDisc((u8 *) header->id); + wbfs_disc_t *disc = WBFS_OpenDisc((u8 *) GameID); if (!disc) { ResumeGui(); @@ -98,11 +98,6 @@ int DiscBrowse(struct discHdr * header, char * alternatedname, int alternatednam return -1; } - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData settingsbg(Resources::GetFile("settings_background.png"), Resources::GetFileSize("settings_background.png")); @@ -113,7 +108,7 @@ int DiscBrowse(struct discHdr * header, char * alternatedname, int alternatednam GuiTrigger trigB; trigB.SetButtonOnlyTrigger(-1, WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B, PAD_BUTTON_B); - GuiText titleTxt(GameTitles.GetTitle(header), 28, ( GXColor ) {0, 0, 0, 255}); + GuiText titleTxt(GameTitles.GetTitle(GameID), 28, ( GXColor ) {0, 0, 0, 255}); titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); titleTxt.SetPosition(12, 40); titleTxt.SetMaxWidth(356, SCROLL_HORIZONTAL); @@ -132,7 +127,7 @@ int DiscBrowse(struct discHdr * header, char * alternatedname, int alternatednam cancelBtnTxt.SetWidescreen(Settings.widescreen); cancelBtnImg.SetWidescreen(Settings.widescreen); } - GuiButton cancelBtn(&cancelBtnImg, &cancelBtnImg, 2, 3, 180, 400, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton cancelBtn(&cancelBtnImg, &cancelBtnImg, 2, 3, 180, 400, &trigA, btnSoundOver, btnSoundClick2, 1); cancelBtn.SetScale(0.9); cancelBtn.SetLabel(&cancelBtnTxt); cancelBtn.SetTrigger(&trigB); @@ -300,7 +295,7 @@ int autoSelectDolMenu(const char *id, bool force) //Indiana Jones and the Staff of Kings (Fate of Atlantis) if (strcmp(id, "RJ8E64") == 0) { - int choice = WindowPrompt(tr( "Select a DOL" ), 0, "Fate of Atlantis", tr( "Default" )); + int choice = WindowPrompt(tr( "Select a DOL" ), 0, "Fate of Atlantis", tr( "Cancel" )); switch (choice) { case 1: @@ -314,7 +309,7 @@ int autoSelectDolMenu(const char *id, bool force) } if (strcmp(id, "RJ8P64") == 0) { - int choice = WindowPrompt(tr( "Select a DOL" ), 0, "Fate of Atlantis", tr( "Default" )); + int choice = WindowPrompt(tr( "Select a DOL" ), 0, "Fate of Atlantis", tr( "Cancel" )); switch (choice) { case 1: @@ -330,7 +325,7 @@ int autoSelectDolMenu(const char *id, bool force) //Metal Slug Anthology (Metal Slug 6) if (strcmp(id, "RMLEH4") == 0) { - int choice = WindowPrompt(tr( "Select a DOL" ), 0, "Metal Slug 6", tr( "Default" )); + int choice = WindowPrompt(tr( "Select a DOL" ), 0, "Metal Slug 6", tr( "Cancel" )); switch (choice) { case 1: @@ -344,7 +339,7 @@ int autoSelectDolMenu(const char *id, bool force) } if (strcmp(id, "RMLP7U") == 0) { - int choice = WindowPrompt(tr( "Select a DOL" ), 0, "Metal Slug 6", tr( "Default" )); + int choice = WindowPrompt(tr( "Select a DOL" ), 0, "Metal Slug 6", tr( "Cancel" )); switch (choice) { case 1: @@ -367,8 +362,7 @@ int autoSelectDolMenu(const char *id, bool force) return -1; } */ - int choice = WindowPrompt(tr( "Select a DOL" ), 0, "Metroid Prime", "Metroid Prime 2", "Metroid Prime 3", - tr( "Default" )); + int choice = WindowPrompt(tr( "Select a DOL" ), 0, "Metroid Prime", "Metroid Prime 2", "Metroid Prime 3", tr( "Cancel" )); switch (choice) { case 1: @@ -394,8 +388,7 @@ int autoSelectDolMenu(const char *id, bool force) return -1; } */ - int choice = WindowPrompt(tr( "Select a DOL" ), 0, "Metroid Prime", "Metroid Prime 2", "Metroid Prime 3", - tr( "Default" )); + int choice = WindowPrompt(tr( "Select a DOL" ), 0, "Metroid Prime", "Metroid Prime 2", "Metroid Prime 3", tr( "Cancel" )); switch (choice) { case 1: @@ -417,7 +410,7 @@ int autoSelectDolMenu(const char *id, bool force) //Rampage: Total Destruction (M1.dol=Rampage, jarvos.dol=Rampage World Tour) if (strcmp(id, "RPGP5D") == 0) { - int choice = WindowPrompt(tr( "Select a DOL" ), 0, "Rampage", "World Tour", tr( "Default" )); + int choice = WindowPrompt(tr( "Select a DOL" ), 0, "Rampage", "World Tour", tr( "Cancel" )); switch (choice) { case 1: @@ -436,7 +429,7 @@ int autoSelectDolMenu(const char *id, bool force) //The House Of The Dead 2 & 3 Return (only to play 2) if (strcmp(id, "RHDE8P") == 0) { - int choice = WindowPrompt(tr( "Select a DOL" ), 0, "HotD 2", tr( "Default" )); + int choice = WindowPrompt(tr( "Select a DOL" ), 0, "HotD 2", tr( "Cancel" )); switch (choice) { case 1: @@ -450,7 +443,7 @@ int autoSelectDolMenu(const char *id, bool force) } if (strcmp(id, "RHDP8P") == 0) { - int choice = WindowPrompt(tr( "Select a DOL" ), 0, "HotD 2", tr( "Default" )); + int choice = WindowPrompt(tr( "Select a DOL" ), 0, "HotD 2", tr( "Cancel" )); switch (choice) { case 1: diff --git a/source/prompts/DiscBrowser.h b/source/prompts/DiscBrowser.h index 06bc538c..d285ff9d 100644 --- a/source/prompts/DiscBrowser.h +++ b/source/prompts/DiscBrowser.h @@ -8,7 +8,10 @@ #ifndef _DISCBROWSER_H_ #define _DISCBROWSER_H_ -int DiscBrowse(struct discHdr * header, char * dolname, int dolname_size); +#include +#include "usbloader/disc.h" + +int DiscBrowse(const char * GameID, char * dolname, int dolname_size); int autoSelectDol(const char *id, bool force); int autoSelectDolMenu(const char *id, bool force); u8 DiscMount(struct discHdr * header); diff --git a/source/prompts/PromptWindows.cpp b/source/prompts/PromptWindows.cpp index a7b0f4fc..2424687e 100644 --- a/source/prompts/PromptWindows.cpp +++ b/source/prompts/PromptWindows.cpp @@ -71,10 +71,6 @@ int OnScreenNumpad(char * var, u32 maxlen) GuiNumpad numpad(var, maxlen); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiTrigger trigA; @@ -89,7 +85,7 @@ int OnScreenNumpad(char * var, u32 maxlen) okBtnTxt.SetWidescreen(Settings.widescreen); okBtnImg.SetWidescreen(Settings.widescreen); } - GuiButton okBtn(&okBtnImg, &okBtnImg, 0, 4, 5, -15, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton okBtn(&okBtnImg, &okBtnImg, 0, 4, 5, -15, &trigA, btnSoundOver, btnSoundClick2, 1); okBtn.SetLabel(&okBtnTxt); GuiText cancelBtnTxt(tr( "Cancel" ), 22, Theme.prompttext); GuiImage cancelBtnImg(&btnOutline); @@ -98,7 +94,7 @@ int OnScreenNumpad(char * var, u32 maxlen) cancelBtnTxt.SetWidescreen(Settings.widescreen); cancelBtnImg.SetWidescreen(Settings.widescreen); } - GuiButton cancelBtn(&cancelBtnImg, &cancelBtnImg, 1, 4, -5, -15, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton cancelBtn(&cancelBtnImg, &cancelBtnImg, 1, 4, -5, -15, &trigA, btnSoundOver, btnSoundClick2, 1); cancelBtn.SetLabel(&cancelBtnTxt); cancelBtn.SetTrigger(&trigB); @@ -148,11 +144,6 @@ int OnScreenKeyboard(char * var, u32 maxlen, int min) GuiKeyboard keyboard(var, maxlen, min, Settings.keyset); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiTrigger trigA; @@ -167,7 +158,7 @@ int OnScreenKeyboard(char * var, u32 maxlen, int min) okBtnTxt.SetWidescreen(Settings.widescreen); okBtnImg.SetWidescreen(Settings.widescreen); } - GuiButton okBtn(&okBtnImg, &okBtnImg, 0, 4, 5, 15, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton okBtn(&okBtnImg, &okBtnImg, 0, 4, 5, 15, &trigA, btnSoundOver, btnSoundClick2, 1); okBtn.SetLabel(&okBtnTxt); GuiText cancelBtnTxt(tr( "Cancel" ), 22, Theme.prompttext); GuiImage cancelBtnImg(&btnOutline); @@ -176,7 +167,7 @@ int OnScreenKeyboard(char * var, u32 maxlen, int min) cancelBtnTxt.SetWidescreen(Settings.widescreen); cancelBtnImg.SetWidescreen(Settings.widescreen); } - GuiButton cancelBtn(&cancelBtnImg, &cancelBtnImg, 1, 4, -5, 15, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton cancelBtn(&cancelBtnImg, &cancelBtnImg, 1, 4, -5, 15, &trigA, btnSoundOver, btnSoundClick2, 1); cancelBtn.SetLabel(&cancelBtnTxt); cancelBtn.SetTrigger(&trigB); @@ -523,10 +514,6 @@ int WindowPrompt(const char *title, const char *msg, const char *btn1Label, cons promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData dialogBox(Resources::GetFile("dialogue_box.png"), Resources::GetFileSize("dialogue_box.png")); @@ -557,7 +544,7 @@ int WindowPrompt(const char *title, const char *msg, const char *btn1Label, cons btn1Img.SetWidescreen(Settings.widescreen); } - GuiButton btn1(&btn1Img, &btn1Img, 0, 3, 0, 0, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn1(&btn1Img, &btn1Img, 0, 3, 0, 0, &trigA, btnSoundOver, btnSoundClick2, 1); btn1.SetLabel(&btn1Txt); btn1.SetState(STATE_SELECTED); @@ -568,7 +555,7 @@ int WindowPrompt(const char *title, const char *msg, const char *btn1Label, cons btn2Txt.SetWidescreen(Settings.widescreen); btn2Img.SetWidescreen(Settings.widescreen); } - GuiButton btn2(&btn2Img, &btn2Img, 0, 3, 0, 0, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn2(&btn2Img, &btn2Img, 0, 3, 0, 0, &trigA, btnSoundOver, btnSoundClick2, 1); btn2.SetLabel(&btn2Txt); if (!btn3Label && !btn4Label) btn2.SetTrigger(&trigB); @@ -579,7 +566,7 @@ int WindowPrompt(const char *title, const char *msg, const char *btn1Label, cons btn3Txt.SetWidescreen(Settings.widescreen); btn3Img.SetWidescreen(Settings.widescreen); } - GuiButton btn3(&btn3Img, &btn3Img, 0, 3, 0, 0, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn3(&btn3Img, &btn3Img, 0, 3, 0, 0, &trigA, btnSoundOver, btnSoundClick2, 1); btn3.SetLabel(&btn3Txt); if (!btn4Label) btn3.SetTrigger(&trigB); @@ -590,7 +577,7 @@ int WindowPrompt(const char *title, const char *msg, const char *btn1Label, cons btn4Txt.SetWidescreen(Settings.widescreen); btn4Img.SetWidescreen(Settings.widescreen); } - GuiButton btn4(&btn4Img, &btn4Img, 0, 3, 0, 0, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn4(&btn4Img, &btn4Img, 0, 3, 0, 0, &trigA, btnSoundOver, btnSoundClick2, 1); btn4.SetLabel(&btn4Txt); if (btn4Label) btn4.SetTrigger(&trigB); @@ -823,11 +810,6 @@ int WindowExitPrompt() GuiWindow promptWindow(640, 480); promptWindow.SetAlignment(ALIGN_LEFT, ALIGN_TOP); promptWindow.SetPosition(0, 0); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData top(exit_top_png, exit_top_png_size); GuiImageData topOver(exit_top_over_png, exit_top_over_png_size); GuiImageData bottom(exit_bottom_png, exit_bottom_png_size); @@ -911,7 +893,7 @@ int WindowExitPrompt() GuiImage btn1Img(&top); GuiImage btn1OverImg(&topOver); - GuiButton btn1(&btn1Img, &btn1OverImg, 0, 3, 0, 0, &trigA, &btnSoundOver, btnClick2, 0); + GuiButton btn1(&btn1Img, &btn1OverImg, 0, 3, 0, 0, &trigA, btnSoundOver, btnSoundClick2, 0); btn1.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_IN, 50); GuiText btn2Txt(tr( "Back to Loader" ), 28, ( GXColor ) @@ -922,7 +904,7 @@ int WindowExitPrompt() btn2Txt.SetWidescreen(Settings.widescreen); btn2Img.SetWidescreen(Settings.widescreen); } - GuiButton btn2(&btn2Img, &btn2Img, 2, 5, -150, 0, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn2(&btn2Img, &btn2Img, 2, 5, -150, 0, &trigA, btnSoundOver, btnSoundClick2, 1); btn2.SetLabel(&btn2Txt); btn2.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, 50); btn2.SetRumble(false); @@ -936,7 +918,7 @@ int WindowExitPrompt() btn3Txt.SetWidescreen(Settings.widescreen); btn3Img.SetWidescreen(Settings.widescreen); } - GuiButton btn3(&btn3Img, &btn3Img, 2, 5, 150, 0, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn3(&btn3Img, &btn3Img, 2, 5, 150, 0, &trigA, btnSoundOver, btnSoundClick2, 1); btn3.SetLabel(&btn3Txt); btn3.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, 50); btn3.SetRumble(false); @@ -944,7 +926,7 @@ int WindowExitPrompt() GuiImage btn4Img(&bottom); GuiImage btn4OverImg(&bottomOver); - GuiButton btn4(&btn4Img, &btn4OverImg, 0, 4, 0, 0, &trigA, &btnSoundOver, btnClick2, 0); + GuiButton btn4(&btn4Img, &btn4OverImg, 0, 4, 0, 0, &trigA, btnSoundOver, btnSoundClick2, 0); btn4.SetTrigger(&trigB); btn4.SetTrigger(&trigHome); btn4.SetEffect(EFFECT_SLIDE_BOTTOM | EFFECT_SLIDE_IN, 50); @@ -1156,10 +1138,6 @@ int GameWindowPrompt() GuiWindow promptWindow(472, 320); promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); @@ -1209,8 +1187,8 @@ int GameWindowPrompt() // nameBtn.SetLabelOver(&nameTxt); nameBtn.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); nameBtn.SetPosition(0, -122); - nameBtn.SetSoundOver(&btnSoundOver); - nameBtn.SetSoundClick(btnClick2); + nameBtn.SetSoundOver(btnSoundOver); + nameBtn.SetSoundClick(btnSoundClick2); if (!mountMethod) nameBtn.SetToolTip(&nameBtnTT, 24, -30, ALIGN_LEFT); if (Settings.godmode == 1 && !mountMethod) @@ -1244,8 +1222,8 @@ int GameWindowPrompt() btn1.SetPosition(0, -20); btn1.SetImage(&diskImg); - btn1.SetSoundOver(&btnSoundOver); - btn1.SetSoundClick(btnClick2); + btn1.SetSoundOver(btnSoundOver); + btn1.SetSoundClick(btnSoundClick2); btn1.SetTrigger(&trigA); btn1.SetState(STATE_SELECTED); @@ -1256,7 +1234,7 @@ int GameWindowPrompt() btn2Txt.SetWidescreen(Settings.widescreen); btn2Img.SetWidescreen(Settings.widescreen); } - GuiButton btn2(&btn2Img, &btn2Img, 1, 5, 0, 0, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn2(&btn2Img, &btn2Img, 1, 5, 0, 0, &trigA, btnSoundOver, btnSoundClick2, 1); if (Settings.godmode == 1 && mountMethod != 2 && mountMethod != 3) { btn2.SetAlignment(ALIGN_RIGHT, ALIGN_BOTTOM); @@ -1278,7 +1256,7 @@ int GameWindowPrompt() btn3Txt.SetWidescreen(Settings.widescreen); btn3Img.SetWidescreen(Settings.widescreen); } - GuiButton btn3(&btn3Img, &btn3Img, 0, 4, 50, -40, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn3(&btn3Img, &btn3Img, 0, 4, 50, -40, &trigA, btnSoundOver, btnSoundClick2, 1); btn3.SetLabel(&btn3Txt); GuiImage btnFavoriteImg1; @@ -1292,25 +1270,25 @@ int GameWindowPrompt() GuiImage btnFavoriteImg5; btnFavoriteImg5.SetWidescreen(Settings.widescreen); - //GuiButton btnFavorite(&btnFavoriteImg,&btnFavoriteImg, 2, 5, -125, -60, &trigA, &btnSoundOver, &btnClick,1); + //GuiButton btnFavorite(&btnFavoriteImg,&btnFavoriteImg, 2, 5, -125, -60, &trigA, btnSoundOver, &btnClick,1); GuiButton btnFavorite1(imgFavorite.GetWidth(), imgFavorite.GetHeight()); GuiButton btnFavorite2(imgFavorite.GetWidth(), imgFavorite.GetHeight()); GuiButton btnFavorite3(imgFavorite.GetWidth(), imgFavorite.GetHeight()); GuiButton btnFavorite4(imgFavorite.GetWidth(), imgFavorite.GetHeight()); GuiButton btnFavorite5(imgFavorite.GetWidth(), imgFavorite.GetHeight()); - SetupFavoriteButton(&btnFavorite1, -198, &btnFavoriteImg1, &btnSoundOver, btnClick2, &trigA); - SetupFavoriteButton(&btnFavorite2, -171, &btnFavoriteImg2, &btnSoundOver, btnClick2, &trigA); - SetupFavoriteButton(&btnFavorite3, -144, &btnFavoriteImg3, &btnSoundOver, btnClick2, &trigA); - SetupFavoriteButton(&btnFavorite4, -117, &btnFavoriteImg4, &btnSoundOver, btnClick2, &trigA); - SetupFavoriteButton(&btnFavorite5, -90, &btnFavoriteImg5, &btnSoundOver, btnClick2, &trigA); + SetupFavoriteButton(&btnFavorite1, -198, &btnFavoriteImg1, btnSoundOver, btnSoundClick2, &trigA); + SetupFavoriteButton(&btnFavorite2, -171, &btnFavoriteImg2, btnSoundOver, btnSoundClick2, &trigA); + SetupFavoriteButton(&btnFavorite3, -144, &btnFavoriteImg3, btnSoundOver, btnSoundClick2, &trigA); + SetupFavoriteButton(&btnFavorite4, -117, &btnFavoriteImg4, btnSoundOver, btnSoundClick2, &trigA); + SetupFavoriteButton(&btnFavorite5, -90, &btnFavoriteImg5, btnSoundOver, btnSoundClick2, &trigA); GuiImage btnLeftImg(&imgLeft); if (Settings.wsprompt) { btnLeftImg.SetWidescreen(Settings.widescreen); } - GuiButton btnLeft(&btnLeftImg, &btnLeftImg, 0, 5, 20, 0, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btnLeft(&btnLeftImg, &btnLeftImg, 0, 5, 20, 0, &trigA, btnSoundOver, btnSoundClick2, 1); btnLeft.SetTrigger(&trigL); btnLeft.SetTrigger(&trigMinus); @@ -1319,7 +1297,7 @@ int GameWindowPrompt() { btnRightImg.SetWidescreen(Settings.widescreen); } - GuiButton btnRight(&btnRightImg, &btnRightImg, 1, 5, -20, 0, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btnRight(&btnRightImg, &btnRightImg, 1, 5, -20, 0, &trigA, btnSoundOver, btnSoundClick2, 1); btnRight.SetTrigger(&trigR); btnRight.SetTrigger(&trigPlus); @@ -1387,7 +1365,7 @@ int GameWindowPrompt() const u8 *gameSoundData = LoadBannerSound(header->id, &gameSoundDataLen); if (gameSoundData) { - gameSound = new GuiSound(gameSoundData, gameSoundDataLen, Settings.gamesoundvolume, false, true); + gameSound = new GuiSound(gameSoundData, gameSoundDataLen, Settings.gamesoundvolume, true); bgMusic->SetVolume(0); if (Settings.gamesound == 2) gameSound->SetLoop(1); gameSound->Play(); @@ -1619,7 +1597,7 @@ int GameWindowPrompt() { promptWindow.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 50); changed = 1; - btnClick2->Play(); + btnSoundClick2->Play(); gameSelected = (gameSelected + 1) % gameList.size(); btnRight.ResetState(); break; @@ -1629,7 +1607,7 @@ int GameWindowPrompt() { promptWindow.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 50); changed = 2; - btnClick2->Play(); + btnSoundClick2->Play(); gameSelected = (gameSelected - 1 + gameList.size()) % gameList.size(); btnLeft.ResetState(); break; @@ -1639,7 +1617,7 @@ int GameWindowPrompt() { promptWindow.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 50); changed = 2; - btnClick2->Play(); + btnSoundClick2->Play(); gameSelected = (gameSelected - 1 + gameList.size()) % gameList.size(); btnRight.ResetState(); break; @@ -1649,7 +1627,7 @@ int GameWindowPrompt() { promptWindow.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 50); changed = 1; - btnClick2->Play(); + btnSoundClick2->Play(); gameSelected = (gameSelected + 1) % gameList.size(); btnLeft.ResetState(); break; @@ -1659,7 +1637,7 @@ int GameWindowPrompt() { promptWindow.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 50); changed = 2; - btnClick2->Play(); + btnSoundClick2->Play(); gameSelected = (gameSelected + 1) % gameList.size(); btnRight.ResetState(); break; @@ -1669,7 +1647,7 @@ int GameWindowPrompt() { promptWindow.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 50); changed = 1; - btnClick2->Play(); + btnSoundClick2->Play(); gameSelected = (gameSelected - 1 + gameList.size()) % gameList.size(); btnLeft.ResetState(); break; @@ -1679,7 +1657,7 @@ int GameWindowPrompt() { promptWindow.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 50); changed = 1; - btnClick2->Play(); + btnSoundClick2->Play(); gameSelected = (gameSelected - 1 + gameList.size()) % gameList.size(); btnRight.ResetState(); break; @@ -1689,7 +1667,7 @@ int GameWindowPrompt() { promptWindow.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 50); changed = 2; - btnClick2->Play(); + btnSoundClick2->Play(); gameSelected = (gameSelected + 1) % gameList.size(); btnLeft.ResetState(); break; @@ -1699,7 +1677,7 @@ int GameWindowPrompt() { // diskImg.SetBetaRotateEffect(45, 90); changed = 3; - btnClick2->Play(); + btnSoundClick2->Play(); gameSelected = (gameSelected + 1) % gameList.size(); btnRight.ResetState(); break; @@ -1710,7 +1688,7 @@ int GameWindowPrompt() // diskImg.SetBetaRotateEffect(-45, 90); // promptWindow.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 1/*50*/); changed = 4; - btnClick2->Play(); + btnSoundClick2->Play(); gameSelected = (gameSelected - 1 + gameList.size()) % gameList.size(); btnLeft.ResetState(); break; @@ -1752,9 +1730,6 @@ int DiscWait(const char *title, const char *msg, const char *btn1Label, const ch GuiWindow promptWindow(472, 320); promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData dialogBox(Resources::GetFile("dialogue_box.png"), Resources::GetFileSize("dialogue_box.png")); @@ -1784,7 +1759,7 @@ int DiscWait(const char *title, const char *msg, const char *btn1Label, const ch btn1Txt.SetWidescreen(Settings.widescreen); btn1Img.SetWidescreen(Settings.widescreen); } - GuiButton btn1(&btn1Img, &btn1Img, 1, 5, 0, 0, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn1(&btn1Img, &btn1Img, 1, 5, 0, 0, &trigA, btnSoundOver, btnSoundClick2, 1); if (btn2Label) { @@ -1808,7 +1783,7 @@ int DiscWait(const char *title, const char *msg, const char *btn1Label, const ch btn2Txt.SetWidescreen(Settings.widescreen); btn2Img.SetWidescreen(Settings.widescreen); } - GuiButton btn2(&btn2Img, &btn2Img, 1, 4, -20, -25, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn2(&btn2Img, &btn2Img, 1, 4, -20, -25, &trigA, btnSoundOver, btnSoundClick2, 1); btn2.SetLabel(&btn2Txt); if (Settings.wsprompt && Settings.widescreen) /////////////adjust buttons for widescreen @@ -1971,11 +1946,6 @@ bool SearchMissingImages(int choice2) promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData dialogBox(Resources::GetFile("dialogue_box.png"), Resources::GetFileSize("dialogue_box.png")); GuiTrigger trigA; @@ -2103,11 +2073,6 @@ bool NetworkInitPrompt() promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData dialogBox(Resources::GetFile("dialogue_box.png"), Resources::GetFileSize("dialogue_box.png")); GuiTrigger trigA; @@ -2136,7 +2101,7 @@ bool NetworkInitPrompt() btn1Txt.SetWidescreen(Settings.widescreen); btn1Img.SetWidescreen(Settings.widescreen); } - GuiButton btn1(&btn1Img, &btn1Img, 2, 4, 0, -45, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn1(&btn1Img, &btn1Img, 2, 4, 0, -45, &trigA, btnSoundOver, btnSoundClick2, 1); btn1.SetLabel(&btn1Txt); btn1.SetState(STATE_SELECTED); @@ -2209,11 +2174,6 @@ int ProgressDownloadWindow(int choice2) promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData dialogBox(Resources::GetFile("dialogue_box.png"), Resources::GetFileSize("dialogue_box.png")); GuiTrigger trigA; @@ -2268,7 +2228,7 @@ int ProgressDownloadWindow(int choice2) btn1Txt.SetWidescreen(Settings.widescreen); btn1Img.SetWidescreen(Settings.widescreen); } - GuiButton btn1(&btn1Img, &btn1Img, 2, 4, 0, -45, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn1(&btn1Img, &btn1Img, 2, 4, 0, -45, &trigA, btnSoundOver, btnSoundClick2, 1); btn1.SetLabel(&btn1Txt); btn1.SetState(STATE_SELECTED); @@ -2812,11 +2772,6 @@ int ProgressUpdateWindow() promptWindow.SetAlignment( ALIGN_CENTRE, ALIGN_MIDDLE ); promptWindow.SetPosition( 0, -10 ); - GuiSound btnSoundOver( button_over_pcm, button_over_pcm_size, Settings.sfxvolume ); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if ( !btnClick2 ) btnClick2 = new GuiSound( button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume ); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, SOUND_PCM, Settings.sfxvolume); - char imgPath[100]; snprintf( imgPath, sizeof( imgPath ), "%sbutton_dialogue_box.png", Settings.theme_path ); GuiImageData btnOutline( imgPath, button_dialogue_box_png ); @@ -2880,7 +2835,7 @@ int ProgressUpdateWindow() btn1Txt.SetWidescreen( Settings.widescreen ); btn1Img.SetWidescreen( Settings.widescreen ); } - GuiButton btn1( &btn1Img, &btn1Img, 2, 4, 0, -40, &trigA, &btnSoundOver, btnClick2, 1 ); + GuiButton btn1( &btn1Img, &btn1Img, 2, 4, 0, -40, &trigA, btnSoundOver, btnSoundClick2, 1 ); btn1.SetLabel( &btn1Txt ); btn1.SetState( STATE_SELECTED ); @@ -3180,11 +3135,6 @@ int ProgressUpdateWindow() promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData dialogBox(Resources::GetFile("dialogue_box.png"), Resources::GetFileSize("dialogue_box.png")); GuiTrigger trigA; @@ -3242,7 +3192,7 @@ int ProgressUpdateWindow() btn1Txt.SetWidescreen(Settings.widescreen); btn1Img.SetWidescreen(Settings.widescreen); } - GuiButton btn1(&btn1Img, &btn1Img, 2, 4, 0, -40, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn1(&btn1Img, &btn1Img, 2, 4, 0, -40, &trigA, btnSoundOver, btnSoundClick2, 1); btn1.SetLabel(&btn1Txt); btn1.SetState(STATE_SELECTED); @@ -3575,11 +3525,6 @@ int CodeDownload(const char *id) promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData dialogBox(Resources::GetFile("dialogue_box.png"), Resources::GetFileSize("dialogue_box.png")); GuiTrigger trigA; @@ -3613,7 +3558,7 @@ int CodeDownload(const char *id) btn1Txt.SetWidescreen(Settings.widescreen); btn1Img.SetWidescreen(Settings.widescreen); } - GuiButton btn1(&btn1Img, &btn1Img, 2, 4, 0, -40, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn1(&btn1Img, &btn1Img, 2, 4, 0, -40, &trigA, btnSoundOver, btnSoundClick2, 1); btn1.SetLabel(&btn1Txt); btn1.SetState(STATE_SELECTED); @@ -3760,11 +3705,6 @@ int HBCWindowPrompt(const char *name, const char *coder, const char *version, co GuiTrigger trigD; trigD.SetButtonOnlyTrigger(-1, WPAD_BUTTON_DOWN | WPAD_CLASSIC_BUTTON_DOWN, PAD_BUTTON_DOWN); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData dialogBox(Resources::GetFile("dialogue_box.png"), Resources::GetFileSize("dialogue_box.png")); GuiImageData whiteBox(Resources::GetFile("bg_options.png"), Resources::GetFileSize("bg_options.png")); @@ -3790,7 +3730,7 @@ int HBCWindowPrompt(const char *name, const char *coder, const char *version, co arrowUpBtn.SetTrigger(&trigA); arrowUpBtn.SetTrigger(&trigU); arrowUpBtn.SetEffectOnOver(EFFECT_SCALE, 50, 130); - arrowUpBtn.SetSoundClick(btnClick2); + arrowUpBtn.SetSoundClick(btnSoundClick2); GuiButton arrowDownBtn(arrowDownImg.GetWidth(), arrowDownImg.GetHeight()); arrowDownBtn.SetImage(&arrowDownImg); @@ -3799,7 +3739,7 @@ int HBCWindowPrompt(const char *name, const char *coder, const char *version, co arrowDownBtn.SetTrigger(&trigA); arrowDownBtn.SetTrigger(&trigD); arrowDownBtn.SetEffectOnOver(EFFECT_SCALE, 50, 130); - arrowDownBtn.SetSoundClick(btnClick2); + arrowDownBtn.SetSoundClick(btnSoundClick2); GuiImageData *iconData = NULL; GuiImage *iconImg = NULL; @@ -3874,7 +3814,7 @@ int HBCWindowPrompt(const char *name, const char *coder, const char *version, co btn1Img.SetWidescreen(Settings.widescreen); } - GuiButton btn1(&btn1Img, &btn1Img, 0, 3, 0, 0, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn1(&btn1Img, &btn1Img, 0, 3, 0, 0, &trigA, btnSoundOver, btnSoundClick2, 1); btn1.SetLabel(&btn1Txt); btn1.SetState(STATE_SELECTED); @@ -3885,7 +3825,7 @@ int HBCWindowPrompt(const char *name, const char *coder, const char *version, co btn2Txt.SetWidescreen(Settings.widescreen); btn2Img.SetWidescreen(Settings.widescreen); } - GuiButton btn2(&btn2Img, &btn2Img, 0, 3, 0, 0, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn2(&btn2Img, &btn2Img, 0, 3, 0, 0, &trigA, btnSoundOver, btnSoundClick2, 1); btn2.SetLabel(&btn2Txt); btn2.SetTrigger(&trigB); diff --git a/source/prompts/TitleBrowser.cpp b/source/prompts/TitleBrowser.cpp index d002d91a..4d4a24c8 100644 --- a/source/prompts/TitleBrowser.cpp +++ b/source/prompts/TitleBrowser.cpp @@ -124,10 +124,6 @@ bool TitleSelector(char output[]) bool exit = false; - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData settingsbg(Resources::GetFile("settings_background.png"), Resources::GetFileSize("settings_background.png")); @@ -150,7 +146,7 @@ bool TitleSelector(char output[]) cancelBtnTxt.SetWidescreen(Settings.widescreen); cancelBtnImg.SetWidescreen(Settings.widescreen); } - GuiButton cancelBtn(&cancelBtnImg, &cancelBtnImg, 2, 3, 180, 400, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton cancelBtn(&cancelBtnImg, &cancelBtnImg, 2, 3, 180, 400, &trigA, btnSoundOver, btnSoundClick2, 1); cancelBtn.SetLabel(&cancelBtnTxt); cancelBtn.SetTrigger(&trigB); @@ -350,11 +346,6 @@ int TitleBrowser() if (IsNetworkInit()) ResumeNetworkWait(); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData settingsbg(Resources::GetFile("settings_background.png"), Resources::GetFileSize("settings_background.png")); @@ -385,7 +376,7 @@ int TitleBrowser() cancelBtnTxt.SetWidescreen(Settings.widescreen); cancelBtnImg.SetWidescreen(Settings.widescreen); } - GuiButton cancelBtn(&cancelBtnImg, &cancelBtnImg, 2, 3, 180, 400, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton cancelBtn(&cancelBtnImg, &cancelBtnImg, 2, 3, 180, 400, &trigA, btnSoundOver, btnSoundClick2, 1); cancelBtn.SetScale(0.9); cancelBtn.SetLabel(&cancelBtnTxt); cancelBtn.SetTrigger(&trigB); diff --git a/source/prompts/filebrowser.cpp b/source/prompts/filebrowser.cpp index 33ff1b64..b309c810 100644 --- a/source/prompts/filebrowser.cpp +++ b/source/prompts/filebrowser.cpp @@ -295,21 +295,11 @@ int BrowseDevice(char * Path, int Path_size, int Flags, FILTERCASCADE *Filter/*= } int menu = MENU_NONE; - /* - GuiText titleTxt("Browse Files", 28, (GXColor){0, 0, 0, 230}); - titleTxt.SetAlignment(ALIGN_LEFT, ALIGN_TOP); - titleTxt.SetPosition(70,20); - */ GuiTrigger trigA; trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); GuiTrigger trigB; trigB.SetButtonOnlyTrigger(-1, WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B, PAD_BUTTON_B); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData folderImgData(Resources::GetFile("icon_folder.png"), Resources::GetFileSize("icon_folder.png")); GuiImage folderImg(&folderImgData); GuiButton folderBtn(folderImg.GetWidth(), folderImg.GetHeight()); @@ -320,8 +310,7 @@ int BrowseDevice(char * Path, int Path_size, int Flags, FILTERCASCADE *Filter/*= folderBtn.SetEffectGrow(); GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); - GuiText ExitBtnTxt(tr( "Cancel" ), 24, ( GXColor ) - { 0, 0, 0, 255}); + GuiText ExitBtnTxt(tr( "Cancel" ), 24, ( GXColor ) {0, 0, 0, 255}); GuiImage ExitBtnImg(&btnOutline); if (Settings.wsprompt) { @@ -337,8 +326,7 @@ int BrowseDevice(char * Path, int Path_size, int Flags, FILTERCASCADE *Filter/*= ExitBtn.SetTrigger(&trigB); ExitBtn.SetEffectGrow(); - GuiText usbBtnTxt(browsers[(curDevice + 1) % browsers.size()].rootdir, 24, ( GXColor ) - { 0, 0, 0, 255}); + GuiText usbBtnTxt(browsers[(curDevice + 1) % browsers.size()].rootdir, 24, ( GXColor ) {0, 0, 0, 255}); GuiImage usbBtnImg(&btnOutline); if (Settings.wsprompt) { @@ -360,7 +348,7 @@ int BrowseDevice(char * Path, int Path_size, int Flags, FILTERCASCADE *Filter/*= okBtnTxt.SetWidescreen(Settings.widescreen); okBtnImg.SetWidescreen(Settings.widescreen); } - GuiButton okBtn(&okBtnImg, &okBtnImg, 0, 4, 40, -35, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton okBtn(&okBtnImg, &okBtnImg, 0, 4, 40, -35, &trigA, btnSoundOver, btnSoundClick2, 1); okBtn.SetLabel(&okBtnTxt); GuiFileBrowser fileBrowser(396, 248); @@ -368,8 +356,7 @@ int BrowseDevice(char * Path, int Path_size, int Flags, FILTERCASCADE *Filter/*= fileBrowser.SetPosition(0, 120); GuiImageData Address(Resources::GetFile("addressbar_textbox.png"), Resources::GetFileSize("addressbar_textbox.png")); - GuiText AdressText((char*) NULL, 20, ( GXColor ) - { 0, 0, 0, 255}); + GuiText AdressText((char*) NULL, 20, ( GXColor ) {0, 0, 0, 255}); AdressText.SetTextf("%s%s", browser->rootdir, browser->dir); AdressText.SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); AdressText.SetPosition(20, 0); diff --git a/source/prompts/gameinfo.cpp b/source/prompts/gameinfo.cpp index aead4724..559138cf 100644 --- a/source/prompts/gameinfo.cpp +++ b/source/prompts/gameinfo.cpp @@ -137,9 +137,6 @@ int showGameInfo(char *ID) txtWindow.SetAlignment(ALIGN_CENTRE, ALIGN_RIGHT); txtWindow.SetPosition(95, 55); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - 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")); @@ -305,7 +302,7 @@ int showGameInfo(char *ID) gameinfoWindow.Append(dialogBoxImg2); gameinfoWindow.Append(dialogBoxImg3); gameinfoWindow.Append(dialogBoxImg4); - + char imgPath[150]; snprintf(imgPath, sizeof(imgPath), "%s%s.png", Settings.covers_path, ID); cover = new GuiImageData(imgPath); //load full id image diff --git a/source/settings/Settings.cpp b/source/settings/Settings.cpp index 9345515f..c7ac0a43 100644 --- a/source/settings/Settings.cpp +++ b/source/settings/Settings.cpp @@ -1,2253 +1,25 @@ #include #include - -#include "usbloader/wbfs.h" -#include "language/gettext.h" -#include "libwiigui/gui.h" -#include "libwiigui/gui_customoptionbrowser.h" -#include "prompts/PromptWindows.h" -#include "prompts/DiscBrowser.h" -#include "settings/SettingsPrompts.h" -#include "settings/CGameSettings.h" -#include "settings/CGameStatistics.h" -#include "settings/GameTitles.h" -#include "prompts/filebrowser.h" -#include "cheats/cheatmenu.h" -#include "themes/CTheme.h" -#include "fatmounter.h" -#include "menu.h" -#include "menu/menus.h" -#include "filelist.h" -#include "FileOperations/fileops.h" -#include "sys.h" -#include "usbloader/partition_usbloader.h" -#include "usbloader/utils.h" -#include "system/IosLoader.h" -#include "xml/xml.h" -#include "wad/nandtitle.h" -#include "prompts/TitleBrowser.h" - -#define MAXOPTIONS 13 - -/*** Extern functions ***/ -extern void ResumeGui(); -extern void HaltGui(); -extern void titles_default(); - -/*** Extern variables ***/ -extern GuiWindow * mainWindow; -extern GuiBGM * bgMusic; -extern GuiImage * bgImg; -extern GuiImageData * pointer[4]; -extern GuiImageData * background; -extern u8 shutdown; -extern u8 reset; -extern u8 mountMethod; -extern struct discHdr *dvdheader; -extern PartList partitions; -extern char game_partition[6]; -extern u8 load_from_fs; - -static const char *opts_no_yes[MAX_ON_OFF] = { trNOOP( "No" ), trNOOP( "Yes" ) }; -static const char *opts_off_on[MAX_ON_OFF] = { trNOOP( "OFF" ), trNOOP( "ON" ) }; -static const char *opts_videomode[VIDEO_MODE_MAX][2] = { { "", trNOOP( "Disc Default" ) }, { - trNOOP( "System Default" ), "" }, { trNOOP( "AutoPatch" ), "" }, { trNOOP( "Force" ), " PAL50" }, { - trNOOP( "Force" ), " PAL60" }, { trNOOP( "Force" ), " NTSC" } }; -static const char *opts_language[MAX_LANGUAGE] = { trNOOP( "App Default" ), trNOOP( "Console Default" ), trNOOP( "Japanese" ), - trNOOP( "English" ), trNOOP( "German" ), trNOOP( "French" ), trNOOP( "Spanish" ), trNOOP( "Italian" ), - trNOOP( "Dutch" ), trNOOP( "SChinese" ), trNOOP( "TChinese" ), trNOOP( "Korean" ) }; -static const char *opts_lockedgames[2] = { trNOOP( "0 (Locked and Unlocked Games)" ), trNOOP( "1 (Unlocked Games Only)" ) }; -static const char *opts_parentalcontrol[5] = { trNOOP( "0 (Everyone)" ), trNOOP( "1 (Child 7+)" ), - trNOOP( "2 (Teen 12+)" ), trNOOP( "3 (Mature 16+)" ), trNOOP( "4 (Adults Only 18+)" ) }; -static const char *opts_error002[3] = { trNOOP( "No" ), trNOOP( "Yes" ), trNOOP( "Anti" ) }; -static const char *opts_partitions[3] = { trNOOP( "Game partition" ), trNOOP( "All partitions" ), - trNOOP( "Remove update" ) }; -static const char *opts_installdir[INSTALL_TO_MAX] = { trNOOP( "None" ), trNOOP( "GAMEID_Gamename" ), - trNOOP( "Gamename [GAMEID]" ) }; - - -static inline bool IsValidPartition(int fs_type, int cios) -{ - if (IosLoader::IsWaninkokoIOS() && NandTitles.VersionOf(TITLE_ID(1, cios)) < 18) - { - return fs_type == FS_TYPE_WBFS; - } - else - { - return fs_type == FS_TYPE_WBFS || fs_type == FS_TYPE_FAT32 || fs_type == FS_TYPE_NTFS; - } -} +#include "settings/menus/GlobalSettings.hpp" +#include "settings/menus/GameSettingsMenu.hpp" /**************************************************************************** * MenuSettings ***************************************************************************/ int MenuSettings() { - int menu = MENU_NONE; - int ret; - int choice = 0; - bool exit = false; + GlobalSettings * Menu = new GlobalSettings(); + mainWindow->Append(Menu); - // backup game language setting - char opt_lang[100]; - strcpy(opt_lang, Settings.language_path); - // backup title override setting - int opt_override = Settings.titlesOverride; - // backup partition index - u8 settingspartitionold = Settings.partition; + Menu->ShowMenu(); - enum - { - FADE, LEFT, RIGHT - }; + int returnMenu = MENU_NONE; - int slidedirection = FADE; + while((returnMenu = Menu->MainLoop()) == MENU_NONE); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiSound btnClick1(button_click_pcm, button_click_pcm_size, Settings.sfxvolume); + delete Menu; - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); - GuiImageData settingsbg(Resources::GetFile("settings_background.png"), Resources::GetFileSize("settings_background.png")); - - GuiImageData MainButtonImgData(Resources::GetFile("settings_title.png"), Resources::GetFileSize("settings_title.png")); - - GuiImageData MainButtonImgOverData(Resources::GetFile("settings_title_over.png"), Resources::GetFileSize("settings_title_over.png")); - - GuiImageData PageindicatorImgData(Resources::GetFile("pageindicator.png"), Resources::GetFileSize("pageindicator.png")); - - GuiImageData arrow_left(Resources::GetFile("startgame_arrow_left.png"), Resources::GetFileSize("startgame_arrow_left.png")); - - GuiImageData arrow_right(Resources::GetFile("startgame_arrow_right.png"), Resources::GetFileSize("startgame_arrow_right.png")); - - GuiImageData creditsImgData(Resources::GetFile("credits_button.png"), Resources::GetFileSize("credits_button.png")); - - GuiImageData creditsOver(Resources::GetFile("credits_button_over.png"), Resources::GetFileSize("credits_button_over.png")); - - GuiImage creditsImg(&creditsImgData); - GuiImage creditsImgOver(&creditsOver); - - GuiTrigger trigA; - trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); - GuiTrigger trigHome; - trigHome.SetButtonOnlyTrigger(-1, WPAD_BUTTON_HOME | WPAD_CLASSIC_BUTTON_HOME, 0); - GuiTrigger trigB; - trigB.SetButtonOnlyTrigger(-1, WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B, PAD_BUTTON_B); - GuiTrigger trigL; - trigL.SetButtonOnlyTrigger(-1, WPAD_BUTTON_LEFT | WPAD_CLASSIC_BUTTON_LEFT, PAD_BUTTON_LEFT); - GuiTrigger trigR; - trigR.SetButtonOnlyTrigger(-1, WPAD_BUTTON_RIGHT | WPAD_CLASSIC_BUTTON_RIGHT, PAD_BUTTON_RIGHT); - GuiTrigger trigMinus; - trigMinus.SetButtonOnlyTrigger(-1, WPAD_BUTTON_MINUS | WPAD_CLASSIC_BUTTON_MINUS, 0); - GuiTrigger trigPlus; - trigPlus.SetButtonOnlyTrigger(-1, WPAD_BUTTON_PLUS | WPAD_CLASSIC_BUTTON_PLUS, 0); - - GuiText titleTxt(tr( "Settings" ), 28, ( GXColor ) {0, 0, 0, 255}); - titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - titleTxt.SetPosition(0, 40); - - GuiImage settingsbackground(&settingsbg); - - GuiText backBtnTxt(tr( "Back" ), 22, Theme.prompttext); - backBtnTxt.SetMaxWidth(btnOutline.GetWidth() - 30); - GuiImage backBtnImg(&btnOutline); - if (Settings.wsprompt == ON) - { - backBtnTxt.SetWidescreen(Settings.widescreen); - backBtnImg.SetWidescreen(Settings.widescreen); - } - GuiButton backBtn(&backBtnImg, &backBtnImg, 2, 3, -180, 400, &trigA, &btnSoundOver, btnClick2, 1); - backBtn.SetLabel(&backBtnTxt); - backBtn.SetTrigger(&trigB); - - GuiButton homo(1, 1); - homo.SetTrigger(&trigHome); - - GuiImage PageindicatorImg1(&PageindicatorImgData); - GuiText PageindicatorTxt1("1", 22, ( GXColor ) {0, 0, 0, 255}); - GuiButton PageIndicatorBtn1(PageindicatorImg1.GetWidth(), PageindicatorImg1.GetHeight()); - PageIndicatorBtn1.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - PageIndicatorBtn1.SetPosition(165, 400); - PageIndicatorBtn1.SetImage(&PageindicatorImg1); - PageIndicatorBtn1.SetLabel(&PageindicatorTxt1); - PageIndicatorBtn1.SetSoundOver(&btnSoundOver); - PageIndicatorBtn1.SetSoundClick(&btnClick1); - PageIndicatorBtn1.SetTrigger(&trigA); - PageIndicatorBtn1.SetEffectGrow(); - - GuiImage PageindicatorImg2(&PageindicatorImgData); - GuiText PageindicatorTxt2("2", 22, ( GXColor ) {0, 0, 0, 255}); - GuiButton PageIndicatorBtn2(PageindicatorImg2.GetWidth(), PageindicatorImg2.GetHeight()); - PageIndicatorBtn2.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - PageIndicatorBtn2.SetPosition(200, 400); - PageIndicatorBtn2.SetImage(&PageindicatorImg2); - PageIndicatorBtn2.SetLabel(&PageindicatorTxt2); - PageIndicatorBtn2.SetSoundOver(&btnSoundOver); - PageIndicatorBtn2.SetSoundClick(&btnClick1); - PageIndicatorBtn2.SetTrigger(&trigA); - PageIndicatorBtn2.SetEffectGrow(); - - GuiImage PageindicatorImg3(&PageindicatorImgData); - GuiText PageindicatorTxt3("3", 22, ( GXColor ) {0, 0, 0, 255}); - GuiButton PageIndicatorBtn3(PageindicatorImg3.GetWidth(), PageindicatorImg3.GetHeight()); - PageIndicatorBtn3.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - PageIndicatorBtn3.SetPosition(235, 400); - PageIndicatorBtn3.SetImage(&PageindicatorImg3); - PageIndicatorBtn3.SetLabel(&PageindicatorTxt3); - PageIndicatorBtn3.SetSoundOver(&btnSoundOver); - PageIndicatorBtn3.SetSoundClick(&btnClick1); - PageIndicatorBtn3.SetTrigger(&trigA); - PageIndicatorBtn3.SetEffectGrow(); - - GuiImage GoLeftImg(&arrow_left); - GuiButton GoLeftBtn(GoLeftImg.GetWidth(), GoLeftImg.GetHeight()); - GoLeftBtn.SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); - GoLeftBtn.SetPosition(25, -25); - GoLeftBtn.SetImage(&GoLeftImg); - GoLeftBtn.SetSoundOver(&btnSoundOver); - GoLeftBtn.SetSoundClick(btnClick2); - GoLeftBtn.SetEffectGrow(); - GoLeftBtn.SetTrigger(&trigA); - GoLeftBtn.SetTrigger(&trigL); - GoLeftBtn.SetTrigger(&trigMinus); - - GuiImage GoRightImg(&arrow_right); - GuiButton GoRightBtn(GoRightImg.GetWidth(), GoRightImg.GetHeight()); - GoRightBtn.SetAlignment(ALIGN_RIGHT, ALIGN_MIDDLE); - GoRightBtn.SetPosition(-25, -25); - GoRightBtn.SetImage(&GoRightImg); - GoRightBtn.SetSoundOver(&btnSoundOver); - GoRightBtn.SetSoundClick(btnClick2); - GoRightBtn.SetEffectGrow(); - GoRightBtn.SetTrigger(&trigA); - GoRightBtn.SetTrigger(&trigR); - GoRightBtn.SetTrigger(&trigPlus); - - char MainButtonText[50]; - snprintf(MainButtonText, sizeof(MainButtonText), "%s", " "); - - GuiImage MainButton1Img(&MainButtonImgData); - GuiImage MainButton1ImgOver(&MainButtonImgOverData); - GuiText MainButton1Txt(MainButtonText, 22, ( GXColor ) {0, 0, 0, 255}); - MainButton1Txt.SetMaxWidth(MainButton1Img.GetWidth()); - GuiButton MainButton1(MainButton1Img.GetWidth(), MainButton1Img.GetHeight()); - MainButton1.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - MainButton1.SetPosition(0, 90); - MainButton1.SetImage(&MainButton1Img); - MainButton1.SetImageOver(&MainButton1ImgOver); - MainButton1.SetLabel(&MainButton1Txt); - MainButton1.SetSoundOver(&btnSoundOver); - MainButton1.SetSoundClick(&btnClick1); - MainButton1.SetEffectGrow(); - MainButton1.SetTrigger(&trigA); - - GuiImage MainButton2Img(&MainButtonImgData); - GuiImage MainButton2ImgOver(&MainButtonImgOverData); - GuiText MainButton2Txt(MainButtonText, 22, ( GXColor ) {0, 0, 0, 255}); - MainButton2Txt.SetMaxWidth(MainButton2Img.GetWidth()); - GuiButton MainButton2(MainButton2Img.GetWidth(), MainButton2Img.GetHeight()); - MainButton2.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - MainButton2.SetPosition(0, 160); - MainButton2.SetImage(&MainButton2Img); - MainButton2.SetImageOver(&MainButton2ImgOver); - MainButton2.SetLabel(&MainButton2Txt); - MainButton2.SetSoundOver(&btnSoundOver); - MainButton2.SetSoundClick(&btnClick1); - MainButton2.SetEffectGrow(); - MainButton2.SetTrigger(&trigA); - - GuiImage MainButton3Img(&MainButtonImgData); - GuiImage MainButton3ImgOver(&MainButtonImgOverData); - GuiText MainButton3Txt(MainButtonText, 22, ( GXColor ) {0, 0, 0, 255}); - MainButton3Txt.SetMaxWidth(MainButton3Img.GetWidth()); - GuiButton MainButton3(MainButton3Img.GetWidth(), MainButton3Img.GetHeight()); - MainButton3.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - MainButton3.SetPosition(0, 230); - MainButton3.SetImage(&MainButton3Img); - MainButton3.SetImageOver(&MainButton3ImgOver); - MainButton3.SetLabel(&MainButton3Txt); - MainButton3.SetSoundOver(&btnSoundOver); - MainButton3.SetSoundClick(&btnClick1); - MainButton3.SetEffectGrow(); - MainButton3.SetTrigger(&trigA); - - GuiImage MainButton4Img(&MainButtonImgData); - GuiImage MainButton4ImgOver(&MainButtonImgOverData); - GuiText MainButton4Txt(MainButtonText, 22, ( GXColor ) {0, 0, 0, 255}); - MainButton4Txt.SetMaxWidth(MainButton4Img.GetWidth()); - GuiButton MainButton4(MainButton4Img.GetWidth(), MainButton4Img.GetHeight()); - MainButton4.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - MainButton4.SetPosition(0, 300); - MainButton4.SetImage(&MainButton4Img); - MainButton4.SetImageOver(&MainButton4ImgOver); - MainButton4.SetLabel(&MainButton4Txt); - MainButton4.SetSoundOver(&btnSoundOver); - MainButton4.SetSoundClick(&btnClick1); - MainButton4.SetEffectGrow(); - MainButton4.SetTrigger(&trigA); - - OptionList options2; - GuiCustomOptionBrowser optionBrowser2(396, 280, &options2, "bg_options_settings.png", 0, 150); - optionBrowser2.SetPosition(0, 90); - optionBrowser2.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - - GuiWindow w(screenwidth, screenheight); - - int pageToDisplay = 1; - while (pageToDisplay > 0) - { - usleep(100); - - menu = MENU_NONE; - - if (pageToDisplay == 1) - { - /** Standard procedure made in all pages **/ - MainButton1.StopEffect(); - MainButton2.StopEffect(); - MainButton3.StopEffect(); - MainButton4.StopEffect(); - - if (slidedirection == RIGHT) - { - MainButton1.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 35); - MainButton2.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 35); - MainButton3.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 35); - MainButton4.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 35); - while (MainButton1.GetEffect() > 0) - usleep(50); - } - else if (slidedirection == LEFT) - { - MainButton1.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 35); - MainButton2.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 35); - MainButton3.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 35); - MainButton4.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 35); - while (MainButton1.GetEffect() > 0) - usleep(50); - } - - HaltGui(); - - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( "GUI Settings" )); - MainButton1Txt.SetText(MainButtonText); - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( "Game Load" )); - MainButton2Txt.SetText(MainButtonText); - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( "Parental Control" )); - MainButton3Txt.SetText(MainButtonText); - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( "Sound" )); - MainButton4Txt.SetText(MainButtonText); - - mainWindow->RemoveAll(); - mainWindow->Append(&w); - w.RemoveAll(); - w.Append(&settingsbackground); - w.Append(&PageIndicatorBtn1); - w.Append(&PageIndicatorBtn2); - w.Append(&PageIndicatorBtn3); - w.Append(&titleTxt); - w.Append(&backBtn); - w.Append(&homo); - w.Append(&GoRightBtn); - w.Append(&GoLeftBtn); - w.Append(&MainButton1); - w.Append(&MainButton2); - w.Append(&MainButton3); - w.Append(&MainButton4); - - PageIndicatorBtn1.SetAlpha(255); - PageIndicatorBtn2.SetAlpha(50); - PageIndicatorBtn3.SetAlpha(50); - - /** Creditsbutton change **/ - MainButton4.SetImage(&MainButton4Img); - MainButton4.SetImageOver(&MainButton4ImgOver); - - /** Disable ability to click through MainButtons */ - optionBrowser2.SetClickable(false); - /** Default no scrollbar and reset position **/ - // optionBrowser2.SetScrollbar(0); - optionBrowser2.SetOffset(0); - - MainButton1.StopEffect(); - MainButton2.StopEffect(); - MainButton3.StopEffect(); - MainButton4.StopEffect(); - - MainButton1.SetEffectGrow(); - MainButton2.SetEffectGrow(); - MainButton3.SetEffectGrow(); - MainButton4.SetEffectGrow(); - - if (slidedirection == FADE) - { - MainButton1.SetEffect(EFFECT_FADE, 20); - MainButton2.SetEffect(EFFECT_FADE, 20); - MainButton3.SetEffect(EFFECT_FADE, 20); - MainButton4.SetEffect(EFFECT_FADE, 20); - } - else if (slidedirection == LEFT) - { - MainButton1.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, 35); - MainButton2.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, 35); - MainButton3.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, 35); - MainButton4.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, 35); - } - else if (slidedirection == RIGHT) - { - MainButton1.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, 35); - MainButton2.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, 35); - MainButton3.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, 35); - MainButton4.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, 35); - } - - mainWindow->Append(&w); - - ResumeGui(); - - while (MainButton1.GetEffect() > 0) - usleep(50); - - } - else if (pageToDisplay == 2) - { - /** Standard procedure made in all pages **/ - MainButton1.StopEffect(); - MainButton2.StopEffect(); - MainButton3.StopEffect(); - MainButton4.StopEffect(); - - if (slidedirection == RIGHT) - { - MainButton1.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 35); - MainButton2.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 35); - MainButton3.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 35); - MainButton4.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 35); - while (MainButton1.GetEffect() > 0) - usleep(50); - } - else if (slidedirection == LEFT) - { - MainButton1.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 35); - MainButton2.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 35); - MainButton3.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 35); - MainButton4.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 35); - while (MainButton1.GetEffect() > 0) - usleep(50); - } - - HaltGui(); - - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( "Custom Paths" )); - MainButton1Txt.SetText(MainButtonText); - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( "Update" )); - MainButton2Txt.SetText(MainButtonText); - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( "Default Settings" )); - MainButton3Txt.SetText(MainButtonText); - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( "Credits" )); - MainButton4Txt.SetText(MainButtonText); - - mainWindow->RemoveAll(); - mainWindow->Append(&w); - w.RemoveAll(); - w.Append(&settingsbackground); - w.Append(&PageIndicatorBtn1); - w.Append(&PageIndicatorBtn2); - w.Append(&PageIndicatorBtn3); - w.Append(&titleTxt); - w.Append(&backBtn); - w.Append(&homo); - w.Append(&GoRightBtn); - w.Append(&GoLeftBtn); - w.Append(&MainButton1); - w.Append(&MainButton2); - w.Append(&MainButton3); - w.Append(&MainButton4); - - PageIndicatorBtn1.SetAlpha(50); - PageIndicatorBtn2.SetAlpha(255); - PageIndicatorBtn3.SetAlpha(50); - - /** Creditsbutton change **/ - MainButton4.SetImage(&creditsImg); - MainButton4.SetImageOver(&creditsImgOver); - - /** Disable ability to click through MainButtons */ - optionBrowser2.SetClickable(false); - /** Default no scrollbar and reset position **/ - // optionBrowser2.SetScrollbar(0); - optionBrowser2.SetOffset(0); - - MainButton1.StopEffect(); - MainButton2.StopEffect(); - MainButton3.StopEffect(); - MainButton4.StopEffect(); - - MainButton1.SetEffectGrow(); - MainButton2.SetEffectGrow(); - MainButton3.SetEffectGrow(); - MainButton4.SetEffectGrow(); - - if (slidedirection == FADE) - { - MainButton1.SetEffect(EFFECT_FADE, 20); - MainButton2.SetEffect(EFFECT_FADE, 20); - MainButton3.SetEffect(EFFECT_FADE, 20); - MainButton4.SetEffect(EFFECT_FADE, 20); - } - else if (slidedirection == LEFT) - { - MainButton1.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, 35); - MainButton2.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, 35); - MainButton3.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, 35); - MainButton4.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, 35); - } - else if (slidedirection == RIGHT) - { - MainButton1.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, 35); - MainButton2.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, 35); - MainButton3.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, 35); - MainButton4.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, 35); - } - - mainWindow->Append(&w); - - ResumeGui(); - - while (MainButton1.GetEffect() > 0) - usleep(50); - - } - else if (pageToDisplay == 3) - { - /** Standard procedure made in all pages **/ - MainButton1.StopEffect(); - MainButton2.StopEffect(); - MainButton3.StopEffect(); - MainButton4.StopEffect(); - - if (slidedirection == RIGHT) - { - MainButton1.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 35); - MainButton2.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 35); - MainButton3.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 35); - MainButton4.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 35); - while (MainButton1.GetEffect() > 0) - usleep(50); - } - else if (slidedirection == LEFT) - { - MainButton1.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 35); - MainButton2.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 35); - MainButton3.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 35); - MainButton4.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 35); - while (MainButton1.GetEffect() > 0) - usleep(50); - } - - HaltGui(); - - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( "Theme Downloader" )); - MainButton1Txt.SetText(MainButtonText); - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( " " )); - MainButton2Txt.SetText(MainButtonText); - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( " " )); - MainButton3Txt.SetText(MainButtonText); - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( " " )); - MainButton4Txt.SetText(MainButtonText); - - mainWindow->RemoveAll(); - mainWindow->Append(&w); - w.RemoveAll(); - w.Append(&settingsbackground); - w.Append(&PageIndicatorBtn1); - w.Append(&PageIndicatorBtn2); - w.Append(&PageIndicatorBtn3); - w.Append(&titleTxt); - w.Append(&backBtn); - w.Append(&homo); - w.Append(&GoRightBtn); - w.Append(&GoLeftBtn); - w.Append(&MainButton1); - - PageIndicatorBtn1.SetAlpha(50); - PageIndicatorBtn2.SetAlpha(50); - PageIndicatorBtn3.SetAlpha(255); - - /** Disable ability to click through MainButtons */ - optionBrowser2.SetClickable(false); - /** Default no scrollbar and reset position **/ - // optionBrowser2.SetScrollbar(0); - optionBrowser2.SetOffset(0); - - MainButton1.StopEffect(); - MainButton2.StopEffect(); - MainButton3.StopEffect(); - MainButton4.StopEffect(); - - MainButton1.SetEffectGrow(); - MainButton2.SetEffectGrow(); - MainButton3.SetEffectGrow(); - MainButton4.SetEffectGrow(); - - if (slidedirection == FADE) - { - MainButton1.SetEffect(EFFECT_FADE, 20); - MainButton2.SetEffect(EFFECT_FADE, 20); - MainButton3.SetEffect(EFFECT_FADE, 20); - MainButton4.SetEffect(EFFECT_FADE, 20); - } - else if (slidedirection == LEFT) - { - MainButton1.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, 35); - MainButton2.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, 35); - MainButton3.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, 35); - MainButton4.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, 35); - } - else if (slidedirection == RIGHT) - { - MainButton1.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, 35); - MainButton2.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, 35); - MainButton3.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, 35); - MainButton4.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, 35); - } - - mainWindow->Append(&w); - - ResumeGui(); - - while (MainButton1.GetEffect() > 0) - usleep(50); - } - - while (menu == MENU_NONE) - { - usleep(100); - - if (shutdown == 1) Sys_Shutdown(); - if (reset == 1) Sys_Reboot(); - - if (pageToDisplay == 1) - { - if (MainButton1.GetState() == STATE_CLICKED) - { - MainButton1.SetEffect(EFFECT_FADE, -20); - MainButton2.SetEffect(EFFECT_FADE, -20); - MainButton3.SetEffect(EFFECT_FADE, -20); - MainButton4.SetEffect(EFFECT_FADE, -20); - while (MainButton1.GetEffect() > 0) - usleep(50); - HaltGui(); - w.Remove(&PageIndicatorBtn1); - w.Remove(&PageIndicatorBtn2); - w.Remove(&PageIndicatorBtn3); - w.Remove(&GoRightBtn); - w.Remove(&GoLeftBtn); - w.Remove(&MainButton1); - w.Remove(&MainButton2); - w.Remove(&MainButton3); - w.Remove(&MainButton4); - titleTxt.SetText(tr( "GUI Settings" )); - exit = false; - options2.ClearList(); - // optionBrowser2.SetScrollbar(1); - w.Append(&optionBrowser2); - optionBrowser2.SetClickable(true); - ResumeGui(); - - optionBrowser2.SetEffect(EFFECT_FADE, 20); - while (optionBrowser2.GetEffect() > 0) - usleep(50); - - int returnhere = 1; - const char * languagefile = strrchr(Settings.language_path, '/'); - if(languagefile) - languagefile += 1; - else - languagefile = tr("Default"); - - bool firstRun = true; - while (!exit) - { - usleep(100); - - returnhere = 1; - - if (shutdown == 1) Sys_Shutdown(); - if (reset == 1) - Sys_Reboot(); - - else if (backBtn.GetState() == STATE_CLICKED) - { - backBtn.ResetState(); - exit = true; - break; - } - - else if (menu == MENU_DISCLIST) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - WindowCredits(); - w.Append(&optionBrowser2); - w.Append(&backBtn); - } - - else if (homo.GetState() == STATE_CLICKED) - { - Settings.Save(); - optionBrowser2.SetState(STATE_DISABLED); - bgMusic->Pause(); - choice = WindowExitPrompt(); - bgMusic->Resume(); - if (choice == 3) - Sys_LoadMenu(); // Back to System Menu - else if (choice == 2) - Sys_BackToLoader(); - else homo.ResetState(); - optionBrowser2.SetState(STATE_DEFAULT); - } - - ret = optionBrowser2.GetClickedOption(); - - if (firstRun || ret >= 0) - { - int Idx = -1; - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "App Language" )); - if (ret == Idx) - { - if (isInserted(Settings.BootDevice)) - { - if (Settings.godmode == 1) - { - w.SetEffect(EFFECT_FADE, -20); - while (w.GetEffect() > 0) - usleep(50); - mainWindow->Remove(&w); - while (returnhere == 1) - returnhere = MenuLanguageSelect(); - if (returnhere == 2) - { - menu = MENU_SETTINGS; - pageToDisplay = 0; - exit = true; - mainWindow->Append(&w); - break; - } - else - { - HaltGui(); - mainWindow->Append(&w); - w.SetEffect(EFFECT_FADE, 20); - ResumeGui(); - while (w.GetEffect() > 0) - usleep(50); - } - } - else - { - WindowPrompt(tr( "Language change:" ), - tr( "Console should be unlocked to modify it." ), tr( "OK" )); - } - } - else - { - WindowPrompt(tr( "No SD-Card inserted!" ), - tr( "Insert an SD-Card to use this option." ), tr( "OK" )); - } - } - - if (!strcmp("notset", Settings.language_path)) - options2.SetValue(Idx, "%s", tr( "Default" )); - else options2.SetValue(Idx, "%s", languagefile); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Display" )); - if (ret == Idx && ++Settings.sinfo >= GAMEINFO_MAX) Settings.sinfo = 0; - static const char *opts[GAMEINFO_MAX] = { trNOOP( "Game ID" ), - trNOOP( "Game Region" ), trNOOP( "Both" ), trNOOP( "Neither" ) }; - options2.SetValue(Idx, "%s", tr( opts[Settings.sinfo] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Clock" )); - if (ret == Idx && ++Settings.hddinfo >= CLOCK_MAX) Settings.hddinfo = 0; //CLOCK - if (Settings.hddinfo == CLOCK_HR12) - options2.SetValue(Idx, "12 %s", tr( "Hour" )); - else if (Settings.hddinfo == CLOCK_HR24) - options2.SetValue(Idx, "24 %s", tr( "Hour" )); - else if (Settings.hddinfo == OFF) options2.SetValue(Idx, "%s", tr( "OFF" )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Tooltips" )); - if (ret == Idx && ++Settings.tooltips >= MAX_ON_OFF) Settings.tooltips = 0; - options2.SetValue(Idx, "%s", tr( opts_off_on[Settings.tooltips] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Flip-X" )); - if (ret == Idx && ++Settings.xflip >= XFLIP_MAX) Settings.xflip = 0; - static const char *opts[XFLIP_MAX][3] = - { { trNOOP( "Right" ), "/", trNOOP( "Next" ) }, { trNOOP( "Left" ), "/", - trNOOP( "Prev" ) }, { trNOOP( "Like SysMenu" ), "", "" }, { - trNOOP( "Right" ), "/", trNOOP( "Prev" ) }, { trNOOP( "DiskFlip" ), - "", "" } }; - options2.SetValue(Idx, "%s%s%s", tr( opts[Settings.xflip][0] ), - opts[Settings.xflip][1], tr( opts[Settings.xflip][2] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Prompts Buttons" )); - if (ret == Idx && ++Settings.wsprompt >= MAX_ON_OFF) Settings.wsprompt = 0; - static const char *opts[MAX_ON_OFF] = { trNOOP( "Normal" ), - trNOOP( "Widescreen Fix" ) }; - options2.SetValue(Idx, "%s", tr( opts[Settings.wsprompt] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Keyboard" )); - if (ret == Idx && ++Settings.keyset >= KEYBOARD_MAX) Settings.keyset = 0; - static const char *opts[KEYBOARD_MAX] = { "QWERTY", "DVORAK", - "QWERTZ", "AZERTY", "QWERTY 2" }; - options2.SetValue(Idx, "%s", opts[Settings.keyset]); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Disc Artwork Download" )); - if (ret == Idx && ++Settings.discart >= 4) Settings.discart = 0; - static const char *opts[4] = { trNOOP( "Only Original" ), trNOOP( "Only Customs" ), - trNOOP( "Original/Customs" ), trNOOP( "Customs/Original" ) }; - options2.SetValue(Idx, "%s", tr( opts[Settings.discart] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Wiilight" )); - if (ret == Idx && ++Settings.wiilight >= WIILIGHT_MAX) Settings.wiilight = 0; - static const char *opts[WIILIGHT_MAX] = { trNOOP( "OFF" ), trNOOP( "ON" ), - trNOOP( "Only for Install" ) }; - options2.SetValue(Idx, "%s", tr( opts[Settings.wiilight] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Rumble" )); - if (ret == Idx && ++Settings.rumble >= MAX_ON_OFF) Settings.rumble = 0; //RUMBLE - options2.SetValue(Idx, "%s", tr( opts_off_on[Settings.rumble] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "AutoInit Network" )); - if (ret == Idx && ++Settings.autonetwork >= MAX_ON_OFF) Settings.autonetwork - = 0; - options2.SetValue(Idx, "%s", tr( opts_off_on[Settings.autonetwork] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "BETA revisions" )); - if (ret == Idx && ++Settings.beta_upgrades >= MAX_ON_OFF) Settings.beta_upgrades - = 0; - options2.SetValue(Idx, "%s", tr( opts_off_on[Settings.beta_upgrades] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Titles from WiiTDB" )); - if (ret == Idx && ++Settings.titlesOverride >= MAX_ON_OFF) Settings.titlesOverride - = 0; - options2.SetValue(Idx, "%s", tr( opts_off_on[Settings.titlesOverride] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Screensaver" )); - if (ret == Idx && ++Settings.screensaver >= SCREENSAVER_MAX) Settings.screensaver - = 0; //RUMBLE - static const char *opts[SCREENSAVER_MAX] = { trNOOP( "OFF" ), - trNOOP( "3 min" ), trNOOP( "5 min" ), trNOOP( "10 min" ), trNOOP( "20 min" ), - trNOOP( "30 min" ), trNOOP( "1 hour" ) }; - options2.SetValue(Idx, "%s", tr( opts[Settings.screensaver] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Mark new games" )); - if (ret == Idx && ++Settings.marknewtitles >= MAX_ON_OFF) Settings.marknewtitles - = 0; - options2.SetValue(Idx, "%s", tr( opts_off_on[Settings.marknewtitles] )); - } - - firstRun = false; - } - } - optionBrowser2.SetEffect(EFFECT_FADE, -20); - while (optionBrowser2.GetEffect() > 0) - usleep(50); - titleTxt.SetText(tr( "Settings" )); - slidedirection = FADE; - if (returnhere != 2) pageToDisplay = 1; - MainButton1.ResetState(); - break; - } - - else if (MainButton2.GetState() == STATE_CLICKED) - { - MainButton1.SetEffect(EFFECT_FADE, -20); - MainButton2.SetEffect(EFFECT_FADE, -20); - MainButton3.SetEffect(EFFECT_FADE, -20); - MainButton4.SetEffect(EFFECT_FADE, -20); - while (MainButton2.GetEffect() > 0) - usleep(50); - HaltGui(); - w.Remove(&PageIndicatorBtn1); - w.Remove(&PageIndicatorBtn2); - w.Remove(&PageIndicatorBtn3); - w.Remove(&GoRightBtn); - w.Remove(&GoLeftBtn); - w.Remove(&MainButton1); - w.Remove(&MainButton2); - w.Remove(&MainButton3); - w.Remove(&MainButton4); - titleTxt.SetText(tr( "Game Load" )); - exit = false; - options2.ClearList(); - w.Append(&optionBrowser2); - optionBrowser2.SetClickable(true); - ResumeGui(); - - optionBrowser2.SetEffect(EFFECT_FADE, 20); - while (optionBrowser2.GetEffect() > 0) - usleep(50); - - bool firstRun = true; - while (!exit) - { - usleep(100); - - if (shutdown == 1) Sys_Shutdown(); - if (reset == 1) - Sys_Reboot(); - - else if (backBtn.GetState() == STATE_CLICKED) - { - backBtn.ResetState(); - exit = true; - break; - } - - else if (homo.GetState() == STATE_CLICKED) - { - Settings.Save(); - optionBrowser2.SetState(STATE_DISABLED); - bgMusic->Pause(); - choice = WindowExitPrompt(); - bgMusic->Resume(); - if (choice == 3) Sys_LoadMenu(); // Back to System Menu - if (choice == 2) - Sys_BackToLoader(); - else homo.ResetState(); - optionBrowser2.SetState(STATE_DEFAULT); - } - - ret = optionBrowser2.GetClickedOption(); - - if (firstRun || ret >= 0) - { - int Idx = -1; - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Video Mode" )); - if (ret == Idx && ++Settings.videomode >= VIDEO_MODE_MAX) Settings.videomode = 0; - options2.SetValue(Idx, "%s%s", opts_videomode[Settings.videomode][0], - tr( opts_videomode[Settings.videomode][1] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "VIDTV Patch" )); - if (ret == Idx && ++Settings.videopatch >= MAX_ON_OFF) Settings.videopatch = 0; - options2.SetValue(Idx, "%s", tr( opts_off_on[Settings.videopatch] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Game Language" )); - if (ret == Idx && ++Settings.language >= MAX_LANGUAGE) Settings.language = 0; - options2.SetValue(Idx, "%s", tr( opts_language[Settings.language] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Patch Country Strings" )); - if (ret == Idx && ++Settings.patchcountrystrings >= MAX_ON_OFF) Settings.patchcountrystrings - = 0; - options2.SetValue(Idx, "%s", tr( opts_off_on[Settings.patchcountrystrings] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "Ocarina"); - if (ret == Idx && ++Settings.ocarina >= MAX_ON_OFF) Settings.ocarina = 0; - options2.SetValue(Idx, "%s", tr( opts_off_on[Settings.ocarina] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Boot/Standard" )); - if (ret == Idx && Settings.godmode == 1) - { - char entered[4]; - snprintf(entered, sizeof(entered), "%i", Settings.cios); - if(OnScreenKeyboard(entered, sizeof(entered), 0)) - { - Settings.cios = atoi(entered); - if(Settings.cios < 200) Settings.cios = 200; - else if(Settings.cios > 255) Settings.cios = 255; - - if(NandTitles.IndexOf(TITLE_ID(1, Settings.cios)) < 0) - { - WindowPrompt(tr("Warning:"), tr("This IOS was not found on the titles list. If you are sure you have it installed than ignore this warning."), tr("OK")); - } - else if(Settings.cios == 254) - { - WindowPrompt(tr("Warning:"), tr("This IOS is the BootMii ios. If you are sure it is not BootMii and you have something else installed there than ignore this warning."), tr("OK")); - } - } - } - if (Settings.godmode == 1) - options2.SetValue(Idx, "IOS %i", Settings.cios); - else options2.SetValue(Idx, "********"); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Partition" )); - if (ret == Idx) - { - // Select the next valid partition, even if that's the same one - int fs_type = partitions.pinfo[Settings.partition].fs_type; - int ios = IOS_GetVersion(); - do - { - Settings.partition = (Settings.partition + 1) % partitions.num; - fs_type = partitions.pinfo[Settings.partition].fs_type; - } - while (!IsValidPartition(fs_type, ios)); - } - - PartInfo pInfo = partitions.pinfo[Settings.partition]; - f32 partition_size = partitions.pentry[Settings.partition].size - * (partitions.sector_size / GB_SIZE); - - // Get the partition name and it's size in GB's - options2.SetValue(Idx, "%s%d (%.2fGB)", pInfo.fs_type == FS_TYPE_FAT32 ? "FAT" - : pInfo.fs_type == FS_TYPE_NTFS ? "NTFS" : "WBFS", pInfo.index, partition_size); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "FAT: Use directories" )); - if (ret == Idx && ++Settings.FatInstallToDir >= INSTALL_TO_MAX) Settings.FatInstallToDir - = 0; - options2.SetValue(Idx, "%s", tr( opts_installdir[Settings.FatInstallToDir] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Quick Boot" )); - if (ret == Idx && ++Settings.quickboot >= MAX_ON_OFF) Settings.quickboot = 0; - options2.SetValue(Idx, "%s", tr( opts_no_yes[Settings.quickboot] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Error 002 fix" )); - if (ret == Idx && ++Settings.error002 >= 3) Settings.error002 = 0; - options2.SetValue(Idx, "%s", tr( opts_error002[Settings.error002] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Install partitions" )); - if (ret == Idx && ++Settings.InstallPartitions >= 3) Settings.InstallPartitions - = 0; - options2.SetValue(Idx, "%s", tr( opts_partitions[Settings.InstallPartitions] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Install 1:1 Copy" )); - if (ret == Idx) - { - Settings.fullcopy = Settings.fullcopy == 0 ? 1 : 0; - } - options2.SetValue(Idx, "%s", tr( opts_no_yes[Settings.fullcopy] )); - } - - if (ret == ++Idx || firstRun) - { - char* name = NULL; - if (firstRun) options2.SetName(Idx, "%s", tr( "Return To" )); - if (ret == Idx) - { - char tidChar[10]; - bool getChannel = TitleSelector(tidChar); - if (getChannel) - { - snprintf(Settings.returnTo, sizeof(Settings.returnTo), "%s", tidChar); - } - } - int haveTitle = NandTitles.FindU32(Settings.returnTo); - if (haveTitle >= 0) - { - name = (char*) NandTitles.NameFromIndex(haveTitle); - if( name && !strlen(name) ) name = NULL; - } - options2.SetValue(Idx, "%s", name ? name : tr( opts_off_on[ 0 ] )); - } - - firstRun = false; - } - } - optionBrowser2.SetEffect(EFFECT_FADE, -20); - while (optionBrowser2.GetEffect() > 0) - usleep(50); - titleTxt.SetText(tr( "Settings" )); - slidedirection = FADE; - pageToDisplay = 1; - MainButton2.ResetState(); - break; - } - - else if (MainButton3.GetState() == STATE_CLICKED) - { - MainButton1.SetEffect(EFFECT_FADE, -20); - MainButton2.SetEffect(EFFECT_FADE, -20); - MainButton3.SetEffect(EFFECT_FADE, -20); - MainButton4.SetEffect(EFFECT_FADE, -20); - while (MainButton3.GetEffect() > 0) - usleep(50); - HaltGui(); - w.Remove(&PageIndicatorBtn1); - w.Remove(&PageIndicatorBtn2); - w.Remove(&PageIndicatorBtn3); - w.Remove(&GoRightBtn); - w.Remove(&GoLeftBtn); - w.Remove(&MainButton1); - w.Remove(&MainButton2); - w.Remove(&MainButton3); - w.Remove(&MainButton4); - titleTxt.SetText(tr( "Parental Control" )); - exit = false; - options2.ClearList(); - w.Append(&optionBrowser2); - optionBrowser2.SetClickable(true); - ResumeGui(); - - optionBrowser2.SetEffect(EFFECT_FADE, 20); - while (optionBrowser2.GetEffect() > 0) - usleep(50); - - bool firstRun = true; - while (!exit) - { - usleep(100); - - if (shutdown == 1) Sys_Shutdown(); - if (reset == 1) - Sys_Reboot(); - - else if (backBtn.GetState() == STATE_CLICKED) - { - backBtn.ResetState(); - exit = true; - break; - } - - else if (homo.GetState() == STATE_CLICKED) - { - Settings.Save(); - optionBrowser2.SetState(STATE_DISABLED); - bgMusic->Pause(); - choice = WindowExitPrompt(); - bgMusic->Resume(); - if (choice == 3) - Sys_LoadMenu(); // Back to System Menu - else if (choice == 2) - Sys_BackToLoader(); - else homo.ResetState(); - optionBrowser2.SetState(STATE_DEFAULT); - } - - ret = optionBrowser2.GetClickedOption(); - - if (firstRun || ret >= 0) - { - - int Idx = -1; - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Console" )); - if (ret == Idx) - { - if (!strcmp("", Settings.unlockCode) && Settings.Parental.enabled == 0) - { - Settings.godmode = !Settings.godmode; - } - else if (Settings.godmode == 0) - { - char entered[20]; - memset(entered, 0, 20); - - //password check to unlock Install,Delete and Format - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - int result = Settings.Parental.enabled == 0 ? OnScreenKeyboard(entered, 20, 0) - : OnScreenNumpad(entered, 5); - w.Append(&optionBrowser2); - w.Append(&backBtn); - if (result == 1) - { - if (!strcmp(entered, Settings.unlockCode) || !memcmp(entered, - Settings.Parental.pin, 4)) //if password correct - { - if (Settings.godmode == 0) - { - WindowPrompt( - tr( "Correct Password" ), - tr( "All the features of USB Loader GX are unlocked." ), - tr( "OK" )); - Settings.godmode = 1; - menu = MENU_DISCLIST; - } - } - else WindowPrompt(tr( "Wrong Password" ), - tr( "USB Loader GX is protected" ), tr( "OK" )); - } - } - else - { - int choice = WindowPrompt(tr( "Lock Console" ), tr( "Are you sure?" ), - tr( "Yes" ), tr( "No" )); - if (choice == 1) - { - WindowPrompt(tr( "Console Locked" ), tr( "USB Loader GX is protected" ), - tr( "OK" )); - Settings.godmode = 0; - menu = MENU_DISCLIST; - } - } - } - static const char *opts[] = { trNOOP( "Locked" ), trNOOP( "Unlocked" ) }; - options2.SetValue(Idx, "%s", tr( opts[Settings.godmode] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Password" )); - if (ret == Idx) - { - if (Settings.godmode == 1) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - char entered[20] = ""; - strlcpy(entered, Settings.unlockCode, sizeof(entered)); - int result = OnScreenKeyboard(entered, 20, 0); - w.Append(&optionBrowser2); - w.Append(&backBtn); - if (result == 1) - { - strlcpy(Settings.unlockCode, entered, sizeof(Settings.unlockCode)); - WindowPrompt(tr( "Password Changed" ), tr( "Password has been changed" ), - tr( "OK" )); - } - } - else - { - WindowPrompt(tr( "Password Changed" ), - tr( "Console should be unlocked to modify it." ), tr( "OK" )); - } - } - if (Settings.godmode != 1) - options2.SetValue(Idx, "********"); - else if (!strcmp("", Settings.unlockCode)) - options2.SetValue(Idx, "%s", tr( "not set" )); - else options2.SetValue(Idx, Settings.unlockCode); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Controllevel" )); - if (ret == Idx && Settings.godmode == 1 && ++Settings.parentalcontrol >= 5) Settings.parentalcontrol - = 0; - if (Settings.godmode == 1) - options2.SetValue(Idx, "%s", tr( opts_parentalcontrol[Settings.parentalcontrol] )); - else options2.SetValue(Idx, "********"); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "GamesLevel" )); - if (ret == Idx && Settings.godmode == 1 && ++Settings.lockedgames >= 2) Settings.lockedgames - = 0; - if (Settings.godmode == 1) - options2.SetValue(Idx, "%s", tr( opts_lockedgames[Settings.lockedgames] )); - else options2.SetValue(Idx, "********"); - } - - firstRun = false; - } - } - optionBrowser2.SetEffect(EFFECT_FADE, -20); - while (optionBrowser2.GetEffect() > 0) - usleep(50); - titleTxt.SetText(tr( "Settings" )); - slidedirection = FADE; - pageToDisplay = 1; - MainButton3.ResetState(); - break; - } - - else if (MainButton4.GetState() == STATE_CLICKED) - { - MainButton1.SetEffect(EFFECT_FADE, -20); - MainButton2.SetEffect(EFFECT_FADE, -20); - MainButton3.SetEffect(EFFECT_FADE, -20); - MainButton4.SetEffect(EFFECT_FADE, -20); - while (MainButton4.GetEffect() > 0) - usleep(50); - HaltGui(); - w.Remove(&PageIndicatorBtn1); - w.Remove(&PageIndicatorBtn2); - w.Remove(&PageIndicatorBtn3); - w.Remove(&GoRightBtn); - w.Remove(&GoLeftBtn); - w.Remove(&MainButton1); - w.Remove(&MainButton2); - w.Remove(&MainButton3); - w.Remove(&MainButton4); - titleTxt.SetText(tr( "Sound" )); - exit = false; - options2.ClearList(); - w.Append(&optionBrowser2); - optionBrowser2.SetClickable(true); - ResumeGui(); - - optionBrowser2.SetEffect(EFFECT_FADE, 20); - while (optionBrowser2.GetEffect() > 0) - usleep(50); - - bool firstRun = true; - while (!exit) - { - usleep(100); - - bool returnhere = true; - - if (shutdown == 1) Sys_Shutdown(); - if (reset == 1) - Sys_Reboot(); - else if (backBtn.GetState() == STATE_CLICKED) - { - backBtn.ResetState(); - exit = true; - break; - } - - else if (homo.GetState() == STATE_CLICKED) - { - Settings.Save(); - optionBrowser2.SetState(STATE_DISABLED); - bgMusic->Pause(); - choice = WindowExitPrompt(); - bgMusic->Resume(); - if (choice == 3) - Sys_LoadMenu(); // Back to System Menu - else if (choice == 2) - Sys_BackToLoader(); - else homo.ResetState(); - optionBrowser2.SetState(STATE_DEFAULT); - } - - ret = optionBrowser2.GetClickedOption(); - - if (firstRun || ret >= 0) - { - int Idx = -1; - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Backgroundmusic" )); - if (ret == Idx) - { - if (isInserted(Settings.BootDevice)) - { - w.SetEffect(EFFECT_FADE, -20); - while (w.GetEffect() > 0) - usleep(50); - mainWindow->Remove(&w); - - returnhere = MenuBackgroundMusic(); - - HaltGui(); - mainWindow->Append(&w); - w.SetEffect(EFFECT_FADE, 20); - ResumeGui(); - while (w.GetEffect() > 0) - usleep(50); - } - else WindowPrompt(tr( "No SD-Card inserted!" ), - tr( "Insert an SD-Card to use this option." ), tr( "OK" )); - } - - char * filename = strrchr(Settings.ogg_path, '/'); - if (filename) - { - filename += 1; - options2.SetValue(Idx, "%s", filename); - } - else options2.SetValue(Idx, "%s", tr( "Standard" )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Music Volume" )); - if (ret == Idx) - { - Settings.volume += 10; - if (Settings.volume > 100) Settings.volume = 0; - bgMusic->SetVolume(Settings.volume); - } - if (Settings.volume > 0) - options2.SetValue(Idx, "%i", Settings.volume); - else options2.SetValue(Idx, "%s", tr( "OFF" )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "SFX Volume" )); - if (ret == Idx) - { - Settings.sfxvolume += 10; - if (Settings.sfxvolume > 100) Settings.sfxvolume = 0; - btnSoundOver.SetVolume(Settings.sfxvolume); - btnClick2->SetVolume(Settings.sfxvolume); - btnClick1.SetVolume(Settings.sfxvolume); - } - if (Settings.sfxvolume > 0) - options2.SetValue(Idx, "%i", Settings.sfxvolume); - else options2.SetValue(Idx, "%s", tr( "OFF" )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Game Sound Mode" )); - if (ret == Idx) - { - Settings.gamesound++; - if (Settings.gamesound > 2) Settings.gamesound = 0; - } - - if (Settings.gamesound == 1) - options2.SetValue(Idx, "%s", tr( "Sound+BGM" )); - else if (Settings.gamesound == 2) - options2.SetValue(Idx, "%s", tr( "Loop Sound" )); - else options2.SetValue(Idx, "%s", tr( "Sound+Quiet" )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Game Sound Volume" )); - if (ret == Idx) - { - Settings.gamesoundvolume += 10; - if (Settings.gamesoundvolume > 100) Settings.gamesoundvolume = 0; - } - - if (Settings.gamesoundvolume > 0) - options2.SetValue(Idx, "%i", Settings.gamesoundvolume); - else options2.SetValue(Idx, "%s", tr( "OFF" )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Music Loop Mode" )); - if (ret == Idx) - { - Settings.musicloopmode++; - if (Settings.musicloopmode > 3) Settings.musicloopmode = 0; - bgMusic->SetLoop(Settings.musicloopmode); - } - - if (Settings.musicloopmode == ONCE) - options2.SetValue(Idx, tr( "Play Once" )); - else if (Settings.musicloopmode == LOOP) - options2.SetValue(Idx, tr( "Loop Music" )); - else if (Settings.musicloopmode == DIR_LOOP) - options2.SetValue(Idx, tr( "Loop Directory" )); - else if (Settings.musicloopmode == RANDOM_BGM) options2.SetValue(Idx, - tr( "Random Directory Music" )); - } - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Reset BG Music" )); - if (ret == Idx) - { - int result = - WindowPrompt(tr( "Reset to standard BGM?" ), 0, tr( "Yes" ), tr( "No" )); - if (result) - { - bgMusic->LoadStandard(); - bgMusic->Play(); - options2.SetValue(Idx, "%s", tr( "Standard" )); - } - } - options2.SetValue(Idx, tr( " " )); - } - - firstRun = false; - } - } - optionBrowser2.SetEffect(EFFECT_FADE, -20); - while (optionBrowser2.GetEffect() > 0) - usleep(50); - titleTxt.SetText(tr( "Settings" )); - slidedirection = FADE; - pageToDisplay = 1; - MainButton4.ResetState(); - break; - } - } - - else if (pageToDisplay == 2) - { - if (MainButton1.GetState() == STATE_CLICKED) - { - MainButton1.SetEffect(EFFECT_FADE, -20); - MainButton2.SetEffect(EFFECT_FADE, -20); - MainButton3.SetEffect(EFFECT_FADE, -20); - MainButton4.SetEffect(EFFECT_FADE, -20); - while (MainButton1.GetEffect() > 0) - usleep(50); - HaltGui(); - w.Remove(&PageIndicatorBtn1); - w.Remove(&PageIndicatorBtn2); - w.Remove(&PageIndicatorBtn3); - w.Remove(&GoRightBtn); - w.Remove(&GoLeftBtn); - w.Remove(&MainButton1); - w.Remove(&MainButton2); - w.Remove(&MainButton3); - w.Remove(&MainButton4); - titleTxt.SetText(tr( "Custom Paths" )); - exit = false; - options2.ClearList(); - // optionBrowser2.SetScrollbar(1); - w.Append(&optionBrowser2); - optionBrowser2.SetClickable(true); - ResumeGui(); - - optionBrowser2.SetEffect(EFFECT_FADE, 20); - while (optionBrowser2.GetEffect() > 0) - usleep(50); - - if (Settings.godmode) - { - bool firstRun = true; - while (!exit) - { - usleep(100); - - if (shutdown == 1) Sys_Shutdown(); - if (reset == 1) - Sys_Reboot(); - - else if (backBtn.GetState() == STATE_CLICKED) - { - backBtn.ResetState(); - exit = true; - break; - } - - else if (homo.GetState() == STATE_CLICKED) - { - Settings.Save(); - optionBrowser2.SetState(STATE_DISABLED); - bgMusic->Pause(); - choice = WindowExitPrompt(); - bgMusic->Resume(); - if (choice == 3) - Sys_LoadMenu(); // Back to System Menu - else if (choice == 2) - Sys_BackToLoader(); - else homo.ResetState(); - optionBrowser2.SetState(STATE_DEFAULT); - } - - ret = optionBrowser2.GetClickedOption(); - - if (firstRun || ret >= 0) - { - - int Idx = -1; - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "3D Cover Path" )); - if (ret == Idx) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - char entered[100] = ""; - strlcpy(entered, Settings.covers_path, sizeof(entered)); - titleTxt.SetText(tr( "3D Cover Path" )); - int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); - titleTxt.SetText(tr( "Custom Paths" )); - w.Append(&optionBrowser2); - w.Append(&backBtn); - if (result == 1) - { - int len = (strlen(entered) - 1); - if (entered[len] != '/') strncat(entered, "/", 1); - strlcpy(Settings.covers_path, entered, sizeof(Settings.covers_path)); - WindowPrompt(tr( "Cover Path Changed" ), 0, tr( "OK" )); - if (!isInserted(Settings.BootDevice)) WindowPrompt(tr( "No SD-Card inserted!" ), - tr( "Insert an SD-Card to save." ), tr( "OK" )); - } - } - options2.SetValue(Idx, "%s", Settings.covers_path); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "2D Cover Path" )); - if (ret == Idx) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - char entered[100] = ""; - strlcpy(entered, Settings.covers2d_path, sizeof(entered)); - titleTxt.SetText(tr( "2D Cover Path" )); - int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); - titleTxt.SetText(tr( "Custom Paths" )); - w.Append(&optionBrowser2); - w.Append(&backBtn); - if (result == 1) - { - int len = (strlen(entered) - 1); - if (entered[len] != '/') strncat(entered, "/", 1); - strlcpy(Settings.covers2d_path, entered, sizeof(Settings.covers2d_path)); - WindowPrompt(tr( "Cover Path Changed" ), 0, tr( "OK" )); - if (!isInserted(Settings.BootDevice)) WindowPrompt(tr( "No SD-Card inserted!" ), - tr( "Insert an SD-Card to save." ), tr( "OK" )); - } - } - options2.SetValue(Idx, "%s", Settings.covers2d_path); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Disc Artwork Path" )); - if (ret == Idx) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - char entered[100] = ""; - strlcpy(entered, Settings.disc_path, sizeof(entered)); - titleTxt.SetText(tr( "Disc Artwork Path" )); - int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); - titleTxt.SetText(tr( "Custom Paths" )); - w.Append(&optionBrowser2); - w.Append(&backBtn); - if (result == 1) - { - int len = (strlen(entered) - 1); - if (entered[len] != '/') strncat(entered, "/", 1); - strlcpy(Settings.disc_path, entered, sizeof(Settings.disc_path)); - WindowPrompt(tr( "Disc Path Changed" ), 0, tr( "OK" )); - if (!isInserted(Settings.BootDevice)) WindowPrompt(tr( "No SD-Card inserted!" ), - tr( "Insert an SD-Card to save." ), tr( "OK" )); - } - } - options2.SetValue(Idx, "%s", Settings.disc_path); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Theme Path" )); - if (ret == Idx) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - char entered[100] = ""; - titleTxt.SetText(tr( "Theme Path" )); - strlcpy(entered, Settings.theme_path, sizeof(entered)); - int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); - HaltGui(); - w.RemoveAll(); - if (result == 1) - { - int len = (strlen(entered) - 1); - if (entered[len] != '/') strncat(entered, "/", 1); - strlcpy(Settings.theme_path, entered, sizeof(Settings.theme_path)); - WindowPrompt(tr( "Theme Path Changed" ), 0, tr( "OK" )); - if (!isInserted(Settings.BootDevice)) - WindowPrompt(tr( "No SD-Card inserted!" ), - tr( "Insert an SD-Card to save." ), tr( "OK" )); - else Settings.Save(); - mainWindow->Remove(bgImg); - HaltGui(); - Theme.Load(Settings.theme_path); - ResumeGui(); - menu = MENU_SETTINGS; - if(pointer[0]) delete pointer[0]; - if(pointer[1]) delete pointer[1]; - if(pointer[2]) delete pointer[2]; - if(pointer[3]) delete pointer[3]; - pointer[0] = Resources::GetImageData("player1_point.png"); - pointer[1] = Resources::GetImageData("player2_point.png"); - pointer[2] = Resources::GetImageData("player3_point.png"); - pointer[3] = Resources::GetImageData("player4_point.png"); - if(background) delete background; - background = Resources::GetImageData(Settings.widescreen ? "wbackground.png" : "background.png"); - if(bgImg) delete bgImg; - bgImg = new GuiImage(background); - mainWindow->Append(bgImg); - mainWindow->Append(&w); - } - w.Append(&settingsbackground); - w.Append(&titleTxt); - titleTxt.SetText(tr( "Custom Paths" )); - w.Append(&backBtn); - w.Append(&optionBrowser2); - ResumeGui(); - } - options2.SetValue(Idx, "%s", Settings.theme_path); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "WiiTDB Path" )); - if (ret == Idx) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - char entered[100] = ""; - titleTxt.SetText(tr( "WiiTDB Path" )); - strlcpy(entered, Settings.titlestxt_path, sizeof(entered)); - int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); - w.Append(&optionBrowser2); - titleTxt.SetText(tr( "Custom Paths" )); - w.Append(&backBtn); - if (result == 1) - { - int len = (strlen(entered) - 1); - if (entered[len] != '/') strncat(entered, "/", 1); - strlcpy(Settings.titlestxt_path, entered, sizeof(Settings.titlestxt_path)); - WindowPrompt(tr( "WiiTDB Path changed." ), 0, tr( "OK" )); - if (isInserted(Settings.BootDevice)) - { - Settings.Save(); - HaltGui(); - Settings.Load(); - ResumeGui(); - } - else WindowPrompt(tr( "No SD-Card inserted!" ), - tr( "Insert an SD-Card to save." ), tr( "OK" )); - } - } - options2.SetValue(Idx, "%s", Settings.titlestxt_path); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Update Path" )); - if (ret == Idx) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - char entered[100] = ""; - strlcpy(entered, Settings.update_path, sizeof(entered)); - titleTxt.SetText(tr( "Update Path" )); - int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); - titleTxt.SetText(tr( "Custom Paths" )); - w.Append(&optionBrowser2); - w.Append(&backBtn); - if (result == 1) - { - int len = (strlen(entered) - 1); - if (entered[len] != '/') strncat(entered, "/", 1); - strlcpy(Settings.update_path, entered, sizeof(Settings.update_path)); - WindowPrompt(tr( "Update Path changed." ), 0, tr( "OK" )); - } - } - options2.SetValue(Idx, "%s", Settings.update_path); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "GCT Cheatcodes Path" )); - if (ret == Idx) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - char entered[100] = ""; - strlcpy(entered, Settings.Cheatcodespath, sizeof(entered)); - titleTxt.SetText(tr( "GCT Cheatcodes Path" )); - int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); - titleTxt.SetText(tr( "Custom Paths" )); - w.Append(&optionBrowser2); - w.Append(&backBtn); - if (result == 1) - { - int len = (strlen(entered) - 1); - if (entered[len] != '/') strncat(entered, "/", 1); - strlcpy(Settings.Cheatcodespath, entered, sizeof(Settings.Cheatcodespath)); - WindowPrompt(tr( "GCT Cheatcodes Path changed" ), 0, tr( "OK" )); - } - } - options2.SetValue(Idx, "%s", Settings.Cheatcodespath); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "TXT Cheatcodes Path" )); - if (ret == Idx) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - char entered[100] = ""; - strlcpy(entered, Settings.TxtCheatcodespath, sizeof(entered)); - titleTxt.SetText(tr( "TXT Cheatcodes Path" )); - int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); - titleTxt.SetText(tr( "Custom Paths" )); - w.Append(&optionBrowser2); - w.Append(&backBtn); - if (result == 1) - { - int len = (strlen(entered) - 1); - if (entered[len] != '/') strncat(entered, "/", 1); - strlcpy(Settings.TxtCheatcodespath, entered, - sizeof(Settings.TxtCheatcodespath)); - WindowPrompt(tr( "TXT Cheatcodes Path changed" ), 0, tr( "OK" )); - } - } - options2.SetValue(Idx, "%s", Settings.TxtCheatcodespath); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "DOL Path" )); - if (ret == Idx) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - char entered[100] = ""; - strlcpy(entered, Settings.dolpath, sizeof(entered)); - titleTxt.SetText(tr( "DOL Path" )); - int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); - titleTxt.SetText(tr( "Custom Paths" )); - w.Append(&optionBrowser2); - w.Append(&backBtn); - if (result == 1) - { - int len = (strlen(entered) - 1); - if (entered[len] != '/') strncat(entered, "/", 1); - strlcpy(Settings.dolpath, entered, sizeof(Settings.dolpath)); - WindowPrompt(tr( "DOL path changed" ), 0, tr( "OK" )); - if (!isInserted(Settings.BootDevice)) - { - WindowPrompt(tr( "No SD-Card inserted!" ), - tr( "Insert an SD-Card to save." ), tr( "OK" )); - } - } - } - options2.SetValue(Idx, "%s", Settings.dolpath); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Homebrew Apps Path" )); - if (ret == Idx) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - char entered[100] = ""; - strlcpy(entered, Settings.homebrewapps_path, sizeof(entered)); - titleTxt.SetText(tr( "Homebrew Apps Path" )); - int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); - titleTxt.SetText(tr( "Custom Paths" )); - w.Append(&optionBrowser2); - w.Append(&backBtn); - if (result == 1) - { - int len = (strlen(entered) - 1); - if (entered[len] != '/') strncat(entered, "/", 1); - strlcpy(Settings.homebrewapps_path, entered, - sizeof(Settings.homebrewapps_path)); - WindowPrompt(tr( "Homebrew Appspath changed" ), 0, tr( "OK" )); - if (!isInserted(Settings.BootDevice)) - { - WindowPrompt(tr( "No SD-Card inserted!" ), - tr( "Insert an SD-Card to save." ), tr( "OK" )); - } - } - } - options2.SetValue(Idx, "%s", Settings.homebrewapps_path); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Theme Download Path" )); - if (ret == Idx) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - char entered[100] = ""; - strlcpy(entered, Settings.theme_downloadpath, sizeof(entered)); - titleTxt.SetText(tr( "Theme Download Path" )); - int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); - titleTxt.SetText(tr( "Custom Paths" )); - w.Append(&optionBrowser2); - w.Append(&backBtn); - if (result == 1) - { - int len = (strlen(entered) - 1); - if (entered[len] != '/') strncat(entered, "/", 1); - strlcpy(Settings.theme_downloadpath, entered, - sizeof(Settings.theme_downloadpath)); - WindowPrompt(tr( "Theme Download Path changed" ), 0, tr( "OK" )); - if (!isInserted(Settings.BootDevice)) WindowPrompt(tr( "No SD-Card inserted!" ), - tr( "Insert an SD-Card to save." ), tr( "OK" )); - } - } - options2.SetValue(Idx, "%s", Settings.theme_downloadpath); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "BCA Codes Path" )); - if (ret == Idx) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - char entered[100] = ""; - strlcpy(entered, Settings.BcaCodepath, sizeof(entered)); - titleTxt.SetText(tr( "BCA Codes Path" )); - int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); - titleTxt.SetText(tr( "Custom Paths" )); - w.Append(&optionBrowser2); - w.Append(&backBtn); - if (result == 1) - { - int len = (strlen(entered) - 1); - if (entered[len] != '/') strncat(entered, "/", 1); - strlcpy(Settings.BcaCodepath, entered, sizeof(Settings.BcaCodepath)); - WindowPrompt(tr( "BCA Codes Path changed" ), 0, tr( "OK" )); - if (!isInserted(Settings.BootDevice)) WindowPrompt(tr( "No SD-Card inserted!" ), - tr( "Insert an SD-Card to save." ), tr( "OK" )); - } - } - options2.SetValue(Idx, "%s", Settings.BcaCodepath); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "WIP Patches Path" )); - if (ret == Idx) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - char entered[100] = ""; - strlcpy(entered, Settings.WipCodepath, sizeof(entered)); - titleTxt.SetText(tr( "WIP Patches Path" )); - int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); - titleTxt.SetText(tr( "Custom Paths" )); - w.Append(&optionBrowser2); - w.Append(&backBtn); - if (result == 1) - { - int len = (strlen(entered) - 1); - if (entered[len] != '/') strncat(entered, "/", 1); - strlcpy(Settings.WipCodepath, entered, sizeof(Settings.WipCodepath)); - WindowPrompt(tr( "WIP Patches Path changed" ), 0, tr( "OK" )); - if (!isInserted(Settings.BootDevice)) WindowPrompt(tr( "No SD-Card inserted!" ), - tr( "Insert an SD-Card to save." ), tr( "OK" )); - } - } - options2.SetValue(Idx, "%s", Settings.WipCodepath); - } - - firstRun = false; - } - } - /** If not godmode don't let him inside **/ - } - else WindowPrompt(tr( "Console Locked" ), tr( "Unlock console to use this option." ), tr( "OK" )); - optionBrowser2.SetEffect(EFFECT_FADE, -20); - while (optionBrowser2.GetEffect() > 0) - usleep(50); - titleTxt.SetText(tr( "Settings" )); - slidedirection = FADE; - pageToDisplay = 2; - MainButton1.ResetState(); - break; - } - - else if (MainButton2.GetState() == STATE_CLICKED) - { - MainButton1.SetEffect(EFFECT_FADE, -20); - MainButton2.SetEffect(EFFECT_FADE, -20); - MainButton3.SetEffect(EFFECT_FADE, -20); - MainButton4.SetEffect(EFFECT_FADE, -20); - while (MainButton2.GetEffect() > 0) - usleep(50); - w.Remove(&PageIndicatorBtn1); - w.Remove(&PageIndicatorBtn2); - w.Remove(&PageIndicatorBtn3); - w.Remove(&GoRightBtn); - w.Remove(&GoLeftBtn); - w.Remove(&MainButton1); - w.Remove(&MainButton2); - w.Remove(&MainButton3); - w.Remove(&MainButton4); - if (isInserted(Settings.BootDevice) && Settings.godmode) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - int ret = ProgressUpdateWindow(); - if (ret < 0) WindowPrompt(tr( "Update failed" ), 0, tr( "OK" )); - w.Append(&optionBrowser2); - w.Append(&backBtn); - } - else WindowPrompt(tr( "Console Locked" ), tr( "Unlock console to use this option." ), tr( "OK" )); - slidedirection = FADE; - pageToDisplay = 2; - MainButton2.ResetState(); - break; - } - - else if (MainButton3.GetState() == STATE_CLICKED) - { - MainButton1.SetEffect(EFFECT_FADE, -20); - MainButton2.SetEffect(EFFECT_FADE, -20); - MainButton3.SetEffect(EFFECT_FADE, -20); - MainButton4.SetEffect(EFFECT_FADE, -20); - while (MainButton3.GetEffect() > 0) - usleep(50); - w.Remove(&PageIndicatorBtn1); - w.Remove(&PageIndicatorBtn2); - w.Remove(&PageIndicatorBtn3); - w.Remove(&GoRightBtn); - w.Remove(&GoLeftBtn); - w.Remove(&MainButton1); - w.Remove(&MainButton2); - w.Remove(&MainButton3); - w.Remove(&MainButton4); - w.Remove(&backBtn); - w.Remove(&optionBrowser2); - if (Settings.godmode) - { - int choice = WindowPrompt(tr( "Are you sure?" ), 0, tr( "Yes" ), tr( "Cancel" )); - if (choice == 1) - { - gettextCleanUp(); - HaltGui(); - Settings.Reset(); - ResumeGui(); - menu = MENU_SETTINGS; - pageToDisplay = 0; - } - } - else WindowPrompt(tr( "Console Locked" ), tr( "Unlock console to use this option." ), tr( "OK" )); - w.Append(&backBtn); - w.Append(&optionBrowser2); - slidedirection = FADE; - pageToDisplay = 2; - MainButton3.ResetState(); - break; - } - - else if (MainButton4.GetState() == STATE_CLICKED) - { - MainButton1.SetEffect(EFFECT_FADE, -20); - MainButton2.SetEffect(EFFECT_FADE, -20); - MainButton3.SetEffect(EFFECT_FADE, -20); - MainButton4.SetEffect(EFFECT_FADE, -20); - while (MainButton4.GetEffect() > 0) - usleep(50); - w.Remove(&PageIndicatorBtn1); - w.Remove(&PageIndicatorBtn2); - w.Remove(&PageIndicatorBtn3); - w.Remove(&GoRightBtn); - w.Remove(&GoLeftBtn); - w.Remove(&MainButton1); - w.Remove(&MainButton2); - w.Remove(&MainButton3); - w.Remove(&MainButton4); - WindowCredits(); - slidedirection = FADE; - pageToDisplay = 2; - MainButton4.ResetState(); - break; - } - } - - else if (pageToDisplay == 3) - { - if (MainButton1.GetState() == STATE_CLICKED) - { - if (isInserted(Settings.BootDevice)) Settings.Save(); - menu = MENU_THEMEDOWNLOADER; - pageToDisplay = 0; - break; - } - } - - if (backBtn.GetState() == STATE_CLICKED) - { - //Add the procedure call to save the global configuration - if (isInserted(Settings.BootDevice)) Settings.Save(); - menu = MENU_DISCLIST; - pageToDisplay = 0; - backBtn.ResetState(); - break; - } - - else if (GoLeftBtn.GetState() == STATE_CLICKED) - { - pageToDisplay--; - /** Change direction of the flying buttons **/ - if (pageToDisplay < 1) pageToDisplay = 3; - slidedirection = LEFT; - GoLeftBtn.ResetState(); - break; - } - - else if (GoRightBtn.GetState() == STATE_CLICKED) - { - pageToDisplay++; - /** Change direction of the flying buttons **/ - if (pageToDisplay > 3) pageToDisplay = 1; - slidedirection = RIGHT; - GoRightBtn.ResetState(); - break; - } - else if (PageIndicatorBtn1.GetState() == STATE_CLICKED) - { - if (pageToDisplay > 1) - { - slidedirection = LEFT; - pageToDisplay = 1; - PageIndicatorBtn1.ResetState(); - break; - } - PageIndicatorBtn1.ResetState(); - } - else if (PageIndicatorBtn2.GetState() == STATE_CLICKED) - { - if (pageToDisplay < 2) - { - slidedirection = RIGHT; - pageToDisplay = 2; - PageIndicatorBtn2.ResetState(); - break; - } - else if (pageToDisplay > 2) - { - slidedirection = LEFT; - pageToDisplay = 2; - PageIndicatorBtn2.ResetState(); - break; - } - else PageIndicatorBtn2.ResetState(); - } - else if (PageIndicatorBtn3.GetState() == STATE_CLICKED) - { - if (pageToDisplay < 3) - { - slidedirection = RIGHT; - pageToDisplay = 3; - PageIndicatorBtn3.ResetState(); - break; - } - else PageIndicatorBtn3.ResetState(); - } - else if (homo.GetState() == STATE_CLICKED) - { - Settings.Save(); - optionBrowser2.SetState(STATE_DISABLED); - bgMusic->Pause(); - choice = WindowExitPrompt(); - bgMusic->Resume(); - - if (choice == 3) - Sys_LoadMenu(); // Back to System Menu - else if (choice == 2) - Sys_BackToLoader(); - else homo.ResetState(); - optionBrowser2.SetState(STATE_DEFAULT); - } - } - } - - w.SetEffect(EFFECT_FADE, -20); - while (w.GetEffect() > 0) - usleep(50); - - // if partition has changed, Reinitialize it - if (Settings.partition != settingspartitionold) - { - PartInfo pinfo = partitions.pinfo[Settings.partition]; - partitionEntry pentry = partitions.pentry[Settings.partition]; - WBFS_OpenPart(pinfo.part_fs, pinfo.index, pentry.sector, pentry.size, (char *) &game_partition); - load_from_fs = pinfo.part_fs; - } - - // if language has changed, reload titles - char opt_langnew[100]; - strcpy(opt_langnew, Settings.language_path); - int opt_overridenew = Settings.titlesOverride; - bool reloaddatabasefile = false; - if (strcmp(opt_lang, opt_langnew) || (opt_override != opt_overridenew && Settings.titlesOverride == 1) - || (Settings.partition != settingspartitionold)) - { - if (Settings.partition != settingspartitionold) - { - reloaddatabasefile = true; - CloseXMLDatabase(); - GameTitles.SetDefault(); - } - OpenXMLDatabase(Settings.titlestxt_path, Settings.db_language, Settings.db_JPtoEN, reloaddatabasefile, - Settings.titlesOverride == 1 ? true : false, true); // open file, reload titles, keep in memory - } - // disable titles from database if setting has changed - if (opt_override != opt_overridenew && Settings.titlesOverride == 0) GameTitles.SetDefault(); - - HaltGui(); - - mainWindow->RemoveAll(); - mainWindow->Append(bgImg); - - ResumeGui(); - return menu; + return returnMenu; } /******************************************************************************** @@ -2255,808 +27,16 @@ int MenuSettings() *********************************************************************************/ int MenuGameSettings(struct discHdr * header) { - int menu = MENU_NONE; - int ret; - int choice = 0; - bool exit = false; + GameSettingsMenu * Menu = new GameSettingsMenu(header); + mainWindow->Append(Menu); - int retVal = 0; + Menu->ShowMenu(); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiSound btnClick1(button_click_pcm, button_click_pcm_size, Settings.sfxvolume); + int returnMenu = MENU_NONE; - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); - GuiImageData settingsbg(Resources::GetFile("settings_background.png"), Resources::GetFileSize("settings_background.png")); + while((returnMenu = Menu->MainLoop()) == MENU_NONE); - GuiImageData MainButtonImgData(Resources::GetFile("settings_title.png"), Resources::GetFileSize("settings_title.png")); + delete Menu; - GuiImageData MainButtonImgOverData(Resources::GetFile("settings_title_over.png"), Resources::GetFileSize("settings_title_over.png")); - - GuiTrigger trigA; - trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); - GuiTrigger trigHome; - trigHome.SetButtonOnlyTrigger(-1, WPAD_BUTTON_HOME | WPAD_CLASSIC_BUTTON_HOME, 0); - GuiTrigger trigB; - trigB.SetButtonOnlyTrigger(-1, WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B, PAD_BUTTON_B); - - char gameName[31]; - if (!mountMethod) - { - if (strlen(GameTitles.GetTitle(header)) < (27 + 3)) - sprintf(gameName, "%s", GameTitles.GetTitle(header)); - else - { - strncpy(gameName, GameTitles.GetTitle(header), 27); - gameName[27] = '\0'; - strncat(gameName, "...", 3); - } - } - else sprintf(gameName, "%c%c%c%c%c%c", header->id[0], header->id[1], header->id[2], header->id[3], header->id[4], - header->id[5]); - - GuiText titleTxt(!mountMethod ? GameTitles.GetTitle(header) : gameName, 28, ( GXColor ) - { 0, 0, 0, 255}); - titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - titleTxt.SetPosition(12, 40); - titleTxt.SetMaxWidth(356, SCROLL_HORIZONTAL); - - GuiImage settingsbackground(&settingsbg); - - GuiText backBtnTxt(tr( "Back" ), 22, Theme.prompttext); - backBtnTxt.SetMaxWidth(btnOutline.GetWidth() - 30); - GuiImage backBtnImg(&btnOutline); - if (Settings.wsprompt == ON) - { - backBtnTxt.SetWidescreen(Settings.widescreen); - backBtnImg.SetWidescreen(Settings.widescreen); - } - GuiButton backBtn(&backBtnImg, &backBtnImg, 2, 3, -180, 400, &trigA, &btnSoundOver, btnClick2, 1); - backBtn.SetLabel(&backBtnTxt); - backBtn.SetTrigger(&trigB); - - GuiButton homo(1, 1); - homo.SetTrigger(&trigHome); - - GuiText saveBtnTxt(tr( "Save" ), 22, Theme.prompttext); - saveBtnTxt.SetMaxWidth(btnOutline.GetWidth() - 30); - GuiImage saveBtnImg(&btnOutline); - if (Settings.wsprompt == ON) - { - saveBtnTxt.SetWidescreen(Settings.widescreen); - saveBtnImg.SetWidescreen(Settings.widescreen); - } - GuiButton saveBtn(&saveBtnImg, &saveBtnImg, 2, 3, 180, 400, &trigA, &btnSoundOver, btnClick2, 1); - saveBtn.SetLabel(&saveBtnTxt); - - char MainButtonText[50]; - snprintf(MainButtonText, sizeof(MainButtonText), "%s", " "); - - GuiImage MainButton1Img(&MainButtonImgData); - GuiImage MainButton1ImgOver(&MainButtonImgOverData); - GuiText MainButton1Txt(MainButtonText, 22, ( GXColor ) - { 0, 0, 0, 255}); - MainButton1Txt.SetMaxWidth(MainButton1Img.GetWidth()); - GuiButton MainButton1(MainButton1Img.GetWidth(), MainButton1Img.GetHeight()); - MainButton1.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - MainButton1.SetPosition(0, 90); - MainButton1.SetImage(&MainButton1Img); - MainButton1.SetImageOver(&MainButton1ImgOver); - MainButton1.SetLabel(&MainButton1Txt); - MainButton1.SetSoundOver(&btnSoundOver); - MainButton1.SetSoundClick(&btnClick1); - MainButton1.SetEffectGrow(); - MainButton1.SetTrigger(&trigA); - - GuiImage MainButton2Img(&MainButtonImgData); - GuiImage MainButton2ImgOver(&MainButtonImgOverData); - GuiText MainButton2Txt(MainButtonText, 22, ( GXColor ) - { 0, 0, 0, 255}); - MainButton2Txt.SetMaxWidth(MainButton2Img.GetWidth()); - GuiButton MainButton2(MainButton2Img.GetWidth(), MainButton2Img.GetHeight()); - MainButton2.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - MainButton2.SetPosition(0, 160); - MainButton2.SetImage(&MainButton2Img); - MainButton2.SetImageOver(&MainButton2ImgOver); - MainButton2.SetLabel(&MainButton2Txt); - MainButton2.SetSoundOver(&btnSoundOver); - MainButton2.SetSoundClick(&btnClick1); - MainButton2.SetEffectGrow(); - MainButton2.SetTrigger(&trigA); - - GuiImage MainButton3Img(&MainButtonImgData); - GuiImage MainButton3ImgOver(&MainButtonImgOverData); - GuiText MainButton3Txt(MainButtonText, 22, ( GXColor ) - { 0, 0, 0, 255}); - MainButton3Txt.SetMaxWidth(MainButton3Img.GetWidth()); - GuiButton MainButton3(MainButton3Img.GetWidth(), MainButton3Img.GetHeight()); - MainButton3.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - MainButton3.SetPosition(0, 230); - MainButton3.SetImage(&MainButton3Img); - MainButton3.SetImageOver(&MainButton3ImgOver); - MainButton3.SetLabel(&MainButton3Txt); - MainButton3.SetSoundOver(&btnSoundOver); - MainButton3.SetSoundClick(&btnClick1); - MainButton3.SetEffectGrow(); - MainButton3.SetTrigger(&trigA); - - GuiImage MainButton4Img(&MainButtonImgData); - GuiImage MainButton4ImgOver(&MainButtonImgOverData); - GuiText MainButton4Txt(MainButtonText, 22, ( GXColor ) {0, 0, 0, 255}); - MainButton4Txt.SetMaxWidth(MainButton4Img.GetWidth()); - GuiButton MainButton4(MainButton4Img.GetWidth(), MainButton4Img.GetHeight()); - MainButton4.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - MainButton4.SetPosition(0, 300); - MainButton4.SetImage(&MainButton4Img); - MainButton4.SetImageOver(&MainButton4ImgOver); - MainButton4.SetLabel(&MainButton4Txt); - MainButton4.SetSoundOver(&btnSoundOver); - MainButton4.SetSoundClick(&btnClick1); - MainButton4.SetEffectGrow(); - MainButton4.SetTrigger(&trigA); - - OptionList options2; - GuiCustomOptionBrowser optionBrowser2(396, 280, &options2, "bg_options_settings.png", 0, 150); - optionBrowser2.SetPosition(0, 90); - optionBrowser2.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); - - GuiWindow w(screenwidth, screenheight); - - GameCFG game_cfg; - - GameCFG* existCFG = GameSettings.GetGameCFG(header->id); - - if (existCFG) - { - memcpy(&game_cfg, existCFG, sizeof(GameCFG)); - } - else - { - snprintf(game_cfg.id, sizeof(game_cfg.id), "%s", (char *) header->id); - game_cfg.video = Settings.videomode; - game_cfg.language = Settings.language; - game_cfg.ocarina = Settings.ocarina; - game_cfg.vipatch = Settings.videopatch; - game_cfg.ios = Settings.cios; - game_cfg.parentalcontrol = 0; - game_cfg.errorfix002 = Settings.error002; - game_cfg.patchcountrystrings = Settings.patchcountrystrings; - game_cfg.loadalternatedol = OFF; - game_cfg.alternatedolstart = 0; - game_cfg.iosreloadblock = OFF; - strcpy(game_cfg.alternatedolname, ""); - game_cfg.returnTo = 1; - game_cfg.Locked = 0; - } - - int pageToDisplay = 1; - while (pageToDisplay > 0) - { - usleep(100); - - menu = MENU_NONE; - - /** Standard procedure made in all pages **/ - MainButton1.StopEffect(); - MainButton2.StopEffect(); - MainButton3.StopEffect(); - MainButton4.StopEffect(); - - HaltGui(); - - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( "Game Load" )); - MainButton1Txt.SetText(MainButtonText); - snprintf(MainButtonText, sizeof(MainButtonText), "Ocarina"); - MainButton2Txt.SetText(MainButtonText); - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( "Uninstall Menu" )); - MainButton3Txt.SetText(MainButtonText); - snprintf(MainButtonText, sizeof(MainButtonText), "%s", tr( "Default Gamesettings" )); - MainButton4Txt.SetText(MainButtonText); - - mainWindow->RemoveAll(); - mainWindow->Append(&w); - w.RemoveAll(); - w.Append(&settingsbackground); - w.Append(&titleTxt); - w.Append(&backBtn); - w.Append(&homo); - w.Append(&MainButton1); - w.Append(&MainButton2); - w.Append(&MainButton3); - w.Append(&MainButton4); - - /** Disable ability to click through MainButtons */ - optionBrowser2.SetClickable(false); - /** Default no scrollbar and reset position **/ - // optionBrowser2.SetScrollbar(0); - optionBrowser2.SetOffset(0); - - MainButton1.StopEffect(); - MainButton2.StopEffect(); - MainButton3.StopEffect(); - MainButton4.StopEffect(); - - MainButton1.SetEffectGrow(); - MainButton2.SetEffectGrow(); - MainButton3.SetEffectGrow(); - MainButton4.SetEffectGrow(); - - MainButton1.SetEffect(EFFECT_FADE, 20); - MainButton2.SetEffect(EFFECT_FADE, 20); - MainButton3.SetEffect(EFFECT_FADE, 20); - MainButton4.SetEffect(EFFECT_FADE, 20); - - mainWindow->Append(&w); - - ResumeGui(); - - while (MainButton1.GetEffect() > 0) - usleep(50); - - while (menu == MENU_NONE) - { - usleep(100); - - if (shutdown == 1) Sys_Shutdown(); - if (reset == 1) Sys_Reboot(); - - if (MainButton1.GetState() == STATE_CLICKED) - { - w.Append(&saveBtn); - MainButton1.SetEffect(EFFECT_FADE, -20); - MainButton2.SetEffect(EFFECT_FADE, -20); - MainButton3.SetEffect(EFFECT_FADE, -20); - MainButton4.SetEffect(EFFECT_FADE, -20); - while (MainButton1.GetEffect() > 0) - usleep(50); - HaltGui(); - w.Remove(&MainButton1); - w.Remove(&MainButton2); - w.Remove(&MainButton3); - w.Remove(&MainButton4); - exit = false; - options2.ClearList(); - w.Append(&optionBrowser2); - optionBrowser2.SetClickable(true); - ResumeGui(); - - optionBrowser2.SetEffect(EFFECT_FADE, 20); - while (optionBrowser2.GetEffect() > 0) - usleep(50); - - int returnhere = 1; - char * languagefile; - languagefile = strrchr(Settings.language_path, '/') + 1; - - bool firstRun = true; - while (!exit) - { - usleep(100); - - returnhere = 1; - - if (shutdown == 1) Sys_Shutdown(); - if (reset == 1) Sys_Reboot(); - if (backBtn.GetState() == STATE_CLICKED) - { - backBtn.ResetState(); - exit = true; - break; - } - - else if (menu == MENU_DISCLIST) - { - w.Remove(&optionBrowser2); - w.Remove(&backBtn); - WindowCredits(); - w.Append(&optionBrowser2); - w.Append(&backBtn); - } - - else if (homo.GetState() == STATE_CLICKED) - { - Settings.Save(); - optionBrowser2.SetState(STATE_DISABLED); - bgMusic->Pause(); - choice = WindowExitPrompt(); - bgMusic->Resume(); - if (choice == 3) - Sys_LoadMenu(); // Back to System Menu - else if (choice == 2) - Sys_BackToLoader(); - else homo.ResetState(); - optionBrowser2.SetState(STATE_DEFAULT); - } - - else if (saveBtn.GetState() == STATE_CLICKED) - { - if (GameSettings.AddGame(game_cfg) && GameSettings.Save()) - { - WindowPrompt(tr( "Successfully Saved" ), 0, tr( "OK" )); - } - else - WindowPrompt(tr( "Save Failed. No device inserted?" ), 0, tr( "OK" )); - - saveBtn.ResetState(); - optionBrowser2.SetFocus(1); - } - - ret = optionBrowser2.GetClickedOption(); - - if (ret >= 0 || firstRun == true) - { - char alternatedname[100]; - int Idx = -1; - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Video Mode" )); - if (ret == Idx && ++game_cfg.video >= VIDEO_MODE_MAX) game_cfg.video = 0; - options2.SetValue(Idx, "%s%s", opts_videomode[game_cfg.video][0], - tr( opts_videomode[game_cfg.video][1] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "VIDTV Patch" )); - if (ret == Idx && ++game_cfg.vipatch >= MAX_ON_OFF) game_cfg.vipatch = 0; - options2.SetValue(Idx, "%s", tr( opts_off_on[game_cfg.vipatch] )); - - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Game Language" )); - if (ret == Idx && ++game_cfg.language >= MAX_LANGUAGE) game_cfg.language = 0; - options2.SetValue(Idx, "%s", tr( opts_language[game_cfg.language] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "Ocarina"); - if (ret == Idx && ++game_cfg.ocarina >= MAX_ON_OFF) game_cfg.ocarina = 0; - options2.SetValue(Idx, "%s", tr( opts_off_on[game_cfg.ocarina] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "IOS"); - if (ret == Idx) - { - char entered[4]; - snprintf(entered, sizeof(entered), "%i", game_cfg.ios); - if(OnScreenKeyboard(entered, sizeof(entered), 0)) - { - game_cfg.ios = atoi(entered); - if(game_cfg.ios < 200) game_cfg.ios = 200; - else if(game_cfg.ios > 255) game_cfg.ios = 255; - - if(NandTitles.IndexOf(TITLE_ID(1, game_cfg.ios)) < 0) - { - WindowPrompt(tr("Warning:"), tr("This IOS was not found on the titles list. If you are sure you have it installed than ignore this warning."), tr("OK")); - } - else if(game_cfg.ios == 254) - { - WindowPrompt(tr("Warning:"), tr("This IOS is the BootMii ios. If you are sure it is not BootMii and you have something else installed there than ignore this warning."), tr("OK")); - } - } - } - options2.SetValue(Idx, "IOS %i", game_cfg.ios); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Parental Control" )); - if (ret == Idx && ++game_cfg.parentalcontrol >= 5) game_cfg.parentalcontrol = 0; - options2.SetValue(Idx, "%s", tr( opts_parentalcontrol[game_cfg.parentalcontrol] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Error 002 fix" )); - if (ret == Idx && ++game_cfg.errorfix002 >= 3) game_cfg.errorfix002 = 0; - options2.SetValue(Idx, "%s", tr( opts_error002[game_cfg.errorfix002] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Return To" )); - if (ret == Idx && ++game_cfg.returnTo >= MAX_ON_OFF) game_cfg.returnTo = 0; - - char text[IMET_MAX_NAME_LEN]; - int channel = NandTitles.FindU32(Settings.returnTo);//is the channel set in the global settings actually installed? - - if (!game_cfg.returnTo || channel < 0)//channel is not installed or the uer wants to not use it - sprintf(text, "%s", tr( opts_off_on[ 0 ] )); - - else snprintf(text, sizeof(text), "%s", NandTitles.NameFromIndex(channel)); - - options2.SetValue(Idx, "%s", text); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Patch Country Strings" )); - if (ret == Idx && ++game_cfg.patchcountrystrings >= MAX_ON_OFF) game_cfg.patchcountrystrings = 0; - options2.SetValue(Idx, "%s", tr( opts_off_on[game_cfg.patchcountrystrings] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Alternate DOL" )); - int last_alternatedol = game_cfg.loadalternatedol; - if (ret == Idx && (game_cfg.loadalternatedol = (game_cfg.loadalternatedol + 2) % 3) >= 3) // 0->2->1->0 - game_cfg.loadalternatedol = 0; - static const char *opts[] = { trNOOP( "Default" ), trNOOP( "Load From SD/USB" ), - trNOOP( "Select a DOL" ) }; - options2.SetValue(Idx, "%s", tr( opts[game_cfg.loadalternatedol] )); - if (last_alternatedol != 1) - { - firstRun = true; // force re-init follow Entries - options2.Resize(Idx + 1); - } - } - - if (game_cfg.loadalternatedol == 2 && (ret == ++Idx || firstRun)) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Selected DOL" )); - if (ret == Idx) - { - if (game_cfg.loadalternatedol == 2) - { - char filename[10]; - snprintf(filename, 7, "%s", (char *) header->id); - int dolchoice = 0; - //alt dol menu for games that require more than a single alt dol - int autodol = autoSelectDolMenu(filename, false); - - if (autodol > 0) - { - game_cfg.alternatedolstart = autodol; - snprintf(alternatedname, sizeof(alternatedname), "%s <%i>", tr( "AUTO" ), - autodol); - } - else if (autodol == 0) - game_cfg.loadalternatedol = 0; // default was chosen - else - { - //check to see if we already know the offset of the correct dol - int autodol = autoSelectDol(filename, false); - //if we do know that offset ask if they want to use it - if (autodol > 0) - { - dolchoice - = WindowPrompt( - 0, - tr( "Do you want to use the alternate DOL that is known to be correct?" ), - tr( "Yes" ), tr( "Pick from a list" ), tr( "Cancel" )); - if (dolchoice == 0) - game_cfg.loadalternatedol = 0; - else if (dolchoice == 1) - { - game_cfg.alternatedolstart = autodol; - snprintf(alternatedname, sizeof(alternatedname), "%s <%i>", - tr( "AUTO" ), autodol); - } - else if (dolchoice == 2) //they want to search for the correct dol themselves - { - int res = DiscBrowse(header, alternatedname, sizeof(alternatedname)); - if ((res >= 0) && (res != 696969)) //if res==696969 they pressed the back button - game_cfg.alternatedolstart = res; - } - } - else - { - int res = DiscBrowse(header, alternatedname, sizeof(alternatedname)); - if ((res >= 0) && (res != 696969)) - { - game_cfg.alternatedolstart = res; - char tmp[170]; - snprintf( - tmp, - sizeof(tmp), - "%s %s - %i", - tr( "It seems that you have some information that will be helpful to us. Please pass this information along to the DEV team." ), - filename, game_cfg.alternatedolstart); - WindowPrompt(0, tmp, tr( "OK" )); - } - } - } - } - } - if (game_cfg.loadalternatedol == 0) - { - firstRun = true; // force re-init follow Entries - options2.Resize(Idx--); // remove this Entry - options2.SetValue(Idx, "%s", tr( "Default" )); // re-set prev Entry - } - else options2.SetValue(Idx, alternatedname); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Block IOS Reload" )); - if (ret == Idx && ++game_cfg.iosreloadblock >= MAX_ON_OFF) game_cfg.iosreloadblock = 0; - options2.SetValue(Idx, "%s", tr( opts_off_on[game_cfg.iosreloadblock] )); - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Game Locked" )); - if (ret == Idx && ++game_cfg.Locked >= MAX_ON_OFF) game_cfg.Locked = 0; - options2.SetValue(Idx, "%s", tr( opts_off_on[game_cfg.Locked] )); - } - - firstRun = false; - } - } - - optionBrowser2.SetEffect(EFFECT_FADE, -20); - while (optionBrowser2.GetEffect() > 0) - usleep(50); - MainButton1.ResetState(); - break; - w.Remove(&saveBtn); - } - - else if (MainButton2.GetState() == STATE_CLICKED) - { - char ID[7]; - snprintf(ID, sizeof(ID), "%c%c%c%c%c%c", header->id[0], header->id[1], header->id[2], header->id[3], - header->id[4], header->id[5]); - CheatMenu(ID); - MainButton2.ResetState(); - break; - } - - else if (MainButton3.GetState() == STATE_CLICKED) - { - MainButton1.SetEffect(EFFECT_FADE, -20); - MainButton2.SetEffect(EFFECT_FADE, -20); - MainButton3.SetEffect(EFFECT_FADE, -20); - MainButton4.SetEffect(EFFECT_FADE, -20); - while (MainButton3.GetEffect() > 0) - usleep(50); - HaltGui(); - w.Remove(&MainButton1); - w.Remove(&MainButton2); - w.Remove(&MainButton3); - w.Remove(&MainButton4); - exit = false; - options2.ClearList(); - w.Append(&optionBrowser2); - optionBrowser2.SetClickable(true); - ResumeGui(); - - bool firstRun = true; - - optionBrowser2.SetEffect(EFFECT_FADE, 20); - while (optionBrowser2.GetEffect() > 0) - usleep(50); - - while (!exit) - { - usleep(100); - - if (shutdown == 1) Sys_Shutdown(); - if (reset == 1) Sys_Reboot(); - if (backBtn.GetState() == STATE_CLICKED) - { - backBtn.ResetState(); - exit = true; - break; - } - - else if (homo.GetState() == STATE_CLICKED) - { - Settings.Save(); - optionBrowser2.SetState(STATE_DISABLED); - bgMusic->Pause(); - choice = WindowExitPrompt(); - bgMusic->Resume(); - if (choice == 3) - Sys_LoadMenu(); // Back to System Menu - else if (choice == 2) - Sys_BackToLoader(); - else homo.ResetState(); - optionBrowser2.SetState(STATE_DEFAULT); - } - - ret = optionBrowser2.GetClickedOption(); - - if (firstRun || ret >= 0) - { - int Idx = -1; - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Uninstall Game" )); - if (ret == Idx) - { - int choice1 = WindowPrompt(tr( "Do you really want to delete:" ), gameName, - tr( "Yes" ), tr( "Cancel" )); - if (choice1 == 1 && !mountMethod) - { - GameSettings.Remove(header->id); - GameSettings.Save(); - GameStatistics.Remove(header->id); - GameStatistics.Save(); - ret = WBFS_RemoveGame(header->id); - if (ret < 0) - { - WindowPrompt(tr( "Can't delete:" ), gameName, tr( "OK" )); - } - else - { - WindowPrompt(tr( "Successfully deleted:" ), gameName, tr( "OK" )); - retVal = 1; - } - } - else if (choice1 == 0) optionBrowser2.SetFocus(1); - } - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Reset Playcounter" )); - if (ret == Idx) - { - int result = WindowPrompt(tr( "Are you sure?" ), 0, tr( "Yes" ), tr( "Cancel" )); - if (result == 1) - { - if (isInserted(Settings.BootDevice)) - { - GameStatistics.SetPlayCount(header->id, 0); - GameStatistics.Save(); - } - } - } - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Delete Cover Artwork" )); - if (ret == Idx) - { - char tmp[200]; - snprintf(tmp, sizeof(tmp), "%s%c%c%c%c%c%c.png", Settings.covers_path, header->id[0], - header->id[1], header->id[2], header->id[3], header->id[4], header->id[5]); - - int choice1 = WindowPrompt(tr( "Delete" ), tmp, tr( "Yes" ), tr( "No" )); - if (choice1 == 1) - { - if (CheckFile(tmp)) remove(tmp); - } - } - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Delete Disc Artwork" )); - if (ret == Idx) - { - char tmp[200]; - snprintf(tmp, sizeof(tmp), "%s%c%c%c%c%c%c.png", Settings.disc_path, header->id[0], - header->id[1], header->id[2], header->id[3], header->id[4], header->id[5]); - - int choice1 = WindowPrompt(tr( "Delete" ), tmp, tr( "Yes" ), tr( "No" )); - if (choice1 == 1) - { - if (CheckFile(tmp)) remove(tmp); - } - } - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Delete Cheat TXT" )); - if (ret == Idx) - { - char tmp[200]; - snprintf(tmp, sizeof(tmp), "%s%c%c%c%c%c%c.txt", Settings.TxtCheatcodespath, - header->id[0], header->id[1], header->id[2], header->id[3], header->id[4], - header->id[5]); - - int choice1 = WindowPrompt(tr( "Delete" ), tmp, tr( "Yes" ), tr( "No" )); - if (choice1 == 1) - { - if (CheckFile(tmp)) remove(tmp); - } - } - } - - if (ret == ++Idx || firstRun) - { - if (firstRun) options2.SetName(Idx, "%s", tr( "Delete Cheat GCT" )); - if (ret == Idx) - { - char tmp[200]; - snprintf(tmp, sizeof(tmp), "%s%c%c%c%c%c%c.gct", Settings.Cheatcodespath, - header->id[0], header->id[1], header->id[2], header->id[3], header->id[4], - header->id[5]); - - int choice1 = WindowPrompt(tr( "Delete" ), tmp, tr( "Yes" ), tr( "No" )); - if (choice1 == 1) - { - if (CheckFile(tmp)) remove(tmp); - } - } - } - - firstRun = false; - } - } - optionBrowser2.SetEffect(EFFECT_FADE, -20); - while (optionBrowser2.GetEffect() > 0) - usleep(50); - pageToDisplay = 1; - MainButton3.ResetState(); - break; - } - - else if (MainButton4.GetState() == STATE_CLICKED) - { - int choice1 = WindowPrompt(tr( "Are you sure?" ), 0, tr( "Yes" ), tr( "Cancel" )); - if (choice1 == 1) - { - game_cfg.video = Settings.videomode; - game_cfg.vipatch = Settings.videopatch; - game_cfg.language = Settings.language; - game_cfg.ocarina = Settings.ocarina; - game_cfg.errorfix002 = Settings.error002; - game_cfg.patchcountrystrings = Settings.patchcountrystrings; - game_cfg.loadalternatedol = OFF; - game_cfg.alternatedolstart = 0; - game_cfg.iosreloadblock = OFF; - game_cfg.ios = Settings.cios; - game_cfg.parentalcontrol = 0; - strcpy(game_cfg.alternatedolname, ""); - game_cfg.returnTo = 1; - game_cfg.Locked = 0; - GameSettings.Remove(header->id); - GameSettings.Save(); - } - - pageToDisplay = 1; - MainButton4.ResetState(); - break; - } - - else if (backBtn.GetState() == STATE_CLICKED) - { - menu = MENU_DISCLIST; - pageToDisplay = 0; - backBtn.ResetState(); - break; - } - - else if (homo.GetState() == STATE_CLICKED) - { - Settings.Save(); - optionBrowser2.SetState(STATE_DISABLED); - bgMusic->Pause(); - choice = WindowExitPrompt(); - bgMusic->Resume(); - - if (choice == 3) - Sys_LoadMenu(); // Back to System Menu - else if (choice == 2) - Sys_BackToLoader(); - else homo.ResetState(); - optionBrowser2.SetState(STATE_DEFAULT); - } - } - } - w.SetEffect(EFFECT_FADE, -20); - while (w.GetEffect() > 0) - usleep(50); - - HaltGui(); - - mainWindow->RemoveAll(); - mainWindow->Append(bgImg); - - ResumeGui(); - return retVal; + return returnMenu; } diff --git a/source/settings/SettingsPrompts.cpp b/source/settings/SettingsPrompts.cpp index 0fba7b2d..43874f90 100644 --- a/source/settings/SettingsPrompts.cpp +++ b/source/settings/SettingsPrompts.cpp @@ -23,10 +23,6 @@ extern u8 shutdown; extern u8 reset; -/*** Extern functions ***/ -extern void ResumeGui(); -extern void HaltGui(); - /**************************************************************************** * MenuOGG @@ -95,11 +91,6 @@ int MenuLanguageSelect() int scrollon; int returnhere = 0; - GuiSound btnSoundOver( button_over_pcm, button_over_pcm_size, Settings.sfxvolume ); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if ( !btnClick2 ) btnClick2 = new GuiSound( button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume ); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData settingsbg(Resources::GetFile("settings_background.png"), Resources::GetFileSize("settings_background.png")); @@ -127,8 +118,8 @@ int MenuLanguageSelect() pathBtn.SetAlignment( ALIGN_CENTRE, ALIGN_TOP ); pathBtn.SetPosition( 0, 28 ); pathBtn.SetLabel( &titleTxt ); - pathBtn.SetSoundOver( &btnSoundOver ); - pathBtn.SetSoundClick( btnClick2 ); + pathBtn.SetSoundOver( btnSoundOver ); + pathBtn.SetSoundClick( btnSoundClick2 ); pathBtn.SetTrigger( &trigA ); pathBtn.SetEffectGrow(); @@ -149,8 +140,8 @@ int MenuLanguageSelect() backBtn.SetPosition( -190, 400 ); backBtn.SetLabel( &backBtnTxt ); backBtn.SetImage( &backBtnImg ); - backBtn.SetSoundOver( &btnSoundOver ); - backBtn.SetSoundClick( btnClick2 ); + backBtn.SetSoundOver( btnSoundOver ); + backBtn.SetSoundClick( btnSoundClick2 ); backBtn.SetTrigger( &trigA ); backBtn.SetTrigger( &trigB ); backBtn.SetEffectGrow(); @@ -168,8 +159,8 @@ int MenuLanguageSelect() defaultBtn.SetPosition( 190, 400 ); defaultBtn.SetLabel( &defaultBtnTxt ); defaultBtn.SetImage( &defaultBtnImg ); - defaultBtn.SetSoundOver( &btnSoundOver ); - defaultBtn.SetSoundClick( btnClick2 ); + defaultBtn.SetSoundOver( btnSoundOver ); + defaultBtn.SetSoundClick( btnSoundClick2 ); defaultBtn.SetTrigger( &trigA ); defaultBtn.SetEffectGrow(); @@ -186,8 +177,8 @@ int MenuLanguageSelect() updateBtn.SetPosition( 0, 400 ); updateBtn.SetLabel( &updateBtnTxt ); updateBtn.SetImage( &updateBtnImg ); - updateBtn.SetSoundOver( &btnSoundOver ); - updateBtn.SetSoundClick( btnClick2 ); + updateBtn.SetSoundOver( btnSoundOver ); + updateBtn.SetSoundClick( btnSoundClick2 ); updateBtn.SetTrigger( &trigA ); updateBtn.SetEffectGrow(); diff --git a/source/settings/menus/CustomPathsSM.cpp b/source/settings/menus/CustomPathsSM.cpp new file mode 100644 index 00000000..d9351244 --- /dev/null +++ b/source/settings/menus/CustomPathsSM.cpp @@ -0,0 +1,252 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 "CustomPathsSM.hpp" +#include "settings/CSettings.h" +#include "prompts/PromptWindows.h" +#include "language/gettext.h" +#include "prompts/filebrowser.h" +#include "themes/CTheme.h" + +CustomPathsSM::CustomPathsSM() + : SettingsMenu(tr("Custom Paths"), &GuiOptions, MENU_NONE) +{ + int Idx = 0; + Options->SetName(Idx++, tr("3D Cover Path")); + Options->SetName(Idx++, tr("2D Cover Path")); + Options->SetName(Idx++, tr("Disc Artwork Path")); + Options->SetName(Idx++, tr("Theme Path")); + Options->SetName(Idx++, tr("WiiTDB Path")); + Options->SetName(Idx++, tr("Update Path")); + Options->SetName(Idx++, tr("GCT Cheatcodes Path")); + Options->SetName(Idx++, tr("TXT Cheatcodes Path")); + Options->SetName(Idx++, tr("DOL Path")); + Options->SetName(Idx++, tr("Homebrew Apps Path")); + Options->SetName(Idx++, tr("Theme Download Path")); + Options->SetName(Idx++, tr("BCA Codes Path")); + Options->SetName(Idx++, tr("WIP Patches Path")); + + SetOptionValues(); +} + +void CustomPathsSM::SetOptionValues() +{ + int Idx = 0; + + //! Settings: 3D Cover Path + Options->SetValue(Idx++, Settings.covers_path); + + //! Settings: 2D Cover Path + Options->SetValue(Idx++, Settings.covers2d_path); + + //! Settings: Disc Artwork Path + Options->SetValue(Idx++, Settings.disc_path); + + //! Settings: Theme Path + Options->SetValue(Idx++, Settings.theme_path); + + //! Settings: WiiTDB Path + Options->SetValue(Idx++, Settings.titlestxt_path); + + //! Settings: Update Path + Options->SetValue(Idx++, Settings.update_path); + + //! Settings: GCT Cheatcodes Path + Options->SetValue(Idx++, Settings.Cheatcodespath); + + //! Settings: TXT Cheatcodes Path + Options->SetValue(Idx++, Settings.TxtCheatcodespath); + + //! Settings: DOL Path + Options->SetValue(Idx++, Settings.dolpath); + + //! Settings: Homebrew Apps Path + Options->SetValue(Idx++, Settings.homebrewapps_path); + + //! Settings: Theme Download Path + Options->SetValue(Idx++, Settings.theme_downloadpath); + + //! Settings: BCA Codes Path + Options->SetValue(Idx++, Settings.BcaCodepath); + + //! Settings: WIP Patches Path + Options->SetValue(Idx++, Settings.WipCodepath); +} + +int CustomPathsSM::GetMenuInternal() +{ + int ret = optionBrowser->GetClickedOption(); + + if (ret < 0) + return MENU_NONE; + + int Idx = -1; + + //! Settings: 3D Cover Path + if (ret == ++Idx) + { + titleTxt->SetText(tr( "3D Cover Path" )); + ChangePath(Settings.covers_path, sizeof(Settings.covers_path)); + } + + //! Settings: 2D Cover Path + else if (ret == ++Idx) + { + titleTxt->SetText(tr( "2D Cover Path" )); + ChangePath(Settings.covers2d_path, sizeof(Settings.covers2d_path)); + } + + //! Settings: Disc Artwork Path + else if (ret == ++Idx) + { + titleTxt->SetText(tr( "Disc Artwork Path" )); + ChangePath(Settings.disc_path, sizeof(Settings.disc_path)); + } + + //! Settings: Theme Path + else if (ret == ++Idx) + { + titleTxt->SetText(tr( "Theme Path" )); + int result = ChangePath(Settings.theme_path, sizeof(Settings.theme_path)); + if (result == 1) + { + HaltGui(); + mainWindow->Remove(bgImg); + Theme.Load(Settings.theme_path); + if(pointer[0]) delete pointer[0]; + if(pointer[1]) delete pointer[1]; + if(pointer[2]) delete pointer[2]; + if(pointer[3]) delete pointer[3]; + pointer[0] = Resources::GetImageData("player1_point.png"); + pointer[1] = Resources::GetImageData("player2_point.png"); + pointer[2] = Resources::GetImageData("player3_point.png"); + pointer[3] = Resources::GetImageData("player4_point.png"); + if(background) delete background; + background = Resources::GetImageData(Settings.widescreen ? "wbackground.png" : "background.png"); + if(bgImg) delete bgImg; + bgImg = new GuiImage(background); + mainWindow->Append(bgImg); + ResumeGui(); + return MENU_SETTINGS; + } + } + + //! Settings: WiiTDB Path + else if (ret == ++Idx) + { + titleTxt->SetText(tr( "WiiTDB Path" )); + ChangePath(Settings.titlestxt_path, sizeof(Settings.titlestxt_path)); + } + + //! Settings: Update Path + else if (ret == ++Idx) + { + titleTxt->SetText(tr( "Update Path" )); + ChangePath(Settings.update_path, sizeof(Settings.update_path)); + } + + //! Settings: GCT Cheatcodes Path + else if (ret == ++Idx) + { + titleTxt->SetText(tr( "GCT Cheatcodes Path" )); + ChangePath(Settings.Cheatcodespath, sizeof(Settings.Cheatcodespath)); + } + + //! Settings: TXT Cheatcodes Path + else if (ret == ++Idx) + { + titleTxt->SetText(tr( "TXT Cheatcodes Path" )); + ChangePath(Settings.TxtCheatcodespath, sizeof(Settings.TxtCheatcodespath)); + } + + //! Settings: DOL Path + else if (ret == ++Idx) + { + titleTxt->SetText(tr( "DOL Path" )); + ChangePath(Settings.dolpath, sizeof(Settings.dolpath)); + } + + //! Settings: Homebrew Apps Path + else if (ret == ++Idx) + { + titleTxt->SetText(tr( "Homebrew Apps Path" )); + ChangePath(Settings.homebrewapps_path, sizeof(Settings.homebrewapps_path)); + } + + //! Settings: Theme Download Path + else if (ret == ++Idx) + { + titleTxt->SetText(tr( "Theme Download Path" )); + ChangePath(Settings.theme_downloadpath, sizeof(Settings.theme_downloadpath)); + } + + //! Settings: BCA Codes Path + else if (ret == ++Idx) + { + titleTxt->SetText(tr( "BCA Codes Path" )); + ChangePath(Settings.BcaCodepath, sizeof(Settings.BcaCodepath)); + } + + //! Settings: WIP Patches Path + else if (ret == ++Idx) + { + titleTxt->SetText(tr( "WIP Patches Path" )); + ChangePath(Settings.WipCodepath, sizeof(Settings.WipCodepath)); + } + + //! Global set back of the titleTxt after a change + titleTxt->SetText(tr( "Custom Paths" )); + SetOptionValues(); + + return MENU_NONE; +} + +int CustomPathsSM::ChangePath(char * SettingsPath, int SizeOfPath) +{ + char entered[300]; + snprintf(entered, sizeof(entered), SettingsPath); + + HaltGui(); + GuiWindow * parent = (GuiWindow *) parentElement; + if(parent) parent->SetState(STATE_DISABLED); + this->SetState(STATE_DEFAULT); + this->Remove(optionBrowser); + ResumeGui(); + + int result = BrowseDevice(entered, sizeof(entered), FB_DEFAULT, noFILES); + + if(parent) parent->SetState(STATE_DEFAULT); + this->Append(optionBrowser); + + if (result == 1) + { + if (entered[strlen(entered)-1] != '/') + strcat(entered, "/"); + + snprintf(SettingsPath, SizeOfPath, entered); + WindowPrompt(tr( "Path Changed" ), 0, tr( "OK" )); + } + + return result; +} diff --git a/source/settings/menus/CustomPathsSM.hpp b/source/settings/menus/CustomPathsSM.hpp new file mode 100644 index 00000000..344e7fb7 --- /dev/null +++ b/source/settings/menus/CustomPathsSM.hpp @@ -0,0 +1,43 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 CUSTOMPATHS_MENU_HPP_ +#define CUSTOMPATHS_MENU_HPP_ + +#include "SettingsMenu.hpp" + +class CustomPathsSM : public SettingsMenu +{ + public: + CustomPathsSM(); + virtual int GetType() { return CCustomPathsSM; }; + protected: + void SetOptionValues(); + int GetMenuInternal(); + int ChangePath(char * SettingsPath, int SizeOfPath); + + OptionList GuiOptions; +}; + + +#endif diff --git a/source/settings/menus/FlyingButtonsMenu.cpp b/source/settings/menus/FlyingButtonsMenu.cpp new file mode 100644 index 00000000..6db2031a --- /dev/null +++ b/source/settings/menus/FlyingButtonsMenu.cpp @@ -0,0 +1,462 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 "FlyingButtonsMenu.hpp" +#include "utils/StringTools.h" +#include "language/gettext.h" +#include "themes/CTheme.h" +#include "menu/menus.h" +#include "sys.h" + +#define FADE_SPEED 20 +#define SLIDE_SPEED 35 +#define DISPLAY_BUTTONS 4 + +FlyingButtonsMenu::FlyingButtonsMenu(const char * menu_title) + : GuiWindow(screenwidth, screenheight) + +{ + currentPage = 0; + returnMenu = MENU_NONE; + CurrentMenu = NULL; + titleTxt = NULL; + GoLeftImg = NULL; + GoLeftBtn = NULL; + GoRightImg = NULL; + GoRightBtn = NULL; + MenuTitle = menu_title ? menu_title : " "; + + trigA = new GuiTrigger(); + trigHome = new GuiTrigger(); + trigB = new GuiTrigger(); + trigL = new GuiTrigger(); + trigR = new GuiTrigger(); + trigMinus = new GuiTrigger(); + trigPlus = new GuiTrigger(); + trigA->SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); + trigHome->SetButtonOnlyTrigger(-1, WPAD_BUTTON_HOME | WPAD_CLASSIC_BUTTON_HOME, 0); + trigB->SetButtonOnlyTrigger(-1, WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B, PAD_BUTTON_B); + trigL->SetButtonOnlyTrigger(-1, WPAD_BUTTON_LEFT | WPAD_CLASSIC_BUTTON_LEFT, PAD_BUTTON_LEFT); + trigR->SetButtonOnlyTrigger(-1, WPAD_BUTTON_RIGHT | WPAD_CLASSIC_BUTTON_RIGHT, PAD_BUTTON_RIGHT); + trigMinus->SetButtonOnlyTrigger(-1, WPAD_BUTTON_MINUS | WPAD_CLASSIC_BUTTON_MINUS, 0); + trigPlus->SetButtonOnlyTrigger(-1, WPAD_BUTTON_PLUS | WPAD_CLASSIC_BUTTON_PLUS, 0); + + btnOutline = Resources::GetImageData("button_dialogue_box.png"); + settingsbg = Resources::GetImageData("settings_background.png"); + MainButtonImgData = Resources::GetImageData("settings_title.png"); + MainButtonImgOverData = Resources::GetImageData("settings_title_over.png"); + PageindicatorImgData = Resources::GetImageData("pageindicator.png"); + arrow_left = Resources::GetImageData("startgame_arrow_left.png"); + arrow_right = Resources::GetImageData("startgame_arrow_right.png"); + + settingsbackground = new GuiImage(settingsbg); + Append(settingsbackground); + + homeBtn = new GuiButton(1, 1); + homeBtn->SetTrigger(trigHome); + Append(homeBtn); + + backBtnTxt = new GuiText(tr( "Back" ), 22, Theme.prompttext); + backBtnTxt->SetMaxWidth(btnOutline->GetWidth() - 30); + backBtnImg = new GuiImage(btnOutline); + if (Settings.wsprompt == ON) + { + backBtnTxt->SetWidescreen(Settings.widescreen); + backBtnImg->SetWidescreen(Settings.widescreen); + } + backBtn = new GuiButton(backBtnImg, backBtnImg, 2, 3, -195, 400, trigA, btnSoundOver, btnSoundClick2, 1); + backBtn->SetLabel(backBtnTxt); + backBtn->SetTrigger(trigB); + Append(backBtn); + + SetEffect(EFFECT_FADE, FADE_SPEED); +} + +FlyingButtonsMenu::~FlyingButtonsMenu() +{ + ResumeGui(); + + SetEffect(EFFECT_FADE, -FADE_SPEED); + while(this->GetEffect() > 0) usleep(100); + + HaltGui(); + if(parentElement) + ((GuiWindow *) parentElement)->Remove(this); + + RemoveAll(); + HideMenu(); + + delete trigA; + delete trigHome; + delete trigB; + delete trigL; + delete trigR; + delete trigMinus; + delete trigPlus; + delete settingsbackground; + delete homeBtn; + delete btnOutline; + delete settingsbg; + delete MainButtonImgData; + delete MainButtonImgOverData; + delete PageindicatorImgData; + delete arrow_left; + delete arrow_right; + delete backBtnTxt; + delete backBtnImg; + delete backBtn; + + ResumeGui(); +} + +void FlyingButtonsMenu::SetPageIndicators() +{ + HaltGui(); + + for(u32 i = 0; i < PageIndicatorBtn.size(); ++i) + { + Remove(PageIndicatorBtn[i]); + delete PageindicatorImg[i]; + delete PageindicatorTxt[i]; + delete PageIndicatorBtn[i]; + } + PageindicatorImg.clear(); + PageindicatorTxt.clear(); + PageIndicatorBtn.clear(); + + int IndicatorCount = ceil(MainButton.size()/(1.0f*DISPLAY_BUTTONS)); + + for(int i = 0; i < IndicatorCount; ++i) + { + PageindicatorImg.push_back(new GuiImage(PageindicatorImgData)); + PageindicatorTxt.push_back(new GuiText(fmt("%i", i), 22, ( GXColor ) {0, 0, 0, 255})); + PageIndicatorBtn.push_back(new GuiButton(PageindicatorImgData->GetWidth(), PageindicatorImgData->GetHeight())); + PageIndicatorBtn[i]->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + PageIndicatorBtn[i]->SetPosition(270-IndicatorCount*35+35*i, 400); + PageIndicatorBtn[i]->SetImage(PageindicatorImg[i]); + PageIndicatorBtn[i]->SetLabel(PageindicatorTxt[i]); + PageIndicatorBtn[i]->SetSoundOver(btnSoundOver); + PageIndicatorBtn[i]->SetSoundClick(btnSoundClick); + PageIndicatorBtn[i]->SetTrigger(trigA); + PageIndicatorBtn[i]->SetEffectGrow(); + PageIndicatorBtn[i]->SetAlpha(i == currentPage ? 255 : 50); + Append(PageIndicatorBtn[i]); + } +} + +void FlyingButtonsMenu::SetMainButton(int position, const char * ButtonText, GuiImageData * imageData, GuiImageData * imageOver) +{ + //! Don't allow operating gui mode while adding/setting the buttons + HaltGui(); + + if(position < (int) MainButton.size()) + { + delete MainButtonImg[position]; + delete MainButtonImgOver[position]; + delete MainButtonTxt[position]; + delete MainButton[position]; + } + else + { + MainButtonImg.resize(position+1); + MainButtonImgOver.resize(position+1); + MainButtonTxt.resize(position+1); + MainButton.resize(position+1); + } + + MainButtonImg[position] = new GuiImage(imageData); + MainButtonImgOver[position] = new GuiImage(imageOver); + + MainButtonTxt[position] = new GuiText(ButtonText, 22, ( GXColor ) {0, 0, 0, 255}); + MainButtonTxt[position]->SetMaxWidth(MainButtonImg[position]->GetWidth()); + + MainButton[position] = new GuiButton(imageData->GetWidth(), imageData->GetHeight()); + MainButton[position]->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + MainButton[position]->SetPosition(0, 90+(position % DISPLAY_BUTTONS)*70); + MainButton[position]->SetImage(MainButtonImg[position]); + MainButton[position]->SetImageOver(MainButtonImgOver[position]); + MainButton[position]->SetLabel(MainButtonTxt[position]); + MainButton[position]->SetSoundOver(btnSoundOver); + MainButton[position]->SetSoundClick(btnSoundClick); + MainButton[position]->SetEffectGrow(); + MainButton[position]->SetTrigger(trigA); +} + +void FlyingButtonsMenu::HideMenu() +{ + HaltGui(); + + if(titleTxt) + Remove(titleTxt); + if(GoLeftBtn) + Remove(GoLeftBtn); + if(GoRightBtn) + Remove(GoRightBtn); + + if(titleTxt) + delete titleTxt; + if(GoLeftImg) + delete GoLeftImg; + if(GoLeftBtn) + delete GoLeftBtn; + if(GoRightImg) + delete GoRightImg; + if(GoRightBtn) + delete GoRightBtn; + titleTxt = NULL; + GoLeftImg = NULL; + GoLeftBtn = NULL; + GoRightImg = NULL; + GoRightBtn = NULL; + + for(u32 i = 0; i < MainButton.size(); ++i) + { + Remove(MainButton[i]); + delete MainButtonImg[i]; + delete MainButtonImgOver[i]; + delete MainButtonTxt[i]; + delete MainButton[i]; + } + for(u32 i = 0; i < PageIndicatorBtn.size(); ++i) + { + Remove(PageIndicatorBtn[i]); + delete PageindicatorImg[i]; + delete PageindicatorTxt[i]; + delete PageIndicatorBtn[i]; + } + + PageindicatorImg.clear(); + PageindicatorTxt.clear(); + PageIndicatorBtn.clear(); + MainButtonImg.clear(); + MainButtonImgOver.clear(); + MainButtonTxt.clear(); + MainButton.clear(); +} + +void FlyingButtonsMenu::ShowMenu() +{ + //! Free memory if not done yet because new is allocated + HideMenu(); + + titleTxt = new GuiText(MenuTitle.c_str(), 28, ( GXColor ) {0, 0, 0, 255}); + titleTxt->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt->SetPosition(0, 40); + Append(titleTxt); + + GoLeftImg = new GuiImage(arrow_left); + GoLeftBtn = new GuiButton(GoLeftImg->GetWidth(), GoLeftImg->GetHeight()); + GoLeftBtn->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + GoLeftBtn->SetPosition(25, -25); + GoLeftBtn->SetImage(GoLeftImg); + GoLeftBtn->SetSoundOver(btnSoundOver); + GoLeftBtn->SetSoundClick(btnSoundClick2); + GoLeftBtn->SetEffectGrow(); + GoLeftBtn->SetTrigger(trigA); + GoLeftBtn->SetTrigger(trigL); + GoLeftBtn->SetTrigger(trigMinus); + Append(GoLeftBtn); + + GoRightImg = new GuiImage(arrow_right); + GoRightBtn = new GuiButton(GoRightImg->GetWidth(), GoRightImg->GetHeight()); + GoRightBtn->SetAlignment(ALIGN_RIGHT, ALIGN_MIDDLE); + GoRightBtn->SetPosition(-25, -25); + GoRightBtn->SetImage(GoRightImg); + GoRightBtn->SetSoundOver(btnSoundOver); + GoRightBtn->SetSoundClick(btnSoundClick2); + GoRightBtn->SetEffectGrow(); + GoRightBtn->SetTrigger(trigA); + GoRightBtn->SetTrigger(trigR); + GoRightBtn->SetTrigger(trigPlus); + Append(GoRightBtn); + + SetupMainButtons(); + AddMainButtons(); + + ShowButtonsEffects(EFFECT_FADE, FADE_SPEED); +} + +void FlyingButtonsMenu::AddMainButtons() +{ + HaltGui(); + + for(u32 i = 0; i < MainButton.size(); ++i) + Remove(MainButton[i]); + + int FirstItem = currentPage*DISPLAY_BUTTONS; + + for(int i = FirstItem; i < (int) MainButton.size() && i < FirstItem+DISPLAY_BUTTONS; ++i) + { + Append(MainButton[i]); + } + + SetPageIndicators(); +} + +void FlyingButtonsMenu::ShowButtonsEffects(int effect, int effect_speed) +{ + int FirstItem = currentPage*DISPLAY_BUTTONS; + + if(FirstItem >= (int) MainButton.size()) + return; + + HaltGui(); + + for(int i = FirstItem; i < (int) MainButton.size() && i < FirstItem+DISPLAY_BUTTONS; ++i) + { + MainButton[i]->StopEffect(); + MainButton[i]->SetEffect(effect, effect_speed); + } + + ResumeGui(); + + //! Don't lock on fade in for initiation purpose + if(effect & EFFECT_FADE && effect_speed > 0) + return; + + while (MainButton[FirstItem]->GetEffect() > 0) + usleep(100); +} + +void FlyingButtonsMenu::SlideButtons(int direction) +{ + int SlideEffectIN = 0; + + if(direction == SLIDE_LEFT) + { + ShowButtonsEffects(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, SLIDE_SPEED); + SlideEffectIN = EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN; + + currentPage--; + + if(currentPage < 0) + currentPage = MainButton.size() > 0 ? ceil(MainButton.size()/4.0f)-1 : 0; + } + else + { + ShowButtonsEffects(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, SLIDE_SPEED); + SlideEffectIN = EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN; + + currentPage++; + + if(currentPage >= ceil(MainButton.size()/4.0f)) + currentPage = 0; + } + + AddMainButtons(); + + ShowButtonsEffects(SlideEffectIN, SLIDE_SPEED); +} + +int FlyingButtonsMenu::MainLoop() +{ + usleep(100); + + if(shutdown) + Sys_Shutdown(); + else if(reset) + Sys_Reboot(); + + if(backBtn->GetState() == STATE_CLICKED) + { + if(CurrentMenu) + { + DeleteSettingsMenu(); + ShowMenu(); + } + else + { + return MENU_DISCLIST; + } + backBtn->ResetState(); + } + else if(GoRightBtn && GoRightBtn->GetState() == STATE_CLICKED) + { + SlideButtons(SLIDE_RIGHT); + GoRightBtn->ResetState(); + } + else if(GoLeftBtn && GoLeftBtn->GetState() == STATE_CLICKED) + { + SlideButtons(SLIDE_LEFT); + GoLeftBtn->ResetState(); + } + else if(homeBtn->GetState() == STATE_CLICKED) + { + Settings.Save(); + if(CurrentMenu) CurrentMenu->SetState(STATE_DISABLED); + bgMusic->Pause(); + int choice = WindowExitPrompt(); + bgMusic->Resume(); + if (choice == 3) + Sys_LoadMenu(); // Back to System Menu + else if (choice == 2) + Sys_BackToLoader(); + homeBtn->ResetState(); + if(CurrentMenu) CurrentMenu->SetState(STATE_DEFAULT); + } + else if(CurrentMenu) + { + int newMenu = CurrentMenu->GetMenu(); + if(newMenu != MENU_NONE) + return newMenu; + } + + for(u32 i = 0; i < PageIndicatorBtn.size(); ++i) + { + if(PageIndicatorBtn[i]->GetState() != STATE_CLICKED) + continue; + + if(i < (u32) currentPage) + { + ShowButtonsEffects(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, SLIDE_SPEED); + currentPage = i; + AddMainButtons(); + ShowButtonsEffects(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, SLIDE_SPEED); + } + else if(i > (u32) currentPage) + { + ShowButtonsEffects(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, SLIDE_SPEED); + currentPage = i; + AddMainButtons(); + ShowButtonsEffects(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, SLIDE_SPEED); + } + + PageIndicatorBtn[i]->ResetState(); + } + + for(u32 i = 0; i < MainButton.size(); ++i) + { + if(MainButton[i]->GetState() != STATE_CLICKED) + continue; + + MainButton[i]->ResetState(); + + CreateSettingsMenu(i); + break; + } + + return returnMenu; +} diff --git a/source/settings/menus/FlyingButtonsMenu.hpp b/source/settings/menus/FlyingButtonsMenu.hpp new file mode 100644 index 00000000..e5d18783 --- /dev/null +++ b/source/settings/menus/FlyingButtonsMenu.hpp @@ -0,0 +1,103 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 FLYINGBUTTONSMENU_HPP_ +#define FLYINGBUTTONSMENU_HPP_ + +#include +#include +#include "libwiigui/gui.h" +#include "SettingsMenu.hpp" +#include "menu.h" + +class FlyingButtonsMenu : public GuiWindow +{ + public: + FlyingButtonsMenu(const char * menu_title); + ~FlyingButtonsMenu(); + int MainLoop(); + void HideMenu(); + void ShowMenu(); + protected: + virtual void CreateSettingsMenu(int index) { }; + virtual void DeleteSettingsMenu() { }; + virtual void SetupMainButtons() { }; + void AddMainButtons(); + void ShowButtonsEffects(int effect, int effect_speed); + void SlideButtons(int slide_direction); + void SetPageIndicators(); + void SetMainButton(int position, const char * ButtonText, GuiImageData * imageData, GuiImageData * imageOver); + + int currentPage; + int returnMenu; + std::string MenuTitle; + enum + { + SLIDE_LEFT, SLIDE_RIGHT + }; + + //!The main settings gui with browser + SettingsMenu * CurrentMenu; + + GuiImageData * btnOutline; + GuiImageData * settingsbg; + GuiImageData * MainButtonImgData; + GuiImageData * MainButtonImgOverData; + GuiImageData * PageindicatorImgData; + GuiImageData * arrow_left; + GuiImageData * arrow_right; + + GuiImage * settingsbackground; + GuiImage * backBtnImg; + GuiImage * PageindicatorImg2; + GuiImage * GoLeftImg; + GuiImage * GoRightImg; + + GuiTrigger * trigA; + GuiTrigger * trigHome; + GuiTrigger * trigB; + GuiTrigger * trigL; + GuiTrigger * trigR; + GuiTrigger * trigMinus; + GuiTrigger * trigPlus; + + GuiText * titleTxt; + GuiText * backBtnTxt; + GuiText * PageindicatorTxt1; + + GuiButton * backBtn; + GuiButton * homeBtn; + GuiButton * GoLeftBtn; + GuiButton * GoRightBtn; + + std::vectorPageindicatorImg; + std::vectorPageindicatorTxt; + std::vectorPageIndicatorBtn; + + std::vector MainButtonImg; + std::vector MainButtonImgOver; + std::vector MainButtonTxt; + std::vector MainButton; +}; + +#endif diff --git a/source/settings/menus/GUISettingsMenu.cpp b/source/settings/menus/GUISettingsMenu.cpp new file mode 100644 index 00000000..b56dd370 --- /dev/null +++ b/source/settings/menus/GUISettingsMenu.cpp @@ -0,0 +1,334 @@ + /**************************************************************************** + * Copyright (C) 2010 + * 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 "GuiSettingsMenu.hpp" +#include "settings/CSettings.h" +#include "prompts/PromptWindows.h" +#include "language/gettext.h" +#include "settings/SettingsPrompts.h" +#include "settings/GameTitles.h" +#include "xml/xml.h" +#include "fatmounter.h" + +static const char * OnOffText[MAX_ON_OFF] = +{ + trNOOP( "OFF" ), + trNOOP( "ON" ) +}; + +static const char * WiilightText[WIILIGHT_MAX] = +{ + trNOOP( "OFF" ), + trNOOP( "ON" ), + trNOOP( "Only for Install" ) +}; + +static const char * GameInfoText[GAMEINFO_MAX] = +{ + trNOOP( "Game ID" ), + trNOOP( "Game Region" ), + trNOOP( "Both" ), + trNOOP( "Neither" ) +}; + +static const char * FlipXText[XFLIP_MAX][3] = +{ + { trNOOP( "Right" ), "/", trNOOP( "Next" ) }, + { trNOOP( "Left" ), "/", trNOOP( "Prev" ) }, + { trNOOP( "Like SysMenu" ), "", "" }, + { trNOOP( "Right" ), "/", trNOOP( "Prev" ) }, + { trNOOP( "DiskFlip" ), "", "" } +}; + +static const char * PromptButtonsText[MAX_ON_OFF] = +{ + trNOOP( "Normal" ), + trNOOP( "Widescreen Fix" ), +}; + +static const char * KeyboardText[KEYBOARD_MAX] = +{ + "QWERTY", + "DVORAK", + "QWERTZ", + "AZERTY", + "QWERTY 2" +}; + +static const char * DiscArtDownloadText[4] = +{ + trNOOP( "Only Original" ), + trNOOP( "Only Customs" ), + trNOOP( "Original/Customs" ), + trNOOP( "Customs/Original" ) +}; + +static const char *ScreensaverText[SCREENSAVER_MAX] = +{ + trNOOP( "OFF" ), + trNOOP( "3 min" ), + trNOOP( "5 min" ), + trNOOP( "10 min" ), + trNOOP( "20 min" ), + trNOOP( "30 min" ), + trNOOP( "1 hour" ) +}; + +GuiSettingsMenu::GuiSettingsMenu() + : SettingsMenu(tr("GUI Settings"), &GuiOptions, MENU_NONE) +{ + int Idx = 0; + Options->SetName(Idx++, "%s", tr( "App Language" )); + Options->SetName(Idx++, "%s", tr( "Display" )); + Options->SetName(Idx++, "%s", tr( "Clock" )); + Options->SetName(Idx++, "%s", tr( "Tooltips" )); + Options->SetName(Idx++, "%s", tr( "Flip-X" )); + Options->SetName(Idx++, "%s", tr( "Prompts Buttons" )); + Options->SetName(Idx++, "%s", tr( "Keyboard" )); + Options->SetName(Idx++, "%s", tr( "Disc Artwork Download" )); + Options->SetName(Idx++, "%s", tr( "Wiilight" )); + Options->SetName(Idx++, "%s", tr( "Rumble" )); + Options->SetName(Idx++, "%s", tr( "AutoInit Network" )); + Options->SetName(Idx++, "%s", tr( "BETA revisions" )); + Options->SetName(Idx++, "%s", tr( "Titles from WiiTDB" )); + Options->SetName(Idx++, "%s", tr( "Screensaver" )); + Options->SetName(Idx++, "%s", tr( "Mark new games" )); + + SetOptionValues(); + + OldTitlesOverride = Settings.titlesOverride; +} + +GuiSettingsMenu::~GuiSettingsMenu() +{ + if (Settings.titlesOverride != OldTitlesOverride) + { + CloseXMLDatabase(); + GameTitles.SetDefault(); + if(Settings.titlesOverride) + OpenXMLDatabase(Settings.titlestxt_path, Settings.db_language, Settings.db_JPtoEN, true, Settings.titlesOverride, true); + } + +} + +void GuiSettingsMenu::SetOptionValues() +{ + int Idx = 0; + + //! Settings: App Language + const char * language = strrchr(Settings.language_path, '/'); + if(language) + language += 1; + if (!language || strcmp(Settings.language_path, "") == 0) + Options->SetValue(Idx++, "%s", tr( "Default" )); + else + Options->SetValue(Idx++, "%s", language); + + //! Settings: Display + Options->SetValue(Idx++, "%s", tr( GameInfoText[Settings.sinfo] )); + + //! Settings: Clock + if (Settings.hddinfo == CLOCK_HR12) + Options->SetValue(Idx++, "12 %s", tr( "Hour" )); + else if (Settings.hddinfo == CLOCK_HR24) + Options->SetValue(Idx++, "24 %s", tr( "Hour" )); + else if (Settings.hddinfo == OFF) + Options->SetValue(Idx++, "%s", tr( "OFF" )); + + //! Settings: Tooltips + Options->SetValue(Idx++, "%s", tr(OnOffText[Settings.tooltips])); + + //! Settings: Flip-X + Options->SetValue(Idx++, "%s%s%s", tr(FlipXText[Settings.xflip][0]), + FlipXText[Settings.xflip][1], tr( FlipXText[Settings.xflip][2] )); + + //! Settings: Prompts Buttons + Options->SetValue(Idx++, "%s", tr( PromptButtonsText[Settings.wsprompt] )); + + //! Settings: Keyboard + Options->SetValue(Idx++, "%s", KeyboardText[Settings.keyset]); + + //! Settings: Disc Artwork Download + Options->SetValue(Idx++, "%s", tr( DiscArtDownloadText[Settings.discart] )); + + //! Settings: Wiilight + Options->SetValue(Idx++, "%s", tr( WiilightText[Settings.wiilight] )); + + //! Settings: Rumble + Options->SetValue(Idx++, "%s", tr( OnOffText[Settings.rumble] )); + + //! Settings: AutoInit Network + Options->SetValue(Idx++, "%s", tr( OnOffText[Settings.autonetwork] )); + + //! Settings: BETA revisions + Options->SetValue(Idx++, "%s", tr( OnOffText[Settings.beta_upgrades] )); + + //! Settings: Titles from WiiTDB + Options->SetValue(Idx++, "%s", tr( OnOffText[Settings.titlesOverride] )); + + //! Settings: Screensaver + Options->SetValue(Idx++, "%s", tr( ScreensaverText[Settings.screensaver] )); + + //! Settings: Mark new games + Options->SetValue(Idx++, "%s", tr( OnOffText[Settings.marknewtitles] )); +} + +int GuiSettingsMenu::GetMenuInternal() +{ + int ret = optionBrowser->GetClickedOption(); + + if (ret < 0) + return MENU_NONE; + + int Idx = -1; + + //! Settings: App Language + if (ret == ++Idx) + { + if (!isInserted(Settings.BootDevice)) + { + WindowPrompt(tr( "No SD-Card inserted!" ), tr( "Insert an SD-Card to use this option." ), tr( "OK" )); + return MENU_NONE; + } + if (!Settings.godmode) + { + WindowPrompt(tr( "Language change:" ), tr( "Console should be unlocked to modify it." ), tr( "OK" )); + return MENU_NONE; + } + SetEffect(EFFECT_FADE, -20); + while (GetEffect() > 0) usleep(100); + HaltGui(); + if(parentElement) + { + ((GuiWindow *) parentElement)->Remove(this); + ((GuiWindow *) parentElement)->SetState(STATE_DISABLED); + } + ResumeGui(); + + int returnhere = 1; + while (returnhere == 1) + returnhere = MenuLanguageSelect(); + + if (returnhere == 2) + return MENU_SETTINGS; + + HaltGui(); + if(parentElement) + { + ((GuiWindow *) parentElement)->Append(this); + ((GuiWindow *) parentElement)->SetState(STATE_DEFAULT); + } + SetEffect(EFFECT_FADE, 20); + ResumeGui(); + while (GetEffect() > 0) usleep(100); + } + + //! Settings: Display + else if (ret == ++Idx) + { + if (++Settings.sinfo >= GAMEINFO_MAX) Settings.sinfo = 0; + } + + //! Settings: Clock + else if (ret == ++Idx) + { + if (++Settings.hddinfo >= CLOCK_MAX) Settings.hddinfo = 0; //CLOCK + } + + //! Settings: Tooltips + else if (ret == ++Idx) + { + if (++Settings.tooltips >= MAX_ON_OFF) Settings.tooltips = 0; + } + + //! Settings: Flip-X + else if (ret == ++Idx) + { + if (++Settings.xflip >= XFLIP_MAX) Settings.xflip = 0; + } + + //! Settings: Prompts Buttons + else if (ret == ++Idx) + { + if (++Settings.wsprompt >= MAX_ON_OFF) Settings.wsprompt = 0; + } + + //! Settings: Keyboard + else if (ret == ++Idx) + { + if (++Settings.keyset >= KEYBOARD_MAX) Settings.keyset = 0; + } + + //! Settings: Disc Artwork Download + else if (ret == ++Idx) + { + if (++Settings.discart >= 4) Settings.discart = 0; + } + + //! Settings: Wiilight + else if (ret == ++Idx) + { + if (++Settings.wiilight >= WIILIGHT_MAX) Settings.wiilight = 0; + } + + //! Settings: Rumble + else if (ret == ++Idx) + { + if (++Settings.rumble >= MAX_ON_OFF) Settings.rumble = 0; //RUMBLE + } + + //! Settings: AutoInit Network + else if (ret == ++Idx) + { + if (++Settings.autonetwork >= MAX_ON_OFF) Settings.autonetwork = 0; + } + + //! Settings: BETA revisions + else if (ret == ++Idx) + { + if (++Settings.beta_upgrades >= MAX_ON_OFF) Settings.beta_upgrades = 0; + } + + //! Settings: Titles from WiiTDB + else if (ret == ++Idx) + { + if (++Settings.titlesOverride >= MAX_ON_OFF) Settings.titlesOverride = 0; + } + + //! Settings: Screensaver + else if (ret == ++Idx) + { + if (++Settings.screensaver >= SCREENSAVER_MAX) Settings.screensaver = 0; + } + + //! Settings: Mark new games + else if (ret == ++Idx) + { + if (++Settings.marknewtitles >= MAX_ON_OFF) Settings.marknewtitles = 0; + } + + SetOptionValues(); + + return MENU_NONE; +} diff --git a/source/settings/menus/GUISettingsMenu.hpp b/source/settings/menus/GUISettingsMenu.hpp new file mode 100644 index 00000000..97680648 --- /dev/null +++ b/source/settings/menus/GUISettingsMenu.hpp @@ -0,0 +1,45 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 GUISETTINGS_MENU_HPP_ +#define GUISETTINGS_MENU_HPP_ + +#include "SettingsMenu.hpp" + +class GuiSettingsMenu : public SettingsMenu +{ + public: + GuiSettingsMenu(); + ~GuiSettingsMenu(); + virtual int GetType() { return CGUISettingsMenu; }; + protected: + void SetOptionValues(); + int GetMenuInternal(); + + int OldTitlesOverride; + + OptionList GuiOptions; +}; + + +#endif diff --git a/source/settings/menus/GameLoadSM.cpp b/source/settings/menus/GameLoadSM.cpp new file mode 100644 index 00000000..571cbc86 --- /dev/null +++ b/source/settings/menus/GameLoadSM.cpp @@ -0,0 +1,327 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 "GameLoadSM.hpp" +#include "settings/CSettings.h" +#include "prompts/PromptWindows.h" +#include "language/gettext.h" +#include "wad/nandtitle.h" +#include "prompts/TitleBrowser.h" +#include "usbloader/GameList.h" +#include "usbloader/wbfs.h" +#include "usbloader/utils.h" +#include "system/IosLoader.h" +#include "settings/GameTitles.h" +#include "xml/xml.h" +#include "menu.h" + +extern PartList partitions; +extern char game_partition[6]; +extern u8 load_from_fs; + +static const char * OnOffText[MAX_ON_OFF] = +{ + trNOOP( "OFF" ), + trNOOP( "ON" ) +}; + +static const char * VideoModeText[VIDEO_MODE_MAX] = +{ + trNOOP( "Disc Default" ), + trNOOP( "System Default" ), + trNOOP( "AutoPatch" ), + trNOOP( "Force PAL50" ), + trNOOP( "Force PAL60" ), + trNOOP( "Force NTSC" ) +}; + +static const char * LanguageText[MAX_LANGUAGE] = +{ + trNOOP( "Disc Default" ), + trNOOP( "Console Default" ), + trNOOP( "Japanese" ), + trNOOP( "English" ), + trNOOP( "German" ), + trNOOP( "French" ), + trNOOP( "Spanish" ), + trNOOP( "Italian" ), + trNOOP( "Dutch" ), + trNOOP( "SChinese" ), + trNOOP( "TChinese" ), + trNOOP( "Korean" ) +}; + +static const char * InstallToText[INSTALL_TO_MAX] = +{ + trNOOP( "None" ), + trNOOP( "GAMEID_Gamename" ), + trNOOP( "Gamename [GAMEID]" ) +}; + +static const char * Error002Text[3] = +{ + trNOOP( "No" ), + trNOOP( "Yes" ), + trNOOP( "Anti" ) +}; + +static const char * InstPartitionsText[3] = +{ + trNOOP( "Game partition" ), + trNOOP( "All partitions" ), + trNOOP( "Remove update" ) +}; + +static inline bool IsValidPartition(int fs_type, int cios) +{ + if (IosLoader::IsWaninkokoIOS() && NandTitles.VersionOf(TITLE_ID(1, cios)) < 18) + { + return fs_type == FS_TYPE_WBFS; + } + else + { + return fs_type == FS_TYPE_WBFS || fs_type == FS_TYPE_FAT32 || fs_type == FS_TYPE_NTFS; + } +} + +GameLoadSM::GameLoadSM() + : SettingsMenu(tr("Game Load"), &GuiOptions, MENU_NONE) +{ + int Idx = 0; + + Options->SetName(Idx++, "%s", tr( "Video Mode" )); + Options->SetName(Idx++, "%s", tr( "VIDTV Patch" )); + Options->SetName(Idx++, "%s", tr( "Game Language" )); + Options->SetName(Idx++, "%s", tr( "Patch Country Strings" )); + Options->SetName(Idx++, "%s", tr( "Ocarina" )); + Options->SetName(Idx++, "%s", tr( "Boot/Standard" )); + Options->SetName(Idx++, "%s", tr( "Partition" )); + Options->SetName(Idx++, "%s", tr( "FAT: Use directories" )); + Options->SetName(Idx++, "%s", tr( "Quick Boot" )); + Options->SetName(Idx++, "%s", tr( "Error 002 fix" )); + Options->SetName(Idx++, "%s", tr( "Install partitions" )); + Options->SetName(Idx++, "%s", tr( "Install 1:1 Copy" )); + Options->SetName(Idx++, "%s", tr( "Return To" )); + + SetOptionValues(); + + OldSettingsPartition = Settings.partition; +} + +GameLoadSM::~GameLoadSM() +{ + // if partition has changed, Reinitialize it + if (Settings.partition != OldSettingsPartition) + { + PartInfo pinfo = partitions.pinfo[Settings.partition]; + partitionEntry pentry = partitions.pentry[Settings.partition]; + WBFS_OpenPart(pinfo.part_fs, pinfo.index, pentry.sector, pentry.size, (char *) &game_partition); + load_from_fs = pinfo.part_fs; + CloseXMLDatabase(); + GameTitles.SetDefault(); + OpenXMLDatabase(Settings.titlestxt_path, Settings.db_language, Settings.db_JPtoEN, true, Settings.titlesOverride, true); + gameList.ReadGameList(); + } +} + +void GameLoadSM::SetOptionValues() +{ + int Idx = 0; + + //! Settings: Video Mode + Options->SetValue(Idx++, "%s", tr(VideoModeText[Settings.videomode])); + + //! Settings: VIDTV Patch + Options->SetValue(Idx++, "%s", tr( OnOffText[Settings.videopatch] )); + + //! Settings: Game Language + Options->SetValue(Idx++, "%s", tr( LanguageText[Settings.language] )); + + //! Settings: Patch Country Strings + Options->SetValue(Idx++, "%s", tr( OnOffText[Settings.patchcountrystrings] )); + + //! Settings: Ocarina + Options->SetValue(Idx++, "%s", tr( OnOffText[Settings.ocarina] )); + + //! Settings: Boot/Standard + if (Settings.godmode) + Options->SetValue(Idx++, "IOS %i", Settings.cios); + else + Options->SetValue(Idx++, "********"); + + //! Settings: Partition + PartInfo pInfo = partitions.pinfo[Settings.partition]; + f32 partition_size = partitions.pentry[Settings.partition].size + * (partitions.sector_size / GB_SIZE); + + // Get the partition name and it's size in GB's + Options->SetValue(Idx++, "%s%d (%.2fGB)", pInfo.fs_type == FS_TYPE_FAT32 ? "FAT" + : pInfo.fs_type == FS_TYPE_NTFS ? "NTFS" : "WBFS", pInfo.index, partition_size); + + //! Settings: FAT: Use directories + Options->SetValue(Idx++, "%s", tr( InstallToText[Settings.FatInstallToDir] )); + + //! Settings: Quick Boot + Options->SetValue(Idx++, "%s", tr( OnOffText[Settings.quickboot] )); + + //! Settings: Error 002 fix + Options->SetValue(Idx++, "%s", tr( Error002Text[Settings.error002] )); + + //! Settings: Install partitions + Options->SetValue(Idx++, "%s", tr( InstPartitionsText[Settings.InstallPartitions] )); + + //! Settings: Install 1:1 Copy + Options->SetValue(Idx++, "%s", tr( OnOffText[Settings.fullcopy] )); + + //! Settings: Return To + const char* TitleName = NULL; + int haveTitle = NandTitles.FindU32(Settings.returnTo); + if (haveTitle >= 0) + TitleName = NandTitles.NameFromIndex(haveTitle); + Options->SetValue(Idx++, "%s", TitleName ? TitleName : strlen(Settings.returnTo) > 0 ? + Settings.returnTo : tr( OnOffText[0] )); +} + +int GameLoadSM::GetMenuInternal() +{ + int ret = optionBrowser->GetClickedOption(); + + if (ret < 0) + return MENU_NONE; + + int Idx = -1; + + //! Settings: Video Mode + if (ret == ++Idx) + { + if (++Settings.videomode >= VIDEO_MODE_MAX) Settings.videomode = 0; + } + + //! Settings: VIDTV Patch + else if (ret == ++Idx) + { + if (++Settings.videopatch >= MAX_ON_OFF) Settings.videopatch = 0; + } + + //! Settings: Game Language + else if (ret == ++Idx) + { + if (++Settings.language >= MAX_LANGUAGE) Settings.language = 0; + } + + //! Settings: Patch Country Strings + else if (ret == ++Idx) + { + if (++Settings.patchcountrystrings >= MAX_ON_OFF) Settings.patchcountrystrings = 0; + } + + //! Settings: Ocarina + else if (ret == ++Idx) + { + if (++Settings.ocarina >= MAX_ON_OFF) Settings.ocarina = 0; + } + + //! Settings: Boot/Standard + else if (ret == ++Idx) + { + if(!Settings.godmode) + return MENU_NONE; + + char entered[4]; + snprintf(entered, sizeof(entered), "%i", Settings.cios); + if(OnScreenKeyboard(entered, sizeof(entered), 0)) + { + Settings.cios = atoi(entered); + if(Settings.cios < 200) Settings.cios = 200; + else if(Settings.cios > 255) Settings.cios = 255; + + if(NandTitles.IndexOf(TITLE_ID(1, Settings.cios)) < 0) + { + WindowPrompt(tr("Warning:"), tr("This IOS was not found on the titles list. If you are sure you have it installed than ignore this warning."), tr("OK")); + } + else if(Settings.cios == 254) + { + WindowPrompt(tr("Warning:"), tr("This IOS is the BootMii ios. If you are sure it is not BootMii and you have something else installed there than ignore this warning."), tr("OK")); + } + } + } + + //! Settings: Partition + else if (ret == ++Idx) + { + // Select the next valid partition, even if that's the same one + int fs_type = partitions.pinfo[Settings.partition].fs_type; + int ios = IOS_GetVersion(); + do + { + Settings.partition = (Settings.partition + 1) % partitions.num; + fs_type = partitions.pinfo[Settings.partition].fs_type; + } + while (!IsValidPartition(fs_type, ios)); + } + + //! Settings: FAT: Use directories + else if (ret == ++Idx) + { + if (++Settings.FatInstallToDir >= INSTALL_TO_MAX) Settings.FatInstallToDir = 0; + } + + //! Settings: Quick Boot + else if (ret == ++Idx) + { + if (++Settings.quickboot >= MAX_ON_OFF) Settings.quickboot = 0; + } + + //! Settings: Error 002 fix + else if (ret == ++Idx ) + { + if (++Settings.error002 >= 3) Settings.error002 = 0; + } + + //! Settings: Install partitions + else if (ret == ++Idx) + { + if (++Settings.InstallPartitions >= 3) Settings.InstallPartitions = 0; + } + + //! Settings: Install 1:1 Copy + else if (ret == ++Idx) + { + if (++Settings.fullcopy >= MAX_ON_OFF) Settings.fullcopy = 0; + } + + //! Settings: Return To + else if (ret == ++Idx) + { + char tidChar[10]; + bool getChannel = TitleSelector(tidChar); + if (getChannel) + snprintf(Settings.returnTo, sizeof(Settings.returnTo), "%s", tidChar); + } + + SetOptionValues(); + + return MENU_NONE; +} + diff --git a/source/settings/menus/GameLoadSM.hpp b/source/settings/menus/GameLoadSM.hpp new file mode 100644 index 00000000..27fd7e7b --- /dev/null +++ b/source/settings/menus/GameLoadSM.hpp @@ -0,0 +1,45 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 GAMELOADSM_HPP_ +#define GAMELOADSM_HPP_ + +#include "SettingsMenu.hpp" + +class GameLoadSM : public SettingsMenu +{ + public: + GameLoadSM(); + ~GameLoadSM(); + virtual int GetType() { return CGameLoadSM; }; + protected: + void SetOptionValues(); + int GetMenuInternal(); + + int OldSettingsPartition; + + OptionList GuiOptions; +}; + + +#endif diff --git a/source/settings/menus/GameSettingsMenu.cpp b/source/settings/menus/GameSettingsMenu.cpp new file mode 100644 index 00000000..37ec3cc5 --- /dev/null +++ b/source/settings/menus/GameSettingsMenu.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 "GameSettingsMenu.hpp" +#include "themes/CTheme.h" +#include "prompts/PromptWindows.h" +#include "settings/GameTitles.h" +#include "language/gettext.h" +#include "wad/nandtitle.h" +#include "cheats/cheatmenu.h" +#include "IndGameLoadSM.hpp" +#include "UninstallSM.hpp" + +GameSettingsMenu::GameSettingsMenu(struct discHdr * header) + : FlyingButtonsMenu(GameTitles.GetTitle(header)) +{ + DiscHeader = header; +} + +GameSettingsMenu::~GameSettingsMenu() +{ +} + +void GameSettingsMenu::SetupMainButtons() +{ + int pos = 0; + + SetMainButton(pos++, tr( "Game Load" ), MainButtonImgData, MainButtonImgOverData); + SetMainButton(pos++, tr( "Ocarina" ), MainButtonImgData, MainButtonImgOverData); + SetMainButton(pos++, tr( "Uninstall Menu" ), MainButtonImgData, MainButtonImgOverData); + SetMainButton(pos++, tr( "Default Gamesettings" ), MainButtonImgData, MainButtonImgOverData); +} + +void GameSettingsMenu::CreateSettingsMenu(int menuNr) +{ + if(CurrentMenu) + return; + + int Idx = 0; + + //! Game Load + if(menuNr == Idx++) + { + HideMenu(); + ResumeGui(); + CurrentMenu = new IndGameLoadSM((const char *) DiscHeader->id); + Append(CurrentMenu); + } + + //! Ocarina + else if(menuNr == Idx++) + { + char ID[7]; + snprintf(ID, sizeof(ID), "%s", (char *) DiscHeader->id); + CheatMenu(ID); + } + + //! Uninstall Menu + else if(menuNr == Idx++) + { + HideMenu(); + ResumeGui(); + CurrentMenu = new UninstallSM(DiscHeader); + Append(CurrentMenu); + } + + //! Default Gamesettings + else if(menuNr == Idx++) + { + int choice = WindowPrompt(tr( "Are you sure?" ), 0, tr( "Yes" ), tr( "Cancel" )); + if (choice == 1) + { + GameSettings.Remove(DiscHeader->id); + GameSettings.Save(); + } + } +} + +void GameSettingsMenu::DeleteSettingsMenu() +{ + if(!CurrentMenu) + return; + + int type = CurrentMenu->GetType(); + + switch(type) + { + case CIndGameLoadSM: + delete ((IndGameLoadSM *) CurrentMenu); + break; + case CUninstallSM: + delete ((UninstallSM *) CurrentMenu); + break; + case CSettingsMenu: + default: + delete CurrentMenu; + break; + } + + CurrentMenu = NULL; +} diff --git a/source/settings/menus/GameSettingsMenu.hpp b/source/settings/menus/GameSettingsMenu.hpp new file mode 100644 index 00000000..91cb6b58 --- /dev/null +++ b/source/settings/menus/GameSettingsMenu.hpp @@ -0,0 +1,44 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 GAMESETTINGSMENU_HPP_ +#define GAMESETTINGSMENU_HPP_ + +#include "FlyingButtonsMenu.hpp" +#include "settings/CGameSettings.h" +#include "usbloader/disc.h" + +class GameSettingsMenu : public FlyingButtonsMenu +{ + public: + GameSettingsMenu(struct discHdr * header); + ~GameSettingsMenu(); + protected: + virtual void CreateSettingsMenu(int index); + virtual void DeleteSettingsMenu(); + virtual void SetupMainButtons(); + + struct discHdr * DiscHeader; +}; + +#endif diff --git a/source/settings/menus/GlobalSettings.cpp b/source/settings/menus/GlobalSettings.cpp new file mode 100644 index 00000000..2b4da498 --- /dev/null +++ b/source/settings/menus/GlobalSettings.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 "GlobalSettings.hpp" +#include "themes/CTheme.h" +#include "prompts/PromptWindows.h" +#include "language/gettext.h" +#include "GuiSettingsMenu.hpp" +#include "GameLoadSM.hpp" +#include "ParentalControlSM.hpp" +#include "SoundSettingsMenu.hpp" +#include "CustomPathsSM.hpp" + +GlobalSettings::GlobalSettings() + : FlyingButtonsMenu(tr("Global Settings")) +{ + creditsImgData = Resources::GetImageData("credits_button.png"); + creditsImgOverData = Resources::GetImageData("credits_button_over.png"); +} + +GlobalSettings::~GlobalSettings() +{ + Settings.Save(); + + delete creditsImgData; + delete creditsImgOverData; +} + +void GlobalSettings::SetupMainButtons() +{ + int pos = 0; + + SetMainButton(pos++, tr( "GUI Settings" ), MainButtonImgData, MainButtonImgOverData); + SetMainButton(pos++, tr( "Game Load" ), MainButtonImgData, MainButtonImgOverData); + SetMainButton(pos++, tr( "Parental Control" ), MainButtonImgData, MainButtonImgOverData); + SetMainButton(pos++, tr( "Sound" ), 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); + SetMainButton(pos++, tr( "Theme Downloader" ), MainButtonImgData, MainButtonImgOverData); +} + +void GlobalSettings::CreateSettingsMenu(int menuNr) +{ + if(CurrentMenu) + return; + + int Idx = 0; + + //! GUI Settings + if(menuNr == Idx++) + { + HideMenu(); + ResumeGui(); + CurrentMenu = new GuiSettingsMenu(); + Append(CurrentMenu); + } + //! Game Load + else if(menuNr == Idx++) + { + HideMenu(); + ResumeGui(); + CurrentMenu = new GameLoadSM(); + Append(CurrentMenu); + } + //! Parental Control + else if(menuNr == Idx++) + { + HideMenu(); + ResumeGui(); + CurrentMenu = new ParentalControlSM(); + Append(CurrentMenu); + } + //! Sound + else if(menuNr == Idx++) + { + HideMenu(); + ResumeGui(); + CurrentMenu = new SoundSettingsMenu(); + Append(CurrentMenu); + } + //! Custom Paths + else if(menuNr == Idx++) + { + if(Settings.godmode) + { + HideMenu(); + ResumeGui(); + CurrentMenu = new CustomPathsSM(); + Append(CurrentMenu); + } + else + WindowPrompt(tr( "Console Locked" ), tr( "Unlock console to use this option." ), tr( "OK" )); + + } + //! Update + else if(menuNr == Idx++) + { + if (Settings.godmode) + { + HideMenu(); + Remove(backBtn); + ResumeGui(); + int ret = ProgressUpdateWindow(); + if (ret < 0) + WindowPrompt(tr( "Update failed" ), 0, tr( "OK" )); + Append(backBtn); + ShowMenu(); + } + else + WindowPrompt(tr( "Console Locked" ), tr( "Unlock console to use this option." ), tr( "OK" )); + } + //! Default Settings + else if(menuNr == Idx++) + { + if (Settings.godmode) + { + int choice = WindowPrompt(tr( "Are you sure you want to reset?" ), 0, tr( "Yes" ), tr( "Cancel" )); + if (choice == 1) + { + HaltGui(); + gettextCleanUp(); + Settings.Reset(); + returnMenu = MENU_SETTINGS; + ResumeGui(); + } + } + else + WindowPrompt(tr( "Console Locked" ), tr( "Unlock console to use this option." ), tr( "OK" )); + } + //! Credits + else if(menuNr == Idx++) + { + HideMenu(); + Remove(backBtn); + ResumeGui(); + WindowCredits(); + Append(backBtn); + ShowMenu(); + } + //! Theme Downloader + else if(menuNr == Idx++) + { + returnMenu = MENU_THEMEDOWNLOADER; + } +} + +void GlobalSettings::DeleteSettingsMenu() +{ + if(!CurrentMenu) + return; + + int type = CurrentMenu->GetType(); + + switch(type) + { + case CGUISettingsMenu: + delete ((GuiSettingsMenu *) CurrentMenu); + break; + case CGameLoadSM: + delete ((GameLoadSM *) CurrentMenu); + break; + case CParentalControlSM: + delete ((ParentalControlSM *) CurrentMenu); + break; + case CSoundSettingsMenu: + delete ((SoundSettingsMenu *) CurrentMenu); + break; + case CCustomPathsSM: + delete ((CustomPathsSM *) CurrentMenu); + break; + case CSettingsMenu: + default: + delete CurrentMenu; + break; + } + + CurrentMenu = NULL; +} diff --git a/source/settings/menus/GlobalSettings.hpp b/source/settings/menus/GlobalSettings.hpp new file mode 100644 index 00000000..97b9400c --- /dev/null +++ b/source/settings/menus/GlobalSettings.hpp @@ -0,0 +1,43 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 GLOBAL_SETTINGS_HPP_ +#define GLOBAL_SETTINGS_HPP_ + +#include "FlyingButtonsMenu.hpp" + +class GlobalSettings : public FlyingButtonsMenu +{ + public: + GlobalSettings(); + ~GlobalSettings(); + protected: + virtual void CreateSettingsMenu(int index); + virtual void DeleteSettingsMenu(); + virtual void SetupMainButtons(); + + GuiImageData * creditsImgData; + GuiImageData * creditsImgOverData; +}; + +#endif diff --git a/source/settings/menus/IndGameLoadSM.cpp b/source/settings/menus/IndGameLoadSM.cpp new file mode 100644 index 00000000..8c3cfce0 --- /dev/null +++ b/source/settings/menus/IndGameLoadSM.cpp @@ -0,0 +1,409 @@ + /**************************************************************************** + * Copyright (C) 2010 + * 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 "settings/CSettings.h" +#include "themes/CTheme.h" +#include "prompts/PromptWindows.h" +#include "prompts/DiscBrowser.h" +#include "language/gettext.h" +#include "wad/nandtitle.h" +#include "IndGameLoadSM.hpp" + +static const char * OnOffText[MAX_ON_OFF] = +{ + trNOOP( "OFF" ), + trNOOP( "ON" ) +}; + +static const char * VideoModeText[VIDEO_MODE_MAX] = +{ + trNOOP( "Disc Default" ), + trNOOP( "System Default" ), + trNOOP( "AutoPatch" ), + trNOOP( "Force PAL50" ), + trNOOP( "Force PAL60" ), + trNOOP( "Force NTSC" ) +}; + +static const char * LanguageText[MAX_LANGUAGE] = +{ + trNOOP( "Disc Default" ), + trNOOP( "Console Default" ), + trNOOP( "Japanese" ), + trNOOP( "English" ), + trNOOP( "German" ), + trNOOP( "French" ), + trNOOP( "Spanish" ), + trNOOP( "Italian" ), + trNOOP( "Dutch" ), + trNOOP( "SChinese" ), + trNOOP( "TChinese" ), + trNOOP( "Korean" ) +}; + +static const char * Error002Text[3] = +{ + trNOOP( "No" ), + trNOOP( "Yes" ), + trNOOP( "Anti" ) +}; + +static const char * ParentalText[5] = +{ + trNOOP( "0 (Everyone)" ), + trNOOP( "1 (Child 7+)" ), + trNOOP( "2 (Teen 12+)" ), + trNOOP( "3 (Mature 16+)" ), + trNOOP( "4 (Adults Only 18+)" ) +}; + +static const char * AlternateDOLText[] = +{ + trNOOP( "OFF" ), + trNOOP( "Load From SD/USB" ), + trNOOP( "Select a DOL" ) +}; + +IndGameLoadSM::IndGameLoadSM(const char * GameID) + : SettingsMenu(tr("Game Load"), &GuiOptions, MENU_NONE) +{ + //! Setup default settings from global settings + snprintf(GameConfig.id, sizeof(GameConfig.id), "%s", (char *) GameID); + SetDefaultConfig(); + + GameCFG * existCFG = GameSettings.GetGameCFG(GameID); + + //! Overwrite with existing if available + if (existCFG) + memcpy(&GameConfig, existCFG, sizeof(GameCFG)); + + 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, Theme.prompttext); + 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(); +} + +IndGameLoadSM::~IndGameLoadSM() +{ + HaltGui(); + //! The rest is destroyed in SettingsMenu.cpp + Remove(saveBtn); + delete saveBtnTxt; + delete saveBtnImg; + delete saveBtn; + ResumeGui(); +} + +void IndGameLoadSM::SetDefaultConfig() +{ + GameConfig.video = Settings.videomode; + GameConfig.language = Settings.language; + GameConfig.ocarina = Settings.ocarina; + GameConfig.vipatch = Settings.videopatch; + GameConfig.ios = Settings.cios; + GameConfig.parentalcontrol = 0; + GameConfig.errorfix002 = Settings.error002; + GameConfig.patchcountrystrings = Settings.patchcountrystrings; + GameConfig.loadalternatedol = OFF; + GameConfig.alternatedolstart = 0; + GameConfig.iosreloadblock = OFF; + strcpy(GameConfig.alternatedolname, ""); + GameConfig.returnTo = 1; + GameConfig.Locked = 0; +} + +void IndGameLoadSM::SetOptionNames() +{ + int Idx = 0; + + Options->SetName(Idx++, "%s", tr( "Video Mode" )); + Options->SetName(Idx++, "%s", tr( "VIDTV Patch" )); + Options->SetName(Idx++, "%s", tr( "Game Language" )); + Options->SetName(Idx++, "%s", tr( "Patch Country Strings" )); + Options->SetName(Idx++, "%s", tr( "Ocarina" )); + Options->SetName(Idx++, "%s", tr( "Game IOS" )); + Options->SetName(Idx++, "%s", tr( "Parental Control" )); + Options->SetName(Idx++, "%s", tr( "Error 002 fix" )); + Options->SetName(Idx++, "%s", tr( "Return To" )); + Options->SetName(Idx++, "%s", tr( "Alternate DOL" )); + Options->SetName(Idx++, "%s", tr( "Select DOL Offset" )); + Options->SetName(Idx++, "%s", tr( "Block IOS Reload" )); + Options->SetName(Idx++, "%s", tr( "Game Lock" )); +} + +void IndGameLoadSM::SetOptionValues() +{ + int Idx = 0; + + //! Settings: Video Mode + Options->SetValue(Idx++, "%s", tr(VideoModeText[GameConfig.video])); + + //! Settings: VIDTV Patch + Options->SetValue(Idx++, "%s", tr(OnOffText[GameConfig.vipatch])); + + //! Settings: Game Language + Options->SetValue(Idx++, "%s", tr(LanguageText[GameConfig.language])); + + //! Settings: Patch Country Strings + Options->SetValue(Idx++, "%s", tr(OnOffText[GameConfig.patchcountrystrings])); + + //! Settings: Ocarina + Options->SetValue(Idx++, "%s", tr(OnOffText[GameConfig.ocarina])); + + //! Settings: Game IOS + Options->SetValue(Idx++, "%i", GameConfig.ios); + + //! Settings: Parental Control + Options->SetValue(Idx++, "%s", tr(ParentalText[GameConfig.parentalcontrol])); + + //! Settings: Error 002 fix + Options->SetValue(Idx++, "%s", tr(Error002Text[GameConfig.errorfix002])); + + //! Settings: Return To + if(GameConfig.returnTo) + { + const char* TitleName = NULL; + int haveTitle = NandTitles.FindU32(Settings.returnTo); + if (haveTitle >= 0) + TitleName = NandTitles.NameFromIndex(haveTitle); + Options->SetValue(Idx++, "%s", TitleName ? TitleName : strlen(Settings.returnTo) > 0 ? + Settings.returnTo : tr( OnOffText[0] )); + } + else + { + Options->SetValue(Idx++, "%s", tr( OnOffText[0] )); + } + + //! Settings: Alternate DOL + Options->SetValue(Idx++, "%s", tr( AlternateDOLText[GameConfig.loadalternatedol] )); + + //! Settings: Select DOL Offset + if(GameConfig.loadalternatedol != 2) + Options->SetValue(Idx++, tr("Not required")); + else + { + if(strcmp(GameConfig.alternatedolname, "") != 0) + Options->SetValue(Idx++, "%i <%s>", GameConfig.alternatedolstart, GameConfig.alternatedolname); + else + Options->SetValue(Idx++, "%i", GameConfig.alternatedolstart); + } + + //! Settings: Block IOS Reload + Options->SetValue(Idx++, "%s", tr( OnOffText[GameConfig.iosreloadblock] )); + + //! Settings: Game Lock + Options->SetValue(Idx++, "%s", tr( OnOffText[GameConfig.Locked] )); +} + +int IndGameLoadSM::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: Video Mode + if (ret == ++Idx) + { + if (++GameConfig.video >= VIDEO_MODE_MAX) GameConfig.video = 0; + } + + //! Settings: VIDTV Patch + else if (ret == ++Idx) + { + if (++GameConfig.vipatch >= MAX_ON_OFF) GameConfig.vipatch = 0; + } + + //! Settings: Game Language + else if (ret == ++Idx) + { + if (++GameConfig.language >= MAX_LANGUAGE) GameConfig.language = 0; + } + + //! Settings: Patch Country Strings + else if (ret == ++Idx) + { + if (++GameConfig.patchcountrystrings >= MAX_ON_OFF) GameConfig.patchcountrystrings = 0; + } + + //! Settings: Ocarina + else if (ret == ++Idx) + { + if (++GameConfig.ocarina >= MAX_ON_OFF) GameConfig.ocarina = 0; + } + + //! Settings: Game IOS + else if (ret == ++Idx) + { + char entered[4]; + snprintf(entered, sizeof(entered), "%i", GameConfig.ios); + if(OnScreenKeyboard(entered, sizeof(entered), 0)) + { + GameConfig.ios = atoi(entered); + if(GameConfig.ios < 200) GameConfig.ios = 200; + else if(GameConfig.ios > 255) GameConfig.ios = 255; + + if(NandTitles.IndexOf(TITLE_ID(1, GameConfig.ios)) < 0) + { + WindowPrompt(tr("Warning:"), tr("This IOS was not found on the titles list. If you are sure you have it installed than ignore this warning."), tr("OK")); + } + else if(GameConfig.ios == 254) + { + WindowPrompt(tr("Warning:"), tr("This IOS is the BootMii ios. If you are sure it is not BootMii and you have something else installed there than ignore this warning."), tr("OK")); + } + } + } + + //! Settings: Parental Control + else if (ret == ++Idx) + { + if (++GameConfig.parentalcontrol >= 5) GameConfig.parentalcontrol = 0; + } + + //! Settings: Error 002 fix + else if (ret == ++Idx) + { + if (++GameConfig.errorfix002 >= 3) GameConfig.errorfix002 = 0; + } + + //! Settings: Return To + else if (ret == ++Idx) + { + if (++GameConfig.returnTo >= MAX_ON_OFF) GameConfig.returnTo = 0; + } + + //! Settings: Alternate DOL + else if (ret == ++Idx) + { + if (--GameConfig.loadalternatedol < 0) // 0->2->1->0 + GameConfig.loadalternatedol = 2; + } + + //! Settings: Select DOL Offset + else if (ret == ++Idx && GameConfig.loadalternatedol == 2) + { + char filename[10]; + snprintf(filename, 7, "%s", GameConfig.id); + + //alt dol menu for games that require more than a single alt dol + int autodol = autoSelectDolMenu(filename, false); + + if (autodol > 0) + { + GameConfig.alternatedolstart = autodol; + snprintf(GameConfig.alternatedolname, sizeof(GameConfig.alternatedolname), "%s <%i>", tr( "AUTO" ), autodol); + SetOptionValues(); + return MENU_NONE; + } + else if (autodol == 0) + { + GameConfig.loadalternatedol = 0; + SetOptionValues(); + return MENU_NONE; + } + + //check to see if we already know the offset of the correct dol + autodol = autoSelectDol(filename, false); + //if we do know that offset ask if they want to use it + if (autodol > 0) + { + int dolchoice = WindowPrompt(0, tr( "Do you want to use the alternate DOL that is known to be correct?" ), + tr( "Yes" ), tr( "Pick from a list" ), tr( "Cancel" )); + if (dolchoice == 1) + { + GameConfig.alternatedolstart = autodol; + snprintf(GameConfig.alternatedolname, sizeof(GameConfig.alternatedolname), "%s <%i>", tr( "AUTO" ), autodol); + } + else if (dolchoice == 2) //they want to search for the correct dol themselves + { + int res = DiscBrowse(GameConfig.id, GameConfig.alternatedolname, sizeof(GameConfig.alternatedolname)); + if ((res >= 0) && (res != 696969)) //if res==696969 they pressed the back button + GameConfig.alternatedolstart = res; + } + } + else + { + int res = DiscBrowse(GameConfig.id, GameConfig.alternatedolname, sizeof(GameConfig.alternatedolname)); + if ((res >= 0) && (res != 696969)) + { + GameConfig.alternatedolstart = res; + char tmp[170]; + snprintf(tmp, sizeof(tmp), "%s %s - %i", tr( "It seems that you have some information that will be helpful to us. Please pass this information along to the DEV team." ), + filename, GameConfig.alternatedolstart); + WindowPrompt(0, tmp, tr( "OK" )); + } + } + + if(GameConfig.alternatedolstart == 0) + GameConfig.loadalternatedol = 0; + } + + //! Settings: Block IOS Reload + else if (ret == ++Idx) + { + if (++GameConfig.iosreloadblock >= MAX_ON_OFF) GameConfig.iosreloadblock = 0; + } + + //! Settings: Game Lock + else if (ret == ++Idx) + { + if (++GameConfig.Locked >= MAX_ON_OFF) GameConfig.Locked = 0; + } + + SetOptionValues(); + + return MENU_NONE; +} + diff --git a/source/settings/menus/IndGameLoadSM.hpp b/source/settings/menus/IndGameLoadSM.hpp new file mode 100644 index 00000000..9b7aaf6c --- /dev/null +++ b/source/settings/menus/IndGameLoadSM.hpp @@ -0,0 +1,51 @@ + /**************************************************************************** + * Copyright (C) 2010 + * 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 INDIVIDUAL_GAMELOAD_SM_HPP +#define INDIVIDUAL_GAMELOAD_SM_HPP + +#include "SettingsMenu.hpp" +#include "settings/CGameSettings.h" + +class IndGameLoadSM : public SettingsMenu +{ + public: + IndGameLoadSM(const char * GameID); + ~IndGameLoadSM(); + virtual int GetType() { return CIndGameLoadSM; }; + protected: + void SetDefaultConfig(); + void SetOptionNames(); + void SetOptionValues(); + int GetMenuInternal(); + + GameCFG GameConfig; + OptionList GuiOptions; + + GuiText * saveBtnTxt; + GuiImage * saveBtnImg; + GuiButton * saveBtn; +}; + + +#endif diff --git a/source/settings/menus/ParentalControlSM.cpp b/source/settings/menus/ParentalControlSM.cpp new file mode 100644 index 00000000..c665dc1b --- /dev/null +++ b/source/settings/menus/ParentalControlSM.cpp @@ -0,0 +1,181 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 "ParentalControlSM.hpp" +#include "settings/CSettings.h" +#include "prompts/PromptWindows.h" +#include "language/gettext.h" + +static const char * LockModeText[] = +{ + trNOOP( "Locked" ), + trNOOP( "Unlocked" ) +}; + +static const char * ParentalText[5] = +{ + trNOOP( "0 (Everyone)" ), + trNOOP( "1 (Child 7+)" ), + trNOOP( "2 (Teen 12+)" ), + trNOOP( "3 (Mature 16+)" ), + trNOOP( "4 (Adults Only 18+)" ) +}; + +static const char * LockedGamesText[2] = +{ + trNOOP( "0 (Locked and Unlocked Games)" ), + trNOOP( "1 (Unlocked Games Only)" ) +}; + +ParentalControlSM::ParentalControlSM() + : SettingsMenu(tr("Parental Control"), &GuiOptions, MENU_NONE) +{ + int Idx = 0; + Options->SetName(Idx++, "%s", tr( "Console" )); + Options->SetName(Idx++, "%s", tr( "Password" )); + Options->SetName(Idx++, "%s", tr( "Controllevel" )); + Options->SetName(Idx++, "%s", tr( "GamesLevel" )); + + SetOptionValues(); +} + +void ParentalControlSM::SetOptionValues() +{ + int Idx = 0; + + //! Settings: Console + Options->SetValue(Idx++, "%s", tr( LockModeText[Settings.godmode] )); + + //! Settings: Password + if (!Settings.godmode) + Options->SetValue(Idx++, "********"); + else if (strcmp(Settings.unlockCode, "") == 0) + Options->SetValue(Idx++, "%s", tr( "not set" )); + else + Options->SetValue(Idx++, Settings.unlockCode); + + //! Settings: Controllevel + if (Settings.godmode) + Options->SetValue(Idx++, "%s", tr( ParentalText[Settings.parentalcontrol] )); + else + Options->SetValue(Idx++, "********"); + + + //! Settings: GamesLevel + if (Settings.godmode) + Options->SetValue(Idx++, "%s", tr( LockedGamesText[Settings.lockedgames] )); + else + Options->SetValue(Idx++, "********"); +} + +int ParentalControlSM::GetMenuInternal() +{ + int ret = optionBrowser->GetClickedOption(); + + if (ret < 0) + return MENU_NONE; + + int Idx = -1; + + //! Settings: Console + if (ret == ++Idx) + { + if (strcmp(Settings.unlockCode, "") == 0 && !Settings.Parental.enabled) + { + Settings.godmode = !Settings.godmode; + } + else if (!Settings.godmode) + { + char entered[20]; + memset(entered, 0, 20); + + //password check to unlock Install,Delete and Format + SetState(STATE_DISABLED); + int result = Settings.Parental.enabled == 0 ? OnScreenKeyboard(entered, 20, 0) : OnScreenNumpad(entered, 5); + SetState(STATE_DEFAULT); + if (result == 1) + { + if (strcmp(entered, Settings.unlockCode) == 0 || !memcmp(entered, Settings.Parental.pin, 4)) //if password correct + { + WindowPrompt( + tr( "Correct Password" ), + tr( "All the features of USB Loader GX are unlocked." ), + tr( "OK" )); + Settings.godmode = 1; + } + else + WindowPrompt(tr( "Wrong Password" ), tr( "USB Loader GX is protected" ), tr( "OK" )); + } + } + else + { + int choice = WindowPrompt(tr( "Lock Console" ), tr( "Are you sure?" ), + tr( "Yes" ), tr( "No" )); + if (choice == 1) + { + WindowPrompt(tr( "Console Locked" ), tr( "USB Loader GX is protected" ), + tr( "OK" )); + Settings.godmode = 0; + } + } + } + + //! Settings: Password + else if (ret == ++Idx) + { + if (Settings.godmode) + { + char entered[20]; + SetState(STATE_DISABLED); + snprintf(entered, sizeof(entered), Settings.unlockCode); + int result = OnScreenKeyboard(entered, 20, 0); + SetState(STATE_DEFAULT); + if (result == 1) + { + snprintf(Settings.unlockCode, sizeof(Settings.unlockCode), entered); + WindowPrompt(tr( "Password Changed" ), tr( "Password has been changed" ), tr( "OK" )); + } + } + else + { + WindowPrompt(tr( "Password Changed" ), tr( "Console should be unlocked to modify it." ), tr( "OK" )); + } + } + + //! Settings: Controllevel + else if (ret == ++Idx) + { + if (Settings.godmode && ++Settings.parentalcontrol >= 5) Settings.parentalcontrol = 0; + } + + //! Settings: GamesLevel + else if (ret == ++Idx) + { + if (Settings.godmode && ++Settings.lockedgames >= 2) Settings.lockedgames = 0; + } + + SetOptionValues(); + + return MENU_NONE; +} diff --git a/source/settings/menus/ParentalControlSM.hpp b/source/settings/menus/ParentalControlSM.hpp new file mode 100644 index 00000000..7a6b2487 --- /dev/null +++ b/source/settings/menus/ParentalControlSM.hpp @@ -0,0 +1,42 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 PARENTALCONTROL_MENU_HPP_ +#define PARENTALCONTROL_MENU_HPP_ + +#include "SettingsMenu.hpp" + +class ParentalControlSM : public SettingsMenu +{ + public: + ParentalControlSM(); + virtual int GetType() { return CParentalControlSM; }; + protected: + void SetOptionValues(); + int GetMenuInternal(); + + OptionList GuiOptions; +}; + + +#endif diff --git a/source/settings/menus/SettingsMenu.cpp b/source/settings/menus/SettingsMenu.cpp new file mode 100644 index 00000000..f2fdf4b0 --- /dev/null +++ b/source/settings/menus/SettingsMenu.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 "SettingsMenu.hpp" +#include "themes/CTheme.h" +#include "language/gettext.h" + +SettingsMenu::SettingsMenu(const char * title, OptionList * opts, int returnTo) + : GuiWindow(screenwidth, screenheight) +{ + Options = opts; + returnToMenu = returnTo; + backBtn = NULL; + trigA = NULL; + trigB = NULL; + backBtnTxt = NULL; + backBtnImg = NULL; + backBtn = NULL; + btnOutline = NULL; + + //! Skipping back button if there is no menu defined to go back to + if(returnToMenu != MENU_NONE) + { + btnOutline = Resources::GetImageData("button_dialogue_box.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); + + backBtnTxt = new GuiText(tr("Back"), 22, (GXColor){0, 0, 0, 255}); + backBtnImg = new GuiImage(btnOutline); + backBtn = new GuiButton(backBtnImg, backBtnImg, 2, 3, -180, 400, trigA, btnSoundOver, btnSoundClick2, 1); + backBtn->SetLabel(backBtnTxt); + backBtn->SetTrigger(trigB); + Append(backBtn); + } + + optionBrowser = new GuiCustomOptionBrowser(396, 280, Options, "bg_options_settings.png", 0, 150); + optionBrowser->SetPosition(0, 90); + optionBrowser->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + + titleTxt = new GuiText(title, 28, (GXColor) {0, 0, 0, 255}); + titleTxt->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt->SetPosition(0, 40); + + Append(optionBrowser); + Append(titleTxt); + + SetEffect(EFFECT_FADE, 50); +} + +SettingsMenu::~SettingsMenu() +{ + ResumeGui(); + + SetEffect(EFFECT_FADE, -50); + while(this->GetEffect() > 0) + usleep(100); + + HaltGui(); + if(parentElement) + ((GuiWindow *) parentElement)->Remove(this); + + RemoveAll(); + + if(btnOutline) + delete btnOutline; + + if(backBtnTxt) + delete backBtnTxt; + if(backBtnImg) + delete backBtnImg; + if(backBtn) + delete backBtn; + + if(trigA) + delete trigA; + if(trigB) + delete trigB; + + delete titleTxt; + + delete optionBrowser; + + ResumeGui(); +} + +int SettingsMenu::GetClickedOption() +{ + if(!optionBrowser) + return -1; + + return optionBrowser->GetClickedOption(); +} + +int SettingsMenu::GetMenu() +{ + if(backBtn && backBtn->GetState() == STATE_CLICKED) + return returnToMenu; + + return GetMenuInternal(); +} diff --git a/source/settings/menus/SettingsMenu.hpp b/source/settings/menus/SettingsMenu.hpp new file mode 100644 index 00000000..4724d93c --- /dev/null +++ b/source/settings/menus/SettingsMenu.hpp @@ -0,0 +1,72 @@ + /**************************************************************************** + * Copyright (C) 2010 + * 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 SETTINGS_MENU_HPP_ +#define SETTINGS_MENU_HPP_ + +#include "libwiigui/gui.h" +#include "libwiigui/gui_customoptionbrowser.h" +#include "menu.h" + +enum +{ + CSettingsMenu = 0, + CGUISettingsMenu, + CGameLoadSM, + CParentalControlSM, + CSoundSettingsMenu, + CCustomPathsSM, + CIndGameLoadSM, + CUninstallSM, +}; + +class SettingsMenu : public GuiWindow +{ + public: + SettingsMenu(const char * title, OptionList * option, int returnTo); + ~SettingsMenu(); + int GetClickedOption(); + int GetMenu(); + virtual int GetType() { return CSettingsMenu; } + protected: + virtual int GetMenuInternal() { return MENU_NONE; }; + int returnToMenu; + + GuiImageData * btnOutline; + + GuiTrigger * trigA; + GuiTrigger * trigB; + + OptionList * Options; + + GuiText * titleTxt; + GuiText * backBtnTxt; + GuiImage * backBtnImg; + GuiButton * backBtn; + + GuiCustomOptionBrowser * optionBrowser; + +}; + + +#endif diff --git a/source/settings/menus/SoundSettingsMenu.cpp b/source/settings/menus/SoundSettingsMenu.cpp new file mode 100644 index 00000000..a6b0a76e --- /dev/null +++ b/source/settings/menus/SoundSettingsMenu.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 "SoundSettingsMenu.hpp" +#include "settings/CSettings.h" +#include "settings/SettingsPrompts.h" +#include "prompts/PromptWindows.h" +#include "language/gettext.h" + +static const char * GameSoundText[] = +{ + trNOOP( "Sound+Quiet" ), + trNOOP( "Sound+BGM" ), + trNOOP( "Loop Sound" ), +}; + +static const char * MusicLoopText[] = +{ + trNOOP( "Play Once" ), + trNOOP( "Loop Music" ), + trNOOP( "Random Directory Music" ), + trNOOP( "Loop Directory" ), +}; + +SoundSettingsMenu::SoundSettingsMenu() + : SettingsMenu(tr("Sound Settings"), &GuiOptions, MENU_NONE) +{ + int Idx = 0; + Options->SetName(Idx++, "%s", tr( "Backgroundmusic" )); + Options->SetName(Idx++, "%s", tr( "Music Volume" )); + Options->SetName(Idx++, "%s", tr( "SFX Volume" )); + Options->SetName(Idx++, "%s", tr( "Game Sound Mode" )); + Options->SetName(Idx++, "%s", tr( "Game Sound Volume" )); + Options->SetName(Idx++, "%s", tr( "Music Loop Mode" )); + Options->SetName(Idx++, "%s", tr( "Reset BG Music" )); + + SetOptionValues(); +} + +void SoundSettingsMenu::SetOptionValues() +{ + int Idx = 0; + + //! Settings: Backgroundmusic + const char * filename = strrchr(Settings.ogg_path, '/'); + if (filename) + Options->SetValue(Idx++, filename+1); + else + Options->SetValue(Idx++, tr( "Default" )); + + //! Settings: Music Volume + if (Settings.volume > 0) + Options->SetValue(Idx++, "%i", Settings.volume); + else + Options->SetValue(Idx++, tr( "OFF" )); + + //! Settings: SFX Volume + if (Settings.sfxvolume > 0) + Options->SetValue(Idx++, "%i", Settings.sfxvolume); + else + Options->SetValue(Idx++, tr( "OFF" )); + + //! Settings: Game Sound Mode + Options->SetValue(Idx++, "%s", tr( GameSoundText[Settings.gamesound] )); + + //! Settings: Game Sound Volume + if (Settings.gamesoundvolume > 0) + Options->SetValue(Idx++, "%i", Settings.gamesoundvolume); + else + Options->SetValue(Idx++, tr( "OFF" )); + + //! Settings: Music Loop Mode + Options->SetValue(Idx++, tr( MusicLoopText[Settings.musicloopmode] )); + + //! Settings: Reset BG Music + Options->SetValue(Idx++, " "); +} + +int SoundSettingsMenu::GetMenuInternal() +{ + int ret = optionBrowser->GetClickedOption(); + + if (ret < 0) + return MENU_NONE; + + int Idx = -1; + + //! Settings: Backgroundmusic + if (ret == ++Idx) + { + SetEffect(EFFECT_FADE, -20); + while (GetEffect() > 0) usleep(100); + HaltGui(); + GuiWindow * parent = (GuiWindow *) parentElement; + if(parent) parent->SetState(STATE_DISABLED); + ResumeGui(); + + MenuBackgroundMusic(); + + HaltGui(); + SetEffect(EFFECT_FADE, 20); + if(parent) parent->SetState(STATE_DEFAULT); + ResumeGui(); + while (GetEffect() > 0) usleep(100); + } + + //! Settings: Music Volume + else if (ret == ++Idx) + { + Settings.volume += 10; + if (Settings.volume > 100) Settings.volume = 0; + bgMusic->SetVolume(Settings.volume); + } + + //! Settings: SFX Volume + else if (ret == ++Idx) + { + Settings.sfxvolume += 10; + if (Settings.sfxvolume > 100) Settings.sfxvolume = 0; + btnSoundOver->SetVolume(Settings.sfxvolume); + btnSoundClick->SetVolume(Settings.sfxvolume); + btnSoundClick2->SetVolume(Settings.sfxvolume); + } + + //! Settings: Game Sound Mode + else if (ret == ++Idx) + { + if (++Settings.gamesound > 2) Settings.gamesound = 0; + } + + //! Settings: Game Sound Volume + else if (ret == ++Idx) + { + Settings.gamesoundvolume += 10; + if (Settings.gamesoundvolume > 100) Settings.gamesoundvolume = 0; + } + + //! Settings: Music Loop Mode + else if (ret == ++Idx) + { + if (++Settings.musicloopmode > 3) Settings.musicloopmode = 0; + bgMusic->SetLoop(Settings.musicloopmode); + } + + //! Settings: Reset BG Music + else if (ret == ++Idx) + { + int result = WindowPrompt(tr( "Reset to default BGM?" ), 0, tr( "Yes" ), tr( "No" )); + if (result) + { + bgMusic->LoadStandard(); + bgMusic->Play(); + } + } + + SetOptionValues(); + + return MENU_NONE; +} diff --git a/source/settings/menus/SoundSettingsMenu.hpp b/source/settings/menus/SoundSettingsMenu.hpp new file mode 100644 index 00000000..5f6bbb05 --- /dev/null +++ b/source/settings/menus/SoundSettingsMenu.hpp @@ -0,0 +1,42 @@ +/**************************************************************************** + * Copyright (C) 2010 + * 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 SOUNDSETTINGS_MENU_HPP_ +#define SOUNDSETTINGS_MENU_HPP_ + +#include "SettingsMenu.hpp" + +class SoundSettingsMenu : public SettingsMenu +{ + public: + SoundSettingsMenu(); + virtual int GetType() { return CSoundSettingsMenu; }; + protected: + void SetOptionValues(); + int GetMenuInternal(); + + OptionList GuiOptions; +}; + + +#endif diff --git a/source/settings/menus/UninstallSM.cpp b/source/settings/menus/UninstallSM.cpp new file mode 100644 index 00000000..d3531edb --- /dev/null +++ b/source/settings/menus/UninstallSM.cpp @@ -0,0 +1,175 @@ + /**************************************************************************** + * Copyright (C) 2010 + * 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 "UninstallSM.hpp" +#include "FileOperations/fileops.h" +#include "settings/CSettings.h" +#include "settings/CGameSettings.h" +#include "settings/CGameStatistics.h" +#include "settings/GameTitles.h" +#include "prompts/PromptWindows.h" +#include "language/gettext.h" +#include "usbloader/wbfs.h" + +extern int mountMethod; + +UninstallSM::UninstallSM(struct discHdr * header) + : SettingsMenu(tr("Uninstall Menu"), &GuiOptions, MENU_NONE) +{ + DiscHeader = header; + + int Idx = 0; + + Options->SetName(Idx++, "%s", tr( "Uninstall Game" )); + Options->SetName(Idx++, "%s", tr( "Reset Playcounter" )); + Options->SetName(Idx++, "%s", tr( "Delete Cover Artwork" )); + Options->SetName(Idx++, "%s", tr( "Delete Disc Artwork" )); + Options->SetName(Idx++, "%s", tr( "Delete Cheat TXT" )); + Options->SetName(Idx++, "%s", tr( "Delete Cheat GCT" )); + + SetOptionValues(); +} + +void UninstallSM::SetOptionValues() +{ + int Idx = 0; + + //! Settings: Uninstall Game + Options->SetValue(Idx++, " "); + + //! Settings: Reset Playcounter + Options->SetValue(Idx++, " "); + + //! Settings: Delete Cover Artwork + Options->SetValue(Idx++, " "); + + //! Settings: Delete Disc Artwork + Options->SetValue(Idx++, " "); + + //! Settings: Delete Cheat TXT + Options->SetValue(Idx++, " "); + + //! Settings: Delete Cheat GCT + Options->SetValue(Idx++, " "); + +} + +int UninstallSM::GetMenuInternal() +{ + int ret = optionBrowser->GetClickedOption(); + + if (ret < 0) + return MENU_NONE; + + int Idx = -1; + + //! Settings: Uninstall Game + if (ret == ++Idx) + { + int choice = WindowPrompt(tr( "Do you really want to delete:" ), GameTitles.GetTitle(DiscHeader), tr( "Yes" ), tr( "Cancel" )); + if (choice == 1) + { + GameSettings.Remove(DiscHeader->id); + GameSettings.Save(); + GameStatistics.Remove(DiscHeader->id); + GameStatistics.Save(); + int ret = 0; + if(!mountMethod) + ret = WBFS_RemoveGame(DiscHeader->id); + if (ret < 0) + WindowPrompt(tr( "Can't delete:" ), GameTitles.GetTitle(DiscHeader), tr( "OK" )); + else + WindowPrompt(tr( "Successfully deleted:" ), GameTitles.GetTitle(DiscHeader), tr( "OK" )); + + return MENU_DISCLIST; + } + } + + //! Settings: Reset Playcounter + else if (ret == ++Idx) + { + int result = WindowPrompt(tr( "Are you sure?" ), 0, tr( "Yes" ), tr( "Cancel" )); + if (result == 1) + { + GameStatistics.SetPlayCount(DiscHeader->id, 0); + GameStatistics.Save(); + } + } + + //! Settings: Delete Cover Artwork + else if (ret == ++Idx) + { + char GameID[7]; + snprintf(GameID, sizeof(GameID), "%s", (char *) DiscHeader->id); + char filepath[200]; + snprintf(filepath, sizeof(filepath), "%s%s.png", Settings.covers_path, GameID); + + int choice = WindowPrompt(tr( "Delete" ), filepath, tr( "Yes" ), tr( "No" )); + if (choice == 1) + if (CheckFile(filepath)) remove(filepath); + } + + //! Settings: Delete Disc Artwork + else if (ret == ++Idx) + { + char GameID[7]; + snprintf(GameID, sizeof(GameID), "%s", (char *) DiscHeader->id); + char filepath[200]; + snprintf(filepath, sizeof(filepath), "%s%s.png", Settings.disc_path, GameID); + + int choice = WindowPrompt(tr( "Delete" ), filepath, tr( "Yes" ), tr( "No" )); + if (choice == 1) + if (CheckFile(filepath)) remove(filepath); + } + + //! Settings: Delete Cheat TXT + else if (ret == ++Idx) + { + char GameID[7]; + snprintf(GameID, sizeof(GameID), "%s", (char *) DiscHeader->id); + char filepath[200]; + snprintf(filepath, sizeof(filepath), "%s%s.txt", Settings.TxtCheatcodespath, GameID); + + int choice = WindowPrompt(tr( "Delete" ), filepath, tr( "Yes" ), tr( "No" )); + if (choice == 1) + if (CheckFile(filepath)) remove(filepath); + } + + //! Settings: Delete Cheat GCT + else if (ret == ++Idx) + { + char GameID[7]; + snprintf(GameID, sizeof(GameID), "%s", (char *) DiscHeader->id); + char filepath[200]; + snprintf(filepath, sizeof(filepath), "%s%s.gct", Settings.Cheatcodespath, GameID); + + int choice = WindowPrompt(tr( "Delete" ), filepath, tr( "Yes" ), tr( "No" )); + if (choice == 1) + if (CheckFile(filepath)) remove(filepath); + } + + SetOptionValues(); + + return MENU_NONE; +} diff --git a/source/settings/menus/UninstallSM.hpp b/source/settings/menus/UninstallSM.hpp new file mode 100644 index 00000000..e639f589 --- /dev/null +++ b/source/settings/menus/UninstallSM.hpp @@ -0,0 +1,44 @@ + /**************************************************************************** + * Copyright (C) 2010 + * 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 UNINSTALL_MENU_HPP_ +#define UNINSTALL_MENU_HPP_ + +#include "SettingsMenu.hpp" + +class UninstallSM : public SettingsMenu +{ + public: + UninstallSM(struct discHdr * header); + virtual int GetType() { return CUninstallSM; }; + protected: + void SetOptionValues(); + int GetMenuInternal(); + + struct discHdr * DiscHeader; + + OptionList GuiOptions; +}; + + +#endif diff --git a/source/sounds/button_click.pcm b/source/sounds/button_click.pcm deleted file mode 100644 index de3d55e3bfe93b57942bea05e10832a4faec5a5b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7204 zcmW+*1$Y%l7rk?5UxeTm!9p5>2Mg}n;2x~F6(|&Gk-s<;myl4bxD=P5!L7w95TL;= zMFW9^=-Zu{|D@l1FWG%NJ9B5|-gC~qkB{>yZqA|fmd;Wly_N%IXIFDqC|BcBf`ydH z@&<=;Zgb8YljUUqH{se!tB?AjzQiTi%W7za(K*^BW{Sy~$(fJb)!e0g2Kw~$9O~IL z-JPD4RW2(;E|Qbw5xJhm(I8b^<>k3N#soo104r|;^^^GaN zHP-j8;jY%Eu-0~ryS!FFh?#$J^Ew2o(nO*}u&v|Z@zsRmuODn`WU@et*{nwYtk;&6j3a9w-eZJ*g-Mbh$^eOv1yRh;@X^<>(~w8Ls3eC3V0oN+JX zuguMv%bZQlJk9EH*7>ZBDaTUwef|4utbNJeLDy)fYN$$Qea?DC4JgpFfJc(m$Ev9# z;3biI60N40`mv68=gG>-D4cF*rDi3nGitx7Zc6B)DpPx^cP7a^(YN#k)6Rrb7zL?? zYMOfsTBy6CyR_Tez1%(4Ddb3JpR-PlP%TV(Q;0fJGpz80xo!@rQ|h2fR!?+3ZQ1kf zAKczqS@s$Gr21D~Q(aV3r?yj3SJJ)|g~)74^-VKV+0->vO)6%7X71~2`l9Zy`%L#m**N7M5I{bg0%#M&zz;{c%DIYI z!B#H$y=))#uS#Fmrc|MO79Ytf&PqY>j#E;^XxWO** zk{j|je4Fp`F(6Slj^J`!o-1%pe9Iz6>=Kj3FLv>F#1rwaxGK(aSq|WNya2J*mxu5m%v%O_>=4()IXo#O?ufJEl8E6h+=fSC{gYw! zCBDEDfGi|3@bn7Swp~0Gcg5f06z0#*T`}5S{!`AtPDjaT*;Lk(!(=DR=_ z<*}!2WB|rp$x%Fur}Imk%u(Jii{n{8IaHRCC1G8-Y$ZF%+%h+3aI*B5-^j|cf-Hus zftahA4C74cf_?rNw+Z@olflvl_;Z2dae{Gtns4$I-oSG>i2Zml_s2f|$TN5%r|=V= z!sEFVJRpdRVCV8@pB;9&J&)tjIF-?O`Xf)`5m@^|yw{&SM3UGk)``#J9!~!qZf&z1!^i)(O2@N-F= z{rCJ64~5m|xdE3Kzr%BOqxS-MZ!<7uBd}Fz@eO5A7G0q0G>0bAaoP)SohVj{6+)81 z^E_g|*u|be&x=!^k0#Jn~ysVnuQOel{X zv?iO+MIBL{&QmPC!gUTx1qR*}mMB7NX%Ts#d4cbbnJvJRRPqM1$CyuVxrA_YE6C26?0jy(+l)%{fJ!TNvCKt)?c0)QW$j9S7@9` zqiWcbRQjD(0re_j z&UvDU2%tt()0{F#q2`{zi|0~tDs66J@AFYnJZldQngTDG4j(NgE{GF|txQoh@D$;q1@It^zvWhl^`403AaIjEtj%V%8(f-$fXerA24#e|kfOb4hiHsP|9YHH zIKcROK>cp4`6&=8mMi0ZAI`@uq2fJ+5${A2F#a>p!9x7T1HDQC(Jt~M{)<=eVlKdW zxDuYe-~=FLDgGHO84k>u%QL~5+wgxKMrwldYr{MF4tV1t5Uo3qt_<)Y6%4olBVFRw z+!oB!54z|)r}Jm9UYwMi0jAJ=hHuN3Xh3Yfg}yiWI&d}|cJd8(y7q(jEtsuEdE|G<#H&8sE+$LAcJTeEyJ}z^D zXFAKK?1mMyzzIqG6=Q~CZ~o>T{0Fpc6=WaNWiII{>tMbpu+~Dk6#97&SLK?#g*Wp* z*q!|xgBb_$0gi?qm?am;@jMNDIgAIxDu3iKQ^0E;=rM@@l+RIwtqT~3)qtAYR%dfD)g~+>dA&$bZh827nBaHzMMDj{mT2|og zyg?|Tz*IlU>arm|pLBT?9Yx1y())Pa0&1o9GPCaIWwtNAwVVk*oeigTN9C={MMv z2b!=MRAh4?eI_t|De{5&z`w=7`4eEjIIw?y*jN~76^s?QF-I^gE<-bEEOO+Luqg?1 z-2q2OV@(biJb^k>S98_8|8Jgnuy80aH`yeB+vZ?B^%3z^5Gjekg9+qqKHF35Y0f+6 zHfEhf^Jo$B@ITd9H3a#|VpvxPIItsIX#$yNnp>zKOhh)EX#N48kH;P!0RGMcewL$0 z^wL}~hrn~$)rccJn+JhI+vu`7*o-y9fN3Alo`HL+gE61e5ju=Bo)2ukC`-s(P@sNR zTdNM#b~s|857c=OGW0h??ed$+w!pVpT!c3a(87r^ydZFIWssq290 z#?Wr)f|F*IKCbsb4U{yS%vSvM*9~4AeyP*+2jgdaOjBe?^UW-?)v4L37V5LwsrIWesw*Rb(Opl~WAqpO(G)Xz^6_3V!%Zy{ptH1NDw%@leMk?}Ba!2Wqh7EIBSvU{?Wd>U+j+2c ztnQ(2>a!-7A>*q*BabW(dwq@6*G+3v4%SAQQWz%@YxXh11mgND?%wOyCfqbMLAZD8 zWRpuLpf+^XG{<*cOnr=>?GL}`&H8t}NdFHrPtd>W$=LT#u=$Lhq^IcvdM)%-3Cvgm zYmC>Ipelywq0kjWakWlw#oOEQegj=w=T=ELyNY@@Mt!C)>lt{zxh{wQJ@qKGa2f_aNt$qJmnFle=r=0~Wif+jcmX;aozG>wqI5>D%t z{u_F%rRf2^P{)+kH*}2t4X3bB&&C>}uwy+itDiolcVI<*aSGLRFWp%;gN?OxY28xS z)g5rXO0P2qOf0lOMeuGVa71}aTWPN8t|_izt_aUboQ%>MkH<#W8`o2r zB42>lOM_h=Kz)ZmYsEuT?LuZ63Y<8E2tRC=nyDBo(0hsZd>yMd+hgqB@Vmax4n5l) zewTBVliV4W**eBy++%igX`dzbdkDrS{(4RsC3+MBg4o?%;uT(vN~n8v4`1x zv-4N$fprgcpPb0P3t9nI0Vl)x;KVq)!OIn_Io4?C!|V2K`>FlGeho$m^s4LC!x~|A z)B(D%-m7;*EsvsEv{?QnFMEvi=!be)7y;8>l89k8~rGqPqAul*fd7#vnnEz$_9JE|%D6HZ1 z9SwnZ?9zwzE*eZ7fj8%X+?#>f$N$s2Z|Sz^C7MzV3N!gp84J}_5nC~;k81C{bv`@i zoml1NBsvqF`6@u=*Y)6KFHl+82EX&y71d*PPerOVDn%L8X)@JQ^+`9?UDPf05@U?k z=k)L-oM<+qtK5IjMRJym+Y|uOd`e)MPS%f^op>7@+xSx(EEN zOhpi>1I<_Sg(|_uXk;d}O=bNQv2_!pC+ny1qbzR>!Ua+<_K4nY@Vlx(bP#5ruV_tx5GBLtjoa$k-3p&0I zmBc#sVvWH-pMuCJ%9|ScwY~>ix9Pr=R*eIWjn-Z|r&_0?m9HwKj;O;rr}k0r z)W5m{YGlXsLF{0hK8hLd11GKm$9#cc{>Bd{vH&L>2Mls!?1#wB+9H>XMV#(3>&$xO z2KAAPtVM272V6*v2%%TK6If{PYO@z3!@8 zD>r=QhB~e4sWPgR3Q_)woRUs)yS?4P?rPVx|FPrkllC$9Kkg^)x$Y%)Z+npa$^PPm zJN2D!oi>iY6Y6wyx;wL-Y52@{HaOAF3}>v99~HY0Je}-}cWyhEog>Zx$J3Gac6*iM za&kI@o!(B6ZQB>@SoF=|@D_YyA!M!VgF?RYR|RT+rQbf z?0LA)Zaskq{p>z=H(1)hua115_TwBcRcTnUia;bcDkK`_cz!x>@s#;dmpU&WPiX)8ab<+mCjk`BvumTBq-@5J71k(r>4Epj&YhfHDRkS zYK+TZ;d1+^eH!+ChrPLEced->o9qL2G%Vc)Tc_F+ZFZ;G)$L|jRUcTMZGWWQ5;itM z-xhWqJH)QxzURJ!T@HlZjbOKz`>y+uUCFL&PqN3_Kf>0v_U|}@8%||d(bQ?|{Ok;J z_B(5xJ7`axc@>2PobIxV-k*cn$rfTD= z5tAVTU(LYp$qqR}>)pEq!ZTuP#t)>G9 z`>VdHH)^$$R3}vj_ty2Uv-3{{?uS^r1{X;;^6~OrrpkcI; z&?yg~NnYqAQw0pr9JS$z_+2K%`2Y9*bNv>-`$PkmOVI@C2VIx}9P9@Si$e6(16s8P zUk4)Jk3;3hLd9Y-a{RIA+ZT0?#)!o=lniFP4t)1TCY}!!BSTrhrP899C?v{>qNq^3 z0$<)nem)UdN*ws&2h=heLiZE$iZFbiow1LE_K!y;?iuP?JwLZ&qbd0+-=eqk~{S}9o+d0GQh3D1hL;umCPxly+r zi)_3ZDvZsM8@)#^l!-ibtt>3_puRWrldK}+qsa0 zO1j*W0@+^n5;aFP4Jj9rlrcLaO0`raBuEaK-R3Xvtk;{`QEgpbhj=g42|7T-rK@}; zQ)I9=&|Awhc`dD_QQVuK(KWi}_PWz{kF5ZmqNx#m<>tFRZl!bXw)?>@wTU!@hEW&# zop#b?LFa;!^aMRC?~uF`GJ9rzp*~e*Lb8L8>pi+^ zSZLUMJzFpL=6D4qQjX?2l&g;)<0q??s&i<|(4HpBP`Cw+1KiIIa;~D7c6={=VR^f;Y;1dFmbW#^@n>UHIJa*+FxHrY4k02+Ylxdq2E< zM9r$lt56~(uE|)Qaqh{UCz&PxEO{m0>Ujb~5ZUf0RG! z)!x3j9CjRE`h@uPr7Y@L*2nMO=hSpx23y@Y}2&w%!4?UA0!NRAIDgs;D{-8)V7fQppSVBD_4_KkAywQqNV4>aKReexIsI zYM%O7mGbg?F<8GG{Wn#4)mgPwNs=VnWu07*Gtyi|sphI7_K8vvs-RNXy_%}6nyTum zJ$7`GDZL=Ix~i(~$|X4}Kg%sSCp}>M7>SliGC}^3L+Evv0+L^7`nBl0~t1lyhBll(V!=_?)NEsQOe zc`{q3$!1>6-}0B-fva-^uFpA?O{-`PkZu!g;%%IZeTwIy9AL{C_}|AraS~7GT2e;- z;+I%ep4)P5{)!iH4KBye>2G>M=O{0yQxlA?g0#|{pN~P}LE1w-s5=#;Li7in0s{R^ zg*coqp`S*#fOw9+p{11Na@-}C;-0&gE)#744WPnU)fa6)5UmE)p_bGEzqe^P-d&;; z+5nvWhQ6he6iPK+W%rvq;(EHiE{T$;Je8#;uBl6R+7+dIh&2nzH{6Fbl1fo=YVY21 z(XON0?KZkJm*LvDR&JUb>u$Nb(BzRTNcm|n#ZtBU%6o)LZiLOFcJJ_!U zHKRD_TbT=TCE$M^tego+F^Ko!cDSwL%DI;|&F-he^Z{abBdy2&L&@P*79i#-xk8YB z)tz;}y2?}(h|=}nv-ROF9LwFgE97i(-?@0KdhG7G5Q>BbLue!HJ)B2DN){hQuB^lL z_#8Cv=NShDqlpTHbMpp<9E3=_uzJjU*`YvG(>nD zJaZi{L4NFm+_;n1^ZPsuUXY(Pe5SX=N(*@%UaI5;KjaVzmS*t1A{f8V_qc&nluOuQ zudJ8zvIia&0;7~Na!qbZ9o1OnQlY@X-=qyNVVn$=z4C*URTWhj z;`gZRka=pp8VtPc3=Z%G`kv>MsFyNLy{F0nKd#C#iBa#U?`5Yvl)ofYk?MmeTp$av?;RPAD6gstsc9JP1npl}rPXjC$NMryN&>lV$|IcXr0h_O z)LA(t^?>T>>VY}~y!afl3Q8Uc2VdC^{Wb&tw#i0ddSlfF_ALi|=>UBH6-ayyr#+6d zmzTo8)FHtBT{2qYr2zD92YZYFt_(sRXb<$SEJc9~!)3Az0LG4#2f*Fj*kdpD_zHF{ zCFOuqedMt02I|em?EF#(cu-6tB#Gzn8GgjG;RzoDCoXded@GHEaBmZV%k`u_@TZ=1 zL`&e|+y>}g6Dz+)Zh0MhKju6CV!Q^jCCXIkgj-w3TR9H8o`$rA$P*`kcqe!R&}kEI z!EYk6(hK-T8t1@^4R+4~4sL@FZp5s0d;^TD3O7V{$;Ur}8_vOs-FW{wC-M)#%NE>> zTF1O#> z)q$0P%l4sNViWCod)zLt33iNo&yB=cx7yV<0o*f}3v$cseESBKp*psly2xnQs~XKT0$(Dj;K3AX*f{%$L};_ih_wXflA1y|G+b7Af>ID0g3{INZ2 zr`l0=9k}{OHV!A>ZEwK>6YLoK7VtOJW!hu*Cp*OUv?pMPCE(KiZF_6YQ*#Eq{Tr+s zYTMai(E2^w%U;8NwOoC79;=ewQfHAV_PY(JM0&U>?nC#)-nDI9dlv$m<;8hBxn^L4 zP22)I+J0|0VV~~*%9#b7Vj%M^TMPDi2d6A$x7wXHp88Xiw3KHmLv0TFCddZm1oaPV z8&)@@P)KBO-r!_p`D^l6I&mv@be~?rrxR%$rP6JB6mm0UnC_dj9wY#v&v>g1)2xyaa}$L4tNlp>Hqos4P8?Y3ThuT8uvNL zpX8_V6HW=a7?NU+n&#A)8d3v_42}#o{EFw=MYggl9E`3F1sr(l)@;|MY>1!k-ctD*#Y1T zquoHThAplv&KHdM{hofHAHWjoQf+z-II;qfIg_R!+m?Vu8c>#f3a?pf+u|1Yo2{mW zZE25~gXRqERvR%h&GmQJu=jWHt`x-W4%$qo5w|DZR<{^FJRVjl0PkOCx7lW{3q0qk z%@3V^cNgGaFWdr`RO5kDOpFozM?RQ2E zTTVC7@p_b=qd(WnP#2XzYp`(KT`AhtX@WT_G`WL-hFVw4ah$(1F>)JZiPxUM5iaOpO?VrJ%^uWtNG~_LV zPV4>8{U(rK-mmP>@e?5_(QoY6#y%Y|FA%8cM?&)J{`J7EKyN?xUmbV(TOqHMj@4aJ zwXW2EprzxpIrW~?_ofKjc{ zPMX7}scm4zrkV1<@^G6QIMW13IuTJf6KFIDUmWVfduG^s<`kmjs@V=~o@uAsQ}Eo8 zxbG}`88fOQlA_>4n}GF&+$i^f+l{AUz}eQo{x~-nJ~ayWpumf=yPD8mP=#N-flILs98YLzNo_J{b=_(*(Kr66(<{s6(%V^<| A+W-In literal 0 HcmV?d00001 diff --git a/source/sounds/button_click2.pcm b/source/sounds/button_click2.pcm deleted file mode 100644 index 113b4321fcd55606cbf33670fd7fda2dd2a0ca4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152064 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 V;4lJ*5jc#%VFV5%@c#jU{{e^U>fZnW diff --git a/source/sounds/button_click2.wav b/source/sounds/button_click2.wav new file mode 100644 index 0000000000000000000000000000000000000000..011c0ada4fc995fa35ade777dc76c01ed3f229af GIT binary patch literal 101424 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%0D;{cR?=WhS*-YtFHwMzOQ#ILY%E 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 XP$l`5^K1sv@N80d0#T?lJNy3u_$(XY literal 0 HcmV?d00001 diff --git a/source/sounds/button_over.pcm b/source/sounds/button_over.pcm deleted file mode 100644 index 64f519cfd74945140b23a9908a514bbd675a0681..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32256 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 diff --git a/source/sounds/button_over.wav b/source/sounds/button_over.wav new file mode 100644 index 0000000000000000000000000000000000000000..8a06f4b06f0f59f7ef087c24aa97fab9a2315755 GIT binary patch literal 21552 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, ALIGN_TOP); - MainButton[i]->SetSoundOver(&btnSoundOver); - MainButton[i]->SetSoundClick(&btnClick1); + MainButton[i]->SetSoundOver(btnSoundOver); + MainButton[i]->SetSoundClick(btnSoundClick); MainButton[i]->SetImage(theme_box_img[i]); MainButton[i]->SetEffectGrow(); MainButton[i]->SetTrigger(&trigA); @@ -360,7 +348,7 @@ int Theme_Downloader() backBtnTxt.SetWidescreen(Settings.widescreen); backBtnImg.SetWidescreen(Settings.widescreen); } - GuiButton backBtn(&backBtnImg, &backBtnImg, 2, 3, -180, 400, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton backBtn(&backBtnImg, &backBtnImg, 2, 3, -180, 400, &trigA, btnSoundOver, btnSoundClick2, 1); backBtn.SetLabel(&backBtnTxt); backBtn.SetTrigger(&trigB); @@ -372,8 +360,8 @@ int Theme_Downloader() GoLeftBtn.SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); GoLeftBtn.SetPosition(25, -25); GoLeftBtn.SetImage(&GoLeftImg); - GoLeftBtn.SetSoundOver(&btnSoundOver); - GoLeftBtn.SetSoundClick(btnClick2); + GoLeftBtn.SetSoundOver(btnSoundOver); + GoLeftBtn.SetSoundClick(btnSoundClick2); GoLeftBtn.SetEffectGrow(); GoLeftBtn.SetTrigger(&trigA); GoLeftBtn.SetTrigger(&trigL); @@ -384,8 +372,8 @@ int Theme_Downloader() GoRightBtn.SetAlignment(ALIGN_RIGHT, ALIGN_MIDDLE); GoRightBtn.SetPosition(-25, -25); GoRightBtn.SetImage(&GoRightImg); - GoRightBtn.SetSoundOver(&btnSoundOver); - GoRightBtn.SetSoundClick(btnClick2); + GoRightBtn.SetSoundOver(btnSoundOver); + GoRightBtn.SetSoundClick(btnSoundClick2); GoRightBtn.SetEffectGrow(); GoRightBtn.SetTrigger(&trigA); GoRightBtn.SetTrigger(&trigR); @@ -399,8 +387,8 @@ int Theme_Downloader() PageIndicatorBtn.SetPosition(110, 400); PageIndicatorBtn.SetImage(&PageindicatorImg); PageIndicatorBtn.SetLabel(&PageindicatorTxt); - PageIndicatorBtn.SetSoundOver(&btnSoundOver); - PageIndicatorBtn.SetSoundClick(&btnClick1); + PageIndicatorBtn.SetSoundOver(btnSoundOver); + PageIndicatorBtn.SetSoundClick(btnSoundClick); PageIndicatorBtn.SetTrigger(&trigA); PageIndicatorBtn.SetEffectGrow(); @@ -412,8 +400,8 @@ int Theme_Downloader() GuiButton wifiBtn(wifiImg.GetWidth(), wifiImg.GetHeight()); wifiBtn.SetImage(&wifiImg); wifiBtn.SetPosition(500, 400); - wifiBtn.SetSoundOver(&btnSoundOver); - wifiBtn.SetSoundClick(&btnClick1); + wifiBtn.SetSoundOver(btnSoundOver); + wifiBtn.SetSoundClick(btnSoundClick); wifiBtn.SetEffectGrow(); wifiBtn.SetTrigger(&trigA); diff --git a/source/utils/timer.c b/source/utils/timer.c new file mode 100644 index 00000000..c8b82021 --- /dev/null +++ b/source/utils/timer.c @@ -0,0 +1,119 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include +#include "timer.h" + +bool TimePassed(int limit) +{ + static time_t starttime = 0; + time_t timer = time(NULL); + + if (starttime == 0) + starttime = timer; + + if(difftime(timer, starttime) >= limit) + { + starttime = 0; + return true; + } + + return false; +} + +#define PERIOD_4 (4 * 365 + 1) +#define PERIOD_100 (PERIOD_4 * 25 - 1) +#define PERIOD_400 (PERIOD_100 * 4 + 1) +void ConvertNTFSDate(u64 ulNTFSDate, TimeStruct * ptm) +{ + unsigned year, mon, day, hour, min, sec; + u64 v64 = ulNTFSDate; + u8 ms[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + unsigned temp; + u32 v; + v64 /= 10000000; + sec = (unsigned)(v64 % 60); + v64 /= 60; + min = (unsigned)(v64 % 60); + v64 /= 60; + hour = (unsigned)(v64 % 24)+1; + v64 /= 24; + + v = (u32)v64; + + year = (unsigned)(1601 + v / PERIOD_400 * 400); + v %= PERIOD_400; + + temp = (unsigned)(v / PERIOD_100); + if (temp == 4) + temp = 3; + year += temp * 100; + v -= temp * PERIOD_100; + + temp = v / PERIOD_4; + if (temp == 25) + temp = 24; + year += temp * 4; + v -= temp * PERIOD_4; + + temp = v / 365; + if (temp == 4) + temp = 3; + year += temp; + v -= temp * 365; + + if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) + ms[1] = 29; + for (mon = 1; mon <= 12; mon++) + { + unsigned s = ms[mon - 1]; + if (v < s) + break; + v -= s; + } + day = (unsigned)v + 1; + + ptm->tm_mday = (u32)day; + ptm->tm_mon = (u32)mon; + ptm->tm_year = (u32)year; + + ptm->tm_hour = (u32)hour; + ptm->tm_min = (u32)min; + ptm->tm_sec = (u32)sec; +} + +void ConvertDosDate(u64 ulDosDate, TimeStruct * ptm) +{ + u32 uDate; + uDate = (u32)(ulDosDate>>16); + ptm->tm_mday = (u32)(uDate&0x1f) ; + ptm->tm_mon = (u32)((((uDate)&0x1E0)/0x20)) ; + ptm->tm_year = (u32)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (u32) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (u32) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (u32) (2*(ulDosDate&0x1f)) ; +} diff --git a/source/utils/timer.h b/source/utils/timer.h new file mode 100644 index 00000000..9180a331 --- /dev/null +++ b/source/utils/timer.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef __TIMER_H +#define __TIMER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct _TimeStruct +{ + u32 tm_sec; /* seconds after the minute - [0,59] */ + u32 tm_min; /* minutes after the hour - [0,59] */ + u32 tm_hour; /* hours since midnight - [0,23] */ + u32 tm_mday; /* day of the month - [1,31] */ + u32 tm_mon; /* months since January - [0,11] */ + u32 tm_year; /* years - [1980..2044] */ +} TimeStruct; + +bool TimePassed(int limit); +void ConvertDosDate(u64 ulDosDate, TimeStruct * ptm); +void ConvertNTFSDate(u64 ulNTFSDate, TimeStruct * ptm); + + +#ifdef __cplusplus +} + +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(); }; + protected: + u64 starttick; +}; + +#endif //__cplusplus + +#endif //__TIMER_H diff --git a/source/utils/uncompress.c b/source/utils/uncompress.c new file mode 100644 index 00000000..049b0386 --- /dev/null +++ b/source/utils/uncompress.c @@ -0,0 +1,185 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#include +#include + +#include "uncompress.h" + +struct _LZ77Info +{ + u16 length : 4; + u16 offset : 12; +} __attribute__((packed)); + +typedef struct _LZ77Info LZ77Info; + +u8 * uncompressLZ77(const u8 *inBuf, u32 inLength, u32 * size) +{ + u8 *buffer = NULL; + if (inLength <= 0x8 || *((const u32 *)inBuf) != 0x4C5A3737 /*"LZ77"*/ || inBuf[4] != 0x10) + return NULL; + + u32 uncSize = le32(((const u32 *)inBuf)[1] << 8); + + const u8 *inBufEnd = inBuf + inLength; + inBuf += 8; + + buffer = (u8 *) malloc(uncSize); + + if (!buffer) + return buffer; + + u8 *bufCur = buffer; + u8 *bufEnd = buffer + uncSize; + + while (bufCur < bufEnd && inBuf < inBufEnd) + { + u8 flags = *inBuf; + ++inBuf; + int i = 0; + for (i = 0; i < 8 && bufCur < bufEnd && inBuf < inBufEnd; ++i) + { + if ((flags & 0x80) != 0) + { + const LZ77Info * info = (const LZ77Info *)inBuf; + inBuf += sizeof (LZ77Info); + int length = info->length + 3; + if (bufCur - info->offset - 1 < buffer || bufCur + length > bufEnd) + return buffer; + memcpy(bufCur, bufCur - info->offset - 1, length); + bufCur += length; + } + else + { + *bufCur = *inBuf; + ++inBuf; + ++bufCur; + } + flags <<= 1; + } + } + + *size = uncSize; + + return buffer; +} + +//Thanks to _demo_ for this function +//src points to the yaz0 source data (to the "real" source data, not at the header!) +//dst points to a buffer uncompressedSize bytes large (you get uncompressedSize from +//the second 4 bytes in the Yaz0 header). +void uncompressYaz0(const u8* srcBuf, u8* dst, int uncompressedSize) +{ + const u8 * src = srcBuf; + + if(memcmp(src, "Yaz0", 4) == 0) + { + src += sizeof(Yaz0_Header); + } + + int srcPlace = 0, dstPlace = 0; //current read/write positions + + u32 validBitCount = 0; //number of valid bits left in "code" byte + u8 currCodeByte = 0; + + while(dstPlace < uncompressedSize) + { + //read new "code" byte if the current one is used up + if(validBitCount == 0) + { + currCodeByte = src[srcPlace]; + ++srcPlace; + validBitCount = 8; + } + + if((currCodeByte & 0x80) != 0) + { + //straight copy + dst[dstPlace] = src[srcPlace]; + dstPlace++; + srcPlace++; + } + else + { + //RLE part + u8 byte1 = src[srcPlace]; + u8 byte2 = src[srcPlace + 1]; + srcPlace += 2; + + u32 dist = ((byte1 & 0xF) << 8) | byte2; + u32 copySource = dstPlace - (dist + 1); + + u32 numBytes = byte1 >> 4; + if(numBytes == 0) + { + numBytes = src[srcPlace] + 0x12; + srcPlace++; + } + else + numBytes += 2; + + //copy run + u32 i = 0; + for(i = 0; i < numBytes; ++i) + { + dst[dstPlace] = dst[copySource]; + copySource++; + dstPlace++; + } + } + + //use next bit from "code" byte + currCodeByte <<= 1; + validBitCount-=1; + } +} + + +u32 CheckIMD5Type(const u8 * buffer, int length) +{ + if(*((u32 *) buffer) != 'IMD5') + { + return *((u32 *) buffer); + } + + const u8 * file = buffer+32; + + if(*((u32 *) file) != 'LZ77') + { + return *((u32 *) file); + } + + u32 uncSize = 0; + u8 * uncompressed_data = uncompressLZ77(file, length-32, &uncSize); + if(!uncompressed_data) + return 0; + + u32 * magic = (u32 *) uncompressed_data; + u32 Type = magic[0]; + free(uncompressed_data); + + return Type; +} diff --git a/source/utils/uncompress.h b/source/utils/uncompress.h new file mode 100644 index 00000000..2ebb3858 --- /dev/null +++ b/source/utils/uncompress.h @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2010 + * 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. + * + * for WiiXplorer 2010 + ***************************************************************************/ +#ifndef __UNCOMPRESS_H +#define __UNCOMPRESS_H + +#include + +#define le16(i) (((((u16) i) & 0xFF) << 8) | ((((u16) i) & 0xFF00) >> 8)) +#define le32(i) (((((u32) i) & 0xFF) << 24) | ((((u32) i) & 0xFF00) << 8) | \ + ((((u32) i) & 0xFF0000) >> 8) | ((((u32) i) & 0xFF000000) >> 24)) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + u32 magic; //Yaz0 + u32 decompressed_size; + u8 zeros[8]; +} Yaz0_Header; + +u8 * uncompressLZ77(const u8 *inBuf, u32 inLength, u32 * uncSize); +void uncompressYaz0(const u8* srcBuf, u8* dst, int uncompressedSize); +u32 CheckIMD5Type(const u8 * buffer, int length); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/wad/wad.cpp b/source/wad/wad.cpp index 559c5185..509fe7cb 100644 --- a/source/wad/wad.cpp +++ b/source/wad/wad.cpp @@ -120,11 +120,6 @@ s32 Wad_Install(FILE *fp) promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData dialogBox(Resources::GetFile("dialogue_box.png"), Resources::GetFileSize("dialogue_box.png")); GuiTrigger trigA; @@ -143,7 +138,7 @@ s32 Wad_Install(FILE *fp) btn1Txt.SetWidescreen(Settings.widescreen); btn1Img.SetWidescreen(Settings.widescreen); } - GuiButton btn1(&btn1Img, &btn1Img, 2, 4, 0, -35, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn1(&btn1Img, &btn1Img, 2, 4, 0, -35, &trigA, btnSoundOver, btnSoundClick2, 1); btn1.SetLabel(&btn1Txt); btn1.SetState(STATE_SELECTED); @@ -305,7 +300,7 @@ s32 Wad_Install(FILE *fp) // Get TMD info tmd_data = (tmd *) SIGNATURE_PAYLOAD(p_tmd); - + char imgPath[150]; // Install contents @@ -439,11 +434,6 @@ s32 Wad_Uninstall(FILE *fp) promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); promptWindow.SetPosition(0, -10); - GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, Settings.sfxvolume); - // because destroy GuiSound must wait while sound playing is finished, we use a global sound - if (!btnClick2) btnClick2 = new GuiSound(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - // GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, Settings.sfxvolume); - GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiImageData dialogBox(Resources::GetFile("dialogue_box.png"), Resources::GetFileSize("dialogue_box.png")); GuiTrigger trigA; @@ -462,7 +452,7 @@ s32 Wad_Uninstall(FILE *fp) btn1Txt.SetWidescreen(Settings.widescreen); btn1Img.SetWidescreen(Settings.widescreen); } - GuiButton btn1(&btn1Img, &btn1Img, 2, 4, 0, -55, &trigA, &btnSoundOver, btnClick2, 1); + GuiButton btn1(&btn1Img, &btn1Img, 2, 4, 0, -55, &trigA, btnSoundOver, btnSoundClick2, 1); btn1.SetLabel(&btn1Txt); btn1.SetState(STATE_SELECTED);