commit c152f5c3d4433c7fef40b8bebb4d183d81502a20 Author: dimok321 <15055714+dimok789@users.noreply.github.com> Date: Sun May 3 18:53:31 2009 +0000 *Added Standard IOS option to global settings. This decides with what IOS the Loader is gonna boot next. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..f73df6a3 --- /dev/null +++ b/Makefile @@ -0,0 +1,150 @@ +#--------------------------------------------------------------------------------- +# Clear the implicit built in rules +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- +ifeq ($(strip $(DEVKITPPC)),) +$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=devkitPPC") +endif + +include $(DEVKITPPC)/wii_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# INCLUDES is a list of directories containing extra header files +#--------------------------------------------------------------------------------- +TARGET := boot +BUILD := build +SOURCES := source source/libwiigui source/images source/fonts source/sounds source/libwbfs +INCLUDES := source + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- + +CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE) +CXXFLAGS = -save-temps -Xassembler -aln=$@.lst $(CFLAGS) +LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map,--section-start,.init=0x80a00100 + +#--------------------------------------------------------------------------------- +# any extra libraries we wish to link with the project +#--------------------------------------------------------------------------------- +LIBS := -lfat -lpngu -lpng -lmetaphrasis -lm -lz -lwiiuse -lbte -lasnd -logc -ltremor -lfreetype +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(DEVKITPPC)/lib $(CURDIR) + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGETDIR)/$(TARGET) +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) +export DEPSDIR := $(CURDIR)/$(BUILD) + +#--------------------------------------------------------------------------------- +# automatically build a list of object files for our project +#--------------------------------------------------------------------------------- +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) +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))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) + export LD := $(CC) +else + export LD := $(CXX) +endif + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \ + $(sFILES:.s=.o) $(SFILES:.S=.o) \ + $(TTFFILES:.ttf=.ttf.o) $(PNGFILES:.png=.png.o) \ + $(OGGFILES:.ogg=.ogg.o) $(PCMFILES:.pcm=.pcm.o) + +#--------------------------------------------------------------------------------- +# build a list of include paths +#--------------------------------------------------------------------------------- +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) \ + -I$(LIBOGC_INC) + +#--------------------------------------------------------------------------------- +# build a list of library paths +#--------------------------------------------------------------------------------- +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \ + -L$(LIBOGC_LIB) + +export OUTPUT := $(CURDIR)/$(TARGET) +.PHONY: $(BUILD) clean + +#--------------------------------------------------------------------------------- +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol + +#--------------------------------------------------------------------------------- +run: + wiiload $(OUTPUT).dol + +#--------------------------------------------------------------------------------- +reload: + wiiload -r $(OUTPUT).dol + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).dol: $(OUTPUT).elf +$(OUTPUT).elf: $(OFILES) + +#--------------------------------------------------------------------------------- +# This rule links in binary data with .ttf, .png, and .mp3 extensions +#--------------------------------------------------------------------------------- +%.ttf.o : %.ttf + @echo $(notdir $<) + $(bin2o) + +%.png.o : %.png + @echo $(notdir $<) + $(bin2o) + +%.ogg.o : %.ogg + @echo $(notdir $<) + $(bin2o) + +%.pcm.o : %.pcm + @echo $(notdir $<) + $(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- diff --git a/gui.pnproj b/gui.pnproj new file mode 100644 index 00000000..97e8c185 --- /dev/null +++ b/gui.pnproj @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gui.pnps b/gui.pnps new file mode 100644 index 00000000..4d76a3fe --- /dev/null +++ b/gui.pnps @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/source/FreeTypeGX.cpp b/source/FreeTypeGX.cpp new file mode 100644 index 00000000..0cbab38e --- /dev/null +++ b/source/FreeTypeGX.cpp @@ -0,0 +1,718 @@ +/* + * FreeTypeGX is a wrapper class for libFreeType which renders a compiled + * FreeType parsable font into a GX texture for Wii homebrew development. + * Copyright (C) 2008 Armin Tamzarian + * Modified by Tantric, 2009 + * + * This file is part of FreeTypeGX. + * + * FreeTypeGX is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FreeTypeGX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FreeTypeGX. If not, see . + */ + +#include "FreeTypeGX.h" + +/** + * Default constructor for the FreeTypeGX class. + * + * @param textureFormat Optional format (GX_TF_*) of the texture as defined by the libogc gx.h header file. If not specified default value is GX_TF_RGBA8. + * @param vertexIndex Optional vertex format index (GX_VTXFMT*) of the glyph textures as defined by the libogc gx.h header file. If not specified default value is GX_VTXFMT1. + */ +FreeTypeGX::FreeTypeGX(uint8_t textureFormat, uint8_t vertexIndex) { + FT_Init_FreeType(&this->ftLibrary); + + this->textureFormat = textureFormat; + this->setVertexFormat(vertexIndex); + this->setCompatibilityMode(FTGX_COMPATIBILITY_NONE); +} + +/** + * Default destructor for the FreeTypeGX class. + */ +FreeTypeGX::~FreeTypeGX() { + this->unloadFont(); +} + +/** + * Convert a short char sctring to a wide char string. + * + * This routine converts a supplied shot character string into a wide character string. + * Note that it is the user's responsibility to clear the returned buffer once it is no longer needed. + * + * @param strChar Character string to be converted. + * @return Wide character representation of supplied character string. + */ +wchar_t* FreeTypeGX::charToWideChar(char* strChar) { + wchar_t *strWChar; + strWChar = new wchar_t[strlen(strChar) + 1]; + + char *tempSrc = strChar; + wchar_t *tempDest = strWChar; + while((*tempDest++ = *tempSrc++)); + + return strWChar; +} + +/** + * + * \overload + */ +wchar_t* FreeTypeGX::charToWideChar(const char* strChar) { + return FreeTypeGX::charToWideChar((char*) strChar); +} + +/** + * Setup the vertex attribute formats for the glyph textures. + * + * This function sets up the vertex format for the glyph texture on the specified vertex format index. + * Note that this function should not need to be called except if the vertex formats are cleared or the specified + * vertex format index is modified. + * + * @param vertexIndex Vertex format index (GX_VTXFMT*) of the glyph textures as defined by the libogc gx.h header file. +*/ +void FreeTypeGX::setVertexFormat(uint8_t vertexIndex) { + this->vertexIndex = vertexIndex; + + GX_SetVtxAttrFmt(this->vertexIndex, GX_VA_POS, GX_POS_XY, GX_S16, 0); + GX_SetVtxAttrFmt(this->vertexIndex, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + GX_SetVtxAttrFmt(this->vertexIndex, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); +} + +/** + * Sets the TEV and VTX rendering compatibility requirements for the class. + * + * This sets up the default TEV opertion and VTX descriptions rendering values for the class. This ensures that FreeTypeGX + * can remain compatible with external liraries or project code. Certain external libraries or code by design or lack of + * foresight assume that the TEV opertion and VTX descriptions values will remain constant or are always returned to a + * certain value. This will enable compatibility with those libraries and any other code which cannot or will not be changed. + * + * @param compatibilityMode Compatibility descritor (FTGX_COMPATIBILITY_*) as defined in FreeTypeGX.h +*/ +void FreeTypeGX::setCompatibilityMode(uint32_t compatibilityMode) { + this->compatibilityMode = compatibilityMode; +} + +/** + * Sets the TEV operation and VTX descriptor values after texture rendering it complete. + * + * This function calls the GX_SetTevOp and GX_SetVtxDesc functions with the compatibility parameters specified + * in setCompatibilityMode. + */ +void FreeTypeGX::setDefaultMode() { + if(this->compatibilityMode) { + switch(this->compatibilityMode & 0x00FF) { + case FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_MODULATE: + GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE); + break; + case FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_DECAL: + GX_SetTevOp(GX_TEVSTAGE0, GX_DECAL); + break; + case FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_BLEND: + GX_SetTevOp(GX_TEVSTAGE0, GX_BLEND); + break; + case FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_REPLACE: + GX_SetTevOp(GX_TEVSTAGE0, GX_REPLACE); + break; + case FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_PASSCLR: + GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR); + break; + default: + break; + } + + switch(this->compatibilityMode & 0xFF00) { + case FTGX_COMPATIBILITY_DEFAULT_VTXDESC_GX_NONE: + GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); + break; + case FTGX_COMPATIBILITY_DEFAULT_VTXDESC_GX_DIRECT: + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + break; + case FTGX_COMPATIBILITY_DEFAULT_VTXDESC_GX_INDEX8: + GX_SetVtxDesc(GX_VA_TEX0, GX_INDEX8); + break; + case FTGX_COMPATIBILITY_DEFAULT_VTXDESC_GX_INDEX16: + GX_SetVtxDesc(GX_VA_TEX0, GX_INDEX16); + break; + default: + break; + } + } +} + +/** + * Loads and processes a specified true type font buffer to a specific point size. + * + * This routine takes a precompiled true type font buffer and loads the necessary processed data into memory. This routine should be called before drawText will succeed. + * + * @param fontBuffer A pointer in memory to a precompiled true type font buffer. + * @param bufferSize Size of the true type font buffer in bytes. + * @param pointSize The desired point size this wrapper's configured font face. + * @param cacheAll Optional flag to specify if all font characters should be cached when the class object is created. If specified as false the characters only become cached the first time they are used. If not specified default value is false. + */ +uint16_t FreeTypeGX::loadFont(uint8_t* fontBuffer, FT_Long bufferSize, FT_UInt pointSize, bool cacheAll) { + this->unloadFont(); + this->ftPointSize = pointSize; + + FT_New_Memory_Face(this->ftLibrary, (FT_Byte *)fontBuffer, bufferSize, 0, &this->ftFace); + + if(this->ftPointSize > 0) + FT_Set_Pixel_Sizes(this->ftFace, 0, this->ftPointSize); + + this->ftSlot = this->ftFace->glyph; + this->ftKerningEnabled = FT_HAS_KERNING(this->ftFace); + + if (cacheAll) { + return this->cacheGlyphDataComplete(); + } + + return 0; +} + +/** + * + * \overload + */ +uint16_t FreeTypeGX::loadFont(const uint8_t* fontBuffer, FT_Long bufferSize, FT_UInt pointSize, bool cacheAll) { + return this->loadFont((uint8_t *)fontBuffer, bufferSize, pointSize, cacheAll); +} + +/** + * Clears all loaded font glyph data. + * + * This routine clears all members of the font map structure and frees all allocated memory back to the system. + */ +void FreeTypeGX::unloadFont() { + if(this->fontData.size() == 0) + return; + + GX_DrawDone(); + GX_Flush(); + + for( std::map::iterator i = this->fontData.begin(); i != this->fontData.end(); i++) { + free(i->second.glyphDataTexture); + } + + this->fontData.clear(); +} + +void FreeTypeGX::changeSize(FT_UInt pointSize) { + this->unloadFont(); + this->ftPointSize = pointSize; + FT_Set_Pixel_Sizes(this->ftFace, 0, this->ftPointSize); +} + +/** + * Adjusts the texture data buffer to necessary width for a given texture format. + * + * This routine determines adjusts the given texture width into the required width to hold the necessary texture data for proper alignment. + * + * @param textureWidth The initial guess for the texture width. + * @param textureFormat The texture format to which the data is to be converted. + * @return The correctly adjusted texture width. + */ +uint16_t FreeTypeGX::adjustTextureWidth(uint16_t textureWidth, uint8_t textureFormat) { + uint16_t alignment; + + switch(textureFormat) { + case GX_TF_I4: /* 8x8 Tiles - 4-bit Intensity */ + case GX_TF_I8: /* 8x4 Tiles - 8-bit Intensity */ + case GX_TF_IA4: /* 8x4 Tiles - 4-bit Intensity, , 4-bit Alpha */ + alignment = 8; + break; + + case GX_TF_IA8: /* 4x4 Tiles - 8-bit Intensity, 8-bit Alpha */ + case GX_TF_RGB565: /* 4x4 Tiles - RGB565 Format */ + case GX_TF_RGB5A3: /* 4x4 Tiles - RGB5A3 Format */ + case GX_TF_RGBA8: /* 4x4 Tiles - RGBA8 Dual Cache Line Format */ + default: + alignment = 4; + break; + } + return textureWidth % alignment == 0 ? textureWidth : alignment + textureWidth - (textureWidth % alignment); + +} + +/** + * Adjusts the texture data buffer to necessary height for a given texture format. + * + * This routine determines adjusts the given texture height into the required height to hold the necessary texture data for proper alignment. + * + * @param textureHeight The initial guess for the texture height. + * @param textureFormat The texture format to which the data is to be converted. + * @return The correctly adjusted texture height. + */ +uint16_t FreeTypeGX::adjustTextureHeight(uint16_t textureHeight, uint8_t textureFormat) { + uint16_t alignment; + + switch(textureFormat) { + case GX_TF_I4: /* 8x8 Tiles - 4-bit Intensity */ + alignment = 8; + break; + + case GX_TF_I8: /* 8x4 Tiles - 8-bit Intensity */ + case GX_TF_IA4: /* 8x4 Tiles - 4-bit Intensity, , 4-bit Alpha */ + case GX_TF_IA8: /* 4x4 Tiles - 8-bit Intensity, 8-bit Alpha */ + case GX_TF_RGB565: /* 4x4 Tiles - RGB565 Format */ + case GX_TF_RGB5A3: /* 4x4 Tiles - RGB5A3 Format */ + case GX_TF_RGBA8: /* 4x4 Tiles - RGBA8 Dual Cache Line Format */ + default: + alignment = 4; + break; + } + return textureHeight % alignment == 0 ? textureHeight : alignment + textureHeight - (textureHeight % alignment); + +} + +/** + * Caches the given font glyph in the instance font texture buffer. + * + * This routine renders and stores the requested glyph's bitmap and relevant information into its own quickly addressible + * structure within an instance-specific map. + * + * @param charCode The requested glyph's character code. + * @return A pointer to the allocated font structure. + */ +ftgxCharData *FreeTypeGX::cacheGlyphData(wchar_t charCode) { + FT_UInt gIndex; + uint16_t textureWidth = 0, textureHeight = 0; + + gIndex = FT_Get_Char_Index( this->ftFace, charCode ); + if (!FT_Load_Glyph(this->ftFace, gIndex, FT_LOAD_DEFAULT )) { + FT_Render_Glyph( this->ftSlot, FT_RENDER_MODE_NORMAL ); + + if(this->ftSlot->format == FT_GLYPH_FORMAT_BITMAP) { + FT_Bitmap *glyphBitmap = &this->ftSlot->bitmap; + + textureWidth = adjustTextureWidth(glyphBitmap->width, this->textureFormat); + textureHeight = adjustTextureHeight(glyphBitmap->rows, this->textureFormat); + + this->fontData[charCode] = (ftgxCharData){ + this->ftSlot->advance.x >> 6, + gIndex, + textureWidth, + textureHeight, + this->ftSlot->bitmap_top, + this->ftSlot->bitmap_top, + textureHeight - this->ftSlot->bitmap_top, + NULL + }; + this->loadGlyphData(glyphBitmap, &this->fontData[charCode]); + + return &this->fontData[charCode]; + } + } + + return NULL; +} + +/** + * Locates each character in this wrapper's configured font face and proccess them. + * + * This routine locates each character in the configured font face and renders the glyph's bitmap. + * Each bitmap and relevant information is loaded into its own quickly addressible structure within an instance-specific map. + */ +uint16_t FreeTypeGX::cacheGlyphDataComplete() { + uint16_t i = 0; + FT_UInt gIndex; + FT_ULong charCode = FT_Get_First_Char( this->ftFace, &gIndex ); + while ( gIndex != 0 ) { + + if(this->cacheGlyphData(charCode) != NULL) { + i++; + } + + charCode = FT_Get_Next_Char( this->ftFace, charCode, &gIndex ); + } + + return i; +} + +/** + * Loads the rendered bitmap into the relevant structure's data buffer. + * + * This routine does a simple byte-wise copy of the glyph's rendered 8-bit grayscale bitmap into the structure's buffer. + * Each byte is converted from the bitmap's intensity value into the a uint32_t RGBA value. + * + * @param bmp A pointer to the most recently rendered glyph's bitmap. + * @param charData A pointer to an allocated ftgxCharData structure whose data represent that of the last rendered glyph. + */ +void FreeTypeGX::loadGlyphData(FT_Bitmap *bmp, ftgxCharData *charData) { + + uint32_t *glyphData = (uint32_t *)memalign(32, charData->textureWidth * charData->textureHeight * 4); + memset(glyphData, 0x00, charData->textureWidth * charData->textureHeight * 4); + + for (uint16_t imagePosY = 0; imagePosY < bmp->rows; imagePosY++) { + for (uint16_t imagePosX = 0; imagePosX < bmp->width; imagePosX++) { + uint32_t pixel = (uint32_t) bmp->buffer[imagePosY * bmp->width + imagePosX]; + glyphData[imagePosY * charData->textureWidth + imagePosX] = 0x00000000 | (pixel << 24) | (pixel << 16) | (pixel << 8) | pixel; + } + } + + switch(this->textureFormat) { + case GX_TF_I4: + charData->glyphDataTexture = Metaphrasis::convertBufferToI4(glyphData, charData->textureWidth, charData->textureHeight); + break; + case GX_TF_I8: + charData->glyphDataTexture = Metaphrasis::convertBufferToI8(glyphData, charData->textureWidth, charData->textureHeight); + break; + case GX_TF_IA4: + charData->glyphDataTexture = Metaphrasis::convertBufferToIA4(glyphData, charData->textureWidth, charData->textureHeight); + break; + case GX_TF_IA8: + charData->glyphDataTexture = Metaphrasis::convertBufferToIA8(glyphData, charData->textureWidth, charData->textureHeight); + break; + case GX_TF_RGB565: + charData->glyphDataTexture = Metaphrasis::convertBufferToRGB565(glyphData, charData->textureWidth, charData->textureHeight); + break; + case GX_TF_RGB5A3: + charData->glyphDataTexture = Metaphrasis::convertBufferToRGB5A3(glyphData, charData->textureWidth, charData->textureHeight); + break; + case GX_TF_RGBA8: + default: + charData->glyphDataTexture = Metaphrasis::convertBufferToRGBA8(glyphData, charData->textureWidth, charData->textureHeight); + break; + } + + free(glyphData); +} + +/** + * Determines the x offset of the rendered string. + * + * This routine calculates the x offset of the rendered string based off of a supplied positional format parameter. + * + * @param width Current pixel width of the string. + * @param format Positional format of the string. + */ +uint16_t FreeTypeGX::getStyleOffsetWidth(uint16_t width, uint16_t format) { + + if (format & FTGX_JUSTIFY_LEFT ) { + return 0; + } + else if (format & FTGX_JUSTIFY_CENTER ) { + return width >> 1; + } + else if (format & FTGX_JUSTIFY_RIGHT ) { + return width; + } + + return 0; +} + +/** + * Determines the y offset of the rendered string. + * + * This routine calculates the y offset of the rendered string based off of a supplied positional format parameter. + * + * @param offset Current pixel offset data of the string. + * @param format Positional format of the string. + */ +uint16_t FreeTypeGX::getStyleOffsetHeight(ftgxDataOffset offset, uint16_t format) { + if (format & FTGX_ALIGN_TOP ) { + return -offset.max; + } + else if (format & FTGX_ALIGN_MIDDLE ) { + return -offset.max; + } + else if (format & FTGX_ALIGN_BOTTOM ) { + return offset.min; + } + + return 0; +} + +/** + * Processes the supplied text string and prints the results at the specified coordinates. + * + * This routine processes each character of the supplied text string, loads the relevant preprocessed bitmap buffer, + * a texture from said buffer, and loads the resultant texture into the EFB. + * + * @param x Screen X coordinate at which to output the text. + * @param y Screen Y coordinate at which to output the text. Note that this value corresponds to the text string origin and not the top or bottom of the glyphs. + * @param text NULL terminated string to output. + * @param color Optional color to apply to the text characters. If not specified default value is ftgxWhite: (GXColor){0xff, 0xff, 0xff, 0xff} + * @param textStyle Flags which specify any styling which should be applied to the rendered string. + * @return The number of characters printed. + */ +uint16_t FreeTypeGX::drawText(int16_t x, int16_t y, wchar_t *text, GXColor color, uint16_t textStyle) { + uint16_t strLength = wcslen(text); + uint16_t x_pos = x, printed = 0; + uint16_t x_offset = 0, y_offset = 0; + GXTexObj glyphTexture; + FT_Vector pairDelta; + + if(textStyle & 0x000F) { + x_offset = this->getStyleOffsetWidth(this->getWidth(text), textStyle); + } + if(textStyle & 0x00F0) { + y_offset = this->getStyleOffsetHeight(this->getOffset(text), textStyle); + } + + for (uint16_t i = 0; i < strLength; i++) { + + ftgxCharData* glyphData = NULL; + if( this->fontData.find(text[i]) != this->fontData.end() ) { + glyphData = &this->fontData[text[i]]; + } + else { + glyphData = this->cacheGlyphData(text[i]); + } + + if(glyphData != NULL) { + + if(this->ftKerningEnabled && i) { + FT_Get_Kerning( this->ftFace, this->fontData[text[i - 1]].glyphIndex, glyphData->glyphIndex, FT_KERNING_DEFAULT, &pairDelta ); + x_pos += pairDelta.x >> 6; + } + + GX_InitTexObj(&glyphTexture, glyphData->glyphDataTexture, glyphData->textureWidth, glyphData->textureHeight, this->textureFormat, GX_CLAMP, GX_CLAMP, GX_FALSE); + this->copyTextureToFramebuffer(&glyphTexture, glyphData->textureWidth, glyphData->textureHeight, x_pos - x_offset, y - glyphData->renderOffsetY - y_offset, color); + + x_pos += glyphData->glyphAdvanceX; + printed++; + } + } + + if(textStyle & 0x0F00) { + this->drawTextFeature(x - x_offset, y, this->getWidth(text), this->getOffset(text), textStyle, color); + } + + return printed; +} + +/** + * \overload + */ +uint16_t FreeTypeGX::drawText(int16_t x, int16_t y, wchar_t const *text, GXColor color, uint16_t textStyle) { + return this->drawText(x, y, (wchar_t *)text, color, textStyle); +} + +void FreeTypeGX::drawTextFeature(int16_t x, int16_t y, uint16_t width, ftgxDataOffset offsetData, uint16_t format, GXColor color) { + uint16_t featureHeight = this->ftPointSize >> 4 > 0 ? this->ftPointSize >> 4 : 1; + + if (format & FTGX_STYLE_UNDERLINE ) { + switch(format & 0x00F0) { + case FTGX_ALIGN_TOP: + this->copyFeatureToFramebuffer(width, featureHeight, x, y + offsetData.max + 1, color); + break; + case FTGX_ALIGN_MIDDLE: + this->copyFeatureToFramebuffer(width, featureHeight, x, y + ((offsetData.max - offsetData.min) >> 1) + 1, color); + break; + case FTGX_ALIGN_BOTTOM: + this->copyFeatureToFramebuffer(width, featureHeight, x, y - offsetData.min, color); + break; + default: + this->copyFeatureToFramebuffer(width, featureHeight, x, y + 1, color); + break; + } + } + + if (format & FTGX_STYLE_STRIKE ) { + switch(format & 0x00F0) { + case FTGX_ALIGN_TOP: + this->copyFeatureToFramebuffer(width, featureHeight, x, y + ((offsetData.max + offsetData.min) >> 1), color); + break; + case FTGX_ALIGN_MIDDLE: + this->copyFeatureToFramebuffer(width, featureHeight, x, y, color); + break; + case FTGX_ALIGN_BOTTOM: + this->copyFeatureToFramebuffer(width, featureHeight, x, y - ((offsetData.max + offsetData.min) >> 1), color); + break; + default: + this->copyFeatureToFramebuffer(width, featureHeight, x, y - ((offsetData.max - offsetData.min) >> 1), color); + break; + } + } +} + +/** + * Processes the supplied string and return the width of the string in pixels. + * + * This routine processes each character of the supplied text string and calculates the width of the entire string. + * Note that if precaching of the entire font set is not enabled any uncached glyph will be cached after the call to this function. + * + * @param text NULL terminated string to calculate. + * @return The width of the text string in pixels. + */ +uint16_t FreeTypeGX::getWidth(wchar_t *text) { + uint16_t strLength = wcslen(text); + uint16_t strWidth = 0; + FT_Vector pairDelta; + + for (uint16_t i = 0; i < strLength; i++) { + + ftgxCharData* glyphData = NULL; + if( this->fontData.find(text[i]) != this->fontData.end() ) { + glyphData = &this->fontData[text[i]]; + } + else { + glyphData = this->cacheGlyphData(text[i]); + } + + if(glyphData != NULL) { + if(this->ftKerningEnabled && (i > 0)) { + FT_Get_Kerning( this->ftFace, this->fontData[text[i - 1]].glyphIndex, glyphData->glyphIndex, FT_KERNING_DEFAULT, &pairDelta ); + strWidth += pairDelta.x >> 6; + } + + strWidth += glyphData->glyphAdvanceX; + } + } + + return strWidth; +} + +/** + * + * \overload + */ +uint16_t FreeTypeGX::getWidth(wchar_t const *text) { + return this->getWidth((wchar_t *)text); +} + +/** + * Processes the supplied string and return the height of the string in pixels. + * + * This routine processes each character of the supplied text string and calculates the height of the entire string. + * Note that if precaching of the entire font set is not enabled any uncached glyph will be cached after the call to this function. + * + * @param text NULL terminated string to calculate. + * @return The height of the text string in pixels. + */ +uint16_t FreeTypeGX::getHeight(wchar_t *text) { + ftgxDataOffset offset = this->getOffset(text); + + return offset.max + offset.min; +} + +/** + * + * \overload + */ +uint16_t FreeTypeGX::getHeight(wchar_t const *text) { + return this->getHeight((wchar_t *)text); +} + +/** + * Get the maximum offset above and minimum offset below the font origin line. + * + * This function calculates the maximum pixel height above the font origin line and the minimum + * pixel height below the font origin line and returns the values in an addressible structure. + * + * @param text NULL terminated string to calculate. + * @return The max and min values above and below the font origin line. + */ +ftgxDataOffset FreeTypeGX::getOffset(wchar_t *text) { + uint16_t strLength = wcslen(text); + uint16_t strMax = 0, strMin = 0; + + for (uint16_t i = 0; i < strLength; i++) { + + ftgxCharData* glyphData = NULL; + if( this->fontData.find(text[i]) != this->fontData.end() ) { + glyphData = &this->fontData[text[i]]; + } + else { + glyphData = this->cacheGlyphData(text[i]); + } + + if(glyphData != NULL) { + strMax = glyphData->renderOffsetMax > strMax ? glyphData->renderOffsetMax : strMax; + strMin = glyphData->renderOffsetMin > strMin ? glyphData->renderOffsetMin : strMin; + } + } + + return (ftgxDataOffset){strMax, strMin}; +} + +/** + * + * \overload + */ +ftgxDataOffset FreeTypeGX::getOffset(wchar_t const *text) { + return this->getOffset(text); +} + +/** + * Copies the supplied texture quad to the EFB. + * + * This routine uses the in-built GX quad builder functions to define the texture bounds and location on the EFB target. + * + * @param texObj A pointer to the glyph's initialized texture object. + * @param texWidth The pixel width of the texture object. + * @param texHeight The pixel height of the texture object. + * @param screenX The screen X coordinate at which to output the rendered texture. + * @param screenY The screen Y coordinate at which to output the rendered texture. + * @param color Color to apply to the texture. + */ +void FreeTypeGX::copyTextureToFramebuffer(GXTexObj *texObj, f32 texWidth, f32 texHeight, int16_t screenX, int16_t screenY, GXColor color) { + + GX_LoadTexObj(texObj, GX_TEXMAP0); + GX_InvalidateTexAll(); + + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + GX_SetVtxDesc (GX_VA_TEX0, GX_DIRECT); + + GX_Begin(GX_QUADS, this->vertexIndex, 4); + GX_Position2s16(screenX, screenY); + GX_Color4u8(color.r, color.g, color.b, color.a); + GX_TexCoord2f32(0.0f, 0.0f); + + GX_Position2s16(texWidth + screenX, screenY); + GX_Color4u8(color.r, color.g, color.b, color.a); + GX_TexCoord2f32(1.0f, 0.0f); + + GX_Position2s16(texWidth + screenX, texHeight + screenY); + GX_Color4u8(color.r, color.g, color.b, color.a); + GX_TexCoord2f32(1.0f, 1.0f); + + GX_Position2s16(screenX, texHeight + screenY); + GX_Color4u8(color.r, color.g, color.b, color.a); + GX_TexCoord2f32(0.0f, 1.0f); + GX_End(); + + this->setDefaultMode(); +} + +/** + * Creates a feature quad to the EFB. + * + * This function creates a simple quad for displaying underline or strikeout text styling. + * + * @param featureWidth The pixel width of the quad. + * @param featureHeight The pixel height of the quad. + * @param screenX The screen X coordinate at which to output the quad. + * @param screenY The screen Y coordinate at which to output the quad. + * @param color Color to apply to the texture. + */ +void FreeTypeGX::copyFeatureToFramebuffer(f32 featureWidth, f32 featureHeight, int16_t screenX, int16_t screenY, GXColor color) { + + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetVtxDesc (GX_VA_TEX0, GX_NONE); + + GX_Begin(GX_QUADS, this->vertexIndex, 4); + GX_Position2s16(screenX, screenY); + GX_Color4u8(color.r, color.g, color.b, color.a); + + GX_Position2s16(featureWidth + screenX, screenY); + GX_Color4u8(color.r, color.g, color.b, color.a); + + GX_Position2s16(featureWidth + screenX, featureHeight + screenY); + GX_Color4u8(color.r, color.g, color.b, color.a); + + GX_Position2s16(screenX, featureHeight + screenY); + GX_Color4u8(color.r, color.g, color.b, color.a); + GX_End(); + + this->setDefaultMode(); +} diff --git a/source/FreeTypeGX.h b/source/FreeTypeGX.h new file mode 100644 index 00000000..935cd7d0 --- /dev/null +++ b/source/FreeTypeGX.h @@ -0,0 +1,286 @@ +/* + * FreeTypeGX is a wrapper class for libFreeType which renders a compiled + * FreeType parsable font into a GX texture for Wii homebrew development. + * Copyright (C) 2008 Armin Tamzarian + * Modified by Tantric, 2009 + * + * This file is part of FreeTypeGX. + * + * FreeTypeGX is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FreeTypeGX is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with FreeTypeGX. If not, see . + */ + +/** \mainpage FreeTypeGX + * + * \section sec_intro Introduction + * + * FreeTypeGX is a wrapper class for libFreeType which renders a compiled FreeType parsable font into a GX texture for Wii homebrew development. + *
+ * FreeTypeGX is written in C++ and makes use of a selectable pre-buffered or buffer-on-demand methodology to allow fast and efficient printing of text to the EFB. + *

+ * This library was developed in-full by Armin Tamzarian with the support of developers in \#wiibrew on EFnet. + * + * \section sec_installation_source Installation (Source Code) + * + * -# Ensure that you have the libFreeType Wii library installed in your development environment with the library added to your Makefile where appropriate. + * -# Ensure that you have the Metaphrasis library installed in your development environment with the library added to your Makefile where appropriate. + * -# Extract the FreeTypeGX archive. + * -# Copy the contents of the src directory into your project's development path. + * -# Include the FreeTypeGX header file in your code using syntax such as the following: + * \code + * #include "FreeTypeGX.h" + * \endcode + * + * \section sec_installation_library Installation (Library) + * + * -# Ensure that you have the libFreeType Wii library installed in your development environment with the library added to your Makefile where appropriate. + * -# Ensure that you have the Metaphrasis library installed in your development environment with the library added to your Makefile where appropriate. + * -# Extract the FreeTypeGX archive. + * -# Copy the contents of the lib directory into your devKitPro/libogc directory. + * -# Include the FreeTypeGX header file in your code using syntax such as the following: + * \code + * #include "FreeTypeGX.h" + * \endcode + * + * \section sec_freetypegx_prerequisites FreeTypeGX Prerequisites + * + * Before you begin using FreeTypeGX in your project you must ensure that the desired font in compiled into your project. For this example I will assume you are building your project with a Makefile using devKitPro evironment and are attempting to include a font whose filename is rursus_compact_mono.ttf. + * + * -# Copy the font into a directory which will be processed by the project's Makefile. If you are unsure about where you should place your font just copy the it into your project's source directory. + * \n\n + * -# Modify the Makefile to convert the font into an object file: + * \code + * %.ttf.o : %.ttf + * @echo $(notdir $<) + * $(bin2o) + * \endcode + * \n + * -# Include the font object's generated header file in your source code: + * \code + * #include "rursus_compact_mono_ttf.h" + * \endcode + * This header file defines the two variables that you will need for use within your project: + * \code + * extern const u8 rursus_compact_mono_ttf[]; A pointer to the font buffer within the compiled project. + * extern const u32 rursus_compact_mono_ttf_size; The size of the font's buffer in bytes. + * \endcode + * + * \section sec_freetypegx_usage FreeTypeGX Usage + * + * -# Within the file you included the FreeTypeGX.h header create an instance object of the FreeTypeGX class: + * \code + * FreeTypeGX *freeTypeGX = new FreeTypeGX(); + * \endcode + * Alternately you can specify a texture format to which you would like to render the font characters. Note that the default value for this parameter is GX_TF_RGBA8. + * \code + * FreeTypeGX *freeTypeGX = new FreeTypeGX(GX_TF_RGB565); + * \endcode + * Furthermore, you can also specify a vertex format index to avoid conflicts with concurrent libraries or other systems. Note that the default value for this parameter is GX_VTXFMT1. + * \code + * FreeTypeGX *freeTypeGX = new FreeTypeGX(GX_TF_RGB565, GX_VTXFMT1); + * \endcode + * \n + * Currently supported textures are: + * \li GX_TF_I4 + * \li GX_TF_I8 + * \li GX_TF_IA4 + * \li GX_TF_IA8 + * \li GX_TF_RGB565 + * \li GX_TF_RGB5A3 + * \li GX_TF_RGBA8 + * + * \n + * -# Using the allocated FreeTypeGX instance object call the loadFont function to load the font from the compiled buffer and specify the desired point size. Note that this function can be called multiple times to load a new: + * \code + * freeTypeGX->loadFont(rursus_compact_mono_ttf, rursus_compact_mono_ttf_size, 64); + * \endcode + * Alternately you can specify a flag which will load and cache all available font glyphs immidiately. Note that on large font sets enabling this feature could take a significant amount of time. + * \code + * freeTypeGX->loadFont(rursus_compact_mono_ttf, rursus_compact_mono_ttf_size, 64, true); + * \endcode + * \n + * -# If necessary you can enable compatibility modes with concurrent libraries or systems. For more information on this feature see the documentation for setCompatibilityMode: + * \code + * freeTypeGX->setCompatibilityMode(FTGX_COMPATIBILITY_GRRLIB); + * \endcode + * -# Using the allocated FreeTypeGX instance object call the drawText function to print a string at the specified screen X and Y coordinates to the current EFB: + * \code + * freeTypeGX->drawText(10, 25, _TEXT("FreeTypeGX Rocks!")); + * \endcode + * Alternately you can specify a GXColor object you would like to apply to the printed characters: + * \code + * freeTypeGX->drawText(10, 25, _TEXT("FreeTypeGX Rocks!"), + * (GXColor){0xff, 0xee, 0xaa, 0xff}); + * \endcode + * Furthermore you can also specify a group of styling parameters which will modify the positioning or style of the text: + * \code + * freeTypeGX->drawText(10, 25, _TEXT("FreeTypeGX Rocks!"), + * (GXColor){0xff, 0xee, 0xaa, 0xff}, + * FTGX_JUSTIFY_CENTER | FTGX_ALIGN_BOTTOM | FTGX_STYLE_UNDERLINE); + * \endcode + * \n + * Currently style parameters are: + * \li FTGX_JUSTIFY_LEFT + * \li FTGX_JUSTIFY_CENTER + * \li FTGX_JUSTIFY_RIGHT + * \li FTGX_ALIGN_TOP + * \li FTGX_ALIGN_MIDDLE + * \li FTGX_ALIGN_BOTTOM + * \li FTGX_STYLE_UNDERLINE + * \li FTGX_STYLE_STRIKE + * + * \section sec_license License + * + * FreeTypeGX is distributed under the GNU Lesser General Public License. + * + * \section sec_contact Contact + * + * If you have any suggestions, questions, or comments regarding this library feel free to e-mail me at tamzarian1989 [at] gmail [dawt] com. + */ + +#ifndef FREETYPEGX_H_ +#define FREETYPEGX_H_ + +#include +#include +#include FT_FREETYPE_H +#include FT_BITMAP_H +#include + +#include +#include +#include + +/*! \struct ftgxCharData_ + * + * Font face character glyph relevant data structure. + */ +typedef struct ftgxCharData_ { + uint16_t glyphAdvanceX; /**< Character glyph X coordinate advance in pixels. */ + uint16_t glyphIndex; /**< Charachter glyph index in the font face. */ + + uint16_t textureWidth; /**< Texture width in pixels/bytes. */ + uint16_t textureHeight; /**< Texture glyph height in pixels/bytes. */ + + uint16_t renderOffsetY; /**< Texture Y axis bearing offset. */ + uint16_t renderOffsetMax; /**< Texture Y axis bearing maximum value. */ + uint16_t renderOffsetMin; /**< Texture Y axis bearing minimum value. */ + + uint32_t* glyphDataTexture; /**< Glyph texture bitmap data buffer. */ +} ftgxCharData; + +/*! \struct ftgxDataOffset_ + * + * Offset structure which hold both a maximum and minimum value. + */ +typedef struct ftgxDataOffset_ { + int16_t max; /**< Maximum data offset. */ + int16_t min; /**< Minimum data offset. */ +} ftgxDataOffset; + +#define _TEXT(t) L ## t /**< Unicode helper macro. */ + +#define FTGX_NULL 0x0000 +#define FTGX_JUSTIFY_LEFT 0x0001 +#define FTGX_JUSTIFY_CENTER 0x0002 +#define FTGX_JUSTIFY_RIGHT 0x0004 + +#define FTGX_ALIGN_TOP 0x0010 +#define FTGX_ALIGN_MIDDLE 0x0020 +#define FTGX_ALIGN_BOTTOM 0x0040 + +#define FTGX_STYLE_UNDERLINE 0x0100 +#define FTGX_STYLE_STRIKE 0x0200 + +#define FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_MODULATE 0X0001 +#define FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_DECAL 0X0002 +#define FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_BLEND 0X0004 +#define FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_REPLACE 0X0008 +#define FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_PASSCLR 0X0010 + +#define FTGX_COMPATIBILITY_DEFAULT_VTXDESC_GX_NONE 0X0100 +#define FTGX_COMPATIBILITY_DEFAULT_VTXDESC_GX_DIRECT 0X0200 +#define FTGX_COMPATIBILITY_DEFAULT_VTXDESC_GX_INDEX8 0X0400 +#define FTGX_COMPATIBILITY_DEFAULT_VTXDESC_GX_INDEX16 0X0800 + +#define FTGX_COMPATIBILITY_NONE 0x0000 +#define FTGX_COMPATIBILITY_GRRLIB FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_PASSCLR | FTGX_COMPATIBILITY_DEFAULT_VTXDESC_GX_NONE +#define FTGX_COMPATIBILITY_LIBWIISPRITE FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_MODULATE | FTGX_COMPATIBILITY_DEFAULT_VTXDESC_GX_DIRECT + +const GXColor ftgxWhite = (GXColor){0xff, 0xff, 0xff, 0xff}; /**< Constant color value used only to sanitize Doxygen documentation. */ + +/*! \class FreeTypeGX + * \brief Wrapper class for the libFreeType library with GX rendering. + * \author Armin Tamzarian + * \version 0.2.4 + * + * FreeTypeGX acts as a wrapper class for the libFreeType library. It supports precaching of transformed glyph data into + * a specified texture format. Rendering of the data to the EFB is accomplished through the application of high performance + * GX texture functions resulting in high throughput of string rendering. + */ +class FreeTypeGX { + + private: + FT_Library ftLibrary; /**< FreeType FT_Library instance. */ + FT_Face ftFace; /**< FreeType reusable FT_Face typographic object. */ + FT_GlyphSlot ftSlot; /**< FreeType reusable FT_GlyphSlot glyph container object. */ + FT_UInt ftPointSize; /**< Requested size of the rendered font. */ + bool ftKerningEnabled; /**< Flag indicating the availability of font kerning data. */ + + uint8_t textureFormat; /**< Defined texture format of the target EFB. */ + uint8_t vertexIndex; /**< Vertex format descriptor index. */ + uint32_t compatibilityMode; /**< Compatibility mode for default tev operations and vertex descriptors. */ + std::map fontData; /**< Map which holds the glyph data structures for the corresponding characters. */ + + static uint16_t adjustTextureWidth(uint16_t textureWidth, uint8_t textureFormat); + static uint16_t adjustTextureHeight(uint16_t textureHeight, uint8_t textureFormat); + + static uint16_t getStyleOffsetWidth(uint16_t width, uint16_t format); + static uint16_t getStyleOffsetHeight(ftgxDataOffset offset, uint16_t format); + + void unloadFont(); + ftgxCharData *cacheGlyphData(wchar_t charCode); + uint16_t cacheGlyphDataComplete(); + void loadGlyphData(FT_Bitmap *bmp, ftgxCharData *charData); + + void setDefaultMode(); + + void drawTextFeature(int16_t x, int16_t y, uint16_t width, ftgxDataOffset offsetData, uint16_t format, GXColor color); + void copyTextureToFramebuffer(GXTexObj *texObj, f32 texWidth, f32 texHeight, int16_t screenX, int16_t screenY, GXColor color); + void copyFeatureToFramebuffer(f32 featureWidth, f32 featureHeight, int16_t screenX, int16_t screenY, GXColor color); + + public: + FreeTypeGX(uint8_t textureFormat = GX_TF_RGBA8, uint8_t vertexIndex = GX_VTXFMT1); + ~FreeTypeGX(); + + static wchar_t* charToWideChar(char* p); + static wchar_t* charToWideChar(const char* p); + void setVertexFormat(uint8_t vertexIndex); + void setCompatibilityMode(uint32_t compatibilityMode); + + uint16_t loadFont(uint8_t* fontBuffer, FT_Long bufferSize, FT_UInt pointSize, bool cacheAll = false); + uint16_t loadFont(const uint8_t* fontBuffer, FT_Long bufferSize, FT_UInt pointSize, bool cacheAll = false); + void changeSize(FT_UInt pointSize); + + uint16_t drawText(int16_t x, int16_t y, wchar_t *text, GXColor color = ftgxWhite, uint16_t textStyling = FTGX_NULL); + uint16_t drawText(int16_t x, int16_t y, wchar_t const *text, GXColor color = ftgxWhite, uint16_t textStyling = FTGX_NULL); + + uint16_t getWidth(wchar_t *text); + uint16_t getWidth(wchar_t const *text); + uint16_t getHeight(wchar_t *text); + uint16_t getHeight(wchar_t const *text); + ftgxDataOffset getOffset(wchar_t *text); + ftgxDataOffset getOffset(wchar_t const *text); +}; + +#endif /* FREETYPEGX_H_ */ diff --git a/source/apploader.c b/source/apploader.c new file mode 100644 index 00000000..3ee503cf --- /dev/null +++ b/source/apploader.c @@ -0,0 +1,327 @@ +#include +#include +#include + +#include "apploader.h" +#include "wdvd.h" +#include "wpad.h" +#include "patchcode.h" +#include "kenobiwii.h" /*FISHEARS*/ + +/*KENOBI! - FISHEARS*/ +extern const unsigned char kenobiwii[]; +extern const int kenobiwii_size; +/*KENOBI! - FISHEARS*/ + +/* Apploader function pointers */ +typedef int (*app_main)(void **dst, int *size, int *offset); +typedef void (*app_init)(void (*report)(const char *fmt, ...)); +typedef void *(*app_final)(); +typedef void (*app_entry)(void (**init)(void (*report)(const char *fmt, ...)), int (**main)(), void *(**final)()); + +/* Apploader pointers */ +static u8 *appldr = (u8 *)0x81200000; + + +/* Constants */ +#define APPLDR_OFFSET 0x2440 + +/* Variables */ +static u32 buffer[0x20] ATTRIBUTE_ALIGN(32); + + +static void __noprint(const char *fmt, ...) +{ +} + + + +bool compare_videomodes(GXRModeObj* mode1, GXRModeObj* mode2) +{ + if (mode1->viTVMode != mode2->viTVMode || mode1->fbWidth != mode2->fbWidth || mode1->efbHeight != mode2->efbHeight || mode1->xfbHeight != mode2->xfbHeight || + mode1->viXOrigin != mode2->viXOrigin || mode1->viYOrigin != mode2->viYOrigin || mode1->viWidth != mode2->viWidth || mode1->viHeight != mode2->viHeight || + mode1->xfbMode != mode2->xfbMode || mode1->field_rendering != mode2->field_rendering || mode1->aa != mode2->aa || mode1->sample_pattern[0][0] != mode2->sample_pattern[0][0] || + mode1->sample_pattern[1][0] != mode2->sample_pattern[1][0] || mode1->sample_pattern[2][0] != mode2->sample_pattern[2][0] || + mode1->sample_pattern[3][0] != mode2->sample_pattern[3][0] || mode1->sample_pattern[4][0] != mode2->sample_pattern[4][0] || + mode1->sample_pattern[5][0] != mode2->sample_pattern[5][0] || mode1->sample_pattern[6][0] != mode2->sample_pattern[6][0] || + mode1->sample_pattern[7][0] != mode2->sample_pattern[7][0] || mode1->sample_pattern[8][0] != mode2->sample_pattern[8][0] || + mode1->sample_pattern[9][0] != mode2->sample_pattern[9][0] || mode1->sample_pattern[10][0] != mode2->sample_pattern[10][0] || + mode1->sample_pattern[11][0] != mode2->sample_pattern[11][0] || mode1->sample_pattern[0][1] != mode2->sample_pattern[0][1] || + mode1->sample_pattern[1][1] != mode2->sample_pattern[1][1] || mode1->sample_pattern[2][1] != mode2->sample_pattern[2][1] || + mode1->sample_pattern[3][1] != mode2->sample_pattern[3][1] || mode1->sample_pattern[4][1] != mode2->sample_pattern[4][1] || + mode1->sample_pattern[5][1] != mode2->sample_pattern[5][1] || mode1->sample_pattern[6][1] != mode2->sample_pattern[6][1] || + mode1->sample_pattern[7][1] != mode2->sample_pattern[7][1] || mode1->sample_pattern[8][1] != mode2->sample_pattern[8][1] || + mode1->sample_pattern[9][1] != mode2->sample_pattern[9][1] || mode1->sample_pattern[10][1] != mode2->sample_pattern[10][1] || + mode1->sample_pattern[11][1] != mode2->sample_pattern[11][1] || mode1->vfilter[0] != mode2->vfilter[0] || + mode1->vfilter[1] != mode2->vfilter[1] || mode1->vfilter[2] != mode2->vfilter[2] || mode1->vfilter[3] != mode2->vfilter[3] || mode1->vfilter[4] != mode2->vfilter[4] || + mode1->vfilter[5] != mode2->vfilter[5] || mode1->vfilter[6] != mode2->vfilter[6] ) + { + return false; + } else + { + return true; + } +} + + +void patch_videomode(GXRModeObj* mode1, GXRModeObj* mode2) +{ + mode1->viTVMode = mode2->viTVMode; + mode1->fbWidth = mode2->fbWidth; + mode1->efbHeight = mode2->efbHeight; + mode1->xfbHeight = mode2->xfbHeight; + mode1->viXOrigin = mode2->viXOrigin; + mode1->viYOrigin = mode2->viYOrigin; + mode1->viWidth = mode2->viWidth; + mode1->viHeight = mode2->viHeight; + mode1->xfbMode = mode2->xfbMode; + mode1->field_rendering = mode2->field_rendering; + mode1->aa = mode2->aa; + mode1->sample_pattern[0][0] = mode2->sample_pattern[0][0]; + mode1->sample_pattern[1][0] = mode2->sample_pattern[1][0]; + mode1->sample_pattern[2][0] = mode2->sample_pattern[2][0]; + mode1->sample_pattern[3][0] = mode2->sample_pattern[3][0]; + mode1->sample_pattern[4][0] = mode2->sample_pattern[4][0]; + mode1->sample_pattern[5][0] = mode2->sample_pattern[5][0]; + mode1->sample_pattern[6][0] = mode2->sample_pattern[6][0]; + mode1->sample_pattern[7][0] = mode2->sample_pattern[7][0]; + mode1->sample_pattern[8][0] = mode2->sample_pattern[8][0]; + mode1->sample_pattern[9][0] = mode2->sample_pattern[9][0]; + mode1->sample_pattern[10][0] = mode2->sample_pattern[10][0]; + mode1->sample_pattern[11][0] = mode2->sample_pattern[11][0]; + mode1->sample_pattern[0][1] = mode2->sample_pattern[0][1]; + mode1->sample_pattern[1][1] = mode2->sample_pattern[1][1]; + mode1->sample_pattern[2][1] = mode2->sample_pattern[2][1]; + mode1->sample_pattern[3][1] = mode2->sample_pattern[3][1]; + mode1->sample_pattern[4][1] = mode2->sample_pattern[4][1]; + mode1->sample_pattern[5][1] = mode2->sample_pattern[5][1]; + mode1->sample_pattern[6][1] = mode2->sample_pattern[6][1]; + mode1->sample_pattern[7][1] = mode2->sample_pattern[7][1]; + mode1->sample_pattern[8][1] = mode2->sample_pattern[8][1]; + mode1->sample_pattern[9][1] = mode2->sample_pattern[9][1]; + mode1->sample_pattern[10][1] = mode2->sample_pattern[10][1]; + mode1->sample_pattern[11][1] = mode2->sample_pattern[11][1]; + mode1->vfilter[0] = mode2->vfilter[0]; + mode1->vfilter[1] = mode2->vfilter[1]; + mode1->vfilter[2] = mode2->vfilter[2]; + mode1->vfilter[3] = mode2->vfilter[3]; + mode1->vfilter[4] = mode2->vfilter[4]; + mode1->vfilter[5] = mode2->vfilter[5]; + mode1->vfilter[6] = mode2->vfilter[6]; +} + +GXRModeObj* vmodes[] = { + &TVNtsc240Ds, + &TVNtsc240DsAa, + &TVNtsc240Int, + &TVNtsc240IntAa, + &TVNtsc480IntDf, + &TVNtsc480IntAa, + &TVNtsc480Prog, + &TVMpal480IntDf, + &TVPal264Ds, + &TVPal264DsAa, + &TVPal264Int, + &TVPal264IntAa, + &TVPal524IntAa, + &TVPal528Int, + &TVPal528IntDf, + &TVPal574IntDfScale, + &TVEurgb60Hz240Ds, + &TVEurgb60Hz240DsAa, + &TVEurgb60Hz240Int, + &TVEurgb60Hz240IntAa, + &TVEurgb60Hz480Int, + &TVEurgb60Hz480IntDf, + &TVEurgb60Hz480IntAa, + &TVEurgb60Hz480Prog, + &TVEurgb60Hz480ProgSoft, + &TVEurgb60Hz480ProgAa +}; + +GXRModeObj* PAL2NTSC[]={ + &TVMpal480IntDf, &TVNtsc480IntDf, + &TVPal264Ds, &TVNtsc240Ds, + &TVPal264DsAa, &TVNtsc240DsAa, + &TVPal264Int, &TVNtsc240Int, + &TVPal264IntAa, &TVNtsc240IntAa, + &TVPal524IntAa, &TVNtsc480IntAa, + &TVPal528Int, &TVNtsc480IntAa, + &TVPal528IntDf, &TVNtsc480IntDf, + &TVPal574IntDfScale, &TVNtsc480IntDf, + &TVEurgb60Hz240Ds, &TVNtsc240Ds, + &TVEurgb60Hz240DsAa, &TVNtsc240DsAa, + &TVEurgb60Hz240Int, &TVNtsc240Int, + &TVEurgb60Hz240IntAa, &TVNtsc240IntAa, + &TVEurgb60Hz480Int, &TVNtsc480IntAa, + &TVEurgb60Hz480IntDf, &TVNtsc480IntDf, + &TVEurgb60Hz480IntAa, &TVNtsc480IntAa, + &TVEurgb60Hz480Prog, &TVNtsc480Prog, + &TVEurgb60Hz480ProgSoft,&TVNtsc480Prog, + &TVEurgb60Hz480ProgAa, &TVNtsc480Prog, + 0,0 +}; + +GXRModeObj* NTSC2PAL[]={ + &TVNtsc240Ds, &TVPal264Ds, + &TVNtsc240DsAa, &TVPal264DsAa, + &TVNtsc240Int, &TVPal264Int, + &TVNtsc240IntAa, &TVPal264IntAa, + &TVNtsc480IntDf, &TVPal528IntDf, + &TVNtsc480IntAa, &TVPal524IntAa, + &TVNtsc480Prog, &TVPal528IntDf, + 0,0 +}; + +GXRModeObj* NTSC2PAL60[]={ + &TVNtsc240Ds, &TVEurgb60Hz240Ds, + &TVNtsc240DsAa, &TVEurgb60Hz240DsAa, + &TVNtsc240Int, &TVEurgb60Hz240Int, + &TVNtsc240IntAa, &TVEurgb60Hz240IntAa, + &TVNtsc480IntDf, &TVEurgb60Hz480IntDf, + &TVNtsc480IntAa, &TVEurgb60Hz480IntAa, + &TVNtsc480Prog, &TVEurgb60Hz480Prog, + 0,0 +}; +bool Search_and_patch_Video_Modes(void *Address, u32 Size, GXRModeObj* Table[]) +{ + u8 *Addr = (u8 *)Address; + bool found = 0; + u32 i; + + while(Size >= sizeof(GXRModeObj)) + { + + + + for(i = 0; Table[i]; i+=2) + { + + + if(compare_videomodes(Table[i], (GXRModeObj*)Addr)) + + { + found = 1; + patch_videomode((GXRModeObj*)Addr, Table[i+1]); + Addr += (sizeof(GXRModeObj)-4); + Size -= (sizeof(GXRModeObj)-4); + break; + } + } + + Addr += 4; + Size -= 4; + } + + + return found; +} + +s32 Apploader_Run(entry_point *entry, u8 cheat, u8 videoSelected, u8 vipatch) +{ + app_entry appldr_entry; + app_init appldr_init; + app_main appldr_main; + app_final appldr_final; + + u32 appldr_len; + s32 ret; + + /* Read apploader header */ + ret = WDVD_Read(buffer, 0x20, APPLDR_OFFSET); + if (ret < 0) + return ret; + + /* Calculate apploader length */ + appldr_len = buffer[5] + buffer[6]; + + /* Read apploader code */ + ret = WDVD_Read(appldr, appldr_len, APPLDR_OFFSET + 0x20); + if (ret < 0) + return ret; + + /* Set apploader entry function */ + appldr_entry = (app_entry)buffer[4]; + + /* Call apploader entry */ + appldr_entry(&appldr_init, &appldr_main, &appldr_final); + + /* Initialize apploader */ + appldr_init(__noprint); + + if (cheat) + { + /*HOOKS STUFF - FISHEARS*/ + memset((void*)0x80001800,0,kenobiwii_size); + memcpy((void*)0x80001800,kenobiwii,kenobiwii_size); + DCFlushRange((void*)0x80001800,kenobiwii_size); + hooktype = 1; + memcpy((void*)0x80001800, (char*)0x80000000, 6); // For WiiRD + /*HOOKS STUFF - FISHEARS*/ + } + + for (;;) { + void *dst = NULL; + int len = 0, offset = 0; + GXRModeObj** table = NULL; + + /* Run apploader main function */ + ret = appldr_main(&dst, &len, &offset); + if (!ret) + break; + + /* Read data from DVD */ + WDVD_Read(dst, len, (u64)(offset << 2)); + + + if (videoSelected == 5) // patch + + { + switch(CONF_GetVideo()) + { + case CONF_VIDEO_PAL: + if(CONF_GetEuRGB60() > 0) + { + table = NTSC2PAL60; + } + else + { + table = NTSC2PAL; + } + break; + + case CONF_VIDEO_MPAL: + + + + table = NTSC2PAL; + break; + + + default: + table = PAL2NTSC; + break; + } + Search_and_patch_Video_Modes(dst, len, table); + } + + /*GAME HOOK - FISHEARS*/ + dogamehooks(dst,len); + + if (vipatch) + vidolpatcher(dst,len); + + + /*LANGUAGE PATCH - FISHEARS*/ + langpatcher(dst,len); + + DCFlushRange(dst, len); + } + /* Set entry point from apploader */ + *entry = appldr_final(); + + return 0; +} +#ifdef __cplusplus +} +#endif diff --git a/source/apploader.h b/source/apploader.h new file mode 100644 index 00000000..5481bbb9 --- /dev/null +++ b/source/apploader.h @@ -0,0 +1,10 @@ +#ifndef _APPLOADER_H_ +#define _APPLOADER_H_ + +/* Entry point */ +typedef void (*entry_point)(void); + +/* Prototypes */ +s32 Apploader_Run(entry_point *, u8, u8, u8); + +#endif diff --git a/source/audio.cpp b/source/audio.cpp new file mode 100644 index 00000000..8d0b8854 --- /dev/null +++ b/source/audio.cpp @@ -0,0 +1,35 @@ +/**************************************************************************** + * libwiigui Template + * Tantric 2009 + * + * audio.cpp + * Audio support + ***************************************************************************/ + +#include +#include +#include + +/**************************************************************************** + * InitAudio + * + * Initializes the Wii's audio subsystem + ***************************************************************************/ +void InitAudio() +{ + AUDIO_Init(NULL); + ASND_Init(); + ASND_Pause(0); +} + +/**************************************************************************** + * ShutdownAudio + * + * Shuts down audio subsystem. Useful to avoid unpleasant sounds if a + * crash occurs during shutdown. + ***************************************************************************/ +void ShutdownAudio() +{ + ASND_Pause(1); + ASND_End(); +} diff --git a/source/audio.h b/source/audio.h new file mode 100644 index 00000000..6efdf6d5 --- /dev/null +++ b/source/audio.h @@ -0,0 +1,15 @@ +/**************************************************************************** + * libwiigui Template + * Tantric 2009 + * + * audio.h + * Audio support + ***************************************************************************/ + +#ifndef _AUDIO_H_ +#define _AUDIO_H_ + +void InitAudio(); +void ShutdownAudio(); + +#endif diff --git a/source/cfg.c b/source/cfg.c new file mode 100644 index 00000000..fb72d6b1 --- /dev/null +++ b/source/cfg.c @@ -0,0 +1,1065 @@ +#include +#include +#include +#include +#include +#include +#include +#include "cfg.h" + +struct SSettings Settings; +//struct SSettings2 Settings2; + + +char *cfg_path = "SD:/apps/usbloader/"; +//char *setting_path = "SD:/apps/usbloader/settings.cfg"; +char current_path[100]; + +/* configurable fields */ + +/* default */ +//int ENTRIES_PER_PAGE = 12; +//int MAX_CHARACTERS = 37; +//int CONSOLE_XCOORD = 260; +//int CONSOLE_YCOORD = 30; +//int CONSOLE_WIDTH = 340; +//int CONSOLE_HEIGHT = 290; +//int CONSOLE_FG_COLOR = 15; +//int CONSOLE_BG_COLOR = 0; +//int COVER_XCOORD = 28; +//int COVER_YCOORD = 105; + +struct CFG CFG; +struct THEME THEME; +u8 ocarinaChoice = 0; +u8 videoChoice = 0; +u8 languageChoice = 0; +u8 viChoice = 0; +u8 iosChoice = 0; +u8 parentalcontrolChoice = 0; + +#define TITLE_MAX 65 + +struct ID_Title +{ + u8 id[5]; + char title[TITLE_MAX]; + u8 block; +}; + +// renamed titles +int num_title = 0; //number of titles +struct ID_Title *cfg_title = NULL; + +#define MAX_SAVED_GAMES 1000 +int num_saved_games = 0; +struct Game_CFG cfg_game[MAX_SAVED_GAMES]; + + +/* For Mapping */ + +static char *cfg_name, *cfg_val; + +struct TextMap +{ + char *name; + int id; +}; + +struct TextMap map_video[] = +{ + { "system", CFG_VIDEO_SYS }, + { "game", CFG_VIDEO_GAME }, + { "patch", CFG_VIDEO_PATCH }, + { "pal50", CFG_VIDEO_PAL50 }, + { "pal60", CFG_VIDEO_PAL60 }, + { "ntsc", CFG_VIDEO_NTSC }, + { NULL, -1 } +}; + +struct TextMap map_language[] = +{ + { "console", CFG_LANG_CONSOLE }, + { "japanese", CFG_LANG_JAPANESE }, + { "english", CFG_LANG_ENGLISH }, + { "german", CFG_LANG_GERMAN }, + { "french", CFG_LANG_FRENCH }, + { "spanish", CFG_LANG_SPANISH }, + { "italian", CFG_LANG_ITALIAN }, + { "dutch", CFG_LANG_DUTCH }, + { "s.chinese", CFG_LANG_S_CHINESE }, + { "t.chinese", CFG_LANG_T_CHINESE }, + { "korean", CFG_LANG_KOREAN }, + { NULL, -1 } +}; + + +struct TextMap map_alignment[] = +{ + { "left", CFG_ALIGN_LEFT }, + { "right", CFG_ALIGN_RIGHT }, + { "center", CFG_ALIGN_CENTRE }, + { "top", CFG_ALIGN_TOP }, + { "bottom", CFG_ALIGN_BOTTOM }, + { "middle", CFG_ALIGN_MIDDLE }, + { NULL, -1 } +}; + +int map_get_id(struct TextMap *map, char *name) +{ + int i; + for (i=0; map[i].name != NULL; i++) { + if (strcmp(name, map[i].name) == 0) return map[i].id; + } + return -1; +} + +char* map_get_name(struct TextMap *map, short id) +{ + int i; + for (i=0; map[i].name != NULL; i++) { + if (id == map[i].id) return map[i].name; + } + return NULL; +} + +bool map_auto(char *name, char *name2, char *val, struct TextMap *map, short *var) +{ + if (strcmp(name, name2) != 0) return false; + int id = map_get_id(map, val); + if (id == -1) { + //printf("MAP FAIL: %s=%s : %d\n", name, val, id); sleep(1); + return false; + } + *var = id; + //printf("MAP AUTO: %s=%s : %d\n", name, val, id); sleep(1); + return true; +} + +bool cfg_map_auto(char *name, struct TextMap *map, short *var) +{ + return map_auto(name, cfg_name, cfg_val, map, var); +} + +bool cfg_map(char *name, char *val, short *var, short id) +{ + if (strcmp(name, cfg_name)==0 && strcmpi(val, cfg_val)==0) + { + *var = id; + return true; + } + return false; +} + +bool cfg_bool(char *name, short *var) +{ + return (cfg_map(name, "0", var, 0) || cfg_map(name, "1", var, 1)); +} + +void cfg_int(char *name, short *var, int count) +{ + char tmp[5]; + short i; + + if (count > 10) //avoid overflow + return; + + for (i = 0; i < count; i++) + { + sprintf(tmp, "%d", i); + cfg_map(name, tmp, var, i); + } +} + +/* Mapping */ + +//static char bg_path[100]; + +void CFG_Default() +{ + CFG.widescreen = CONF_GetAspectRatio(); + + if (CFG.widescreen) { + snprintf(CFG.theme_path, sizeof(CFG.theme_path), "SD:/wtheme/"); + } + else + { + snprintf(CFG.theme_path, sizeof(CFG.theme_path), "SD:/theme/"); + } +// CFG.simple = 0; +// CFG.video = CFG_VIDEO_DEFAULT; +// CFG.home = CFG_HOME_REBOOT; +// CFG.download = 0; +// CFG.language = CFG_LANG_CONSOLE; +// CFG.ocarina = 0; +// CFG.vipatch = 0; +// CFG.savesettings = 0; + CFG.parentalcontrol = 0; + CFG.maxcharacters = 38; + CFG.godmode = 0; +// CFG.installdownload = 0; +// CFG.hidesettingmenu = 0; + snprintf(CFG.covers_path, sizeof(CFG.covers_path), "SD:/images/"); + snprintf(CFG.disc_path, sizeof(CFG.disc_path), "SD:/images/disc/"); + snprintf(CFG.unlockCode, sizeof(CFG.unlockCode), "ab121b"); + + //all alignments are left top here + THEME.selection_x = 200; + THEME.selection_y = 40; + THEME.selection_w = 396; + THEME.selection_h = 280; + THEME.cover_x = 26; + THEME.cover_y = 55; + THEME.showID = 1; + THEME.id_x = 68; + THEME.id_y = 305; + THEME.region_x = 68; + THEME.region_y = 30; + THEME.power_x = 576; + THEME.power_y = 355; + THEME.home_x = 485;//215; + THEME.home_y = 367; + THEME.setting_x = 60;//-210 + THEME.setting_y = 367; + THEME.showHDD = -1; //default, non-force mode + THEME.showGameCnt = -1; //default, non-force mode + THEME.showToolTip = 1; //1 means use settings, 0 means force turn off + THEME.install_x = 16;//-280 + THEME.install_y = 355; + THEME.showBattery = 1; + THEME.showRegion = 1; + THEME.hddInfo_x = 0; + THEME.hddInfo_y = 330; + THEME.hddInfoAlign = CFG_ALIGN_CENTRE; + THEME.gameCnt_x = 0; + THEME.gameCnt_y = 350; + THEME.gameCntAlign = CFG_ALIGN_CENTRE; + THEME.battery1_x = 245; + THEME.battery1_y = 400; + THEME.battery2_x = 335; + THEME.battery2_y = 400; + THEME.battery3_x = 245; + THEME.battery3_y = 425; + THEME.battery4_x = 335; + THEME.battery4_y = 425; + THEME.info_r = 63; + THEME.info_g = 154; + THEME.info_b = 192; + THEME.clock_x = 0; + THEME.clock_y = -120; + THEME.clockAlign = CFG_ALIGN_CENTRE; + THEME.sdcard_x = 160; + THEME.sdcard_y = 390; + +} + + +char* strcopy(char *dest, char *src, int size) +{ + strncpy(dest,src,size); + dest[size-1] = 0; + return dest; +} + +char *cfg_get_title(u8 *id) +{ + int i; + for (i=0; iid); + if (title) return title; + return header->title; +} + +void title_set(char *id, char *title, u8 block) +{ + char *idt = cfg_get_title((u8*)id); + if (idt) { + // replace + strcopy(idt, title, TITLE_MAX); + } else { + cfg_title = realloc(cfg_title, (num_title+1) * sizeof(struct ID_Title)); + if (!cfg_title) { + // error + num_title = 0; + return; + } + // add + memcpy(cfg_title[num_title].id, id, 4); + cfg_title[num_title].id[4] = 0; + cfg_title[num_title].block = block; + strcopy(cfg_title[num_title].title, title, TITLE_MAX); + num_title++; + } +} + +u8 cfg_get_block(u8 *id) +{ + int i; + for (i=0; iid); +} + +// trim leading and trailing whitespace +// copy at max n or at max size-1 +char* trim_n_copy(char *dest, char *src, int n, int size) +{ + int len; + // trim leading white space + while (isspace(*src)) { src++; n--; } + len = strlen(src); + if (len > n) len = n; + // trim trailing white space + while (len > 0 && isspace(src[len-1])) len--; + if (len >= size) len = size-1; + strncpy(dest, src, len); + dest[len] = 0; + //printf("trim_copy: '%s' %d\n", dest, len); //sleep(1); + return dest; +} + +char* trimcopy(char *dest, char *src, int size) +{ + int len; + while (*src == ' ') src++; + len = strlen(src); + // trim trailing " \r\n" + while (len > 0 && strchr(" \r\n", src[len-1])) len--; + if (len >= size) len = size-1; + strncpy(dest, src, len); + dest[len] = 0; + return dest; +} + +void widescreen_set(char *name, char *val) +{ + cfg_name = name; + cfg_val = val; + + if (cfg_bool("widescreen", &CFG.widescreen)) //reset default + { + if (CFG.widescreen) { +// snprintf(CFG.covers_path, sizeof(CFG.covers_path), "SD:/wimages/"); + snprintf(CFG.theme_path, sizeof(CFG.theme_path), "SD:/wtheme/"); + } + else + { +// snprintf(CFG.covers_path, sizeof(CFG.covers_path), "SD:/images/"); + snprintf(CFG.theme_path, sizeof(CFG.theme_path), "SD:/theme/"); + } + } +} + + + +void cfg_set(char *name, char *val) +{ + cfg_name = name; + cfg_val = val; +/* + if (!CFG.widescreen &&(strcmp(name, "images_path") == 0)) { + strcopy(CFG.images_path, val, sizeof(CFG.images_path)); + snprintf(bg_path, sizeof(bg_path), "%sbg.png", CFG.images_path); //reset path + return; + } + + if (CFG.widescreen && strcmp(name, "wimages_path") == 0) { + strcopy(CFG.images_path, val, sizeof(CFG.images_path)); + snprintf(bg_path, sizeof(bg_path), "%swbg.png", CFG.images_path); //reset path + return; + } + + if (cfg_map_auto("video", map_video, &CFG.video)) + return; + + if (cfg_map_auto("language", map_language, &CFG.language)) + return; + + if (cfg_map_auto("layout", map_layout, &CFG.layout)) { + cfg_layout(); + } + + cfg_bool("ocarina", &CFG.ocarina); + cfg_bool("covers", &CFG.covers); + cfg_bool("download", &CFG.download); + cfg_bool("savesettings", &CFG.savesettings); + cfg_bool("installdownload", &CFG.installdownload); + cfg_bool("hidesettingmenu", &CFG.hidesettingmenu); + cfg_map("home", "exit", &CFG.home, CFG_HOME_EXIT); + cfg_map("home", "reboot", &CFG.home, CFG_HOME_REBOOT); + cfg_int("simple", &CFG.simple, 3); +*/ + if (!CFG.widescreen &&(strcmp(name, "theme_path") == 0)) { + strcopy(CFG.theme_path, val, sizeof(CFG.theme_path)); + return; + } + + if (CFG.widescreen && strcmp(name, "wtheme_path") == 0) { + strcopy(CFG.theme_path, val, sizeof(CFG.theme_path)); + return; + } + + if (strcmp(name, "cover_path") == 0) { + strcopy(CFG.covers_path, val, sizeof(CFG.covers_path)); + return; + } + + if (strcmp(name, "disc_path") == 0) { + strcopy(CFG.disc_path, val, sizeof(CFG.disc_path)); + return; + } + + cfg_int("parentalcontrol", &CFG.parentalcontrol, 4); + cfg_bool("godmode", &CFG.godmode); + + if (strcmp(name, "unlock_code") == 0) { + strcopy(CFG.unlockCode, val, sizeof(CFG.unlockCode)); + return; + } +} + +void theme_set(char *name, char *val) +{ + cfg_name = name; + cfg_val = val; + + if(strcmp(cfg_name, "gamelist_coords") == 0) { + int x,y,w,h; + if (sscanf(val, "%d,%d,%d,%d", &x, &y, &w, &h) == 4) { + THEME.selection_x = x - (x % 4); + THEME.selection_y = y; + THEME.selection_w = w; + THEME.selection_h = h; + } + } + + else if (strcmp(cfg_name, "covers_coords") == 0) { + int x,y; + if (sscanf(val, "%d,%d", &x, &y) == 2) { + THEME.cover_x = x - (x % 4); + THEME.cover_y = y; + } + } + + else if (strcmp(cfg_name, "id_coords") == 0) { + int x,y; + if (sscanf(val, "%d,%d", &x, &y) == 2) { + THEME.id_x = x - (x % 4); + THEME.id_y = y; + } + } + + else if (strcmp(cfg_name, "hddinfo_coords") == 0) { + int x,y; + if (sscanf(val, "%d,%d", &x, &y) == 2) { + THEME.hddInfo_x = x - (x % 4); + THEME.hddInfo_y = y; + } + } + + else if (strcmp(cfg_name, "gamecount_coords") == 0) { + int x,y; + if (sscanf(val, "%d,%d", &x, &y) == 2) { + THEME.gameCnt_x = x - (x % 4); + THEME.gameCnt_y = y; + } + } + + else if (strcmp(cfg_name, "region_coords") == 0) { + int x,y; + if (sscanf(val, "%d,%d", &x, &y) == 2) { + THEME.region_x = x - (x % 4); + THEME.region_y = y; + } + } + + else if (strcmp(cfg_name, "power_coords") == 0) { + int x,y; + if (sscanf(val, "%d,%d", &x, &y) == 2) { + THEME.power_x = x - (x % 4); + THEME.power_y = y; + } + } + + else if (strcmp(cfg_name, "home_coords") == 0) { + int x,y; + if (sscanf(val, "%d,%d", &x, &y) == 2) { + THEME.home_x = x - (x % 4); + THEME.home_y = y; + } + } + + else if (strcmp(cfg_name, "setting_coords") == 0) { + int x,y; + if (sscanf(val, "%d,%d", &x, &y) == 2) { + THEME.setting_x = x - (x % 4); + THEME.setting_y = y; + } + } + + else if (strcmp(cfg_name, "install_coords") == 0) { + int x,y; + if (sscanf(val, "%d,%d", &x, &y) == 2) { + THEME.install_x = x - (x % 4); + THEME.install_y = y; + } + } + + else if (strcmp(cfg_name, "battery1_coords") == 0) { + int x,y; + if (sscanf(val, "%d,%d", &x, &y) == 2) { + THEME.battery1_x = x - (x % 4); + THEME.battery1_y = y; + } + } + + else if (strcmp(cfg_name, "battery2_coords") == 0) { + int x,y; + if (sscanf(val, "%d,%d", &x, &y) == 2) { + THEME.battery2_x = x - (x % 4); + THEME.battery2_y = y; + } + } + + else if (strcmp(cfg_name, "battery3_coords") == 0) { + int x,y; + if (sscanf(val, "%d,%d", &x, &y) == 2) { + THEME.battery3_x = x - (x % 4); + THEME.battery3_y = y; + } + } + + else if (strcmp(cfg_name, "battery4_coords") == 0) { + int x,y; + if (sscanf(val, "%d,%d", &x, &y) == 2) { + THEME.battery4_x = x - (x % 4); + THEME.battery4_y = y; + } + } + + else if (strcmp(cfg_name, "clock_coords") == 0) { + short x,y; + if (sscanf(val, "%hd,%hd", &x, &y) == 2) { + THEME.clock_x = x - (x % 4); + THEME.clock_y = y; + } + } + + else if (strcmp(cfg_name, "sdcard_coords") == 0) { + short x,y; + if (sscanf(val, "%hd,%hd", &x, &y) == 2) { + THEME.sdcard_x = x - (x % 4); + THEME.sdcard_y = y; + } + } + + else if (strcmp(cfg_name, "info_color") == 0) { + short x,y,z; + if (sscanf(val, "%hd,%hd, %hd", &x, &y, &z) == 3) { + THEME.info_r = x; + THEME.info_g = y; + THEME.info_b = z; + } + } + + cfg_bool("show_id", &THEME.showID); + cfg_bool("show_tooltip", &THEME.showToolTip); + cfg_bool("show_hddinfo", &THEME.showHDD); + cfg_bool("show_gamecount", &THEME.showGameCnt); + cfg_bool("show_region", &THEME.showRegion); + cfg_bool("show_battery", &THEME.showBattery); + cfg_map_auto("hddinfo_align", map_alignment, &THEME.hddInfoAlign); + cfg_map_auto("gamecount_align", map_alignment, &THEME.gameCntAlign); + cfg_map_auto("clock_align", map_alignment, &THEME.clockAlign); + + /* + else if (strcmp(cfg_name, "entry_lines") == 0) { + int x; + if (sscanf(val, "%d", &x) == 1) { + ENTRIES_PER_PAGE = x; + } + } + + else if (strcmp(cfg_name, "max_characters") == 0) { + int x; + if (sscanf(val, "%d", &x) == 1) { + MAX_CHARACTERS = x; + } + }*/ +} + +void global_cfg_set(char *name, char *val) +{ + cfg_name = name; + cfg_val = val; + + if (strcmp(name, "video") == 0) { + int i; + if (sscanf(val, "%d", &i) == 1) { + Settings.video = i; + } + return; + } + else if (strcmp(name, "vpatch") == 0) { + int i; + if (sscanf(val, "%d", &i) == 1) { + Settings.vpatch =i; + } + return; + } + + else if (strcmp(name, "language") == 0) { + int i; + if (sscanf(val, "%d", &i) == 1) { + Settings.language = i; + } + return; + } + else if (strcmp(name, "ocarina") == 0) { + int i; + if (sscanf(val, "%d", &i) == 1) { + Settings.ocarina = i; + } + return; + } + else if (strcmp(name, "hddinfo") == 0) { + int i; + if (sscanf(val, "%d", &i) == 1) { + Settings.hddinfo = i; + } + return; + } + else if (strcmp(name, "sinfo") == 0) { + int i; + if (sscanf(val, "%d", &i) == 1) { + Settings.sinfo = i; + } + return; + } + else if (strcmp(name, "rumble") == 0) { + int i; + if (sscanf(val, "%d", &i) == 1) { + Settings.rumble = i; + } + return; + } + else if (strcmp(name, "volume") == 0) { + int i; + if (sscanf(val, "%d", &i) == 1) { + Settings.volume = i; + } + return; + } + else if (strcmp(name, "tooltips") == 0) { + int i; + if (sscanf(val, "%d", &i) == 1) { + Settings.tooltips = i; + } + return; + } + else if (strcmp(name, "password") == 0) { + strcopy(Settings.unlockCode, val, sizeof(Settings.unlockCode)); + return; + } + else if (strcmp(name, "parentalcontrol") == 0) { + int i; + if (sscanf(val, "%d", &i) == 1) { + Settings.parentalcontrol = i; + } + return; + } + else if (strcmp(name, "cios") == 0) { + int i; + if (sscanf(val, "%d", &i) == 1) { + Settings.cios = i; + } + return; + } +} + +// split line to part1 delimiter part2 +bool trimsplit(char *line, char *part1, char *part2, char delim, int size) +{ + char *eq = strchr(line, delim); + if (!eq) return false; + trim_n_copy(part1, line, eq-line, size); + trimcopy(part2, eq+1, size); + return true; +} +void cfg_parseline(char *line, void (*set_func)(char*, char*)) +{ + // split name = value + char tmp[200], name[100], val[100]; + strcopy(tmp, line, sizeof(tmp)); + char *eq = strchr(tmp, '='); + if (!eq) return; + *eq = 0; + trimcopy(name, tmp, sizeof(name)); + trimcopy(val, eq+1, sizeof(val)); + //printf("CFG: %s = %s\n", name, val); + set_func(name, val); +} + +void cfg_parsetitleline(char *line, void (*set_func)(char*, char*, u8)) +{ + // split name = value + char tmp[200], name[100], val[100]; + int block = 0; + strcopy(tmp, line, sizeof(tmp)); + char *eq = strchr(tmp, '='); + if (!eq) return; + *eq = 0; + trimcopy(name, tmp, sizeof(name)); + + char *blockpos = strrchr(eq+1, '='); + + if (!blockpos) + trimcopy(val, eq+1, sizeof(val)); + + else + { + *blockpos = 0; + trimcopy(val, eq+1, sizeof(val)); + if (sscanf(blockpos+1, "%d", &block) != 1) + { + block = 0; + } + } + set_func(name, val, block); +} + +bool cfg_parsefile(char *fname, void (*set_func)(char*, char*)) +{ + FILE *f; + char line[200]; + + //printf("opening(%s)\n", fname); + f = fopen(fname, "rb"); + if (!f) { + //printf("error opening(%s)\n", fname); + return false; + } + + while (fgets(line, sizeof(line), f)) { + // lines starting with # are comments + if (line[0] == '#') continue; + cfg_parseline(line, set_func); + } + return true; +} + +bool cfg_parsetitlefile(char *fname, void (*set_func)(char*, char*, u8)) +{ + FILE *f; + char line[200]; + + //printf("opening(%s)\n", fname); + f = fopen(fname, "rb"); + if (!f) { + //printf("error opening(%s)\n", fname); + return false; + } + + while (fgets(line, sizeof(line), f)) { + // lines starting with # are comments + if (line[0] == '#') continue; + cfg_parsetitleline(line, set_func); + } + return true; +} + +/* +void cfg_parsearg(int argc, char **argv) +{ + int i; + char *eq; + char pathname[200]; + for (i=1; ivideo = opt_v; + } + if (map_auto("language", opt_name, opt_val, map_language, &opt_l)) { + // valid option, assign + game->language = opt_l; + } + if (strcmp("ocarina", opt_name) == 0) { + if (sscanf(opt_val, "%hd", &opt_c) == 1) { + game->ocarina = opt_c; + } + } + if (strcmp("vipatch", opt_name) == 0) { + if (sscanf(opt_val, "%hd", &opt_c) == 1) { + game->vipatch = opt_c; + } + } + if (strcmp("ios", opt_name) == 0) { + if (sscanf(opt_val, "%hd", &opt_c) == 1) { + game->ios = opt_c; + } + } + if (strcmp("pctrl", opt_name) == 0) { + if (sscanf(opt_val, "%hd", &opt_c) == 1) { + game->parentalcontrol = opt_c; + } + } + } + // next opt + if (np) p = np + 1; else p = NULL; + } +} + +bool cfg_load_games() +{ + return cfg_parsefile("SD:/config/settings.cfg", &game_set); +} + +bool cfg_save_games() +{ + FILE *f; + int i; + mkdir("SD:/config/", 0777); + f = fopen("SD:/config/settings.cfg", "wb"); + if (!f) { + printf("Error saving %s\n", "settings.cfg"); + sleep(1); + return false; + } + fprintf(f, "# USB Loader settings file\n"); + fprintf(f, "# note: this file is automatically generated\n"); + fprintf(f, "# Num Games: %d\n", num_saved_games); + for (i=0; i +#include "disc.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define CFG_HOME_REBOOT 0 +#define CFG_HOME_EXIT 1 + +#define CFG_VIDEO_SYS 0 // system default +#define CFG_VIDEO_DEFAULT 1 +#define CFG_VIDEO_GAME 1 // game default +#define CFG_VIDEO_PATCH 2 // patch mode +#define CFG_VIDEO_PAL50 3 // force PAL +#define CFG_VIDEO_PAL60 4 // force PAL60 +#define CFG_VIDEO_NTSC 5 // force NTSC +#define CFG_VIDEO_COUNT 6 + +#define CFG_LANG_CONSOLE 0 +#define CFG_LANG_JAPANESE 1 +#define CFG_LANG_ENGLISH 2 +#define CFG_LANG_GERMAN 3 +#define CFG_LANG_FRENCH 4 +#define CFG_LANG_SPANISH 5 +#define CFG_LANG_ITALIAN 6 +#define CFG_LANG_DUTCH 7 +#define CFG_LANG_S_CHINESE 8 +#define CFG_LANG_T_CHINESE 9 +#define CFG_LANG_KOREAN 10 +#define CFG_LANG_COUNT 11 + +#define CFG_ALIGN_LEFT 0 +#define CFG_ALIGN_RIGHT 1 +#define CFG_ALIGN_CENTRE 2 +#define CFG_ALIGN_TOP 3 +#define CFG_ALIGN_BOTTOM 4 +#define CFG_ALIGN_MIDDLE 5 + +extern char *cfg_path; +//extern char *cfg_images_path; + +struct CFG +{ +// char *background; +// short covers; +// short simple; +// short video; +// short language; +// short ocarina; +// short vipatch; +// short home; +// short download; +// short installdownload; +// short hidesettingmenu; +// short savesettings; + short widescreen; + short parentalcontrol; + short maxcharacters; + short godmode; + char unlockCode[20]; + char covers_path[100]; + char theme_path[100]; + char disc_path[100]; +}; + +struct THEME +{ + int selection_x; + int selection_y; + int selection_w; + int selection_h; + int cover_x; + int cover_y; + short showID; + int id_x; + int id_y; + int region_x; + int region_y; + int power_x; + int power_y; + int home_x; + int home_y; + int battery1_x; + int battery2_x; + int battery3_x; + int battery4_x; + int battery1_y; + int battery2_y; + int battery3_y; + int battery4_y; +// short showPower; +// short showHome; + int setting_x; + int setting_y; + int install_x; + int install_y; + short showHDD; + short hddInfoAlign; + int hddInfo_x; + int hddInfo_y; + short showGameCnt; + short gameCntAlign; + int gameCnt_x; + int gameCnt_y; + short showRegion; + short showBattery; + short showToolTip; + //color + short info_r; + short info_g; + short info_b; + short clock_x; + short clock_y; + short clockAlign; + int sdcard_x; + int sdcard_y; +}; + +extern struct CFG CFG; +extern struct THEME THEME; +extern u8 ocarinaChoice; +extern u8 videoChoice; +extern u8 languageChoice; +extern u8 viChoice; +extern u8 iosChoice; +extern u8 parentalcontrolChoice; + +struct Game_CFG +{ + u8 id[8]; + u8 video; + u8 language; + u8 ocarina; + u8 vipatch; + u8 ios; + u8 parentalcontrol; +}; + + +void CFG_Default(); +void CFG_Load(int argc, char **argv); +struct Game_CFG* CFG_get_game_opt(u8 *id); +bool CFG_save_game_opt(u8 *id); +bool CFG_forget_game_opt(u8 *id); + +//Astidof - Begin of modification +enum { + ConsoleLangDefault, + jap, + eng, + ger, + fren, + esp, + it, + dut, + schin, + tchin, + kor +}; + +enum { + systemdefault, + discdefault, + patch, + pal50, + pal60, + ntsc +}; + +enum { + off, + on, +}; + +enum { + GameID, + GameRegion, + Both, + Neither, +}; + +enum { + i249, + i222, +}; + +enum { + ios249, + ios222, +}; + +enum { + HDDInfo, + Clock, +}; + +enum { + RumbleOff, + RumbleOn, +}; + +enum { + TooltipsOff, + TooltipsOn, +}; + +enum { + v10, + v20, + v30, + v40, + v50, + v60, + v70, + v80, + v90, + v100, + v0, +}; + +enum { + ParentalControlOff, + ParentalControlLevel1, + ParentalControlLevel2, + ParentalControlLevel3 +}; + +struct SSettings { + int video; + int language; + int ocarina; + int vpatch; + int sinfo; + int hddinfo; + int rumble; + int volume; + int tooltips; + char unlockCode[20]; + int parentalcontrol; + int cios; +}; + +void CFG_LoadGlobal(void); +bool cfg_save_global(void); +//Astidof - End of modification + +char *get_title(struct discHdr *header); +u8 get_block(struct discHdr *header); + +void CFG_Cleanup(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/disc.c b/source/disc.c new file mode 100644 index 00000000..19a48097 --- /dev/null +++ b/source/disc.c @@ -0,0 +1,310 @@ +#include +#include +#include +#include +#include +#include + + +#include "apploader.h" +#include "disc.h" +#include "video.h" +#include "wdvd.h" +#include "fst.h" + +/* Constants */ +#define PTABLE_OFFSET 0x40000 +#define WII_MAGIC 0x5D1C9EA3 + +/* Disc pointers */ +static u32 *buffer = (u32 *)0x93000000; +static u8 *diskid = (u8 *)0x80000000; +static char gameid[8]; + + +void __Disc_SetLowMem(void) +{ + /* Setup low memory */ + *(vu32 *)0x80000030 = 0x00000000; + *(vu32 *)0x80000060 = 0x38A00040; + *(vu32 *)0x800000E4 = 0x80431A80; + *(vu32 *)0x800000EC = 0x81800000; + *(vu32 *)0x800000F4 = 0x817E5480; + *(vu32 *)0x800000F8 = 0x0E7BE2C0; + *(vu32 *)0x800000FC = 0x2B73A840; + + /* Copy disc ID */ + memcpy((void *)0x80003180, (void *)0x80000000, 4); + + /* Flush cache */ + DCFlushRange((void *)0x80000000, 0x3F00); +} + +void __Disc_SetVMode(u8 videoselected) +{ + GXRModeObj *vmode = NULL; + + u32 progressive, tvmode, vmode_reg = 0; + + /* Get video mode configuration */ + progressive = (CONF_GetProgressiveScan() > 0) && VIDEO_HaveComponentCable(); + tvmode = CONF_GetVideo(); + + /* Select video mode register */ + switch (tvmode) { + case CONF_VIDEO_PAL: + vmode_reg = (CONF_GetEuRGB60() > 0) ? 5 : 1; + break; + + case CONF_VIDEO_MPAL: + vmode_reg = 4; + break; + + case CONF_VIDEO_NTSC: + vmode_reg = 0; + break; + } + + switch (videoselected) { + case 0: + + /* Select video mode */ + switch(diskid[3]) { + /* PAL */ + case 'D': + case 'F': + case 'P': + case 'X': + case 'Y': + if (tvmode != CONF_VIDEO_PAL) { + vmode_reg = 5; + vmode = (progressive) ? &TVNtsc480Prog : &TVEurgb60Hz480IntDf; + } + + break; + + /* NTSC or unknown */ + case 'E': + case 'J': + if (tvmode != CONF_VIDEO_NTSC) { + vmode_reg = 0; + vmode = (progressive) ? &TVNtsc480Prog : &TVNtsc480IntDf; + } + + break; + } + break; + + case 1: + vmode_reg = 1; + progressive = (CONF_GetProgressiveScan() > 0) && VIDEO_HaveComponentCable(); + vmode = (progressive) ? &TVEurgb60Hz480Prog : &TVPal528IntDf; + break; + case 2: + vmode_reg = 5; + progressive = (CONF_GetProgressiveScan() > 0) && VIDEO_HaveComponentCable(); + vmode = (progressive) ? &TVEurgb60Hz480Prog : &TVEurgb60Hz480IntDf; + break; + case 3: + vmode_reg = 0; + progressive = (CONF_GetProgressiveScan() > 0) && VIDEO_HaveComponentCable(); + vmode = (progressive) ? &TVNtsc480Prog : &TVNtsc480IntDf; + break; + case 4: + // vmode = VIDEO_GetPreferredMode(NULL); + break; + } + + /* Set video mode register */ + *(vu32 *)0x800000CC = vmode_reg; + + /* Set video mode */ + if (vmode) { + + VIDEO_Configure(vmode); + + /* Setup video */ + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + + if (vmode->viTVMode & VI_NON_INTERLACE) + VIDEO_WaitVSync(); + } +} + +void __Disc_SetTime(void) +{ + /* Extern */ + extern void settime(long long); + + /* Set proper time */ + settime(secs_to_ticks(time(NULL) - 946684800)); +} + +s32 __Disc_FindPartition(u64 *outbuf) +{ + u64 offset = 0, table_offset = 0; + + u32 cnt, nb_partitions; + s32 ret; + + /* Read partition info */ + ret = WDVD_UnencryptedRead(buffer, 0x20, PTABLE_OFFSET); + if (ret < 0) + return ret; + + /* Get data */ + nb_partitions = buffer[0]; + table_offset = buffer[1] << 2; + + /* Read partition table */ + ret = WDVD_UnencryptedRead(buffer, 0x20, table_offset); + if (ret < 0) + return ret; + + /* Find game partition */ + for (cnt = 0; cnt < nb_partitions; cnt++) { + u32 type = buffer[cnt * 2 + 1]; + + /* Game partition */ + if(!type) + offset = buffer[cnt * 2] << 2; + } + + /* No game partition found */ + if (!offset) + return -1; + + /* Set output buffer */ + *outbuf = offset; + + return 0; +} + + +s32 Disc_Init(void) +{ + /* Init DVD subsystem */ + return WDVD_Init(); +} + +s32 Disc_Open(void) +{ + s32 ret; + + /* Reset drive */ + ret = WDVD_Reset(); + if (ret < 0) + return ret; + + /* Read disc ID */ + return WDVD_ReadDiskId(diskid); +} + +s32 Disc_Wait(void) +{ + u32 cover = 0; + s32 ret; + + /* Wait for disc */ + while (!(cover & 0x2)) { + /* Get cover status */ + ret = WDVD_GetCoverStatus(&cover); + if (ret < 0) + return ret; + } + + return 0; +} + +s32 Disc_SetUSB(u8 *id, int ios222) +{ + /* Set USB mode */ + return WDVD_SetUSBMode(id, ios222); +} + +s32 Disc_ReadHeader(void *outbuf) +{ + /* Read disc header */ + return WDVD_UnencryptedRead(outbuf, sizeof(struct discHdr), 0); +} + +s32 Disc_IsWii(void) +{ + struct discHdr *header = (struct discHdr *)buffer; + + s32 ret; + + /* Read disc header */ + ret = Disc_ReadHeader(header); + if (ret < 0) + return ret; + + /* Check magic word */ + if (header->magic != WII_MAGIC) + return -1; + + return 0; +} + +s32 Disc_BootPartition(u64 offset, u8 videoselected, u8 cheat, u8 vipatch) +{ + entry_point p_entry; + + s32 ret; + + /* Open specified partition */ + ret = WDVD_OpenPartition(offset); + if (ret < 0) + return ret; + + /* Run apploader */ + ret = Apploader_Run(&p_entry, cheat, videoselected, vipatch); + if (ret < 0) + return ret; + + /* Setup low memory */ + __Disc_SetLowMem(); + + /* Set an appropiate video mode */ + __Disc_SetVMode(videoselected); + + /* Set time */ + __Disc_SetTime(); + + if (cheat == 1) { + /* OCARINA STUFF - FISHEARS*/ + memset(gameid, 0, 8); + memcpy(gameid, (char*)0x80000000, 6); + do_sd_code(gameid); + /* OCARINA STUFF - FISHEARS*/ + } + + /* Disconnect Wiimote */ + WPAD_Flush(0); + WPAD_Disconnect(0); + WPAD_Shutdown(); + + /* Shutdown IOS subsystems */ + SYS_ResetSystem(SYS_SHUTDOWN, 0, 0); + + /* Jump to entry point */ + p_entry(); + + return 0; +} + +s32 Disc_WiiBoot(u8 videoselected, u8 cheat, u8 vipatch) +{ + u64 offset; + s32 ret; + + /* Find game partition offset */ + ret = __Disc_FindPartition(&offset); + if (ret < 0) + return ret; + + /* Boot partition */ + return Disc_BootPartition(offset, videoselected, cheat, vipatch); +} diff --git a/source/disc.h b/source/disc.h new file mode 100644 index 00000000..f9f4aa2f --- /dev/null +++ b/source/disc.h @@ -0,0 +1,58 @@ +#ifndef _DISC_H_ +#define _DISC_H_ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif +/* Disc header structure */ +struct discHdr +{ + /* Game ID */ + u8 id[6]; + + /* Game version */ + u16 version; + + /* Audio streaming */ + u8 streaming; + u8 bufsize; + + /* Padding */ + u8 unused1[14]; + + /* Magic word */ + u32 magic; + + /* Padding */ + u8 unused2[4]; + + /* Game title */ + char title[64]; + + /* Encryption/Hashing */ + u8 encryption; + u8 h3_verify; + + /* Padding */ + u8 unused3[30]; +} ATTRIBUTE_PACKED; + +/* Prototypes */ +s32 Disc_Init(void); +s32 Disc_Open(void); +s32 Disc_Wait(void); +void __Disc_SetLowMem(void); +s32 Disc_SetUSB(u8 *, int ios222); +s32 Disc_ReadHeader(void *); +s32 Disc_IsWii(void); +s32 Disc_BootPartition(u64, u8, u8, u8); +s32 Disc_WiiBoot(u8, u8, u8); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/dns.c b/source/dns.c new file mode 100644 index 00000000..91fc1dea --- /dev/null +++ b/source/dns.c @@ -0,0 +1,122 @@ +#include "dns.h" + +/** + * Resolves a domainname to an ip address + * It makes use of net_gethostbyname from libogc, which in turn makes use of a Wii BIOS function + * Just like the net_gethostbyname function this function is NOT threadsafe! + * + * @param char* The domain name to resolve + * @return u32 The ipaddress represented by four bytes inside an u32 (in network order) + */ +u32 getipbyname(char *domain) +{ + //Care should be taken when using net_gethostbyname, + //it returns a static buffer which makes it not threadsafe + //TODO: implement some locking mechanism to make below code atomic + struct hostent *host = net_gethostbyname(domain); + + if(host == NULL) { + return 0; + } + + u32 *ip = (u32*)host->h_addr_list[0]; + return *ip; +} + + + +//Defines how many DNS entries should be cached by getipbynamecached() +#define MAX_DNS_CACHE_ENTRIES 20 + +//The cache is defined as a linked list, +//The last resolved domain name will always be at the front +//This will allow heavily used domainnames to always stay cached +struct dnsentry { + char *domain; + u32 ip; + struct dnsentry *nextnode; +} ; + +static struct dnsentry *firstdnsentry = NULL; +static int dnsentrycount = 0; + +/** + * Performs the same function as getipbyname(), + * except that it will prevent extremely expensive net_gethostbyname() calls by caching the result + */ +u32 getipbynamecached(char *domain) +{ + //Search if this domainname is already cached + struct dnsentry *node = firstdnsentry; + struct dnsentry *previousnode = NULL; + + while(node != NULL) + { + if(strcmp(node->domain, domain) == 0) + { + //DNS node found in the cache, move it to the front of the list + if(previousnode != NULL) + previousnode->nextnode = node->nextnode; + + if(node != firstdnsentry) + node->nextnode = firstdnsentry; + firstdnsentry = node; + + return node->ip; + } + //Go to the next element in the list + previousnode = node; + node = node->nextnode; + } + u32 ip = getipbyname(domain); + + //No cache of this domain could be found, create a cache node and add it to the front of the cache + struct dnsentry *newnode = malloc(sizeof(struct dnsentry)); + if(newnode == NULL) { + return ip; + } + + newnode->ip = ip; + newnode->domain = malloc(strlen(domain)+1); + if(newnode->domain == NULL) + { + free(newnode); + return ip; + } + strcpy(newnode->domain, domain); + + newnode->nextnode = firstdnsentry; + firstdnsentry = newnode; + dnsentrycount++; + + //If the cache grows too big delete the last (and probably least important) node of the list + if(dnsentrycount > MAX_DNS_CACHE_ENTRIES) + { + struct dnsentry *node = firstdnsentry; + struct dnsentry *previousnode = NULL; + + //Fetch the last two elements of the list + while(node->nextnode != NULL) + { + previousnode = node; + node = node->nextnode; + } + + if(node == NULL) + { + printf("Configuration error, MAX_DNS_ENTRIES reached while the list is empty\n"); + exit(1); + } else if(previousnode == NULL) + { + firstdnsentry = NULL; + } else { + previousnode->nextnode = NULL; + } + + free(node->domain); + free(node); + dnsentrycount--; + } + + return newnode->ip; +} diff --git a/source/dns.h b/source/dns.h new file mode 100644 index 00000000..28617246 --- /dev/null +++ b/source/dns.h @@ -0,0 +1,23 @@ +#ifndef _DNS_H_ +#define _DNS_H_ + +#include +#include +#include +#include + +#include //usleep + +#ifdef __cplusplus +extern "C" +{ +#endif + +u32 getipbyname(char *domain); +u32 getipbynamecached(char *domain); + +#ifdef __cplusplus +} +#endif + +#endif /* _DNS_H_ */ diff --git a/source/dvd_broadway.c b/source/dvd_broadway.c new file mode 100644 index 00000000..16e6a9e9 --- /dev/null +++ b/source/dvd_broadway.c @@ -0,0 +1,612 @@ +/* + * Copyright (C) 2008 Nuke (wiinuke@gmail.com) + * + * this file is part of GeckoOS for USB Gecko + * http://www.usbgecko.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dvd_broadway.h" + +#define DI_CMDCTX_CNT 4 + +#define DVD_DISKIDSIZE 0x20 +#define DVD_DRVINFSIZE 0x20 + +#define IOCTL_DI_INQUIRY 0x12 +#define IOCTL_DI_READID 0x70 +#define IOCTL_DI_READ 0x71 +#define IOCTL_DI_WAITCVRCLOSE 0x79 +#define IOCTL_DI_COVER 0x7A +#define IOCTL_DI_RESETNOTIFY 0x7E +#define IOCTL_DI_RESET 0x8A +#define IOCTL_DI_OPENPART 0x8B +#define IOCTL_DI_CLOSEPART 0x8C +#define IOCTL_DI_UNENCREAD 0x8D +#define IOCTL_DI_ENABLE_DVD 0x8E +#define IOCTL_DI_SEEK 0xAB +#define IOCTL_DI_READ_DVDVIDEO 0xD0 +#define IOCTL_DI_STOPLASER 0xD2 +#define IOCTL_DI_OFFSET 0xD9 +#define IOCTL_DI_REQERROR 0xE0 +#define IOCTL_DI_STOPMOTOR 0xE3 +#define IOCTL_DI_SETOFFBASE 0xF0 +#define IOCTL_DI_GETOFFBASE 0xF1 +#define IOCTL_DI_SETCRYPTMODE 0xF2 +#define IOCTL_DI_GETCRYPTMODE 0xF3 +#define IOCTL_DI_SETDVDROMMODE 0xF4 +#define IOCTL_DI_GETDVDROMMODE 0xF5 + +#define _SHIFTL(v, s, w) \ + ((u32) (((u32)(v) & ((0x01 << (w)) - 1)) << (s))) +#define _SHIFTR(v, s, w) \ + ((u32)(((u32)(v) >> (s)) & ((0x01 << (w)) - 1))) + +struct dicommand +{ + u32 diReg[8]; +}; + +struct dicontext +{ + lwp_node node; + dvdcallbacklow cb; + struct dicommand *cmd; +}; + +static s32 __dvd_fd = -1; +static u32 __dvd_spinupval = 1; +static lwp_queue __di_contextq; +static u32 __dvd_readlength = 0; +static u32 __dvd_cbinprogress = 0; +static u32 __dvd_reqinprogress = 0; +static u32 __dvd_lowinitcalled = 0; +static struct dicommand *__di_commands = NULL; +static struct dicontext __di_contexts[DI_CMDCTX_CNT]; +static u32 __di_regbuffer[0x08] ATTRIBUTE_ALIGN(32); +static u32 __di_regvalcache[0x08] ATTRIBUTE_ALIGN(32); +static u32 __di_lastticketerror[0x08] ATTRIBUTE_ALIGN(32); +static ioctlv __di_iovector[0x08] ATTRIBUTE_ALIGN(32); +static char __di_fs[] ATTRIBUTE_ALIGN(32) = "/dev/di"; + +extern u32 __IPC_ClntInit(); + +static __inline__ lwp_node* __lwp_queue_head(lwp_queue *queue) +{ + return (lwp_node*)queue; +} + +static __inline__ lwp_node* __lwp_queue_tail(lwp_queue *queue) +{ + return (lwp_node*)&queue->perm_null; +} + + +static __inline__ void __lwp_queue_init_empty(lwp_queue *queue) +{ + queue->first = __lwp_queue_tail(queue); + queue->perm_null = NULL; + queue->last = __lwp_queue_head(queue); +} + +static struct dicontext* __dvd_getcontext(dvdcallbacklow cb) +{ + struct dicontext *ctx; + + ctx = (struct dicontext*)__lwp_queue_get(&__di_contextq); + if(ctx!=NULL) ctx->cb = cb; + + return ctx; +} + +static s32 __dvd_iostransactionCB(s32 result,void *usrdata) +{ + struct dicontext *ctx = (struct dicontext*)usrdata; + + __dvd_reqinprogress = 0; + + if(ctx->cb!=NULL) { + __dvd_cbinprogress = 1; + if(result!=0) __dvd_readlength = 0; + ctx->cb(result); + __dvd_cbinprogress = 0; + } + __lwp_queue_append(&__di_contextq,&ctx->node); + + return 0; +} + +static s32 __dvd_ioscoverregisterCB(s32 result,void *usrdata) +{ + struct dicontext *ctx = (struct dicontext*)usrdata; + + __dvd_reqinprogress = 0; + __di_regvalcache[1] = __di_regbuffer[0]; + + if(ctx->cb!=NULL) { + __dvd_cbinprogress = 1; + ctx->cb(result); + __dvd_cbinprogress = 0; + } + __lwp_queue_append(&__di_contextq,&ctx->node); + + return 0; +} + +static s32 __dvd_ioscovercloseCB(s32 result,void *usrdata) +{ + struct dicontext *ctx = (struct dicontext*)usrdata; + + __dvd_reqinprogress = 0; + + if(ctx->cb!=NULL) { + __dvd_cbinprogress = 1; + ctx->cb(result); + __dvd_cbinprogress = 0; + } + __lwp_queue_append(&__di_contextq,&ctx->node); + + return 0; +} + +s32 bwDVD_LowInit() +{ + s32 i,ret = 0; + u32 ipclo,ipchi; + lwp_queue inactives; + struct dicontext *ctx; + + if(__dvd_lowinitcalled==0) { + ret = __IPC_ClntInit(); + if(ret<0) return ret; + + ipclo = (((u32)IPC_GetBufferLo()+0x1f)&~0x1f); + ipchi = (u32)IPC_GetBufferHi(); + if(ipchi>=(ipclo+(sizeof(struct dicommand)*DI_CMDCTX_CNT))) { + __di_commands = (struct dicommand*)ipclo; + IPC_SetBufferLo((void*)(ipclo+(sizeof(struct dicommand)*DI_CMDCTX_CNT))); + + memset(__di_commands,0,(sizeof(struct dicommand)*DI_CMDCTX_CNT)); + + i = 0; + __lwp_queue_init_empty(&__di_contextq); + __lwp_queue_initialize(&inactives,__di_contexts,DI_CMDCTX_CNT,sizeof(struct dicontext)); + while((ctx=(struct dicontext*)__lwp_queue_get(&inactives))!=NULL) { + ctx->cmd = &__di_commands[i]; + ctx->cb = NULL; + __lwp_queue_append(&__di_contextq,&ctx->node); + + i++; + } + } + + ret = IOS_Open(__di_fs,0); + if(ret<0) return ret; + + __dvd_fd = ret; +// __dvd_lowinitcalled = 1; + + // printf("DVD_LowInit(%d)\n",ret); + } + return 0; +} + +s32 bwDVD_LowInquiry(dvddrvinfo *info,dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_INQUIRY<<24); + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_INQUIRY,cmd->diReg,sizeof(struct dicommand),info,DVD_DRVINFSIZE,__dvd_iostransactionCB,ctx); + + return ret; +} + +s32 bwDVD_LowReadID(dvddiskid *diskID,dvdcallbacklow cb) +{ + s32 ret = 0; + struct dicontext *ctx; + struct dicommand *cmd; + +// printf("DVD_LowReadID()\n"); + + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_READID<<24); + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_READID,cmd->diReg,sizeof(struct dicommand),diskID,DVD_DISKIDSIZE,__dvd_iostransactionCB,ctx); + +// printf("DVD_LowReadID(%d)\n",ret); + return ret; +} + + +s32 bwDVD_LowRead(void *buf,u32 len,u32 offset,dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + if(buf==NULL || ((u32)buf%32)!=0) return -1; + + __dvd_reqinprogress = 1; + __dvd_readlength = len; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_READ<<24); + cmd->diReg[1] = len; + cmd->diReg[2] = offset; + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_READ,cmd->diReg,sizeof(struct dicommand),buf,len,__dvd_iostransactionCB,ctx); + + return ret; +} + +// never got this function working, probably removed from wii +s32 bwDVD_LowReadVideo(void *buf,u32 len,u32 offset,dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + __dvd_reqinprogress = 1; + __dvd_readlength = len; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_READ_DVDVIDEO<<24); + cmd->diReg[1] = len; + cmd->diReg[2] = offset; + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_READ_DVDVIDEO,cmd->diReg,sizeof(struct dicommand),buf,len,__dvd_iostransactionCB,ctx); + + return ret; +} + + + +s32 bwDVD_LowStopLaser(dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_STOPLASER<<24); + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_STOPLASER,cmd->diReg,sizeof(struct dicommand),__di_regvalcache,0x20,__dvd_iostransactionCB,ctx); + + return ret; +} + +// never got this function working, probably removed from wii +s32 bwDVD_EnableVideo(dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_ENABLE_DVD<<24); + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_ENABLE_DVD,cmd->diReg,sizeof(struct dicommand),__di_regvalcache,0x20,__dvd_iostransactionCB,ctx); + + return ret; +} + +s32 bwDVD_LowSeek(u32 offset,dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_SEEK<<24); + cmd->diReg[1] = offset; + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_SEEK,cmd->diReg,sizeof(struct dicommand),NULL,0,__dvd_iostransactionCB,ctx); + + return ret; +} + +s32 bwDVD_LowOffset(u64 offset,dvdcallbacklow cb) +{ + s32 ret; + u32 *off = (u32*)(void*)(&offset); + struct dicontext *ctx; + struct dicommand *cmd; + + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_OFFSET<<24); + cmd->diReg[1] = 0; + if(off[0]) cmd->diReg[1] = 1; + cmd->diReg[2] = off[1]; + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_OFFSET,cmd->diReg,sizeof(struct dicommand),__di_regvalcache,0x20,__dvd_iostransactionCB,ctx); + + return ret; +} + +s32 bwDVD_LowPrepareCoverRegister(dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_COVER<<24); + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_COVER,cmd->diReg,sizeof(struct dicommand),__di_regbuffer,0x20,__dvd_ioscoverregisterCB,ctx); + + return ret; +} + +s32 bwDVD_LowOpenPartition(u32 offset,void *eticket,u32 certin_len,void *certificate_in,void *certificate_out,dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + if(eticket!=NULL && ((u32)eticket%32)!=0) return -1; + if(certificate_in!=NULL && ((u32)certificate_in%32)!=0) return -1; + if(certificate_out!=NULL && ((u32)certificate_out%32)!=0) return -1; + + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_OPENPART<<24); + cmd->diReg[1] = offset; + + __di_iovector[0].data = cmd; + __di_iovector[0].len = sizeof(struct dicommand); + + __di_iovector[1].data = eticket; + if(eticket==NULL) __di_iovector[1].len = 0; + else __di_iovector[1].len = 676; + + __di_iovector[2].data = certificate_in; + if(certificate_in==NULL) __di_iovector[2].len = 0; + else __di_iovector[2].len = certin_len; + + __di_iovector[3].data = certificate_out; + __di_iovector[3].len = 18916; + __di_iovector[4].data = __di_lastticketerror; + __di_iovector[4].len = 0x20; + ret = IOS_IoctlvAsync(__dvd_fd,IOCTL_DI_OPENPART,3,2,__di_iovector,__dvd_iostransactionCB,ctx); + + return ret; +} + +s32 bwDVD_LowClosePartition(dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_CLOSEPART<<24); + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_CLOSEPART,cmd->diReg,sizeof(struct dicommand),NULL,0,__dvd_iostransactionCB,ctx); + + return ret; +} + +s32 bwDVD_LowUnencryptedRead(void *buf,u32 len,u32 offset,dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + __dvd_reqinprogress = 1; + __dvd_readlength = len; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_UNENCREAD<<24); + cmd->diReg[1] = len; + cmd->diReg[2] = offset; + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_UNENCREAD,cmd->diReg,sizeof(struct dicommand),buf,len,__dvd_iostransactionCB,ctx); + + return ret; +} + +s32 bwDVD_LowWaitCoverClose(dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_WAITCVRCLOSE<<24); + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_WAITCVRCLOSE,cmd->diReg,sizeof(struct dicommand),NULL,0,__dvd_ioscovercloseCB,ctx); + + return ret; +} + +s32 bwDVD_LowResetNotify() +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + if(__dvd_cbinprogress==1) return -1; + + ctx = __dvd_getcontext(NULL); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_RESETNOTIFY<<24); + ret = IOS_Ioctl(__dvd_fd,IOCTL_DI_RESETNOTIFY,cmd->diReg,sizeof(struct dicommand),NULL,0); + + return ret; +} + +s32 bwDVD_LowReset(dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + +// printf("DVD_LowReset()\n"); + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_RESET<<24); + cmd->diReg[1] = __dvd_spinupval; + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_RESET,cmd->diReg,sizeof(struct dicommand),NULL,0,__dvd_iostransactionCB,ctx); + +// printf("DVD_LowReset(%d)\n",ret); + return ret; +} + +s32 bwDVD_LowStopMotor(u8 stop1,u8 stop2,dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_STOPMOTOR<<24); + cmd->diReg[1] = (stop1<<24); + cmd->diReg[2] = (stop2<<24); + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_STOPMOTOR,cmd->diReg,sizeof(struct dicommand),__di_regvalcache,0x20,__dvd_iostransactionCB,ctx); + + return ret; + +} + +s32 bwDVD_LowRequestError(dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_REQERROR<<24); + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_REQERROR,cmd->diReg,sizeof(struct dicommand),__di_regvalcache,0x20,__dvd_iostransactionCB,ctx); + + return ret; +} + + +s32 bwDVD_SetDecryption(s32 mode, dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_SETCRYPTMODE<<24); + cmd->diReg[1] = mode; + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_SETCRYPTMODE,cmd->diReg,sizeof(struct dicommand),__di_regvalcache,0x20,__dvd_iostransactionCB,ctx); + + return ret; + +} + +s32 bwDVD_SetOffset(u32 offset, dvdcallbacklow cb) +{ + s32 ret; + struct dicontext *ctx; + struct dicommand *cmd; + + __dvd_reqinprogress = 1; + + ctx = __dvd_getcontext(cb); + if(ctx==NULL) return IPC_ENOMEM; + + cmd = ctx->cmd; + cmd->diReg[0] = (IOCTL_DI_SETOFFBASE<<24); + cmd->diReg[1] = offset; + ret = IOS_IoctlAsync(__dvd_fd,IOCTL_DI_SETOFFBASE,cmd->diReg,sizeof(struct dicommand),__di_regvalcache,0x20,__dvd_iostransactionCB,ctx); + + return ret; + +} diff --git a/source/dvd_broadway.h b/source/dvd_broadway.h new file mode 100644 index 00000000..c0ca133b --- /dev/null +++ b/source/dvd_broadway.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008 Nuke (wiinuke@gmail.com) + * + * this file is part of GeckoOS for USB Gecko + * http://www.usbgecko.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __DVD_BROADWAY_H__ +#define __DVD_BROADWAY_H__ + +#include +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef void (*dvdcallbacklow)(s32 result); + +s32 bwDVD_LowInit(); +s32 bwDVD_LowInquiry(dvddrvinfo *info,dvdcallbacklow cb); +s32 bwDVD_LowReadID(dvddiskid *diskID,dvdcallbacklow cb); +s32 bwDVD_LowClosePartition(dvdcallbacklow cb); +s32 bwDVD_LowOpenPartition(u32 offset,void *eticket,u32 certin_len,void *certificate_in,void *certificate_out,dvdcallbacklow cb); +s32 bwDVD_LowUnencryptedRead(void *buf,u32 len,u32 offset,dvdcallbacklow cb); +s32 bwDVD_LowReset(dvdcallbacklow cb); +s32 bwDVD_LowWaitCoverClose(dvdcallbacklow cb); +s32 bwDVD_LowRead(void *buf,u32 len,u32 offset,dvdcallbacklow cb); +s32 bwDVD_EnableVideo(dvdcallbacklow cb); +s32 bwDVD_LowReadVideo(void *buf,u32 len,u32 offset,dvdcallbacklow cb); +s32 bwDVD_SetDecryption(s32 mode, dvdcallbacklow cb); +s32 bwDVD_SetOffset(u32 offset, dvdcallbacklow cb); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/source/filelist.h b/source/filelist.h new file mode 100644 index 00000000..6c72c440 --- /dev/null +++ b/source/filelist.h @@ -0,0 +1,212 @@ +/**************************************************************************** + * libwiigui Template + * Tantric 2009 + * + * imagelist.h + * Contains a list of all of the files in the images, fonts, sounds folders + ***************************************************************************/ + +#ifndef _FILELIST_H_ +#define _FILELIST_H_ + +#include + +extern const u8 font_ttf[]; +extern const u32 font_ttf_size; + +extern const u8 sdcard_png[]; +extern const u32 sdcard_png_size; + +extern const u8 bg_music_ogg[]; +extern const u32 bg_music_ogg_size; + +extern const u8 credits_music_ogg[]; +extern const u32 credits_music_ogg_size; + +extern const u8 credits_button_png[]; +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_click_pcm[]; +extern const u32 button_click_pcm_size; + +extern const u8 button_click2_pcm[]; +extern const u32 button_click2_pcm_size; + +extern const u8 tooltip_png[]; +extern const u32 tooltip_png_size; + +extern const u8 startgame_arrow_left_png[]; +extern const u32 startgame_arrow_left_png_size; + +extern const u8 startgame_arrow_right_png[]; +extern const u32 startgame_arrow_right_png_size; + +extern const u8 tooltip_medium_png[]; +extern const u32 tooltip_medium_png_size; + +extern const u8 tooltip_large_png[]; +extern const u32 tooltip_large_png_size; + +extern const u8 credits_bg_png[]; +extern const u32 credits_bg_png_size; + +extern const u8 little_star_png[]; +extern const u32 little_star_png_size; + +extern const u8 background_png[]; +extern const u32 background_png_size; + +extern const u8 wbackground_png[]; +extern const u32 wbackground_png_size; + +extern const u8 bg_options_settings_png[]; +extern const u32 bg_options_settings_png_size; + +extern const u8 settings_background_png[]; +extern const u32 settings_background_png_size; + +extern const u8 nocover_png[]; +extern const u32 nocover_png_size; + +extern const u8 nodisc_png[]; +extern const u32 nodisc_png_size; + +extern const u8 button_install_png[]; +extern const u32 button_install_png_size; + +extern const u8 button_install_over_png[]; +extern const u32 button_install_over_png_size; + +extern const u8 dialogue_box_startgame_png[]; +extern const u32 dialogue_box_startgame_png_size; + +extern const u8 wdialogue_box_startgame_png[]; +extern const u32 wdialogue_box_startgame_png_size; + +extern const u8 button_dialogue_box_startgame_png[]; +extern const u32 button_dialogue_box_startgame_size; + +extern const u8 button_dialogue_box_png[]; +extern const u32 button_dialogue_box_size; + +extern const u8 keyboard_textbox_png[]; +extern const u32 keyboard_textbox_png_size; + +extern const u8 keyboard_key_png[]; +extern const u32 keyboard_key_png_size; + +extern const u8 keyboard_key_over_png[]; +extern const u32 keyboard_key_over_png_size; + +extern const u8 keyboard_mediumkey_png[]; +extern const u32 keyboard_mediumkey_png_size; + +extern const u8 keyboard_mediumkey_over_png[]; +extern const u32 keyboard_mediumkey_over_png_size; + +extern const u8 keyboard_largekey_png[]; +extern const u32 keyboard_largekey_png_size; + +extern const u8 keyboard_largekey_over_png[]; +extern const u32 keyboard_largekey_over_png_size; + +extern const u8 menu_button_png[]; +extern const u32 menu_button_size; + +extern const u8 menu_button_over_png[]; +extern const u32 menu_button_over_size; + +extern const u8 settings_button_png[]; +extern const u32 settings_button_size; + +extern const u8 settings_button_over_png[]; +extern const u32 settings_button_over_size; + +extern const u8 settings_menu_button_png[]; +extern const u32 settings_menu_button_size; + +extern const u8 wiimote_poweroff_png[]; +extern const u32 wiimote_poweroff_png_size; + +extern const u8 dialogue_box_png[]; +extern const u32 dialogue_box_png_size; + +extern const u8 wiimote_poweroff_over_png[]; +extern const u32 wiimote_poweroff_over_png_size; + +extern const u8 bg_options_png[]; +extern const u32 bg_options_png_size; + +extern const u8 bg_options_entry_png[]; +extern const u32 bg_options_entry_png_size; + +extern const u8 scrollbar_png[]; +extern const u32 scrollbar_png_size; + +extern const u8 scrollbar_arrowup_png[]; +extern const u32 scrollbar_arrowup_png_size; + +extern const u8 scrollbar_arrowup_over_png[]; +extern const u32 scrollbar_arrowup_over_png_size; + +extern const u8 scrollbar_arrowdown_png[]; +extern const u32 scrollbar_arrowdown_png_size; + +extern const u8 scrollbar_arrowdown_over_png[]; +extern const u32 scrollbar_arrowdown_over_png_size; + +extern const u8 scrollbar_box_png[]; +extern const u32 scrollbar_box_png_size; + +extern const u8 scrollbar_box_over_png[]; +extern const u32 scrollbar_box_over_png_size; + +extern const u8 progressbar_png[]; +extern const u32 progressbar_png_size; + +extern const u8 progressbar_empty_png[]; +extern const u32 progressbar_empty_png_size; + +extern const u8 progressbar_outline_png[]; +extern const u32 progressbar_outline_png_size; + +extern const u8 player1_point_png[]; +extern const u32 player1_point_png_size; + +extern const u8 player2_point_png[]; +extern const u32 player2_point_png_size; + +extern const u8 player3_point_png[]; +extern const u32 player3_point_png_size; + +extern const u8 player4_point_png[]; +extern const u32 player4_point_png_size; + +extern const u8 player1_grab_png[]; +extern const u32 player1_grab_png_size; + +extern const u8 player2_grab_png[]; +extern const u32 player2_grab_png_size; + +extern const u8 player3_grab_png[]; +extern const u32 player3_grab_png_size; + +extern const u8 player4_grab_png[]; +extern const u32 player4_grab_png_size; + +extern const u8 battery_bar_png[]; +extern const u32 battery_bar_png_size; + +extern const u8 battery_red_png[]; +extern const u32 battery_red_png_size; + +extern const u8 battery_png[]; +extern const u32 battery_png_size; + +#endif diff --git a/source/fonts/font.ttf b/source/fonts/font.ttf new file mode 100644 index 00000000..d4c461ca Binary files /dev/null and b/source/fonts/font.ttf differ diff --git a/source/fst.c b/source/fst.c new file mode 100644 index 00000000..d73235b7 --- /dev/null +++ b/source/fst.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2008 Nuke (wiinuke@gmail.com) + * + * this file is part of GeckoOS for USB Gecko + * http://www.usbgecko.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ogc/ipc.h" +#include "fst.h" +#include "dvd_broadway.h" +#include "wpad.h" + + +#define FSTDIRTYPE 1 +#define FSTFILETYPE 0 +#define ENTRYSIZE 0xC +#define FILEDIR "SD:/codes" + +#define MAX_FILENAME_LEN 128 + + +static vu32 dvddone = 0; + + +// Real basic +u32 do_sd_code(char *filename) +{ + FILE *fp; + u8 *filebuff; + u32 filesize; + u32 ret; + char filepath[128]; + + __io_wiisd.startup(); + ret = fatMountSimple("SD", &__io_wiisd); + + if (!ret) { + return 0; + } + + fflush(stdout); + + sprintf(filepath, FILEDIR "/%s", filename); + filepath[16] = 0x2E; + filepath[17] = 0x67; + filepath[18] = 0x63; + filepath[19] = 0x74; + filepath[20] = 0; + //printf("filename %s\n",filepath); + + fp = fopen(filepath, "rb"); + if (!fp) { + fatUnmount("SD"); + __io_wiisd.shutdown(); + return 0; + } + + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + filebuff = (u8*) malloc (filesize); + if(filebuff == 0){ + fclose(fp); + sleep(2); + return 0; + } + + ret = fread(filebuff, 1, filesize, fp); + if(ret != filesize){ + free(filebuff); + fclose(fp); + fatUnmount("SD"); + __io_wiisd.shutdown(); + return 0; + } + + memcpy((void*)0x800027E8,filebuff,filesize); + *(vu8*)0x80001807 = 0x01; + + + + free(filebuff); + fclose(fp); + + fatUnmount("SD"); + __io_wiisd.shutdown(); + + return 1; +} + + diff --git a/source/fst.h b/source/fst.h new file mode 100644 index 00000000..e61d4b18 --- /dev/null +++ b/source/fst.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2008 Nuke (wiinuke@gmail.com) + * + * this file is part of GeckoOS for USB Gecko + * http://www.usbgecko.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __FST_H__ +#define __FST_H__ + +//u32 do_fst(u32 fstlocation); +u32 do_sd_code(char *filename); +#endif diff --git a/source/fwrite_patch.h b/source/fwrite_patch.h new file mode 100644 index 00000000..54fedcc4 --- /dev/null +++ b/source/fwrite_patch.h @@ -0,0 +1,15 @@ +unsigned char fwrite_patch_bin[] = { + 0x7c, 0x84, 0x29, 0xd6, 0x39, 0x40, 0x00, 0x00, 0x94, 0x21, 0xff, 0xf0, + 0x93, 0xe1, 0x00, 0x0c, 0x7f, 0x8a, 0x20, 0x00, 0x40, 0x9c, 0x00, 0x64, + 0x3d, 0x00, 0xcd, 0x00, 0x3d, 0x60, 0xcd, 0x00, 0x3d, 0x20, 0xcd, 0x00, + 0x61, 0x08, 0x68, 0x14, 0x61, 0x6b, 0x68, 0x24, 0x61, 0x29, 0x68, 0x20, + 0x39, 0x80, 0x00, 0xd0, 0x38, 0xc0, 0x00, 0x19, 0x38, 0xe0, 0x00, 0x00, + 0x91, 0x88, 0x00, 0x00, 0x7c, 0x03, 0x50, 0xae, 0x54, 0x00, 0xa0, 0x16, + 0x64, 0x00, 0xb0, 0x00, 0x90, 0x0b, 0x00, 0x00, 0x90, 0xc9, 0x00, 0x00, + 0x80, 0x09, 0x00, 0x00, 0x70, 0x1f, 0x00, 0x01, 0x40, 0x82, 0xff, 0xf8, + 0x80, 0x0b, 0x00, 0x00, 0x90, 0xe8, 0x00, 0x00, 0x54, 0x00, 0x37, 0xfe, + 0x7d, 0x4a, 0x02, 0x14, 0x7f, 0x8a, 0x20, 0x00, 0x41, 0x9c, 0xff, 0xc8, + 0x7c, 0xa3, 0x2b, 0x78, 0x83, 0xe1, 0x00, 0x0c, 0x38, 0x21, 0x00, 0x10, + 0x4e, 0x80, 0x00, 0x20 +}; +unsigned int fwrite_patch_bin_len = 136; diff --git a/source/geckomenu.h b/source/geckomenu.h new file mode 100644 index 00000000..458e6288 --- /dev/null +++ b/source/geckomenu.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2008 Nuke (wiinuke@gmail.com) + * + * this file is part of GeckoOS for USB Gecko + * http://www.usbgecko.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __GECKOMENU_H__ +#define __GECKOMENU_H__ + + +#define ROOTMENU 0 +#define ABOUTMENU 1 +#define CONFIGMENU 2 +#define REBOOTMENU 3 +#define root_itemcount 7 +#define about_itemcount 4 +#define config_itemcount 9 +#define rebooter_itemcount 6 + +u32 currentmenu; // 0 ROOT +u32 rootmenu_item; +u32 menufreeze; +u32 langselect; +u32 langsaved; +u32 pal60select; +u32 pal50select; +u32 viselect; +u32 ntscselect; +u32 hookselect; +u32 ocarinaselect; +u32 recoveryselect; +u32 regionfreeselect; +u32 nocopyselect; +u32 buttonskipselect; + +u32 doprogress(u32 progstate, u32 noelements); +void drawmenu(u32 menuid); +void drawselected(u32 menuidpos); +void processwpad(); +void clearscreen(u32 *framebuffer, u16 xscreen, u16 yscreen, u16 width, u16 height, u32 color); +void drawicon(u32 *framebuffer, u16 xscreen, u16 yscreen, u16 width, u16 height, u32 gicon); +u32 CvtRGB (u8 r1, u8 g1, u8 b1, u8 r2, u8 g2, u8 b2); + + +#endif // __GECKOLOAD_H__ diff --git a/source/http.c b/source/http.c new file mode 100644 index 00000000..c187e1af --- /dev/null +++ b/source/http.c @@ -0,0 +1,234 @@ +#include "http.h" + +/** + * Emptyblock is a statically defined variable for functions to return if they are unable + * to complete a request + */ +const struct block emptyblock = {0, NULL}; + +//The maximum amount of bytes to send per net_write() call +#define NET_BUFFER_SIZE 1024 + +// Write our message to the server +static s32 send_message(s32 server, char *msg) { + s32 bytes_transferred = 0; + s32 remaining = strlen(msg); + while (remaining) { + if ((bytes_transferred = net_write(server, msg, remaining > NET_BUFFER_SIZE ? NET_BUFFER_SIZE : remaining)) > 0) { + remaining -= bytes_transferred; + usleep (20 * 1000); + } else if (bytes_transferred < 0) { + return bytes_transferred; + } else { + return -ENODATA; + } + } + return 0; +} + +/** + * Connect to a remote server via TCP on a specified port + * + * @param u32 ip address of the server to connect to + * @param u32 the port to connect to on the server + * @return s32 The connection to the server (negative number if connection could not be established) + */ +static s32 server_connect(u32 ipaddress, u32 socket_port) { + //Initialize socket + s32 connection = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + if (connection < 0) return connection; + + struct sockaddr_in connect_addr; + memset(&connect_addr, 0, sizeof(connect_addr)); + connect_addr.sin_family = AF_INET; + connect_addr.sin_port = socket_port; + connect_addr.sin_addr.s_addr= ipaddress; + + //Attemt to open the socket + if (net_connect(connection, (struct sockaddr*)&connect_addr, sizeof(connect_addr)) == -1) { + net_close(connection); + return -1; + } + return connection; +} + +//The amount of memory in bytes reserved initially to store the HTTP response in +//Be careful in increasing this number, reading from a socket on the Wii +//will fail if you request more than 20k or so +#define HTTP_BUFFER_SIZE 1024 * 5 + +//The amount of memory the buffer should expanded with if the buffer is full +#define HTTP_BUFFER_GROWTH 1024 * 5 + +/** + * This function reads all the data from a connection into a buffer which it returns. + * It will return an empty buffer if something doesn't go as planned + * + * @param s32 connection The connection identifier to suck the response out of + * @return block A 'block' struct (see http.h) in which the buffer is located + */ +struct block read_message(s32 connection) +{ + //Create a block of memory to put in the response + struct block buffer; + buffer.data = malloc(HTTP_BUFFER_SIZE); + buffer.size = HTTP_BUFFER_SIZE; + + if(buffer.data == NULL) { + return emptyblock; + } + + //The offset variable always points to the first byte of memory that is free in the buffer + u32 offset = 0; + + while(1) + { + //Fill the buffer with a new batch of bytes from the connection, + //starting from where we left of in the buffer till the end of the buffer + s32 bytes_read = net_read(connection, buffer.data + offset, buffer.size - offset); + + //Anything below 0 is an error in the connection + if(bytes_read < 0) + { + //printf("Connection error from net_read() Errorcode: %i\n", bytes_read); + return emptyblock; + } + + //No more bytes were read into the buffer, + //we assume this means the HTTP response is done + if(bytes_read == 0) + { + break; + } + + offset += bytes_read; + + //Check if we have enough buffer left over, + //if not expand it with an additional HTTP_BUFFER_GROWTH worth of bytes + if(offset >= buffer.size) + { + buffer.size += HTTP_BUFFER_GROWTH; + buffer.data = realloc(buffer.data, buffer.size); + + if(buffer.data == NULL) + { + return emptyblock; + } + } + } + + //At the end of above loop offset should be precisely the amount of bytes that were read from the connection + buffer.size = offset; + + //Shrink the size of the buffer so the data fits exactly in it + realloc(buffer.data, buffer.size); + + return buffer; +} + +/** + * Downloads the contents of a URL to memory + * This method is not threadsafe (because networking is not threadsafe on the Wii) + */ +struct block downloadfile(const char *url) +{ + //Check if the url starts with "http://", if not it is not considered a valid url + if(strncmp(url, "http://", strlen("http://")) != 0) + { + //printf("URL '%s' doesn't start with 'http://'\n", url); + return emptyblock; + } + + //Locate the path part of the url by searching for '/' past "http://" + char *path = strchr(url + strlen("http://"), '/'); + + //At the very least the url has to end with '/', ending with just a domain is invalid + if(path == NULL) + { + //printf("URL '%s' has no PATH part\n", url); + return emptyblock; + } + + //Extract the domain part out of the url + int domainlength = path - url - strlen("http://"); + + if(domainlength == 0) + { + //printf("No domain part in URL '%s'\n", url); + return emptyblock; + } + + char domain[domainlength + 1]; + strncpy(domain, url + strlen("http://"), domainlength); + domain[domainlength] = '\0'; + + //Parsing of the URL is done, start making an actual connection + u32 ipaddress = getipbynamecached(domain); + + if(ipaddress == 0) + { + //printf("\ndomain %s could not be resolved", domain); + return emptyblock; + } + + + s32 connection = server_connect(ipaddress, 80); + + if(connection < 0) { + //printf("Error establishing connection"); + return emptyblock; + } + + //Form a nice request header to send to the webserver + char* headerformat = "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: WiiEarthh 1.0\r\n\r\n";; + char header[strlen(headerformat) + strlen(domain) + strlen(path)]; + sprintf(header, headerformat, path, domain); + + //Do the request and get the response + send_message(connection, header); + struct block response = read_message(connection); + net_close(connection); + + //Search for the 4-character sequence \r\n\r\n in the response which signals the start of the http payload (file) + unsigned char *filestart = NULL; + u32 filesize = 0; + int i; + for(i = 3; i < response.size; i++) + { + if(response.data[i] == '\n' && + response.data[i-1] == '\r' && + response.data[i-2] == '\n' && + response.data[i-3] == '\r') + { + filestart = response.data + i + 1; + filesize = response.size - i - 1; + break; + } + } + + if(filestart == NULL) + { + //printf("HTTP Response was without a file\n"); + free(response.data); + return emptyblock; + } + + //Copy the file part of the response into a new memoryblock to return + struct block file; + file.data = malloc(filesize); + file.size = filesize; + + if(file.data == NULL) + { + //printf("No more memory to copy file from HTTP response\n"); + free(response.data); + return emptyblock; + } + + memcpy(file.data, filestart, filesize); + + //Dispose of the original response + free(response.data); + + return file; +} diff --git a/source/http.h b/source/http.h new file mode 100644 index 00000000..0698f0ce --- /dev/null +++ b/source/http.h @@ -0,0 +1,38 @@ +#ifndef _HTTP_H_ +#define _HTTP_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "dns.h" + +/** + * A simple structure to keep track of the size of a malloc()ated block of memory + */ +struct block +{ + u32 size; + unsigned char *data; +}; + +extern const struct block emptyblock; + +struct block downloadfile(const char *url); + +#ifdef __cplusplus +} +#endif + +#endif /* _HTTP_H_ */ diff --git a/source/images/background.png b/source/images/background.png new file mode 100644 index 00000000..e3b6babe Binary files /dev/null and b/source/images/background.png differ diff --git a/source/images/battery.png b/source/images/battery.png new file mode 100644 index 00000000..9a4c9afb Binary files /dev/null and b/source/images/battery.png differ diff --git a/source/images/battery_bar.png b/source/images/battery_bar.png new file mode 100644 index 00000000..3aff7411 Binary files /dev/null and b/source/images/battery_bar.png differ diff --git a/source/images/battery_red.png b/source/images/battery_red.png new file mode 100644 index 00000000..b55dc655 Binary files /dev/null and b/source/images/battery_red.png differ diff --git a/source/images/bg_options.png b/source/images/bg_options.png new file mode 100644 index 00000000..5b694e0a Binary files /dev/null and b/source/images/bg_options.png differ diff --git a/source/images/bg_options_entry.png b/source/images/bg_options_entry.png new file mode 100644 index 00000000..13e87188 Binary files /dev/null and b/source/images/bg_options_entry.png differ diff --git a/source/images/bg_options_settings.png b/source/images/bg_options_settings.png new file mode 100644 index 00000000..55cd5ed3 Binary files /dev/null and b/source/images/bg_options_settings.png differ diff --git a/source/images/button_dialogue_box.png b/source/images/button_dialogue_box.png new file mode 100644 index 00000000..a7523b83 Binary files /dev/null and b/source/images/button_dialogue_box.png differ diff --git a/source/images/button_install.png b/source/images/button_install.png new file mode 100644 index 00000000..178c2796 Binary files /dev/null and b/source/images/button_install.png differ diff --git a/source/images/button_install_over.png b/source/images/button_install_over.png new file mode 100644 index 00000000..d8ad727b Binary files /dev/null and b/source/images/button_install_over.png differ diff --git a/source/images/credits_bg.png b/source/images/credits_bg.png new file mode 100644 index 00000000..54d797dd Binary files /dev/null and b/source/images/credits_bg.png differ diff --git a/source/images/credits_button.png b/source/images/credits_button.png new file mode 100644 index 00000000..87777317 Binary files /dev/null and b/source/images/credits_button.png differ diff --git a/source/images/credits_button_over.png b/source/images/credits_button_over.png new file mode 100644 index 00000000..54e83dbc Binary files /dev/null and b/source/images/credits_button_over.png differ diff --git a/source/images/dialogue_box.png b/source/images/dialogue_box.png new file mode 100644 index 00000000..d16b96cc Binary files /dev/null and b/source/images/dialogue_box.png differ diff --git a/source/images/dialogue_box_install.png b/source/images/dialogue_box_install.png new file mode 100644 index 00000000..0bfaa26e Binary files /dev/null and b/source/images/dialogue_box_install.png differ diff --git a/source/images/dialogue_box_startgame.png b/source/images/dialogue_box_startgame.png new file mode 100644 index 00000000..cd491ffe Binary files /dev/null and b/source/images/dialogue_box_startgame.png differ diff --git a/source/images/keyboard_key.png b/source/images/keyboard_key.png new file mode 100644 index 00000000..d98e89f1 Binary files /dev/null and b/source/images/keyboard_key.png differ diff --git a/source/images/keyboard_key_over.png b/source/images/keyboard_key_over.png new file mode 100644 index 00000000..1c901a2e Binary files /dev/null and b/source/images/keyboard_key_over.png differ diff --git a/source/images/keyboard_largekey.png b/source/images/keyboard_largekey.png new file mode 100644 index 00000000..97590457 Binary files /dev/null and b/source/images/keyboard_largekey.png differ diff --git a/source/images/keyboard_largekey_over.png b/source/images/keyboard_largekey_over.png new file mode 100644 index 00000000..c61c8a61 Binary files /dev/null and b/source/images/keyboard_largekey_over.png differ diff --git a/source/images/keyboard_mediumkey.png b/source/images/keyboard_mediumkey.png new file mode 100644 index 00000000..286fca98 Binary files /dev/null and b/source/images/keyboard_mediumkey.png differ diff --git a/source/images/keyboard_mediumkey_over.png b/source/images/keyboard_mediumkey_over.png new file mode 100644 index 00000000..4703e936 Binary files /dev/null and b/source/images/keyboard_mediumkey_over.png differ diff --git a/source/images/keyboard_textbox.png b/source/images/keyboard_textbox.png new file mode 100644 index 00000000..ad0d08aa Binary files /dev/null and b/source/images/keyboard_textbox.png differ diff --git a/source/images/little_star.png b/source/images/little_star.png new file mode 100644 index 00000000..de0da22c Binary files /dev/null and b/source/images/little_star.png differ diff --git a/source/images/menu_button.png b/source/images/menu_button.png new file mode 100644 index 00000000..56ffd1dd Binary files /dev/null and b/source/images/menu_button.png differ diff --git a/source/images/menu_button_over.png b/source/images/menu_button_over.png new file mode 100644 index 00000000..ee6c5e31 Binary files /dev/null and b/source/images/menu_button_over.png differ diff --git a/source/images/nocover.png b/source/images/nocover.png new file mode 100644 index 00000000..3e96c5e8 Binary files /dev/null and b/source/images/nocover.png differ diff --git a/source/images/nodisc.png b/source/images/nodisc.png new file mode 100644 index 00000000..d5131789 Binary files /dev/null and b/source/images/nodisc.png differ diff --git a/source/images/player1_grab.png b/source/images/player1_grab.png new file mode 100644 index 00000000..6eb1f314 Binary files /dev/null and b/source/images/player1_grab.png differ diff --git a/source/images/player1_point.png b/source/images/player1_point.png new file mode 100644 index 00000000..c06c08c7 Binary files /dev/null and b/source/images/player1_point.png differ diff --git a/source/images/player2_grab.png b/source/images/player2_grab.png new file mode 100644 index 00000000..1de198f5 Binary files /dev/null and b/source/images/player2_grab.png differ diff --git a/source/images/player2_point.png b/source/images/player2_point.png new file mode 100644 index 00000000..6e952d24 Binary files /dev/null and b/source/images/player2_point.png differ diff --git a/source/images/player3_grab.png b/source/images/player3_grab.png new file mode 100644 index 00000000..52253d14 Binary files /dev/null and b/source/images/player3_grab.png differ diff --git a/source/images/player3_point.png b/source/images/player3_point.png new file mode 100644 index 00000000..704f84e0 Binary files /dev/null and b/source/images/player3_point.png differ diff --git a/source/images/player4_grab.png b/source/images/player4_grab.png new file mode 100644 index 00000000..1e4fdc3d Binary files /dev/null and b/source/images/player4_grab.png differ diff --git a/source/images/player4_point.png b/source/images/player4_point.png new file mode 100644 index 00000000..dd8f1ad3 Binary files /dev/null and b/source/images/player4_point.png differ diff --git a/source/images/progressbar.png b/source/images/progressbar.png new file mode 100644 index 00000000..747f6a4d Binary files /dev/null and b/source/images/progressbar.png differ diff --git a/source/images/progressbar_empty.png b/source/images/progressbar_empty.png new file mode 100644 index 00000000..e72f0c43 Binary files /dev/null and b/source/images/progressbar_empty.png differ diff --git a/source/images/progressbar_outline.png b/source/images/progressbar_outline.png new file mode 100644 index 00000000..6b665457 Binary files /dev/null and b/source/images/progressbar_outline.png differ diff --git a/source/images/scrollbar.png b/source/images/scrollbar.png new file mode 100644 index 00000000..032074db Binary files /dev/null and b/source/images/scrollbar.png differ diff --git a/source/images/scrollbar_arrowdown.png b/source/images/scrollbar_arrowdown.png new file mode 100644 index 00000000..ebd748c6 Binary files /dev/null and b/source/images/scrollbar_arrowdown.png differ diff --git a/source/images/scrollbar_arrowup.png b/source/images/scrollbar_arrowup.png new file mode 100644 index 00000000..6cef2fa0 Binary files /dev/null and b/source/images/scrollbar_arrowup.png differ diff --git a/source/images/scrollbar_box.png b/source/images/scrollbar_box.png new file mode 100644 index 00000000..9b568f3e Binary files /dev/null and b/source/images/scrollbar_box.png differ diff --git a/source/images/sdcard.png b/source/images/sdcard.png new file mode 100644 index 00000000..0911f548 Binary files /dev/null and b/source/images/sdcard.png differ diff --git a/source/images/settings_background.png b/source/images/settings_background.png new file mode 100644 index 00000000..7378501d Binary files /dev/null and b/source/images/settings_background.png differ diff --git a/source/images/settings_button.png b/source/images/settings_button.png new file mode 100644 index 00000000..a87dea3a Binary files /dev/null and b/source/images/settings_button.png differ diff --git a/source/images/settings_button_over.png b/source/images/settings_button_over.png new file mode 100644 index 00000000..f29a269f Binary files /dev/null and b/source/images/settings_button_over.png differ diff --git a/source/images/settings_menu_button.png b/source/images/settings_menu_button.png new file mode 100644 index 00000000..4a782e42 Binary files /dev/null and b/source/images/settings_menu_button.png differ diff --git a/source/images/startgame_arrow_left.png b/source/images/startgame_arrow_left.png new file mode 100644 index 00000000..1df71ebc Binary files /dev/null and b/source/images/startgame_arrow_left.png differ diff --git a/source/images/startgame_arrow_right.png b/source/images/startgame_arrow_right.png new file mode 100644 index 00000000..51637bf6 Binary files /dev/null and b/source/images/startgame_arrow_right.png differ diff --git a/source/images/tooltip.png b/source/images/tooltip.png new file mode 100644 index 00000000..8bd8cf13 Binary files /dev/null and b/source/images/tooltip.png differ diff --git a/source/images/tooltip_large.png b/source/images/tooltip_large.png new file mode 100644 index 00000000..a6ca2822 Binary files /dev/null and b/source/images/tooltip_large.png differ diff --git a/source/images/tooltip_medium.png b/source/images/tooltip_medium.png new file mode 100644 index 00000000..f663779f Binary files /dev/null and b/source/images/tooltip_medium.png differ diff --git a/source/images/wbackground.png b/source/images/wbackground.png new file mode 100644 index 00000000..57c0a2ce Binary files /dev/null and b/source/images/wbackground.png differ diff --git a/source/images/wdialogue_box_startgame.png b/source/images/wdialogue_box_startgame.png new file mode 100644 index 00000000..318bf0be Binary files /dev/null and b/source/images/wdialogue_box_startgame.png differ diff --git a/source/images/wiimote_poweroff.png b/source/images/wiimote_poweroff.png new file mode 100644 index 00000000..d56959eb Binary files /dev/null and b/source/images/wiimote_poweroff.png differ diff --git a/source/images/wiimote_poweroff_over.png b/source/images/wiimote_poweroff_over.png new file mode 100644 index 00000000..dce26baf Binary files /dev/null and b/source/images/wiimote_poweroff_over.png differ diff --git a/source/input.cpp b/source/input.cpp new file mode 100644 index 00000000..a4ef1460 --- /dev/null +++ b/source/input.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** + * libwiigui Template + * Tantric 2009 + * + * input.cpp + * Wii/GameCube controller management + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "menu.h" +#include "video.h" +#include "input.h" +#include "libwiigui/gui.h" + +int rumbleRequest[4] = {0,0,0,0}; +GuiTrigger userInput[4]; +static int rumbleCount[4] = {0,0,0,0}; + +/**************************************************************************** + * ShutoffRumble + ***************************************************************************/ + +void ShutoffRumble() +{ + for(int i=0;i<4;i++) + { + WPAD_Rumble(i, 0); + rumbleCount[i] = 0; + } +} + +/**************************************************************************** + * DoRumble + ***************************************************************************/ + +void DoRumble(int i) +{ + if(rumbleRequest[i] && rumbleCount[i] < 3) + { + WPAD_Rumble(i, 1); // rumble on + rumbleCount[i]++; + } + else if(rumbleRequest[i]) + { + rumbleCount[i] = 20; + rumbleRequest[i] = 0; + } + else + { + if(rumbleCount[i]) + rumbleCount[i]--; + WPAD_Rumble(i, 0); // rumble off + } +} + +/**************************************************************************** + * WPAD_Stick + * + * Get X/Y value from Wii Joystick (classic, nunchuk) input + ***************************************************************************/ + +s8 WPAD_Stick(u8 chan, u8 right, int axis) +{ + float mag = 0.0; + float ang = 0.0; + WPADData *data = WPAD_Data(chan); + + switch (data->exp.type) + { + case WPAD_EXP_NUNCHUK: + case WPAD_EXP_GUITARHERO3: + if (right == 0) + { + mag = data->exp.nunchuk.js.mag; + ang = data->exp.nunchuk.js.ang; + } + break; + + case WPAD_EXP_CLASSIC: + if (right == 0) + { + mag = data->exp.classic.ljs.mag; + ang = data->exp.classic.ljs.ang; + } + else + { + mag = data->exp.classic.rjs.mag; + ang = data->exp.classic.rjs.ang; + } + break; + + default: + break; + } + + /* calculate x/y value (angle need to be converted into radian) */ + if (mag > 1.0) mag = 1.0; + else if (mag < -1.0) mag = -1.0; + double val; + + if(axis == 0) // x-axis + val = mag * sin((PI * ang)/180.0f); + else // y-axis + val = mag * cos((PI * ang)/180.0f); + + return (s8)(val * 128.0f); +} diff --git a/source/input.h b/source/input.h new file mode 100644 index 00000000..aed6ab33 --- /dev/null +++ b/source/input.h @@ -0,0 +1,23 @@ +/**************************************************************************** + * libwiigui Template + * Tantric 2009 + * + * input.h + * Wii/GameCube controller management + ***************************************************************************/ + +#ifndef _INPUT_H_ +#define _INPUT_H_ + +#include +#include + +#define PI 3.14159265f +#define PADCAL 50 + +extern int rumbleRequest[4]; + +void ShutoffRumble(); +void DoRumble(int i); + +#endif diff --git a/source/kenobiwii.h b/source/kenobiwii.h new file mode 100644 index 00000000..bc3d0ed1 --- /dev/null +++ b/source/kenobiwii.h @@ -0,0 +1,264 @@ +/* + This file was autogenerated by raw2c. +Visit http://www.devkitpro.org +*/ + +const unsigned char kenobiwii[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x26, 0xa0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x21, 0xff, 0x58, 0x90, 0x01, 0x00, 0x08, + 0x7c, 0x08, 0x02, 0xa6, 0x90, 0x01, 0x00, 0xac, 0x7c, 0x00, 0x00, 0x26, 0x90, 0x01, 0x00, 0x0c, + 0x7c, 0x09, 0x02, 0xa6, 0x90, 0x01, 0x00, 0x10, 0x7c, 0x01, 0x02, 0xa6, 0x90, 0x01, 0x00, 0x14, + 0xbc, 0x61, 0x00, 0x18, 0x7f, 0x20, 0x00, 0xa6, 0x63, 0x3a, 0x20, 0x00, 0x73, 0x5a, 0xf9, 0xff, + 0x7f, 0x40, 0x01, 0x24, 0xd8, 0x41, 0x00, 0x98, 0xd8, 0x61, 0x00, 0xa0, 0x3f, 0xe0, 0x80, 0x00, + 0x3e, 0x80, 0xcc, 0x00, 0xa3, 0x94, 0x40, 0x10, 0x63, 0x95, 0x00, 0xff, 0xb2, 0xb4, 0x40, 0x10, + 0x48, 0x00, 0x06, 0xb1, 0x3a, 0xa0, 0x00, 0x00, 0x3a, 0xc0, 0x00, 0x19, 0x3a, 0xe0, 0x00, 0xd0, + 0x3f, 0x00, 0xcd, 0x00, 0x63, 0xf2, 0x26, 0xa0, 0x80, 0x01, 0x00, 0xac, 0x90, 0x12, 0x00, 0x04, + 0x92, 0xb8, 0x64, 0x3c, 0x48, 0x00, 0x04, 0x85, 0x41, 0x82, 0x05, 0xfc, 0x2c, 0x1d, 0x00, 0x04, + 0x40, 0x80, 0x00, 0x10, 0x2c, 0x1d, 0x00, 0x01, 0x41, 0x80, 0x05, 0xec, 0x48, 0x00, 0x03, 0xa8, + 0x41, 0x82, 0x05, 0x48, 0x2c, 0x1d, 0x00, 0x06, 0x41, 0x82, 0x00, 0x8c, 0x2c, 0x1d, 0x00, 0x07, + 0x41, 0x82, 0x03, 0x8c, 0x2c, 0x1d, 0x00, 0x08, 0x41, 0x82, 0x05, 0xd8, 0x2c, 0x1d, 0x00, 0x09, + 0x41, 0x82, 0x00, 0xa0, 0x2c, 0x1d, 0x00, 0x10, 0x41, 0x82, 0x00, 0x98, 0x2c, 0x1d, 0x00, 0x2f, + 0x41, 0x82, 0x00, 0x70, 0x2c, 0x1d, 0x00, 0x30, 0x41, 0x82, 0x00, 0x78, 0x2c, 0x1d, 0x00, 0x38, + 0x41, 0x82, 0x05, 0x80, 0x2c, 0x1d, 0x00, 0x40, 0x41, 0x82, 0x03, 0x9c, 0x2c, 0x1d, 0x00, 0x41, + 0x41, 0x82, 0x03, 0xb0, 0x2c, 0x1d, 0x00, 0x44, 0x41, 0x82, 0x00, 0x68, 0x2c, 0x1d, 0x00, 0x50, + 0x41, 0x82, 0x00, 0x20, 0x2c, 0x1d, 0x00, 0x60, 0x41, 0x82, 0x00, 0x24, 0x2c, 0x1d, 0x00, 0x89, + 0x41, 0x82, 0x00, 0x50, 0x2c, 0x1d, 0x00, 0x99, 0x41, 0x82, 0x05, 0x64, 0x48, 0x00, 0x05, 0x68, + 0x80, 0x72, 0x00, 0x00, 0x48, 0x00, 0x04, 0x81, 0x48, 0x00, 0x05, 0x5c, 0x48, 0x00, 0x05, 0xe5, + 0x48, 0x00, 0x05, 0x54, 0x38, 0x80, 0x00, 0x01, 0x90, 0x92, 0x00, 0x00, 0x48, 0x00, 0x05, 0x48, + 0x48, 0x00, 0x04, 0x61, 0x3a, 0x00, 0x00, 0xa0, 0x63, 0xec, 0x26, 0xc4, 0x48, 0x00, 0x03, 0x6c, + 0x38, 0x60, 0x01, 0x20, 0x63, 0xec, 0x26, 0xc4, 0x48, 0x00, 0x04, 0x21, 0x48, 0x00, 0x05, 0x28, + 0x2f, 0x1d, 0x00, 0x10, 0x2e, 0x9d, 0x00, 0x44, 0x63, 0xe4, 0x1a, 0xb4, 0x3c, 0x60, 0x80, 0x00, + 0x60, 0x63, 0x03, 0x00, 0x48, 0x00, 0x05, 0x65, 0x38, 0x63, 0x0a, 0x00, 0x48, 0x00, 0x05, 0x5d, + 0x38, 0x63, 0x06, 0x00, 0x48, 0x00, 0x05, 0x55, 0x63, 0xec, 0x26, 0xb4, 0x92, 0xac, 0x00, 0x00, + 0x92, 0xac, 0x00, 0x04, 0x92, 0xac, 0x00, 0x08, 0x63, 0xe4, 0x26, 0xc4, 0x81, 0x24, 0x00, 0x18, + 0x80, 0x72, 0x00, 0x00, 0x2c, 0x03, 0x00, 0x02, 0x40, 0x82, 0x00, 0x0c, 0x41, 0x96, 0x00, 0x0c, + 0x48, 0x00, 0x00, 0x20, 0x38, 0x60, 0x00, 0x00, 0x90, 0x6c, 0x00, 0x0c, 0x40, 0x82, 0x00, 0x14, + 0x40, 0x96, 0x00, 0x10, 0x61, 0x29, 0x04, 0x00, 0x91, 0x24, 0x00, 0x18, 0x48, 0x00, 0x02, 0x70, + 0x55, 0x29, 0x05, 0xa8, 0x91, 0x24, 0x00, 0x18, 0x41, 0x96, 0x04, 0xac, 0x41, 0x9a, 0x00, 0x08, + 0x39, 0x8c, 0x00, 0x04, 0x38, 0x60, 0x00, 0x04, 0x48, 0x00, 0x03, 0x61, 0x40, 0x99, 0x00, 0x10, + 0x39, 0x8c, 0x00, 0x04, 0x38, 0x60, 0x00, 0x04, 0x48, 0x00, 0x03, 0x51, 0x63, 0xe4, 0x26, 0xb4, + 0x80, 0x64, 0x00, 0x00, 0x80, 0x84, 0x00, 0x04, 0x7c, 0x72, 0xfb, 0xa6, 0x7c, 0x95, 0xfb, 0xa6, + 0x48, 0x00, 0x04, 0x74, 0x7c, 0x32, 0x43, 0xa6, 0x7c, 0x3a, 0x02, 0xa6, 0x7c, 0x73, 0x43, 0xa6, + 0x7c, 0x7b, 0x02, 0xa6, 0x54, 0x63, 0x05, 0xa8, 0x90, 0x60, 0x26, 0xdc, 0x54, 0x63, 0x06, 0x20, + 0x60, 0x63, 0x20, 0x00, 0x54, 0x63, 0x04, 0x5e, 0x7c, 0x7b, 0x03, 0xa6, 0x3c, 0x60, 0x80, 0x00, + 0x60, 0x63, 0x1a, 0xf4, 0x7c, 0x7a, 0x03, 0xa6, 0x4c, 0x00, 0x01, 0x2c, 0x7c, 0x00, 0x04, 0xac, + 0x4c, 0x00, 0x00, 0x64, 0x3c, 0x60, 0x80, 0x00, 0x60, 0x63, 0x26, 0xc4, 0x90, 0x23, 0x00, 0x14, + 0x7c, 0x61, 0x1b, 0x78, 0x7c, 0x73, 0x42, 0xa6, 0xbc, 0x41, 0x00, 0x24, 0x7c, 0x24, 0x0b, 0x78, + 0x7c, 0x32, 0x42, 0xa6, 0x90, 0x04, 0x00, 0x1c, 0x90, 0x24, 0x00, 0x20, 0x7c, 0x68, 0x02, 0xa6, + 0x90, 0x64, 0x00, 0x9c, 0x7c, 0x60, 0x00, 0x26, 0x90, 0x64, 0x00, 0x00, 0x7c, 0x61, 0x02, 0xa6, + 0x90, 0x64, 0x00, 0x04, 0x7c, 0x69, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x08, 0x7c, 0x72, 0x02, 0xa6, + 0x90, 0x64, 0x00, 0x0c, 0x7c, 0x73, 0x02, 0xa6, 0x90, 0x64, 0x00, 0x10, 0x39, 0x20, 0x00, 0x00, + 0x7d, 0x32, 0xfb, 0xa6, 0x7d, 0x35, 0xfb, 0xa6, 0xd0, 0x04, 0x00, 0xa0, 0xd0, 0x24, 0x00, 0xa4, + 0xd0, 0x44, 0x00, 0xa8, 0xd0, 0x64, 0x00, 0xac, 0xd0, 0x84, 0x00, 0xb0, 0xd0, 0xa4, 0x00, 0xb4, + 0xd0, 0xc4, 0x00, 0xb8, 0xd0, 0xe4, 0x00, 0xbc, 0xd1, 0x04, 0x00, 0xc0, 0xd1, 0x24, 0x00, 0xc4, + 0xd1, 0x44, 0x00, 0xc8, 0xd1, 0x64, 0x00, 0xcc, 0xd1, 0x84, 0x00, 0xd0, 0xd1, 0xa4, 0x00, 0xd4, + 0xd1, 0xc4, 0x00, 0xd8, 0xd1, 0xe4, 0x00, 0xdc, 0xd2, 0x04, 0x00, 0xe0, 0xd2, 0x24, 0x00, 0xe4, + 0xd2, 0x44, 0x00, 0xe8, 0xd2, 0x64, 0x00, 0xec, 0xd2, 0x84, 0x00, 0xf0, 0xd2, 0xa4, 0x00, 0xf4, + 0xd2, 0xc4, 0x00, 0xf8, 0xd2, 0xe4, 0x00, 0xfc, 0xd3, 0x04, 0x01, 0x00, 0xd3, 0x24, 0x01, 0x04, + 0xd3, 0x44, 0x01, 0x08, 0xd3, 0x64, 0x01, 0x0c, 0xd3, 0x84, 0x01, 0x10, 0xd3, 0xa4, 0x01, 0x14, + 0xd3, 0xc4, 0x01, 0x18, 0xd3, 0xe4, 0x01, 0x1c, 0x3f, 0xe0, 0x80, 0x00, 0x63, 0xe5, 0x26, 0xb4, + 0x82, 0x05, 0x00, 0x00, 0x82, 0x25, 0x00, 0x04, 0x82, 0x65, 0x00, 0x0c, 0x2c, 0x13, 0x00, 0x00, + 0x41, 0x82, 0x00, 0x74, 0x2c, 0x13, 0x00, 0x02, 0x40, 0x82, 0x00, 0x18, 0x81, 0x24, 0x00, 0x14, + 0x39, 0x33, 0x00, 0x03, 0x91, 0x25, 0x00, 0x00, 0x91, 0x25, 0x00, 0x0c, 0x48, 0x00, 0x00, 0x6c, + 0x7c, 0x10, 0x98, 0x00, 0x41, 0x82, 0x00, 0x38, 0x7c, 0x11, 0x98, 0x00, 0x41, 0x82, 0x00, 0x30, + 0x7d, 0x30, 0x8a, 0x14, 0x91, 0x25, 0x00, 0x0c, 0x82, 0x05, 0x00, 0x08, 0x2c, 0x10, 0x00, 0x00, + 0x41, 0x82, 0x00, 0x48, 0x80, 0x64, 0x00, 0x10, 0x7c, 0x10, 0x18, 0x00, 0x40, 0x82, 0x00, 0x10, + 0x3a, 0x00, 0x00, 0x00, 0x92, 0x05, 0x00, 0x08, 0x48, 0x00, 0x00, 0x30, 0x3a, 0x20, 0x00, 0x00, + 0x92, 0x25, 0x00, 0x0c, 0x81, 0x24, 0x00, 0x18, 0x61, 0x29, 0x04, 0x00, 0x91, 0x24, 0x00, 0x18, + 0x48, 0x00, 0x00, 0x30, 0x7e, 0x12, 0xfb, 0xa6, 0x7e, 0x35, 0xfb, 0xa6, 0x39, 0x20, 0x00, 0x01, + 0x91, 0x25, 0x00, 0x0c, 0x48, 0x00, 0x00, 0x1c, 0x38, 0xa0, 0x00, 0x02, 0x63, 0xe4, 0x26, 0xa0, + 0x90, 0xa4, 0x00, 0x00, 0x38, 0x60, 0x00, 0x11, 0x48, 0x00, 0x01, 0xbd, 0x4b, 0xff, 0xfc, 0x1d, + 0x7c, 0x20, 0x00, 0xa6, 0x54, 0x21, 0x07, 0xfa, 0x54, 0x21, 0x04, 0x5e, 0x7c, 0x20, 0x01, 0x24, + 0x63, 0xe1, 0x26, 0xc4, 0x80, 0x61, 0x00, 0x00, 0x7c, 0x6f, 0xf1, 0x20, 0x80, 0x61, 0x00, 0x14, + 0x7c, 0x7a, 0x03, 0xa6, 0x80, 0x61, 0x00, 0x18, 0x7c, 0x7b, 0x03, 0xa6, 0x80, 0x61, 0x00, 0x9c, + 0x7c, 0x68, 0x03, 0xa6, 0xb8, 0x41, 0x00, 0x24, 0x80, 0x01, 0x00, 0x1c, 0x80, 0x21, 0x00, 0x20, + 0x4c, 0x00, 0x01, 0x2c, 0x7c, 0x00, 0x04, 0xac, 0x4c, 0x00, 0x00, 0x64, 0x92, 0xb2, 0x00, 0x00, + 0x48, 0x00, 0x02, 0x50, 0x2e, 0x9d, 0x00, 0x02, 0x38, 0x60, 0x00, 0x08, 0x63, 0xec, 0x26, 0xa8, + 0x48, 0x00, 0x00, 0xf9, 0x80, 0xac, 0x00, 0x00, 0x80, 0x6c, 0x00, 0x04, 0x98, 0x65, 0x00, 0x00, + 0x41, 0x94, 0x00, 0x10, 0xb0, 0x65, 0x00, 0x00, 0x41, 0x96, 0x00, 0x08, 0x90, 0x65, 0x00, 0x00, + 0x7c, 0x00, 0x28, 0xac, 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x2f, 0xac, 0x4c, 0x00, 0x01, 0x2c, + 0x48, 0x00, 0x02, 0x04, 0x48, 0x00, 0x01, 0x1d, 0x38, 0x60, 0x00, 0x04, 0x63, 0xec, 0x26, 0xa8, + 0x48, 0x00, 0x00, 0xb9, 0x82, 0x0c, 0x00, 0x00, 0x63, 0xec, 0x27, 0xe8, 0x48, 0x00, 0x00, 0x1c, + 0x48, 0x00, 0x01, 0x01, 0x38, 0x60, 0x00, 0x08, 0x63, 0xec, 0x26, 0xa8, 0x48, 0x00, 0x00, 0x9d, + 0x82, 0x0c, 0x00, 0x04, 0x81, 0x8c, 0x00, 0x00, 0x63, 0xfb, 0x26, 0xb0, 0x3a, 0x20, 0x0f, 0x80, + 0x48, 0x00, 0x02, 0x3d, 0x41, 0x82, 0x00, 0x20, 0x7e, 0x23, 0x8b, 0x78, 0x48, 0x00, 0x00, 0x7d, + 0x48, 0x00, 0x00, 0xd1, 0x41, 0x82, 0xff, 0xfc, 0x7d, 0x8c, 0x72, 0x14, 0x35, 0x6b, 0xff, 0xff, + 0x41, 0x81, 0xff, 0xe8, 0x80, 0x7b, 0x00, 0x00, 0x2c, 0x03, 0x00, 0x00, 0x41, 0x82, 0x00, 0x08, + 0x48, 0x00, 0x00, 0x59, 0x7c, 0x00, 0x60, 0xac, 0x7c, 0x00, 0x04, 0xac, 0x7c, 0x00, 0x67, 0xac, + 0x4c, 0x00, 0x01, 0x2c, 0x48, 0x00, 0x01, 0x80, 0x7f, 0xc8, 0x02, 0xa6, 0x3c, 0x60, 0xa0, 0x00, + 0x48, 0x00, 0x00, 0x15, 0x76, 0x03, 0x08, 0x00, 0x56, 0x1d, 0x86, 0x3e, 0x7f, 0xc8, 0x03, 0xa6, + 0x4e, 0x80, 0x00, 0x20, 0x92, 0xf8, 0x68, 0x14, 0x90, 0x78, 0x68, 0x24, 0x92, 0xd8, 0x68, 0x20, + 0x80, 0xb8, 0x68, 0x20, 0x70, 0xa5, 0x00, 0x01, 0x40, 0x82, 0xff, 0xf8, 0x82, 0x18, 0x68, 0x24, + 0x90, 0xb8, 0x68, 0x14, 0x4e, 0x80, 0x00, 0x20, 0x7d, 0x48, 0x02, 0xa6, 0x7c, 0x69, 0x03, 0xa6, + 0x39, 0xc0, 0x00, 0x00, 0x48, 0x00, 0x00, 0x79, 0x48, 0x00, 0x00, 0x75, 0x4b, 0xff, 0xff, 0xad, + 0x41, 0x82, 0xff, 0xf4, 0x7f, 0xae, 0x61, 0xae, 0x39, 0xce, 0x00, 0x01, 0x42, 0x00, 0xff, 0xe8, + 0x7d, 0x48, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x7d, 0x48, 0x02, 0xa6, 0x7c, 0x69, 0x03, 0xa6, + 0x39, 0xc0, 0x00, 0x00, 0x7c, 0x6c, 0x70, 0xae, 0x48, 0x00, 0x00, 0x1d, 0x41, 0x82, 0xff, 0xf8, + 0x39, 0xce, 0x00, 0x01, 0x42, 0x00, 0xff, 0xf0, 0x7d, 0x48, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, + 0x38, 0x60, 0x00, 0xaa, 0x7f, 0xc8, 0x02, 0xa6, 0x54, 0x63, 0xa0, 0x16, 0x64, 0x63, 0xb0, 0x00, + 0x3a, 0xc0, 0x00, 0x19, 0x3a, 0xe0, 0x00, 0xd0, 0x3f, 0x00, 0xcd, 0x00, 0x4b, 0xff, 0xff, 0x69, + 0x56, 0x03, 0x37, 0xff, 0x7f, 0xc8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x7f, 0xc8, 0x02, 0xa6, + 0x3c, 0x60, 0xd0, 0x00, 0x4b, 0xff, 0xff, 0x51, 0x56, 0x03, 0x37, 0xff, 0x41, 0x82, 0xff, 0xf4, + 0x7f, 0xc8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x4b, 0xff, 0xff, 0xb9, 0x38, 0x60, 0x00, 0x08, + 0x63, 0xec, 0x26, 0xa8, 0x4b, 0xff, 0xff, 0x55, 0x80, 0xac, 0x00, 0x04, 0x81, 0x8c, 0x00, 0x00, + 0x63, 0xfb, 0x26, 0xb0, 0x62, 0xb1, 0xf8, 0x00, 0x7e, 0x0c, 0x28, 0x50, 0x48, 0x00, 0x00, 0xf1, + 0x41, 0x81, 0x00, 0x10, 0x82, 0x3b, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x41, 0x82, 0x00, 0x68, + 0x7e, 0x23, 0x8b, 0x78, 0x4b, 0xff, 0xff, 0x55, 0x4b, 0xff, 0xff, 0xa5, 0x4b, 0xff, 0xff, 0xa1, + 0x4b, 0xff, 0xfe, 0xd9, 0x41, 0x82, 0xff, 0xf4, 0x2c, 0x1d, 0x00, 0xcc, 0x41, 0x82, 0x00, 0x48, + 0x2c, 0x1d, 0x00, 0xbb, 0x41, 0x82, 0xff, 0xdc, 0x2c, 0x1d, 0x00, 0xaa, 0x40, 0x82, 0xff, 0xdc, + 0x7d, 0x8c, 0x72, 0x14, 0x35, 0x6b, 0xff, 0xff, 0x41, 0x80, 0x00, 0x2c, 0x4b, 0xff, 0xff, 0xb4, + 0x7e, 0xb5, 0xfb, 0xa6, 0x7e, 0xb2, 0xfb, 0xa6, 0x63, 0xe4, 0x26, 0xc4, 0x81, 0x24, 0x00, 0x18, + 0x55, 0x29, 0x05, 0xa8, 0x91, 0x24, 0x00, 0x18, 0x48, 0x00, 0x00, 0x0c, 0x38, 0x60, 0x00, 0x80, + 0x4b, 0xff, 0xff, 0x25, 0x80, 0x92, 0x00, 0x00, 0x2c, 0x04, 0x00, 0x00, 0x40, 0x82, 0xf9, 0xf8, + 0xb3, 0x94, 0x40, 0x10, 0xc8, 0x41, 0x00, 0x98, 0xc8, 0x61, 0x00, 0xa0, 0x7f, 0x20, 0x00, 0xa6, + 0x80, 0x01, 0x00, 0xac, 0x7c, 0x08, 0x03, 0xa6, 0x80, 0x01, 0x00, 0x0c, 0x7c, 0x0f, 0xf1, 0x20, + 0x80, 0x01, 0x00, 0x10, 0x7c, 0x09, 0x03, 0xa6, 0x80, 0x01, 0x00, 0x14, 0x7c, 0x01, 0x03, 0xa6, + 0xb8, 0x61, 0x00, 0x18, 0x80, 0x01, 0x00, 0x08, 0x38, 0x21, 0x00, 0xa8, 0x4c, 0x00, 0x01, 0x2c, + 0x7c, 0x00, 0x04, 0xac, 0x4e, 0x80, 0x00, 0x20, 0x7e, 0x23, 0x20, 0x50, 0x3c, 0xa0, 0x48, 0x00, + 0x52, 0x25, 0x01, 0xba, 0x90, 0xa3, 0x00, 0x00, 0x7c, 0x00, 0x18, 0xac, 0x7c, 0x00, 0x04, 0xac, + 0x7c, 0x00, 0x1f, 0xac, 0x4c, 0x00, 0x01, 0x2c, 0x4e, 0x80, 0x00, 0x20, 0x7d, 0x70, 0x8b, 0xd7, + 0x7d, 0x4b, 0x89, 0xd6, 0x7d, 0x4a, 0x80, 0x50, 0x91, 0x5b, 0x00, 0x00, 0x4e, 0x80, 0x00, 0x20, + 0x7f, 0xa8, 0x02, 0xa6, 0x63, 0xef, 0x27, 0xe8, 0x63, 0xe7, 0x18, 0x08, 0x3c, 0xc0, 0x80, 0x00, + 0x7c, 0xd0, 0x33, 0x78, 0x39, 0x00, 0x00, 0x00, 0x3c, 0x60, 0x00, 0xd0, 0x60, 0x63, 0xc0, 0xde, + 0x80, 0x8f, 0x00, 0x00, 0x7c, 0x03, 0x20, 0x00, 0x40, 0x82, 0x00, 0x18, 0x80, 0x8f, 0x00, 0x04, + 0x7c, 0x03, 0x20, 0x00, 0x40, 0x82, 0x00, 0x0c, 0x39, 0xef, 0x00, 0x08, 0x48, 0x00, 0x00, 0x0c, + 0x7f, 0xa8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x80, 0x6f, 0x00, 0x00, 0x80, 0x8f, 0x00, 0x04, + 0x39, 0xef, 0x00, 0x08, 0x71, 0x09, 0x00, 0x01, 0x2f, 0x89, 0x00, 0x00, 0x39, 0x20, 0x00, 0x00, + 0x54, 0x6a, 0x1f, 0x7e, 0x54, 0x65, 0x3f, 0x7e, 0x74, 0x6b, 0x10, 0x00, 0x54, 0x63, 0x01, 0xfe, + 0x40, 0x82, 0x00, 0x0c, 0x54, 0xcc, 0x00, 0x0c, 0x48, 0x00, 0x00, 0x08, 0x7e, 0x0c, 0x83, 0x78, + 0x2e, 0x05, 0x00, 0x00, 0x2c, 0x0a, 0x00, 0x01, 0x41, 0xa0, 0x00, 0x2c, 0x41, 0xa2, 0x00, 0xe4, + 0x2c, 0x0a, 0x00, 0x03, 0x41, 0xa0, 0x01, 0xb0, 0x41, 0x82, 0x02, 0x54, 0x2c, 0x0a, 0x00, 0x05, + 0x41, 0x80, 0x02, 0xdc, 0x41, 0xa2, 0x04, 0xe8, 0x2c, 0x0a, 0x00, 0x07, 0x41, 0xa0, 0x05, 0x14, + 0x48, 0x00, 0x05, 0xf8, 0x7d, 0x8c, 0x1a, 0x14, 0x2c, 0x05, 0x00, 0x03, 0x41, 0x82, 0x00, 0x48, + 0x41, 0x81, 0x00, 0x60, 0x40, 0xbe, 0xff, 0x84, 0x2e, 0x05, 0x00, 0x01, 0x41, 0x91, 0x00, 0x2c, + 0x54, 0x8a, 0x84, 0x3e, 0x41, 0x92, 0x00, 0x10, 0x7c, 0x89, 0x61, 0xae, 0x39, 0x29, 0x00, 0x01, + 0x48, 0x00, 0x00, 0x0c, 0x7c, 0x89, 0x63, 0x2e, 0x39, 0x29, 0x00, 0x02, 0x35, 0x4a, 0xff, 0xff, + 0x40, 0xa0, 0xff, 0xe4, 0x4b, 0xff, 0xff, 0x54, 0x55, 0x8c, 0x00, 0x3a, 0x90, 0x8c, 0x00, 0x00, + 0x4b, 0xff, 0xff, 0x48, 0x7c, 0x89, 0x23, 0x78, 0x40, 0x9e, 0x04, 0xd0, 0x35, 0x29, 0xff, 0xff, + 0x41, 0x80, 0x04, 0xc8, 0x7c, 0xa9, 0x78, 0xae, 0x7c, 0xa9, 0x61, 0xae, 0x4b, 0xff, 0xff, 0xf0, + 0x39, 0xef, 0x00, 0x08, 0x40, 0xbe, 0xff, 0x24, 0x80, 0xaf, 0xff, 0xf8, 0x81, 0x6f, 0xff, 0xfc, + 0x54, 0xb1, 0x04, 0x3e, 0x54, 0xaa, 0x85, 0x3e, 0x54, 0xa5, 0x27, 0x3e, 0x2e, 0x85, 0x00, 0x01, + 0x41, 0x96, 0x00, 0x10, 0x41, 0xb5, 0x00, 0x14, 0x7c, 0x89, 0x61, 0xae, 0x48, 0x00, 0x00, 0x10, + 0x7c, 0x89, 0x63, 0x2e, 0x48, 0x00, 0x00, 0x08, 0x7c, 0x89, 0x61, 0x2e, 0x7c, 0x84, 0x5a, 0x14, + 0x7d, 0x29, 0x8a, 0x14, 0x35, 0x4a, 0xff, 0xff, 0x40, 0x80, 0xff, 0xd4, 0x4b, 0xff, 0xfe, 0xdc, + 0x54, 0x69, 0x07, 0xff, 0x41, 0x82, 0x00, 0x10, 0x55, 0x08, 0xf8, 0x7e, 0x71, 0x09, 0x00, 0x01, + 0x2f, 0x89, 0x00, 0x00, 0x2e, 0x85, 0x00, 0x04, 0x2d, 0x8a, 0x00, 0x05, 0x7d, 0x13, 0x43, 0x78, + 0x52, 0x68, 0x08, 0x3c, 0x40, 0x9e, 0x00, 0x78, 0x41, 0x8d, 0x04, 0xbc, 0x7d, 0x8c, 0x1a, 0x14, + 0x41, 0x8c, 0x00, 0x0c, 0x41, 0x94, 0x00, 0x30, 0x48, 0x00, 0x00, 0x1c, 0x40, 0x94, 0x00, 0x10, + 0x55, 0x8c, 0x00, 0x3a, 0x81, 0x6c, 0x00, 0x00, 0x48, 0x00, 0x00, 0x1c, 0x55, 0x8c, 0x00, 0x3c, + 0xa1, 0x6c, 0x00, 0x00, 0x7c, 0x89, 0x20, 0xf8, 0x55, 0x29, 0x84, 0x3e, 0x7d, 0x6b, 0x48, 0x38, + 0x54, 0x84, 0x04, 0x3e, 0x7f, 0x0b, 0x20, 0x40, 0x70, 0xa9, 0x00, 0x03, 0x41, 0x82, 0x00, 0x18, + 0x2c, 0x09, 0x00, 0x02, 0x41, 0x82, 0x00, 0x18, 0x41, 0x81, 0x00, 0x1c, 0x40, 0x9a, 0x00, 0x20, + 0x48, 0x00, 0x00, 0x18, 0x41, 0x9a, 0x00, 0x18, 0x48, 0x00, 0x00, 0x10, 0x41, 0x99, 0x00, 0x10, + 0x48, 0x00, 0x00, 0x08, 0x41, 0x98, 0x00, 0x08, 0x61, 0x08, 0x00, 0x01, 0x40, 0x8e, 0xfe, 0x3c, + 0x41, 0x94, 0xfe, 0x38, 0x81, 0x6f, 0xff, 0xf8, 0x40, 0x9e, 0x00, 0x20, 0x70, 0x6c, 0x00, 0x08, + 0x41, 0x82, 0x00, 0x0c, 0x71, 0x0c, 0x00, 0x01, 0x41, 0x82, 0x00, 0x10, 0x39, 0x8b, 0x00, 0x10, + 0x51, 0x8b, 0x03, 0x36, 0x48, 0x00, 0x00, 0x08, 0x55, 0x6b, 0x07, 0x16, 0x91, 0x6f, 0xff, 0xf8, + 0x4b, 0xff, 0xfe, 0x08, 0x40, 0xbe, 0xfe, 0x04, 0x54, 0x69, 0x16, 0xba, 0x54, 0x6e, 0x87, 0xfe, + 0x2d, 0x8e, 0x00, 0x00, 0x2e, 0x05, 0x00, 0x04, 0x70, 0xae, 0x00, 0x03, 0x2e, 0x8e, 0x00, 0x02, + 0x41, 0x94, 0x00, 0x14, 0x41, 0x96, 0x00, 0x50, 0x7c, 0x64, 0x07, 0x34, 0x7c, 0x84, 0x7a, 0x14, + 0x48, 0x00, 0x00, 0x68, 0x54, 0x65, 0xa7, 0xff, 0x41, 0x82, 0x00, 0x0c, 0x7d, 0x27, 0x48, 0x2e, + 0x7c, 0x84, 0x4a, 0x14, 0x41, 0x8e, 0x00, 0x08, 0x7c, 0x8c, 0x22, 0x14, 0x2e, 0x8e, 0x00, 0x01, + 0x41, 0x96, 0x00, 0x08, 0x80, 0x84, 0x00, 0x00, 0x54, 0x63, 0x67, 0xff, 0x41, 0x82, 0x00, 0x3c, + 0x40, 0x90, 0x00, 0x0c, 0x7c, 0x84, 0x32, 0x14, 0x48, 0x00, 0x00, 0x30, 0x7c, 0x84, 0x82, 0x14, + 0x48, 0x00, 0x00, 0x28, 0x54, 0x65, 0xa7, 0xff, 0x41, 0x82, 0x00, 0x0c, 0x7d, 0x27, 0x48, 0x2e, + 0x7c, 0x84, 0x4a, 0x14, 0x40, 0x90, 0x00, 0x0c, 0x7c, 0xcc, 0x21, 0x2e, 0x4b, 0xff, 0xfd, 0x7c, + 0x7e, 0x0c, 0x21, 0x2e, 0x4b, 0xff, 0xfd, 0x74, 0x40, 0x90, 0x00, 0x0c, 0x7c, 0x86, 0x23, 0x78, + 0x4b, 0xff, 0xfd, 0x68, 0x7c, 0x90, 0x23, 0x78, 0x4b, 0xff, 0xfd, 0x60, 0x54, 0x89, 0x1e, 0x78, + 0x39, 0x29, 0x00, 0x40, 0x2c, 0x05, 0x00, 0x02, 0x41, 0x80, 0x00, 0x4c, 0x54, 0x6b, 0x67, 0xbf, + 0x2c, 0x0b, 0x00, 0x01, 0x41, 0x80, 0x00, 0x14, 0x41, 0x82, 0x00, 0x08, 0x48, 0x00, 0x00, 0x10, + 0x41, 0xbe, 0xfd, 0x38, 0x48, 0x00, 0x00, 0x08, 0x40, 0xbe, 0xfd, 0x30, 0x2c, 0x05, 0x00, 0x03, + 0x41, 0x81, 0x00, 0x10, 0x41, 0xa2, 0x00, 0x10, 0x7d, 0xe7, 0x48, 0x2e, 0x4b, 0xff, 0xfd, 0x1c, + 0x7d, 0xe7, 0x49, 0x2e, 0x7c, 0x64, 0x07, 0x34, 0x54, 0x84, 0x1a, 0x78, 0x7d, 0xef, 0x22, 0x14, + 0x4b, 0xff, 0xfd, 0x08, 0x40, 0xbe, 0xfd, 0x04, 0x7c, 0xa7, 0x4a, 0x14, 0x40, 0x92, 0x00, 0x14, + 0x54, 0x64, 0x04, 0x3e, 0x91, 0xe5, 0x00, 0x00, 0x90, 0x85, 0x00, 0x04, 0x4b, 0xff, 0xfc, 0xec, + 0x81, 0x25, 0x00, 0x04, 0x2c, 0x09, 0x00, 0x00, 0x41, 0xa2, 0xfc, 0xe0, 0x39, 0x29, 0xff, 0xff, + 0x91, 0x25, 0x00, 0x04, 0x81, 0xe5, 0x00, 0x00, 0x4b, 0xff, 0xfc, 0xd0, 0x40, 0xbe, 0xfc, 0xcc, + 0x54, 0x6b, 0x16, 0xba, 0x7f, 0x47, 0x5a, 0x14, 0x81, 0x3a, 0x00, 0x00, 0x54, 0x6e, 0x67, 0xbe, + 0x41, 0x92, 0x00, 0x84, 0x2e, 0x05, 0x00, 0x05, 0x40, 0x90, 0x01, 0x74, 0x2e, 0x05, 0x00, 0x03, + 0x40, 0x90, 0x00, 0x90, 0x2e, 0x05, 0x00, 0x01, 0x54, 0x65, 0x87, 0xff, 0x41, 0x82, 0x00, 0x08, + 0x7c, 0x8c, 0x22, 0x14, 0x2f, 0x0e, 0x00, 0x01, 0x40, 0x92, 0x00, 0x24, 0x41, 0xb9, 0x00, 0x18, + 0x41, 0x9a, 0x00, 0x0c, 0x88, 0x84, 0x00, 0x00, 0x48, 0x00, 0x00, 0xf8, 0xa0, 0x84, 0x00, 0x00, + 0x48, 0x00, 0x00, 0xf0, 0x80, 0x84, 0x00, 0x00, 0x48, 0x00, 0x00, 0xe8, 0x54, 0x73, 0xe5, 0x3e, + 0x41, 0xb9, 0x00, 0x20, 0x41, 0x9a, 0x00, 0x10, 0x99, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x01, + 0x48, 0x00, 0x00, 0x18, 0xb1, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x02, 0x48, 0x00, 0x00, 0x0c, + 0x91, 0x24, 0x00, 0x00, 0x38, 0x84, 0x00, 0x04, 0x36, 0x73, 0xff, 0xff, 0x40, 0x80, 0xff, 0xd4, + 0x4b, 0xff, 0xfc, 0x38, 0x54, 0x65, 0x87, 0xff, 0x41, 0x82, 0x00, 0x08, 0x7c, 0x84, 0x62, 0x14, + 0x71, 0xc5, 0x00, 0x01, 0x41, 0x82, 0x00, 0x9c, 0x7c, 0x84, 0x4a, 0x14, 0x48, 0x00, 0x00, 0x94, + 0x54, 0x6a, 0x87, 0xbe, 0x54, 0x8e, 0x16, 0xba, 0x7e, 0x67, 0x72, 0x14, 0x40, 0x92, 0x00, 0x08, + 0x3a, 0x6f, 0xff, 0xfc, 0x80, 0x9a, 0x00, 0x00, 0x81, 0x33, 0x00, 0x00, 0x71, 0x4b, 0x00, 0x01, + 0x41, 0x82, 0x00, 0x08, 0x7c, 0x9a, 0x23, 0x78, 0x71, 0x4b, 0x00, 0x02, 0x41, 0x82, 0x00, 0x10, + 0x7d, 0x33, 0x4b, 0x78, 0x40, 0xb2, 0x00, 0x08, 0x7e, 0x6c, 0x9a, 0x14, 0x54, 0x65, 0x67, 0x3f, + 0x2c, 0x05, 0x00, 0x09, 0x40, 0x80, 0x00, 0x54, 0x48, 0x00, 0x00, 0x79, 0x7c, 0x89, 0x22, 0x14, + 0x48, 0x00, 0x00, 0x40, 0x7c, 0x89, 0x21, 0xd6, 0x48, 0x00, 0x00, 0x38, 0x7d, 0x24, 0x23, 0x78, + 0x48, 0x00, 0x00, 0x30, 0x7d, 0x24, 0x20, 0x38, 0x48, 0x00, 0x00, 0x28, 0x7d, 0x24, 0x22, 0x78, + 0x48, 0x00, 0x00, 0x20, 0x7d, 0x24, 0x20, 0x30, 0x48, 0x00, 0x00, 0x18, 0x7d, 0x24, 0x24, 0x30, + 0x48, 0x00, 0x00, 0x10, 0x5d, 0x24, 0x20, 0x3e, 0x48, 0x00, 0x00, 0x08, 0x7d, 0x24, 0x26, 0x30, + 0x90, 0x9a, 0x00, 0x00, 0x4b, 0xff, 0xfb, 0x84, 0x2c, 0x05, 0x00, 0x0a, 0x41, 0x81, 0xfb, 0x7c, + 0xc0, 0x5a, 0x00, 0x00, 0xc0, 0x73, 0x00, 0x00, 0x41, 0x82, 0x00, 0x0c, 0xec, 0x43, 0x10, 0x2a, + 0x48, 0x00, 0x00, 0x08, 0xec, 0x43, 0x00, 0xb2, 0xd0, 0x5a, 0x00, 0x00, 0x4b, 0xff, 0xfb, 0x5c, + 0x7d, 0x48, 0x02, 0xa6, 0x54, 0xa5, 0x1e, 0x78, 0x7d, 0x4a, 0x2a, 0x14, 0x80, 0x9a, 0x00, 0x00, + 0x81, 0x33, 0x00, 0x00, 0x7d, 0x48, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x20, 0x40, 0xbe, 0xfb, 0x3c, + 0x54, 0x69, 0xc0, 0x3e, 0x7d, 0x8e, 0x63, 0x78, 0x48, 0x00, 0x00, 0x35, 0x41, 0x92, 0x00, 0x0c, + 0x7e, 0x31, 0x22, 0x14, 0x48, 0x00, 0x00, 0x08, 0x7d, 0x29, 0x22, 0x14, 0x54, 0x64, 0xc4, 0x3f, + 0x38, 0xa0, 0x00, 0x00, 0x41, 0x82, 0xfb, 0x14, 0x7d, 0x45, 0x88, 0xae, 0x7d, 0x45, 0x49, 0xae, + 0x38, 0xa5, 0x00, 0x01, 0x7c, 0x05, 0x20, 0x00, 0x4b, 0xff, 0xff, 0xec, 0x2e, 0x8a, 0x00, 0x04, + 0x55, 0x31, 0x36, 0xba, 0x2c, 0x11, 0x00, 0x3c, 0x7e, 0x27, 0x88, 0x2e, 0x40, 0x82, 0x00, 0x08, + 0x7d, 0xd1, 0x73, 0x78, 0x41, 0x96, 0x00, 0x08, 0xa2, 0x31, 0x00, 0x00, 0x55, 0x29, 0x56, 0xba, + 0x2c, 0x09, 0x00, 0x3c, 0x7d, 0x27, 0x48, 0x2e, 0x40, 0x82, 0x00, 0x08, 0x7d, 0xc9, 0x73, 0x78, + 0x41, 0x96, 0x00, 0x08, 0xa1, 0x29, 0x00, 0x00, 0x4e, 0x80, 0x00, 0x20, 0x2c, 0x05, 0x00, 0x04, + 0x40, 0x80, 0x00, 0x28, 0x7c, 0x89, 0x23, 0x78, 0x7d, 0xc3, 0x62, 0x14, 0x55, 0xce, 0x00, 0x3c, + 0x4b, 0xff, 0xff, 0xad, 0x7c, 0x84, 0x20, 0xf8, 0x54, 0x84, 0x04, 0x3e, 0x7d, 0x2b, 0x20, 0x38, + 0x7e, 0x24, 0x20, 0x38, 0x4b, 0xff, 0xfb, 0xbc, 0x54, 0x6b, 0xe4, 0x3e, 0x4b, 0xff, 0xfb, 0xb4, + 0x7c, 0x9a, 0x23, 0x78, 0x54, 0x84, 0x18, 0x38, 0x40, 0x92, 0x00, 0x20, 0x40, 0x9e, 0x00, 0x0c, + 0x7d, 0xe8, 0x03, 0xa6, 0x4e, 0x80, 0x00, 0x21, 0x7d, 0xe4, 0x7a, 0x14, 0x39, 0xef, 0x00, 0x07, + 0x55, 0xef, 0x00, 0x38, 0x4b, 0xff, 0xfa, 0x64, 0x2e, 0x05, 0x00, 0x03, 0x41, 0x91, 0x00, 0x5c, + 0x3c, 0xa0, 0x48, 0x00, 0x7d, 0x83, 0x62, 0x14, 0x55, 0x8c, 0x00, 0x3a, 0x40, 0x92, 0x00, 0x20, + 0x40, 0xbe, 0xfa, 0x48, 0x57, 0x44, 0x00, 0x3a, 0x7c, 0x8c, 0x20, 0x50, 0x50, 0x85, 0x01, 0xba, + 0x50, 0x65, 0x07, 0xfe, 0x90, 0xac, 0x00, 0x00, 0x4b, 0xff, 0xfa, 0x30, 0x40, 0xbe, 0xff, 0xbc, + 0x7d, 0x2c, 0x78, 0x50, 0x51, 0x25, 0x01, 0xba, 0x90, 0xac, 0x00, 0x00, 0x39, 0x8c, 0x00, 0x04, + 0x7d, 0x6f, 0x22, 0x14, 0x39, 0x6b, 0xff, 0xfc, 0x7d, 0x2b, 0x60, 0x50, 0x51, 0x25, 0x01, 0xba, + 0x90, 0xab, 0x00, 0x00, 0x4b, 0xff, 0xff, 0x94, 0x2e, 0x05, 0x00, 0x06, 0x41, 0x92, 0x00, 0x28, + 0x4b, 0xff, 0xfb, 0x20, 0x55, 0x8c, 0x84, 0x3e, 0x57, 0x44, 0x84, 0x3e, 0x57, 0x5a, 0x04, 0x3e, + 0x7c, 0x0c, 0x20, 0x00, 0x41, 0x80, 0xfb, 0xa4, 0x7c, 0x0c, 0xd0, 0x00, 0x40, 0x80, 0xfb, 0x9c, + 0x4b, 0xff, 0xf9, 0xd8, 0x57, 0x45, 0xff, 0xfe, 0x68, 0xa5, 0x00, 0x01, 0x71, 0x03, 0x00, 0x01, + 0x7c, 0x05, 0x18, 0x00, 0x41, 0x82, 0x00, 0x1c, 0x51, 0x1a, 0x0f, 0xbc, 0x6b, 0x5a, 0x00, 0x02, + 0x57, 0x45, 0xff, 0xff, 0x41, 0x82, 0x00, 0x08, 0x6b, 0x5a, 0x00, 0x01, 0x93, 0x4f, 0xff, 0xfc, + 0x53, 0x48, 0x07, 0xfe, 0x4b, 0xff, 0xf9, 0xa4, 0x2c, 0x0b, 0x00, 0x00, 0x40, 0x82, 0xf9, 0x94, + 0x40, 0x92, 0x00, 0x0c, 0x39, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x14, 0x54, 0x69, 0x06, 0xff, + 0x40, 0x82, 0x00, 0x08, 0x40, 0x9e, 0x00, 0x10, 0x54, 0x65, 0x67, 0xfe, 0x7d, 0x08, 0x4c, 0x30, + 0x7d, 0x08, 0x2a, 0x78, 0x54, 0x85, 0x00, 0x1f, 0x41, 0x82, 0x00, 0x08, 0x7c, 0xa6, 0x2b, 0x78, + 0x54, 0x85, 0x80, 0x1f, 0x41, 0x82, 0x00, 0x08, 0x7c, 0xb0, 0x2b, 0x78, 0x4b, 0xff, 0xf9, 0x5c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + +}; +const int kenobiwii_size = sizeof(kenobiwii); diff --git a/source/libwbfs/libwbfs.c b/source/libwbfs/libwbfs.c new file mode 100644 index 00000000..16faaebe --- /dev/null +++ b/source/libwbfs/libwbfs.c @@ -0,0 +1,618 @@ +// Copyright 2009 Kwiirk +// Licensed under the terms of the GNU GPL, version 2 +// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + + +#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))) +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); + return p; + } + } + 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<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<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<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;imax_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, u8 *data, u32 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<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<hd_sec_sz_s; + ptr += nlb<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<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;imax_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;jn_wbfs_sec_per_disc;j++) + if(wbfs_ntohs(di->wlba_table[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; + int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s; + for(i=0;imax_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;in_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<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<freeblks[i] = wbfs_htonl(v & ~(1<freeblks[i]); + p->freeblks[i] = wbfs_htonl(v | 1<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; + used = wbfs_malloc(p->n_wii_sec_per_disc); + if(!used) + ERROR("unable to alloc memory"); + 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;imax_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; + if(spinner){ + // count total number to write for spinner + for(i=0; in_wbfs_sec_per_disc;i++) + if(copy_1_1 || block_used(used,i,wii_sec_per_wbfs_sect)) tot += wii_sec_per_wbfs_sect; + spinner(0,tot); + } + for(i=0; in_wbfs_sec_per_disc;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; jwbfs_sec_sz>>2)) + (j*(p->wii_sec_sz>>2)); + + read_src_wii_disc(callback_data,offset,p->wii_sec_sz,copy_buffer); + + //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); + } + } + info->wlba_table[i] = wbfs_htons(bl); + } + // 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); +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 0; +} +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(d->header->disc_header_copy+0x20, 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; +} + +// trim the file-system to its minimum size +u32 wbfs_trim(wbfs_t*p); + +// 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; +} +u32 wbfs_extract_file(wbfs_disc_t*d, char *path); + +u32 wbfs_estimate_disc( + wbfs_t *p, read_wiidisc_callback_t read_src_wii_disc, + void *callback_data, + partition_selector_t sel) +{ + u8 *b; + int disc_info_sz_lba; + 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++; + } + } + +error: + if (d) + wd_close_disc(d); + + if (used) + wbfs_free(used); + + if (info) + wbfs_iofree(info); + + return tot * ((p->wbfs_sec_sz / p->hd_sec_sz) * 512); +} diff --git a/source/libwbfs/libwbfs.h b/source/libwbfs/libwbfs.h new file mode 100644 index 00000000..d96c6628 --- /dev/null +++ b/source/libwbfs/libwbfs.h @@ -0,0 +1,223 @@ +#ifndef LIBWBFS_H +#define LIBWBFS_H + +#include "libwbfs_os.h" // this file is provided by the project wanting to compile libwbfs +#include "wiidisc.h" + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +typedef u32 be32_t; +typedef u16 be16_t; + + + +typedef struct wbfs_head +{ + be32_t magic; + // parameters copied in the partition for easy dumping, and bug reports + be32_t n_hd_sec; // total number of hd_sec in this partition + u8 hd_sec_sz_s; // sector size in this partition + u8 wbfs_sec_sz_s; // size of a wbfs sec + u8 padding3[2]; + u8 disc_table[0]; // size depends on hd sector size +}__attribute((packed)) wbfs_head_t ; + +typedef struct wbfs_disc_info +{ + u8 disc_header_copy[0x100]; + be16_t wlba_table[0]; +}wbfs_disc_info_t; + +// WBFS first wbfs_sector structure: +// +// ----------- +// | wbfs_head | (hd_sec_sz) +// ----------- +// | | +// | disc_info | +// | | +// ----------- +// | | +// | disc_info | +// | | +// ----------- +// | | +// | ... | +// | | +// ----------- +// | | +// | disc_info | +// | | +// ----------- +// | | +// |freeblk_tbl| +// | | +// ----------- +// + +// callback definition. Return 1 on fatal error (callback is supposed to make retries until no hopes..) +typedef int (*rw_sector_callback_t)(void*fp,u32 lba,u32 count,void*iobuf); +typedef void (*progress_callback_t)(int status,int total); + + +typedef struct wbfs_s +{ + wbfs_head_t *head; + + /* hdsectors, the size of the sector provided by the hosting hard drive */ + u32 hd_sec_sz; + u8 hd_sec_sz_s; // the power of two of the last number + u32 n_hd_sec; // the number of hd sector in the wbfs partition + + /* standard wii sector (0x8000 bytes) */ + u32 wii_sec_sz; + u8 wii_sec_sz_s; + u32 n_wii_sec; + u32 n_wii_sec_per_disc; + + /* The size of a wbfs sector */ + u32 wbfs_sec_sz; + u32 wbfs_sec_sz_s; + u16 n_wbfs_sec; // this must fit in 16 bit! + u16 n_wbfs_sec_per_disc; // size of the lookup table + + u32 part_lba; + /* virtual methods to read write the partition */ + rw_sector_callback_t read_hdsector; + rw_sector_callback_t write_hdsector; + void *callback_data; + + u16 max_disc; + u32 freeblks_lba; + u32 *freeblks; + u16 disc_info_sz; + + u8 *tmp_buffer; // pre-allocated buffer for unaligned read + + u32 n_disc_open; + +}wbfs_t; + +typedef struct wbfs_disc_s +{ + wbfs_t *p; + wbfs_disc_info_t *header; // pointer to wii header + int i; // disc index in the wbfs header (disc_table) +}wbfs_disc_t; + + +#define WBFS_MAGIC (('W'<<24)|('B'<<16)|('F'<<8)|('S')) + +/*! @brief open a MSDOS partitionned harddrive. This tries to find a wbfs partition into the harddrive + @param read_hdsector,write_hdsector: accessors to a harddrive + @hd_sector_size: size of the hd sector. Can be set to zero if the partition in already initialized + @num_hd_sector: number of sectors in this disc. Can be set to zero if the partition in already initialized + @reset: not implemented, This will format the whole harddrive with one wbfs partition that fits the whole disk. + calls wbfs_error() to have textual meaning of errors + @return NULL in case of error +*/ +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, int reset); + +/*! @brief open a wbfs partition + @param read_hdsector,write_hdsector: accessors to the partition + @hd_sector_size: size of the hd sector. Can be set to zero if the partition in already initialized + @num_hd_sector: number of sectors in this partition. Can be set to zero if the partition in already initialized + @partition_lba: The partitio offset if you provided accessors to the whole disc. + @reset: initialize the partition with an empty wbfs. + calls wbfs_error() to have textual meaning of errors + @return NULL in case of error +*/ +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 partition_lba, int reset); + + +/*! @brief close a wbfs partition, and sync the metadatas to the disc */ +void wbfs_close(wbfs_t*); + +/*! @brief open a disc inside a wbfs partition use a 6 char discid+vendorid + @return NULL if discid is not present +*/ +wbfs_disc_t *wbfs_open_disc(wbfs_t* p, u8 *diskid); + +/*! @brief close a already open disc inside a wbfs partition */ +void wbfs_close_disc(wbfs_disc_t*d); + +u32 wbfs_sector_used(wbfs_t *p,wbfs_disc_info_t *di); + +/*! @brief accessor to the wii disc + @param d: a pointer to already open disc + @param offset: an offset inside the disc, *points 32bit words*, allowing to access 16GB data + @param len: The length of the data to fetch, in *bytes* + */ +// 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); + +/*! @return the number of discs inside the paritition */ +u32 wbfs_count_discs(wbfs_t*p); +/*! get the disc info of ith disc inside the partition. It correspond to the first 0x100 bytes of the wiidvd + http://www.wiibrew.org/wiki/Wiidisc#Header + @param i: index of the disc inside the partition + @param header: pointer to 0x100 bytes to write the header + @size: optional pointer to a 32bit word that will get the size in 32bit words of the DVD taken on the partition. +*/ +u32 wbfs_get_disc_info(wbfs_t*p, u32 i,u8 *header,int header_size,u32 *size); + +/*! get the number of used block of the partition. + to be multiplied by p->wbfs_sec_sz (use 64bit multiplication) to have the number in bytes +*/ +u32 wbfs_count_usedblocks(wbfs_t*p); + +/******************* write access ******************/ + +/*! add a wii dvd inside the partition + @param read_src_wii_disc: a callback to access the wii dvd. offsets are in 32bit, len in bytes! + @callback_data: private data passed to the callback + @spinner: a pointer to a function that is regulary called to update a progress bar. + @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, + progress_callback_t spinner,partition_selector_t sel,int copy_1_1); + + +/*! remove a wiidvd inside a partition */ +u32 wbfs_rm_disc(wbfs_t*p, u8* discid); + +/*! rename a game */ +u32 wbfs_ren_disc(wbfs_t*p, u8* discid, u8* newname); + +/*! trim the file-system to its minimum size + This allows to use wbfs as a wiidisc container + */ +u32 wbfs_trim(wbfs_t*p); + +/*! extract a disc from the wbfs, unused sectors are just untouched, allowing descent filesystem to only really usefull space to store the disc. +Even if the filesize is 4.7GB, the disc usage will be less. + */ +u32 wbfs_extract_disc(wbfs_disc_t*d, rw_sector_callback_t write_dst_wii_sector,void *callback_data,progress_callback_t spinner); + +/*! extract a file from the wii disc filesystem. + E.G. Allows to extract the opening.bnr to install a game as a system menu channel + */ +u32 wbfs_extract_file(wbfs_disc_t*d, char *path); + +// remove some sanity checks +void wbfs_set_force_mode(int force); + +u32 wbfs_estimate_disc( + wbfs_t *p, read_wiidisc_callback_t read_src_wii_disc, + void *callback_data, + partition_selector_t sel); + +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/source/libwbfs/libwbfs_os.h b/source/libwbfs/libwbfs_os.h new file mode 100644 index 00000000..7b3efbaa --- /dev/null +++ b/source/libwbfs/libwbfs_os.h @@ -0,0 +1,33 @@ +#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) +#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/libwbfs/rijndael.c b/source/libwbfs/rijndael.c new file mode 100644 index 00000000..baf8c871 --- /dev/null +++ b/source/libwbfs/rijndael.c @@ -0,0 +1,398 @@ +/* Rijndael Block Cipher - rijndael.c + + Written by Mike Scott 21st April 1999 + mike@compapp.dcu.ie + + Permission for free direct or derivative use is granted subject + to compliance with any conditions that the originators of the + algorithm place on its exploitation. + +*/ + +#include +#include + +#define u8 unsigned char /* 8 bits */ +#define u32 unsigned long /* 32 bits */ +#define u64 unsigned long long + +/* rotates x one bit to the left */ + +#define ROTL(x) (((x)>>7)|((x)<<1)) + +/* Rotates 32-bit word left by 1, 2 or 3 byte */ + +#define ROTL8(x) (((x)<<8)|((x)>>24)) +#define ROTL16(x) (((x)<<16)|((x)>>16)) +#define ROTL24(x) (((x)<<24)|((x)>>8)) + +/* Fixed Data */ + +static u8 InCo[4]={0xB,0xD,0x9,0xE}; /* Inverse Coefficients */ + +static u8 fbsub[256]; +static u8 rbsub[256]; +static u8 ptab[256],ltab[256]; +static u32 ftable[256]; +static u32 rtable[256]; +static u32 rco[30]; + +/* Parameter-dependent data */ + +int Nk,Nb,Nr; +u8 fi[24],ri[24]; +u32 fkey[120]; +u32 rkey[120]; + +static u32 pack(u8 *b) +{ /* pack bytes into a 32-bit Word */ + return ((u32)b[3]<<24)|((u32)b[2]<<16)|((u32)b[1]<<8)|(u32)b[0]; +} + +static void unpack(u32 a,u8 *b) +{ /* unpack bytes from a word */ + b[0]=(u8)a; + b[1]=(u8)(a>>8); + b[2]=(u8)(a>>16); + b[3]=(u8)(a>>24); +} + +static u8 xtime(u8 a) +{ + u8 b; + if (a&0x80) b=0x1B; + else b=0; + a<<=1; + a^=b; + return a; +} + +static u8 bmul(u8 x,u8 y) +{ /* x.y= AntiLog(Log(x) + Log(y)) */ + if (x && y) return ptab[(ltab[x]+ltab[y])%255]; + else return 0; +} + +static u32 SubByte(u32 a) +{ + u8 b[4]; + unpack(a,b); + b[0]=fbsub[b[0]]; + b[1]=fbsub[b[1]]; + b[2]=fbsub[b[2]]; + b[3]=fbsub[b[3]]; + return pack(b); +} + +static u8 product(u32 x,u32 y) +{ /* dot product of two 4-byte arrays */ + u8 xb[4],yb[4]; + unpack(x,xb); + unpack(y,yb); + return bmul(xb[0],yb[0])^bmul(xb[1],yb[1])^bmul(xb[2],yb[2])^bmul(xb[3],yb[3]); +} + +static u32 InvMixCol(u32 x) +{ /* matrix Multiplication */ + u32 y,m; + u8 b[4]; + + m=pack(InCo); + b[3]=product(m,x); + m=ROTL24(m); + b[2]=product(m,x); + m=ROTL24(m); + b[1]=product(m,x); + m=ROTL24(m); + b[0]=product(m,x); + y=pack(b); + return y; +} + +u8 ByteSub(u8 x) +{ + u8 y=ptab[255-ltab[x]]; /* multiplicative inverse */ + x=y; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; x=ROTL(x); + y^=x; y^=0x63; + return y; +} + +void gentables(void) +{ /* generate tables */ + int i; + u8 y,b[4]; + + /* use 3 as primitive root to generate power and log tables */ + + ltab[0]=0; + ptab[0]=1; ltab[1]=0; + ptab[1]=3; ltab[3]=1; + for (i=2;i<256;i++) + { + ptab[i]=ptab[i-1]^xtime(ptab[i-1]); + ltab[ptab[i]]=i; + } + + /* affine transformation:- each bit is xored with itself shifted one bit */ + + fbsub[0]=0x63; + rbsub[0x63]=0; + for (i=1;i<256;i++) + { + y=ByteSub((u8)i); + fbsub[i]=y; rbsub[y]=i; + } + + for (i=0,y=1;i<30;i++) + { + rco[i]=y; + y=xtime(y); + } + + /* calculate forward and reverse tables */ + for (i=0;i<256;i++) + { + y=fbsub[i]; + b[3]=y^xtime(y); b[2]=y; + b[1]=y; b[0]=xtime(y); + ftable[i]=pack(b); + + y=rbsub[i]; + b[3]=bmul(InCo[0],y); b[2]=bmul(InCo[1],y); + b[1]=bmul(InCo[2],y); b[0]=bmul(InCo[3],y); + rtable[i]=pack(b); + } +} + +void gkey(int nb,int nk,char *key) +{ /* blocksize=32*nb bits. Key=32*nk bits */ + /* currently nb,bk = 4, 6 or 8 */ + /* key comes as 4*Nk bytes */ + /* Key Scheduler. Create expanded encryption key */ + int i,j,k,m,N; + int C1,C2,C3; + u32 CipherKey[8]; + + Nb=nb; Nk=nk; + + /* Nr is number of rounds */ + if (Nb>=Nk) Nr=6+Nb; + else Nr=6+Nk; + + C1=1; + if (Nb<8) { C2=2; C3=3; } + else { C2=3; C3=4; } + + /* pre-calculate forward and reverse increments */ + for (m=j=0;j>8)])^ + ROTL16(ftable[(u8)(x[fi[m+1]]>>16)])^ + ROTL24(ftable[x[fi[m+2]]>>24]); + } + t=x; x=y; y=t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m=j=0;j>8)])^ + ROTL16((u32)fbsub[(u8)(x[fi[m+1]]>>16)])^ + ROTL24((u32)fbsub[x[fi[m+2]]>>24]); + } + for (i=j=0;i>8)])^ + ROTL16(rtable[(u8)(x[ri[m+1]]>>16)])^ + ROTL24(rtable[x[ri[m+2]]>>24]); + } + t=x; x=y; y=t; /* swap pointers */ + } + +/* Last Round - unroll if possible */ + for (m=j=0;j>8)])^ + ROTL16((u32)rbsub[(u8)(x[ri[m+1]]>>16)])^ + ROTL24((u32)rbsub[x[ri[m+2]]>>24]); + } + for (i=j=0;i +// Licensed under the terms of the GNU GPL, version 2 +// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt + +#include "wiidisc.h" + +void aes_set_key(u8 *key); +void aes_decrypt(u8 *iv, u8 *inbuf, u8 *outbuf, unsigned long long len); + +static void _decrypt_title_key(u8 *tik, u8 *title_key) +{ + u8 common_key[16]={ + 0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48, 0xd9, 0xc5, 0x45, + 0x73, 0x81, 0xaa, 0xf7 + };; + u8 iv[16]; + + wbfs_memset(iv, 0, sizeof iv); + wbfs_memcpy(iv, tik + 0x01dc, 8); + aes_set_key(common_key); + //_aes_cbc_dec(common_key, iv, tik + 0x01bf, 16, title_key); + aes_decrypt(iv, tik + 0x01bf,title_key,16); +} +static u32 _be32(const u8 *p) +{ + return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; +} + +static void disc_read(wiidisc_t *d,u32 offset, u8 *data, u32 len) +{ + if(data){ + int ret=0; + if(len==0) + return ; + ret = d->read(d->fp,offset,len,data); + if(ret) + wbfs_fatal("error reading disc"); + } + if(d->sector_usage_table) + { + u32 blockno = offset>>13; + do + { + d->sector_usage_table[blockno]=1; + blockno+=1; + if(len>0x8000) + len-=0x8000; + }while(len>0x8000); + } +} + +static void partition_raw_read(wiidisc_t *d,u32 offset, u8 *data, u32 len) +{ + disc_read(d, d->partition_raw_offset + offset, data, len); +} + +static void partition_read_block(wiidisc_t *d,u32 blockno, u8 *block) +{ + u8*raw = d->tmp_buffer; + u8 iv[16]; + u32 offset; + if(d->sector_usage_table) + d->sector_usage_table[d->partition_block+blockno]=1; + offset = d->partition_data_offset + ((0x8000>>2) * blockno); + partition_raw_read(d,offset, raw, 0x8000); + + // decrypt data + memcpy(iv, raw + 0x3d0, 16); + aes_set_key(d->disc_key); + aes_decrypt(iv, raw + 0x400,block,0x7c00); +} + +static void partition_read(wiidisc_t *d,u32 offset, u8 *data, u32 len,int fake) +{ + u8 *block = d->tmp_buffer2; + u32 offset_in_block; + u32 len_in_block; + if(fake && d->sector_usage_table==0) + return; + + while(len) { + offset_in_block = offset % (0x7c00>>2); + len_in_block = 0x7c00 - (offset_in_block<<2); + if (len_in_block > len) + len_in_block = len; + if(!fake){ + partition_read_block(d,offset / (0x7c00>>2), block); + wbfs_memcpy(data, block + (offset_in_block<<2), len_in_block); + }else + d->sector_usage_table[d->partition_block+(offset/(0x7c00>>2))]=1; + data += len_in_block; + offset += len_in_block>>2; + len -= len_in_block; + } +} + + +static u32 do_fst(wiidisc_t *d,u8 *fst, const char *names, u32 i) +{ + u32 offset; + u32 size; + const char *name; + u32 j; + + name = names + (_be32(fst + 12*i) & 0x00ffffff); + size = _be32(fst + 12*i + 8); + + if (i == 0) { + for (j = 1; j < size && !d->extracted_buffer; ){ + j = do_fst(d,fst, names, j); + } + return size; + } + //printf("name %s\n",name); + + if (fst[12*i]) { + + for (j = i + 1; j < size && !d->extracted_buffer; ) + j = do_fst(d,fst, names, j); + + return size; + } else { + offset = _be32(fst + 12*i + 4); + if(d->extract_pathname && strcmp(name, d->extract_pathname)==0) + { + d->extracted_buffer = wbfs_ioalloc(size); + partition_read(d,offset, d->extracted_buffer, size,0); + }else + partition_read(d,offset, 0, size,1); + return i + 1; + } +} + +static void do_files(wiidisc_t*d) +{ + u8 *b = wbfs_ioalloc(0x480); // XXX: determine actual header size + u32 dol_offset; + u32 fst_offset; + u32 fst_size; + u32 apl_offset; + u32 apl_size; + u8 *apl_header = wbfs_ioalloc(0x20); + u8 *fst; + u32 n_files; + partition_read(d,0, b, 0x480,0); + + dol_offset = _be32(b + 0x0420); + fst_offset = _be32(b + 0x0424); + fst_size = _be32(b + 0x0428)<<2; + + apl_offset = 0x2440>>2; + partition_read(d,apl_offset, apl_header, 0x20,0); + apl_size = 0x20 + _be32(apl_header + 0x14) + _be32(apl_header + 0x18); + // fake read dol and partition + partition_read(d,apl_offset, 0, apl_size,1); + partition_read(d,dol_offset, 0, (fst_offset - dol_offset)<<2,1); + + + fst = wbfs_ioalloc(fst_size); + if (fst == 0) + wbfs_fatal("malloc fst"); + partition_read(d,fst_offset, fst, fst_size,0); + n_files = _be32(fst + 8); + + if (n_files > 1) + do_fst(d,fst, (char *)fst + 12*n_files, 0); + wbfs_iofree(b); + wbfs_iofree(apl_header); + wbfs_iofree(fst); +} + +static void do_partition(wiidisc_t*d) +{ + u8 *tik = wbfs_ioalloc(0x2a4); + u8 *b = wbfs_ioalloc(0x1c); + u64 tmd_offset; + u32 tmd_size; + u8 *tmd; + u64 cert_offset; + u32 cert_size; + u8 *cert; + u64 h3_offset; + + // read ticket, and read some offsets and sizes + partition_raw_read(d,0, tik, 0x2a4); + partition_raw_read(d,0x2a4>>2, b, 0x1c); + + tmd_size = _be32(b); + tmd_offset = _be32(b + 4); + cert_size = _be32(b + 8); + cert_offset = _be32(b + 0x0c); + h3_offset = _be32(b + 0x10); + d->partition_data_offset = _be32(b + 0x14); + d->partition_block = (d->partition_raw_offset+d->partition_data_offset)>>13; + tmd = wbfs_ioalloc(tmd_size); + if (tmd == 0) + wbfs_fatal("malloc tmd"); + partition_raw_read(d,tmd_offset, tmd, tmd_size); + + cert = wbfs_ioalloc(cert_size); + if (cert == 0) + wbfs_fatal("malloc cert"); + partition_raw_read(d,cert_offset, cert, cert_size); + + + _decrypt_title_key(tik, d->disc_key); + + partition_raw_read(d,h3_offset, 0, 0x18000); + wbfs_iofree(b); + wbfs_iofree(tik); + wbfs_iofree(cert); + wbfs_iofree(tmd); + + do_files(d); + +} +static int test_parition_skip(u32 partition_type,partition_selector_t part_sel) +{ + switch(part_sel) + { + case ALL_PARTITIONS: + return 0; + case REMOVE_UPDATE_PARTITION: + return (partition_type==1); + case ONLY_GAME_PARTITION: + return (partition_type!=0); + default: + return (partition_type!=part_sel); + } +} +static void do_disc(wiidisc_t*d) +{ + u8 *b = wbfs_ioalloc(0x100); + u64 partition_offset[32]; // XXX: don't know the real maximum + u64 partition_type[32]; // XXX: don't know the real maximum + u32 n_partitions; + u32 magic; + u32 i; + disc_read(d,0, b, 0x100); + magic=_be32(b+24); + if(magic!=0x5D1C9EA3){ + wbfs_error("not a wii disc"); + return ; + } + disc_read(d,0x40000>>2, b, 0x100); + n_partitions = _be32(b); + disc_read(d,_be32(b + 4), b, 0x100); + for (i = 0; i < n_partitions; i++){ + partition_offset[i] = _be32(b + 8 * i); + partition_type[i] = _be32(b + 8 * i+4); + } + for (i = 0; i < n_partitions; i++) { + d->partition_raw_offset = partition_offset[i]; + if(!test_parition_skip(partition_type[i],d->part_sel)) + do_partition(d); + } + wbfs_iofree(b); +} + +wiidisc_t *wd_open_disc(read_wiidisc_callback_t read,void*fp) +{ + wiidisc_t *d = wbfs_malloc(sizeof(wiidisc_t)); + if(!d) + return 0; + wbfs_memset(d,0,sizeof(wiidisc_t)); + d->read = read; + d->fp = fp; + d->part_sel = ALL_PARTITIONS; + d->tmp_buffer = wbfs_ioalloc(0x8000); + d->tmp_buffer2 = wbfs_malloc(0x8000); + + return d; +} +void wd_close_disc(wiidisc_t *d) +{ + wbfs_iofree(d->tmp_buffer); + wbfs_free(d->tmp_buffer2); + wbfs_free(d); +} +// returns a buffer allocated with wbfs_ioalloc() or NULL if not found of alloc error +// XXX pathname not implemented. files are extracted by their name. +// first file found with that name is returned. +u8 * wd_extract_file(wiidisc_t *d, partition_selector_t partition_type, char *pathname) +{ + u8 *retval = 0; + d->extract_pathname = pathname; + d->extracted_buffer = 0; + d->part_sel = partition_type; + do_disc(d); + d->extract_pathname = 0; + d->part_sel = ALL_PARTITIONS; + retval = d->extracted_buffer; + d->extracted_buffer = 0; + return retval; +} + +void wd_build_disc_usage(wiidisc_t *d, partition_selector_t selector, u8* usage_table) +{ + d->sector_usage_table = usage_table; + wbfs_memset(usage_table,0,143432*2); + d->part_sel = selector; + do_disc(d); + d->part_sel = ALL_PARTITIONS; + d->sector_usage_table = 0; +} + +void wd_fix_partition_table(wiidisc_t *d, partition_selector_t selector, u8* partition_table) +{ + u8 *b = partition_table; + u32 partition_offset; + u32 partition_type; + u32 n_partitions,i,j; + u32 *b32; + if(selector == ALL_PARTITIONS) + return; + n_partitions = _be32(b); + if(_be32(b + 4)-(0x40000>>2) >0x50) + wbfs_fatal("cannot modify this partition table. Please report the bug."); + + b += (_be32(b + 4)-(0x40000>>2))*4; + j=0; + for (i = 0; i < n_partitions; i++){ + partition_offset = _be32(b + 8 * i); + partition_type = _be32(b + 8 * i+4); + if(!test_parition_skip(partition_type,selector)) + { + b32 = (u32*)(b + 8 * j); + b32[0] = wbfs_htonl(partition_offset); + b32[1] = wbfs_htonl(partition_type); + j++; + } + } + b32 = (u32*)(partition_table); + *b32 = wbfs_htonl(j); +} + diff --git a/source/libwbfs/wiidisc.h b/source/libwbfs/wiidisc.h new file mode 100644 index 00000000..24a27f43 --- /dev/null +++ b/source/libwbfs/wiidisc.h @@ -0,0 +1,67 @@ +#ifndef WIIDISC_H +#define WIIDISC_H +#include +#include "libwbfs_os.h" // this file is provided by the project wanting to compile libwbfs and wiidisc + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ +#if 0 //removes extra automatic indentation by editors + } +#endif +// callback definition. Return 1 on fatal error (callback is supposed to make retries until no hopes..) +// offset points 32bit words, count counts bytes +typedef int (*read_wiidisc_callback_t)(void*fp,u32 offset,u32 count,void*iobuf); + +typedef enum{ + UPDATE_PARTITION_TYPE=0, + GAME_PARTITION_TYPE, + OTHER_PARTITION_TYPE, + // value in between selects partition types of that value + ALL_PARTITIONS=0xffffffff-3, + REMOVE_UPDATE_PARTITION, // keeps game + channel installers + ONLY_GAME_PARTITION, +}partition_selector_t; + +typedef struct wiidisc_s +{ + read_wiidisc_callback_t read; + void *fp; + u8 *sector_usage_table; + + // everything points 32bit words. + u32 disc_raw_offset; + u32 partition_raw_offset; + u32 partition_data_offset; + u32 partition_data_size; + u32 partition_block; + + u8 *tmp_buffer; + u8 *tmp_buffer2; + u8 disc_key[16]; + int dont_decrypt; + + partition_selector_t part_sel; + + char *extract_pathname; + u8 *extracted_buffer; +}wiidisc_t; + +wiidisc_t *wd_open_disc(read_wiidisc_callback_t read,void*fp); +void wd_close_disc(wiidisc_t *); +// returns a buffer allocated with wbfs_ioalloc() or NULL if not found of alloc error +u8 * wd_extract_file(wiidisc_t *d, partition_selector_t partition_type, char *pathname); + +void wd_build_disc_usage(wiidisc_t *d, partition_selector_t selector, u8* usage_table); + +// effectively remove not copied partition from the partition table. +void wd_fix_partition_table(wiidisc_t *d, partition_selector_t selector, u8* partition_table); + +#if 0 +{ +#endif +#ifdef __cplusplus + } +#endif /* __cplusplus */ + +#endif diff --git a/source/libwiigui/gui.h b/source/libwiigui/gui.h new file mode 100644 index 00000000..3459042a --- /dev/null +++ b/source/libwiigui/gui.h @@ -0,0 +1,839 @@ +/*!\mainpage libwiigui Documentation + * + * \section Introduction + * libwiigui is a GUI library for the Wii, created to help structure the + * design of a complicated GUI interface, and to enable an author to create + * a sophisticated, feature-rich GUI. It was originally conceived and written + * after I started to design a GUI for Snes9x GX, and found libwiisprite and + * GRRLIB inadequate for the purpose. It uses GX for drawing, and makes use + * of PNGU for displaying images and FreeTypeGX for text. It was designed to + * be flexible and is easy to modify - don't be afraid to change the way it + * works or expand it to suit your GUI's purposes! If you do, and you think + * your changes might benefit others, please share them so they might be + * added to the project! + * + * \section Quickstart + * Start from the supplied template example. For more advanced uses, see the + * source code for Snes9x GX, FCE Ultra GX, and Visual Boy Advance GX. + + * \section Contact + * If you have any suggestions for the library or documentation, or want to + * contribute, please visit the libwiigui website: + + * http://code.google.com/p/libwiigui/ + * \section Credits + * This library was wholly designed and written by Tantric. Thanks to the + * authors of PNGU and FreeTypeGX, of which this library makes use. Thanks + * also to the authors of GRRLIB and libwiisprite for laying the foundations. + * +*/ + +#ifndef LIBWIIGUI_H +#define LIBWIIGUI_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pngu/pngu.h" +#include "FreeTypeGX.h" +#include "video.h" +#include "filelist.h" +#include "input.h" +#include "oggplayer.h" + +extern FreeTypeGX *fontSystem; + +#define SCROLL_INITIAL_DELAY 20 +#define SCROLL_LOOP_DELAY 3 +#define PAGESIZE 9 +#define MAX_OPTIONS 170 + +typedef void (*UpdateCallback)(void * e); + +enum +{ + ALIGN_LEFT, + ALIGN_RIGHT, + ALIGN_CENTRE, + ALIGN_TOP, + ALIGN_BOTTOM, + ALIGN_MIDDLE +}; + +enum +{ + STATE_DEFAULT, + STATE_SELECTED, + STATE_CLICKED, + STATE_HELD, + STATE_DISABLED +}; + +enum +{ + SOUND_PCM, + SOUND_OGG +}; + +enum +{ + IMAGE_TEXTURE, + IMAGE_COLOR, + IMAGE_DATA +}; + +enum +{ + TRIGGER_SIMPLE, + TRIGGER_HELD, + TRIGGER_BUTTON_ONLY, + TRIGGER_BUTTON_ONLY_IN_FOCUS +}; + +typedef struct _paddata { + u16 btns_d; + u16 btns_u; + u16 btns_h; + s8 stickX; + s8 stickY; + s8 substickX; + s8 substickY; + u8 triggerL; + u8 triggerR; +} PADData; + +#define EFFECT_SLIDE_TOP 1 +#define EFFECT_SLIDE_BOTTOM 2 +#define EFFECT_SLIDE_RIGHT 4 +#define EFFECT_SLIDE_LEFT 8 +#define EFFECT_SLIDE_IN 16 +#define EFFECT_SLIDE_OUT 32 +#define EFFECT_FADE 64 +#define EFFECT_SCALE 128 +#define EFFECT_COLOR_TRANSITION 256 + +//!Sound conversion and playback. A wrapper for other sound libraries - ASND, libmad, ltremor, etc +class GuiSound +{ + public: + //!Constructor + //!\param s Pointer to the sound data + //!\param l Length of sound data + //!\param t Sound format type (SOUND_PCM or SOUND_OGG) + GuiSound(const u8 * s, int l, int t); + GuiSound(const u8 * s, int l, int t, int v); + //!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); + protected: + const u8 * sound; //!< Pointer to the sound data + int type; //!< Sound format type (SOUND_PCM or SOUND_OGG) + s32 length; //!< Length of sound data + s32 voice; //!< Currently assigned ASND voice channel + s32 volume; //!< Sound volume (0-100) + bool loop; //!< Loop sound playback +}; + +//!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 +{ + public: + //!Constructor + GuiTrigger(); + //!Destructor + ~GuiTrigger(); + //!Sets a simple trigger. Requires: element is selected, and trigger button is pressed + //!\param ch Controller channel number + //!\param wiibtns Wii controller trigger button(s) - classic controller buttons are considered separately + //!\param gcbtns GameCube controller trigger button(s) + void SetSimpleTrigger(s32 ch, u32 wiibtns, u16 gcbtns); + //!Sets a held trigger. Requires: element is selected, and trigger button is pressed + //!\param ch Controller channel number + //!\param wiibtns Wii controller trigger button(s) - classic controller buttons are considered separately + //!\param gcbtns GameCube controller trigger button(s) + void SetHeldTrigger(s32 ch, u32 wiibtns, u16 gcbtns); + //!Sets a button-only trigger. Requires: Trigger button is pressed + //!\param ch Controller channel number + //!\param wiibtns Wii controller trigger button(s) - classic controller buttons are considered separately + //!\param gcbtns GameCube controller trigger button(s) + void SetButtonOnlyTrigger(s32 ch, u32 wiibtns, u16 gcbtns); + //!Sets a button-only trigger. Requires: trigger button is pressed and parent window of element is in focus + //!\param ch Controller channel number + //!\param wiibtns Wii controller trigger button(s) - classic controller buttons are considered separately + //!\param gcbtns GameCube controller trigger button(s) + void SetButtonOnlyInFocusTrigger(s32 ch, u32 wiibtns, u16 gcbtns); + //!Get X/Y value from Wii Joystick (classic, nunchuk) input + //!\param right Controller stick (left = 0, right = 1) + //!\param axis Controller stick axis (x-axis = 0, y-axis = 1) + //!\return Stick value + s8 WPAD_Stick(u8 right, int axis); + //!Move menu selection left (via pad/joystick). Allows scroll delay and button overriding + //!\return true if selection should be moved left, false otherwise + bool Left(); + //!Move menu selection right (via pad/joystick). Allows scroll delay and button overriding + //!\return true if selection should be moved right, false otherwise + bool Right(); + //!Move menu selection up (via pad/joystick). Allows scroll delay and button overriding + //!\return true if selection should be moved up, false otherwise + bool Up(); + //!Move menu selection down (via pad/joystick). Allows scroll delay and button overriding + //!\return true if selection should be moved down, false otherwise + bool Down(); + + u8 type; //!< trigger type (TRIGGER_SIMPLE, TRIGGER_HELD, TRIGGER_BUTTON_ONLY, TRIGGER_BUTTON_ONLY_IN_FOCUS) + s32 chan; //!< Trigger controller channel (0-3, -1 for all) + WPADData wpad; //!< Wii controller trigger data + PADData pad; //!< GameCube controller trigger data +}; + +extern GuiTrigger userInput[4]; + +//!Primary GUI class. Most other classes inherit from this class. +class GuiElement +{ + public: + //!Constructor + GuiElement(); + //!Destructor + ~GuiElement(); + //!Set the element's parent + //!\param e Pointer to parent element + void SetParent(GuiElement * e); + //!Gets the current leftmost coordinate of the element + //!Considers horizontal alignment, x offset, width, and parent element's GetLeft() / GetWidth() values + //!\return left coordinate + int GetLeft(); + //!Gets the current topmost coordinate of the element + //!Considers vertical alignment, y offset, height, and parent element's GetTop() / GetHeight() values + //!\return top coordinate + int GetTop(); + //!Sets the minimum y offset of the element + //!\param y Y offset + void SetMinY(int y); + //!Gets the minimum y offset of the element + //!\return Minimum Y offset + int GetMinY(); + //!Sets the maximum y offset of the element + //!\param y Y offset + void SetMaxY(int y); + //!Gets the maximum y offset of the element + //!\return Maximum Y offset + int GetMaxY(); + //!Sets the minimum x offset of the element + //!\param x X offset + void SetMinX(int x); + //!Gets the minimum x offset of the element + //!\return Minimum X offset + int GetMinX(); + //!Sets the maximum x offset of the element + //!\param x X offset + void SetMaxX(int x); + //!Gets the maximum x offset of the element + //!\return Maximum X offset + int GetMaxX(); + //!Gets the current width of the element. Does not currently consider the scale + //!\return width + int GetWidth(); + //!Gets the height of the element. Does not currently consider the scale + //!\return height + int GetHeight(); + //!Sets the size (width/height) of the element + //!\param w Width of element + //!\param h Height of element + void SetSize(int w, int h); + //!Checks whether or not the element is visible + //!\return true if visible, false otherwise + bool IsVisible(); + //!Checks whether or not the element is selectable + //!\return true if selectable, false otherwise + bool IsSelectable(); + //!Checks whether or not the element is clickable + //!\return true if clickable, false otherwise + bool IsClickable(); + //!Checks whether or not the element is holdable + //!\return true if holdable, false otherwise + bool IsHoldable(); + //!Sets whether or not the element is selectable + //!\param s Selectable + void SetSelectable(bool s); + //!Sets whether or not the element is clickable + //!\param c Clickable + void SetClickable(bool c); + //!Sets whether or not the element is holdable + //!\param c Holdable + void SetHoldable(bool d); + //!Gets the element's current state + //!\return state + int GetState(); + //!Gets the controller channel that last changed the element's state + //!\return Channel number (0-3, -1 = no channel) + int GetStateChan(); + //!Sets the element's alpha value + //!\param a alpha value + void SetAlpha(int a); + //!Gets the element's alpha value + //!Considers alpha, alphaDyn, and the parent element's GetAlpha() value + //!\return alpha + int GetAlpha(); + //!Sets the element's scale + //!\param s scale (1 is 100%) + void SetScale(float s); + //!Gets the element's current scale + //!Considers scale, scaleDyn, and the parent element's GetScale() value + float GetScale(); + //!Set a new GuiTrigger for the element + //!\param t Pointer to GuiTrigger + void SetTrigger(GuiTrigger * t); + //!\overload + //!\param i Index of trigger array to set + //!\param t Pointer to GuiTrigger + void SetTrigger(u8 i, GuiTrigger * t); + //!Checks whether rumble was requested by the element + //!\return true is rumble was requested, false otherwise + bool Rumble(); + //!Sets whether or not the element is requesting a rumble event + //!\param r true if requesting rumble, false if not + void SetRumble(bool r); + //!Set an effect for the element + //!\param e Effect to enable + //!\param a Amount of the effect (usage varies on effect) + //!\param t Target amount of the effect (usage varies on effect) + void SetEffect(int e, int a, int t=0); + //!Sets an effect to be enabled on wiimote cursor over + //!\param e Effect to enable + //!\param a Amount of the effect (usage varies on effect) + //!\param t Target amount of the effect (usage varies on effect) + void SetEffectOnOver(int e, int a, int t=0); + //!Shortcut to SetEffectOnOver(EFFECT_SCALE, 4, 110) + void SetEffectGrow(); + //!Gets the current element effects + //!\return element effects + int GetEffect(); + //!Checks whether the specified coordinates are within the element's boundaries + //!\param x X coordinate + //!\param y Y coordinate + //!\return true if contained within, false otherwise + bool IsInside(int x, int y); + //!Sets the element's position + //!\param x X coordinate + //!\param y Y coordinate + void SetPosition(int x, int y); + //!Updates the element's effects (dynamic values) + //!Called by Draw(), used for animation purposes + void UpdateEffects(); + //!Sets a function to called after after Update() + //!Callback function can be used to response to changes in the state of the element, and/or update the element's attributes + void SetUpdateCallback(UpdateCallback u); + //!Checks whether the element is in focus + //!\return true if element is in focus, false otherwise + int IsFocused(); + //!Sets the element's visibility + //!\param v Visibility (true = visible) + virtual void SetVisible(bool v); + //!Sets the element's focus + //!\param f Focus (true = in focus) + virtual void SetFocus(int f); + //!Sets the element's state + //!\param s State (STATE_DEFAULT, STATE_SELECTED, STATE_CLICKED, STATE_DISABLED) + //!\param c Controller channel (0-3, -1 = none) + virtual void SetState(int s, int c = -1); + //!Resets the element's state to STATE_DEFAULT + virtual void ResetState(); + //!Gets whether or not the element is in STATE_SELECTED + //!\return true if selected, false otherwise + virtual int GetSelected(); + //!Sets the element's alignment respective to its parent element + //!\param hor Horizontal alignment (ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTRE) + //!\param vert Vertical alignment (ALIGN_TOP, ALIGN_BOTTOM, ALIGN_MIDDLE) + virtual void SetAlignment(int hor, int vert); + //!Called constantly to allow the element to respond to the current input data + //!\param t Pointer to a GuiTrigger, containing the current input data from PAD/WPAD + virtual void Update(GuiTrigger * t); + //!Called constantly to redraw the element + virtual void Draw(); + protected: + //int position2; //! B Scrollbariable + bool visible; //!< Visibility of the element. If false, Draw() is skipped + int focus; //!< Element focus (-1 = focus disabled, 0 = not focused, 1 = focused) + int width; //!< Element width + int height; //!< Element height + int xoffset; //!< Element X offset + int yoffset; //!< Element Y offset + int ymin; //!< Element's min Y offset allowed + int ymax; //!< Element's max Y offset allowed + int xmin; //!< Element's min X offset allowed + int xmax; //!< Element's max X offset allowed + int xoffsetDyn; //!< Element X offset, dynamic (added to xoffset value for animation effects) + int yoffsetDyn; //!< Element Y offset, dynamic (added to yoffset value for animation effects) + int alpha; //!< Element alpha value (0-255) + f32 scale; //!< Element scale (1 = 100%) + int alphaDyn; //!< Element alpha, dynamic (multiplied by alpha value for blending/fading effects) + f32 scaleDyn; //!< Element scale, dynamic (multiplied by alpha value for blending/fading effects) + bool rumble; //!< Wiimote rumble (on/off) - set to on when this element requests a rumble event + int effects; //!< Currently enabled effect(s). 0 when no effects are enabled + int effectAmount; //!< Effect amount. Used by different effects for different purposes + int effectTarget; //!< Effect target amount. Used by different effects for different purposes + int effectsOver; //!< Effects to enable when wiimote cursor is over this element. Copied to effects variable on over event + int effectAmountOver; //!< EffectAmount to set when wiimote cursor is over this element + int effectTargetOver; //!< EffectTarget to set when wiimote cursor is over this element + int alignmentHor; //!< Horizontal element alignment, respective to parent element (LEFT, RIGHT, CENTRE) + int alignmentVert; //!< Horizontal element alignment, respective to parent element (TOP, BOTTOM, MIDDLE) + int state; //!< Element state (DEFAULT, SELECTED, CLICKED, DISABLED) + int stateChan; //!< Which controller channel is responsible for the last change in state + bool selectable; //!< Whether or not this element selectable (can change to SELECTED state) + bool clickable; //!< Whether or not this element is clickable (can change to CLICKED state) + bool holdable; //!< Whether or not this element is holdable (can change to HELD state) + GuiTrigger * trigger[5]; //!< GuiTriggers (input actions) that this element responds to + GuiElement * parentElement; //!< Parent element + UpdateCallback updateCB; //!< Callback function to call when this element is updated +}; + +//!Allows GuiElements to be grouped together into a "window" +class GuiWindow : public GuiElement +{ + public: + //!Constructor + GuiWindow(); + //!\overload + //!\param w Width of window + //!\param h Height of window + GuiWindow(int w, int h); + //!Destructor + ~GuiWindow(); + //!Appends a GuiElement to the GuiWindow + //!\param e The GuiElement to append. If it is already in the GuiWindow, it is removed first + void Append(GuiElement* e); + //!Inserts a GuiElement into the GuiWindow at the specified index + //!\param e The GuiElement to insert. If it is already in the GuiWindow, it is removed first + //!\param i Index in which to insert the element + void Insert(GuiElement* e, u32 i); + //!Removes the specified GuiElement from the GuiWindow + //!\param e GuiElement to be removed + void Remove(GuiElement* e); + //!Removes all GuiElements + void RemoveAll(); + //!Returns the GuiElement at the specified index + //!\param index The index of the element + //!\return A pointer to the element at the index, NULL on error (eg: out of bounds) + GuiElement* GetGuiElementAt(u32 index) const; + //!Returns the size of the list of elements + //!\return The size of the current element list + u32 GetSize(); + //!Sets the visibility of the window + //!\param v visibility (true = visible) + void SetVisible(bool v); + //!Resets the window's state to STATE_DEFAULT + void ResetState(); + //!Sets the window's state + //!\param s State + void SetState(int s); + //!Gets the index of the GuiElement inside the window that is currently selected + //!\return index of selected GuiElement + int GetSelected(); + //!Sets the window focus + //!\param f Focus + void SetFocus(int f); + //!Change the focus to the specified element + //!This is intended for the primary GuiWindow only + //!\param e GuiElement that should have focus + void ChangeFocus(GuiElement * e); + //!Changes window focus to the next focusable window or element + //!If no element is in focus, changes focus to the first available element + //!If B or 1 button is pressed, changes focus to the next available element + //!This is intended for the primary GuiWindow only + //!\param t Pointer to a GuiTrigger, containing the current input data from PAD/WPAD + void ToggleFocus(GuiTrigger * t); + //!Moves the selected element to the element to the left or right + //!\param d Direction to move (-1 = left, 1 = right) + void MoveSelectionHor(int d); + //!Moves the selected element to the element above or below + //!\param d Direction to move (-1 = up, 1 = down) + void MoveSelectionVert(int d); + //!Draws all the elements in this GuiWindow + void Draw(); + //!Updates the window and all elements contains within + //!Allows the GuiWindow and all elements to respond to the input data specified + //!\param t Pointer to a GuiTrigger, containing the current input data from PAD/WPAD + void Update(GuiTrigger * t); + protected: + std::vector _elements; //!< Contains all elements within the GuiWindow +}; + +//!Converts image data into GX-useable RGBA8. Currently designed for use only with PNG files +class GuiImageData +{ + public: + //!Constructor + //!Converts the image data to RGBA8 - expects PNG format + //!\param i Image data + GuiImageData(const u8 * i); + GuiImageData(const char * imgPath, const u8 * buffer); + //!Destructor + ~GuiImageData(); + //!Gets a pointer to the image data + //!\return pointer to image data + u8 * GetImage(); + //!Gets the image width + //!\return image width + int GetWidth(); + //!Gets the image height + //!\return image height + int GetHeight(); + protected: + u8 * data; //!< Image data + int height; //!< Height of image + int width; //!< Width of image +}; + +//!Display, manage, and manipulate images in the GUI +class GuiImage : public GuiElement +{ + public: + //!Constructor + GuiImage(); + //!\overload + //!\param img Pointer to GuiImageData element + GuiImage(GuiImageData * img); + //!\overload + //!Sets up a new image from the image data specified + //!\param img + //!\param w Image width + //!\param h Image height + GuiImage(u8 * img, int w, int h); + //!\overload + //!Creates an image filled with the specified color + //!\param w Image width + //!\param h Image height + //!\param c Image color + GuiImage(int w, int h, GXColor c); + //!Destructor + ~GuiImage(); + //!Sets the image rotation angle for drawing + //!\param a Angle (in degrees) + void SetAngle(float a); + //!Sets the number of times to draw the image horizontally + //!\param t Number of times to draw the image + void SetTile(int t); + // not NULL set horizontal scale to 0.8 //added + void SetWidescreen(short w); + //!Constantly called to draw the image + void Draw(); + //!Gets the image data + //!\return pointer to image data + u8 * GetImage(); + //!Sets up a new image using the GuiImageData object specified + //!\param img Pointer to GuiImageData object + void SetImage(GuiImageData * img); + //!\overload + //!\param img Pointer to image data + //!\param w Width + //!\param h Height + void SetImage(u8 * img, int w, int h); + //!Gets the pixel color at the specified coordinates of the image + //!\param x X coordinate + //!\param y Y coordinate + GXColor GetPixel(int x, int y); + //!Sets the pixel color at the specified coordinates of the image + //!\param x X coordinate + //!\param y Y coordinate + //!\param color Pixel color + void SetPixel(int x, int y, GXColor color); + //!Directly modifies the image data to create a color-striped effect + //!Alters the RGB values by the specified amount + //!\param s Amount to increment/decrement the RGB values in the image + void ColorStripe(int s); + //!Sets a stripe effect on the image, overlaying alpha blended rectangles + //!Does not alter the image data + //!\param s Alpha amount to draw over the image + void SetStripe(int s); + protected: + int imgType; //!< Type of image data (IMAGE_TEXTURE, IMAGE_COLOR, IMAGE_DATA) + u8 * image; //!< Poiner to image data. May be shared with GuiImageData data + f32 imageangle; //!< Angle to draw the image + int tile; //!< Number of times to draw (tile) the image horizontally + int stripe; //!< Alpha value (0-255) to apply a stripe effect to the texture + short widescreen; //added +}; + +//!Display, manage, and manipulate text in the GUI +class GuiText : public GuiElement +{ + public: + //!Constructor + //!\param t Text + //!\param s Font size + //!\param c Font color + GuiText(const char * t, int s, GXColor c); + //!\overload + //!\Assumes SetPresets() has been called to setup preferred text attributes + //!\param t Text + GuiText(const char * t); + //!Destructor + ~GuiText(); + //!Sets the text of the GuiText element + //!\param t Text + void SetText(const char * t); + //!Sets up preset values to be used by GuiText(t) + //!Useful when printing multiple text elements, all with the same attributes set + //!\param sz Font size + //!\param c Font color + //!\param w Maximum width of texture image (for text wrapping) + //!\param s Font size + //!\param h Text alignment (horizontal) + //!\param v Text alignment (vertical) + void SetPresets(int sz, GXColor c, int w, u16 s, int h, int v); + //!Sets the font size + //!\param s Font size + void SetFontSize(int s); + //!Sets the maximum width of the drawn texture image + //!If the text exceeds this, it is wrapped to the next line + //!\param w Maximum width + void SetMaxWidth(int w); + //!Sets the font color + //!\param c Font color + void SetColor(GXColor c); + //!Sets the FreeTypeGX style attributes + //!\param s Style attributes + void SetStyle(u16 s); + //!Sets the text alignment + //!\param hor Horizontal alignment (ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTRE) + //!\param vert Vertical alignment (ALIGN_TOP, ALIGN_BOTTOM, ALIGN_MIDDLE) + void SetAlignment(int hor, int vert); + //!Constantly called to draw the text + void Draw(); + protected: + wchar_t* text; //!< Unicode text value + int size; //!< Font size + int maxWidth; //!< Maximum width of the generated text object (for text wrapping) + u16 style; //!< FreeTypeGX style attributes + GXColor color; //!< Font color +}; + +//!Display, manage, and manipulate buttons in the GUI. Buttons can have images, icons, text, and sound set (all of which are optional) +class GuiButton : public GuiElement +{ + public: + //!Constructor + //!\param w Width + //!\param h Height + GuiButton(int w, int h); + //!Destructor + ~GuiButton(); + //!Sets the button's image + //!\param i Pointer to GuiImage object + void SetImage(GuiImage* i); + //!Sets the button's image on over + //!\param i Pointer to GuiImage object + void SetImageOver(GuiImage* i); + //!Sets the button's image on hold + //!\param i Pointer to GuiImage object + void SetAngle(float a); + void SetImageHold(GuiImage* i); + //!Sets the button's image on click + //!\param i Pointer to GuiImage object + void SetImageClick(GuiImage* i); + //!Sets the button's icon + //!\param i Pointer to GuiImage object + void SetIcon(GuiImage* i); + //!Sets the button's icon on over + //!\param i Pointer to GuiImage object + void SetIconOver(GuiImage* i); + //!Sets the button's icon on hold + //!\param i Pointer to GuiImage object + void SetIconHold(GuiImage* i); + //!Sets the button's icon on click + //!\param i Pointer to GuiImage object + void SetIconClick(GuiImage* i); + //!Sets the button's label + //!\param t Pointer to GuiText object + //!\param n Index of label to set (optional, default is 0) + void SetLabel(GuiText* t, int n = 0); + //!Sets the button's label on over (eg: different colored text) + //!\param t Pointer to GuiText object + //!\param n Index of label to set (optional, default is 0) + void SetLabelOver(GuiText* t, int n = 0); + //!Sets the button's label on hold + //!\param t Pointer to GuiText object + //!\param n Index of label to set (optional, default is 0) + void SetLabelHold(GuiText* t, int n = 0); + //!Sets the button's label on click + //!\param t Pointer to GuiText object + //!\param n Index of label to set (optional, default is 0) + void SetLabelClick(GuiText* t, int n = 0); + //!Sets the sound to play on over + //!\param s Pointer to GuiSound object + void SetSoundOver(GuiSound * s); + //!Sets the sound to play on hold + //!\param s Pointer to GuiSound object + void SetSoundHold(GuiSound * s); + //!Sets the sound to play on click + //!\param s Pointer to GuiSound object + void SetSoundClick(GuiSound * s); + //!Constantly called to draw the GuiButtons ToolTip + //!Sets the button's Tooltip on over + //!\param i Pointer to GuiImage object, t Pointer to GuiText, x & y Positioning + void SetToolTip(GuiImage* i, GuiText * t, int x, int y); + //!Constantly called to draw the GuiButton + void Draw(); + //!Constantly called to allow the GuiButton to respond to updated input data + //!\param t Pointer to a GuiTrigger, containing the current input data from PAD/WPAD + void Update(GuiTrigger * t); + //!Deactivate/Activate pointing on Games while B scrolling + void ScrollIsOn(int f); + protected: + GuiImage * image; //!< Button image (default) + GuiImage * imageOver; //!< Button image for STATE_SELECTED + GuiImage * imageHold; //!< Button image for STATE_HELD + GuiImage * imageClick; //!< Button image for STATE_CLICKED + GuiImage * icon; //!< Button icon (drawn after button image) + GuiImage * iconOver; //!< Button icon for STATE_SELECTED + GuiImage * iconHold; //!< Button icon for STATE_HELD + GuiImage * iconClick; //!< Button icon for STATE_CLICKED + GuiImage * toolTip; //!< Tooltip for STATE_SELECTED + GuiText * toolTipTxt;//!< Tooltip Text + time_t time1, time2;//!< Tooltip timeconstants + GuiText * label[3]; //!< Label(s) to display (default) + GuiText * labelOver[3]; //!< Label(s) to display for STATE_SELECTED + GuiText * labelHold[3]; //!< Label(s) to display for STATE_HELD + GuiText * labelClick[3]; //!< Label(s) to display for STATE_CLICKED + GuiSound * soundOver; //!< Sound to play for STATE_SELECTED + GuiSound * soundHold; //!< Sound to play for STATE_HELD + GuiSound * soundClick; //!< Sound to play for STATE_CLICKED +}; + +typedef struct _keytype { + char ch, chShift; +} Key; + +//!On-screen keyboard +class GuiKeyboard : public GuiWindow +{ + public: + GuiKeyboard(char * t, u32 m); + ~GuiKeyboard(); + void Update(GuiTrigger * t); + char kbtextstr[256]; + protected: + u32 kbtextmaxlen; + Key keys[4][11]; + int shift; + int caps; + GuiText * kbText; + GuiImage * keyTextboxImg; + GuiText * keyCapsText; + GuiImage * keyCapsImg; + GuiImage * keyCapsOverImg; + GuiButton * keyCaps; + GuiText * keyShiftText; + GuiImage * keyShiftImg; + GuiImage * keyShiftOverImg; + GuiButton * keyShift; + GuiText * keyBackText; + GuiImage * keyBackImg; + GuiImage * keyBackOverImg; + GuiButton * keyBack; + GuiImage * keySpaceImg; + GuiImage * keySpaceOverImg; + GuiButton * keySpace; + GuiButton * keyBtn[4][11]; + GuiImage * keyImg[4][11]; + GuiImage * keyImgOver[4][11]; + GuiText * keyTxt[4][11]; + GuiImageData * keyTextbox; + GuiImageData * key; + GuiImageData * keyOver; + GuiImageData * keyMedium; + GuiImageData * keyMediumOver; + GuiImageData * keyLarge; + GuiImageData * keyLargeOver; + GuiSound * keySoundOver; + GuiSound * keySoundClick; + GuiTrigger * trigA; + GuiTrigger * trigB; +}; + +typedef struct _optionlist { + int length; + char name[MAX_OPTIONS][60]; + char value[MAX_OPTIONS][30]; +} OptionList; + +//!Display a list of menu options +class GuiOptionBrowser : public GuiElement +{ + public: + GuiOptionBrowser(int w, int h, OptionList * l, const u8 *imagebg, int scrollbar); + GuiOptionBrowser(int w, int h, OptionList * l, const char * themePath, const u8 *imagebg, int scrollbar, int start); + ~GuiOptionBrowser(); + void SetCol2Position(int x); + int FindMenuItem(int c, int d); + int GetClickedOption(); + int GetSelectedOption(); + void ResetState(); + void SetFocus(int f); + void Draw(); + void Update(GuiTrigger * t); + GuiText * optionVal[PAGESIZE]; + protected: + int selectedItem; + int listOffset; + + OptionList * options; + int optionIndex[PAGESIZE]; + GuiButton * optionBtn[PAGESIZE]; + GuiText * optionTxt[PAGESIZE]; + GuiImage * optionBg[PAGESIZE]; + + GuiButton * arrowUpBtn; + GuiButton * arrowDownBtn; + GuiButton * scrollbarBoxBtn; + + GuiImage * bgOptionsImg; + GuiImage * bgOptionsOverImg; + GuiImage * scrollbarImg; + GuiImage * arrowDownImg; + GuiImage * arrowDownOverImg; + GuiImage * arrowUpImg; + GuiImage * arrowUpOverImg; + GuiImage * scrollbarBoxImg; + GuiImage * scrollbarBoxOverImg; + + GuiImageData * bgOptions; + GuiImageData * bgOptionsOver; + GuiImageData * bgOptionsEntry; + GuiImageData * scrollbar; + GuiImageData * arrowDown; + GuiImageData * arrowDownOver; + GuiImageData * arrowUp; + GuiImageData * arrowUpOver; + GuiImageData * scrollbarBox; + GuiImageData * scrollbarBoxOver; + + GuiSound * btnSoundOver; + GuiSound * btnSoundClick; + GuiTrigger * trigA; + GuiTrigger * trigB; + GuiTrigger * trigHeldA; +}; +#endif diff --git a/source/libwiigui/gui_button.cpp b/source/libwiigui/gui_button.cpp new file mode 100644 index 00000000..7729c135 --- /dev/null +++ b/source/libwiigui/gui_button.cpp @@ -0,0 +1,355 @@ +/**************************************************************************** + * libwiigui + * + * Tantric 2009 + * + * gui_button.cpp + * + * GUI class definitions + ***************************************************************************/ + +#include "gui.h" + +static int scrollison; + +/** + * Constructor for the GuiButton class. + */ + +GuiButton::GuiButton(int w, int h) +{ + width = w; + height = h; + image = NULL; + imageOver = NULL; + imageHold = NULL; + imageClick = NULL; + icon = NULL; + iconOver = NULL; + iconHold = NULL; + iconClick = NULL; + toolTip = NULL; + toolTipTxt = NULL; + + for(int i=0; i < 3; i++) + { + label[i] = NULL; + labelOver[i] = NULL; + labelHold[i] = NULL; + labelClick[i] = NULL; + } + + soundOver = NULL; + soundHold = NULL; + soundClick = NULL; + selectable = true; + holdable = false; + clickable = true; +} + +/** + * Destructor for the GuiButton class. + */ +GuiButton::~GuiButton() +{ +} + +void GuiButton::SetImage(GuiImage* img) +{ + image = img; + if(img) img->SetParent(this); +} +void GuiButton::SetImageOver(GuiImage* img) +{ + imageOver = img; + if(img) img->SetParent(this); +} +void GuiButton::SetImageHold(GuiImage* img) +{ + imageHold = img; + if(img) img->SetParent(this); +} +void GuiButton::SetImageClick(GuiImage* img) +{ + imageClick = img; + if(img) img->SetParent(this); +} +void GuiButton::SetIcon(GuiImage* img) +{ + icon = img; + if(img) img->SetParent(this); +} +void GuiButton::SetIconOver(GuiImage* img) +{ + iconOver = img; + if(img) img->SetParent(this); +} +void GuiButton::SetIconHold(GuiImage* img) +{ + iconHold = img; + if(img) img->SetParent(this); +} +void GuiButton::SetIconClick(GuiImage* img) +{ + iconClick = img; + if(img) img->SetParent(this); +} +void GuiButton::SetLabel(GuiText* txt, int n) +{ + label[n] = txt; + if(txt) txt->SetParent(this); +} +void GuiButton::SetLabelOver(GuiText* txt, int n) +{ + labelOver[n] = txt; + if(txt) txt->SetParent(this); +} +void GuiButton::SetLabelHold(GuiText* txt, int n) +{ + labelHold[n] = txt; + if(txt) txt->SetParent(this); +} +void GuiButton::SetLabelClick(GuiText* txt, int n) +{ + labelClick[n] = txt; + if(txt) txt->SetParent(this); +} +void GuiButton::SetSoundOver(GuiSound * snd) +{ + soundOver = snd; +} +void GuiButton::SetSoundHold(GuiSound * snd) +{ + soundHold = snd; +} +void GuiButton::SetSoundClick(GuiSound * snd) +{ + soundClick = snd; +} + +//No delay for now +void GuiButton::SetToolTip(GuiImage* img, GuiText * txt, int x, int y) +{ + if(img) + { + + toolTip = img; + img->SetParent(this); + img->SetAlignment(ALIGN_RIGHT, ALIGN_TOP); + img->SetPosition(x,y); + if(txt) + { + toolTipTxt = txt; + txt->SetParent(img); + } + + } +} +/** + * Draw the button on screen + */ +void GuiButton::Draw() +{ + if(!this->IsVisible()) + return; + + // draw image + if((state == STATE_SELECTED || state == STATE_HELD) && imageOver) + imageOver->Draw(); + else if(image) + image->Draw(); + // draw icon + if((state == STATE_SELECTED || state == STATE_HELD) && iconOver) + iconOver->Draw(); + else if(icon) + icon->Draw(); + // draw text + for(int i=0; i<3; i++) + { + if((state == STATE_SELECTED || state == STATE_HELD) && labelOver[i]) + labelOver[i]->Draw(); + else if(label[i]) + label[i]->Draw(); + } + + //draw ToolTip + if(state == STATE_SELECTED && toolTip) + { + if (time2 == 0) + time(&time2); + + time(&time1); + + if (difftime(time1, time2) >= 2) { + toolTip->Draw(); + if (toolTipTxt) + { + toolTipTxt->Draw(); + } + } + } + + this->UpdateEffects(); +} + +void GuiButton::ScrollIsOn(int f) +{ + scrollison = f; +} + +void GuiButton::Update(GuiTrigger * t) +{ + if(state == STATE_CLICKED || state == STATE_DISABLED || !t) + return; + else if(parentElement && parentElement->GetState() == STATE_DISABLED) + return; + + if(state != STATE_SELECTED && toolTip) { + time2 = 0; + } + + + #ifdef HW_RVL + // cursor + if(t->wpad.ir.valid) + { + if(this->IsInside(t->wpad.ir.x, t->wpad.ir.y)) + { + if(state == STATE_DEFAULT) // we weren't on the button before! + { + if(scrollison == 0) { + this->SetState(STATE_SELECTED, t->chan); + } + + if(this->Rumble() && scrollison == 0) + rumbleRequest[t->chan] = 1; + + if(soundOver && scrollison == 0) + soundOver->Play(); + + if(effectsOver && !effects && scrollison == 0) + { + // initiate effects + effects = effectsOver; + effectAmount = effectAmountOver; + effectTarget = effectTargetOver; + } + } + } + else + { + if(state == STATE_SELECTED && (stateChan == t->chan || stateChan == -1)) + this->ResetState(); + + if(effectTarget == effectTargetOver && effectAmount == effectAmountOver) + { + // initiate effects (in reverse) + effects = effectsOver; + effectAmount = -effectAmountOver; + effectTarget = 100; + } + } + } + #endif + + // button triggers + if(this->IsClickable() && scrollison == 0) + { + s32 wm_btns, wm_btns_trig, cc_btns, cc_btns_trig; + for(int i=0; i<6; i++) + { + if(trigger[i] && (trigger[i]->chan == -1 || trigger[i]->chan == t->chan)) + { + // higher 16 bits only (wiimote) + wm_btns = t->wpad.btns_d << 16; + wm_btns_trig = trigger[i]->wpad.btns_d << 16; + + // lower 16 bits only (classic controller) + cc_btns = t->wpad.btns_d >> 16; + cc_btns_trig = trigger[i]->wpad.btns_d >> 16; + + if( + (t->wpad.btns_d > 0 && + wm_btns == wm_btns_trig || + (cc_btns == cc_btns_trig && t->wpad.exp.type == EXP_CLASSIC)) || + (t->pad.btns_d == trigger[i]->pad.btns_d && t->pad.btns_d > 0)) + { + if(t->chan == stateChan || stateChan == -1) + { + if(state == STATE_SELECTED) + { + this->SetState(STATE_CLICKED, t->chan); + + if(soundClick) + soundClick->Play(); + } + else if(trigger[i]->type == TRIGGER_BUTTON_ONLY) + { + this->SetState(STATE_CLICKED, t->chan); + } + else if(trigger[i]->type == TRIGGER_BUTTON_ONLY_IN_FOCUS && + parentElement->IsFocused()) + { + this->SetState(STATE_CLICKED, t->chan); + } + } + } + } + } + } + + if(this->IsHoldable()) + { + bool held = false; + s32 wm_btns, wm_btns_h, wm_btns_trig, cc_btns, cc_btns_h, cc_btns_trig; + + for(int i=0; i<6; i++) + { + if(trigger[i] && (trigger[i]->chan == -1 || trigger[i]->chan == t->chan)) + { + // higher 16 bits only (wiimote) + wm_btns = t->wpad.btns_d << 16; + wm_btns_h = t->wpad.btns_h << 16; + wm_btns_trig = trigger[i]->wpad.btns_h << 16; + + // lower 16 bits only (classic controller) + cc_btns = t->wpad.btns_d >> 16; + cc_btns_h = t->wpad.btns_h >> 16; + cc_btns_trig = trigger[i]->wpad.btns_h >> 16; + + if( + (t->wpad.btns_d > 0 && + wm_btns == wm_btns_trig || + (cc_btns == cc_btns_trig && t->wpad.exp.type == EXP_CLASSIC)) || + (t->pad.btns_d == trigger[i]->pad.btns_h && t->pad.btns_d > 0)) + { + if(trigger[i]->type == TRIGGER_HELD && state == STATE_SELECTED && + (t->chan == stateChan || stateChan == -1)) + this->SetState(STATE_CLICKED, t->chan); + } + + if( + (t->wpad.btns_h > 0 && + wm_btns_h == wm_btns_trig || + (cc_btns_h == cc_btns_trig && t->wpad.exp.type == EXP_CLASSIC)) || + (t->pad.btns_h == trigger[i]->pad.btns_h && t->pad.btns_h > 0)) + { + if(trigger[i]->type == TRIGGER_HELD) + held = true; + } + + if(!held && state == STATE_HELD && stateChan == t->chan) + { + this->ResetState(); + } + else if(held && state == STATE_CLICKED && stateChan == t->chan) + { + this->SetState(STATE_HELD, t->chan); + } + } + } + } + + if(updateCB) + updateCB(this); +} diff --git a/source/libwiigui/gui_customoptionbrowser.cpp b/source/libwiigui/gui_customoptionbrowser.cpp new file mode 100644 index 00000000..ba2fcacc --- /dev/null +++ b/source/libwiigui/gui_customoptionbrowser.cpp @@ -0,0 +1,531 @@ +/**************************************************************************** + * libwiigui + * + * gui_customoptionbrowser.cpp + * + * GUI class definitions + ***************************************************************************/ + +#include "gui.h" +#include "../wpad.h" +#include "gui_customoptionbrowser.h" + +#include + + +#define GAMESELECTSIZE 30 + +static int scrollbaron = 0; +//int vol; +extern const int vol; +/** + * Constructor for the GuiCustomOptionBrowser class. + */ +GuiCustomOptionBrowser::GuiCustomOptionBrowser(int w, int h, customOptionList * l, const char *themePath, const char *custombg, const u8 *imagebg, int scrollon) +{ + width = w; + height = h; + options = l; + size = ((l->length > PAGESIZE)? PAGESIZE: l->length); + scrollbaron = scrollon; + selectable = true; + listOffset = this->FindMenuItem(-1, 1); + selectedItem = 0; + focus = 1; // allow focus + char imgPath[100]; + + trigA = new GuiTrigger; + 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, SOUND_PCM, vol); + + snprintf(imgPath, sizeof(imgPath), "%s%s", themePath, custombg); + bgOptions = new GuiImageData(imgPath, imagebg); + + bgOptionsImg = new GuiImage(bgOptions); + bgOptionsImg->SetParent(this); + bgOptionsImg->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + + snprintf(imgPath, sizeof(imgPath), "%sbg_options_entry.png", themePath); + bgOptionsEntry = new GuiImageData(imgPath, bg_options_entry_png); + if (scrollbaron == 1) { + snprintf(imgPath, sizeof(imgPath), "%sscrollbar.png", themePath); + scrollbar = new GuiImageData(imgPath, scrollbar_png); + scrollbarImg = new GuiImage(scrollbar); + scrollbarImg->SetParent(this); + scrollbarImg->SetAlignment(ALIGN_RIGHT, ALIGN_TOP); + scrollbarImg->SetPosition(0, 4); + + snprintf(imgPath, sizeof(imgPath), "%sscrollbar_arrowdown.png", themePath); + arrowDown = new GuiImageData(imgPath, scrollbar_arrowdown_png); + arrowDownImg = new GuiImage(arrowDown); + arrowDownOver = new GuiImageData(imgPath, scrollbar_arrowdown_png); + arrowDownOverImg = new GuiImage(arrowDownOver); + snprintf(imgPath, sizeof(imgPath), "%sscrollbar_arrowup.png", themePath); + arrowUp = new GuiImageData(imgPath, scrollbar_arrowup_png); + arrowUpImg = new GuiImage(arrowUp); + arrowUpOver = new GuiImageData(imgPath, scrollbar_arrowup_png); + arrowUpOverImg = new GuiImage(arrowUpOver); + snprintf(imgPath, sizeof(imgPath), "%sscrollbar_box.png", themePath); + scrollbarBox = new GuiImageData(imgPath, scrollbar_box_png); + scrollbarBoxImg = new GuiImage(scrollbarBox); + scrollbarBoxOver = new GuiImageData(imgPath, scrollbar_box_png); + scrollbarBoxOverImg = new GuiImage(scrollbarBoxOver); + + arrowUpBtn = new GuiButton(arrowUpImg->GetWidth(), arrowUpImg->GetHeight()); + arrowUpBtn->SetParent(this); + arrowUpBtn->SetImage(arrowUpImg); + arrowUpBtn->SetImageOver(arrowUpOverImg); + arrowUpBtn->SetImageHold(arrowUpOverImg); + arrowUpBtn->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + arrowUpBtn->SetPosition(width/2-18+7,-18); + arrowUpBtn->SetSelectable(false); + arrowUpBtn->SetTrigger(trigA); + arrowUpBtn->SetEffectOnOver(EFFECT_SCALE, 50, 130); + arrowUpBtn->SetSoundClick(btnSoundClick); + + arrowDownBtn = new GuiButton(arrowDownImg->GetWidth(), arrowDownImg->GetHeight()); + arrowDownBtn->SetParent(this); + arrowDownBtn->SetImage(arrowDownImg); + arrowDownBtn->SetImageOver(arrowDownOverImg); + arrowDownBtn->SetImageHold(arrowDownOverImg); + arrowDownBtn->SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + arrowDownBtn->SetPosition(width/2-18+7,18); + arrowDownBtn->SetSelectable(false); + arrowDownBtn->SetTrigger(trigA); + arrowDownBtn->SetEffectOnOver(EFFECT_SCALE, 50, 130); + arrowDownBtn->SetSoundClick(btnSoundClick); + + scrollbarBoxBtn = new GuiButton(scrollbarBoxImg->GetWidth(), scrollbarBoxImg->GetHeight()); + scrollbarBoxBtn->SetParent(this); + scrollbarBoxBtn->SetImage(scrollbarBoxImg); + scrollbarBoxBtn->SetImageOver(scrollbarBoxOverImg); + scrollbarBoxBtn->SetImageHold(scrollbarBoxOverImg); + scrollbarBoxBtn->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + scrollbarBoxBtn->SetSelectable(false); + scrollbarBoxBtn->SetEffectOnOver(EFFECT_SCALE, 50, 120); + scrollbarBoxBtn->SetMinY(0); + scrollbarBoxBtn->SetMaxY(height-30); + scrollbarBoxBtn->SetHoldable(true); + scrollbarBoxBtn->SetTrigger(trigHeldA); + } + + optionIndex = new int[size]; + optionVal = new GuiText * [size]; + optionBtn = new GuiButton * [size]; + optionTxt = new GuiText * [size]; + optionBg = new GuiImage * [size]; + + for(int i=0; i < size; i++) + { + optionTxt[i] = new GuiText(options->name[i], 20, (GXColor){0, 0, 0, 0xff}); + optionTxt[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + optionTxt[i]->SetPosition(24,0); + + optionBg[i] = new GuiImage(bgOptionsEntry); + + optionVal[i] = new GuiText(NULL, 20, (GXColor){0, 0, 0, 0xff}); + optionVal[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + optionVal[i]->SetPosition(250,0); + + optionBtn[i] = new GuiButton(width-28,GAMESELECTSIZE); + optionBtn[i]->SetParent(this); + optionBtn[i]->SetLabel(optionTxt[i], 0); + optionBtn[i]->SetLabel(optionVal[i], 1); + optionBtn[i]->SetImageOver(optionBg[i]); + optionBtn[i]->SetPosition(5,GAMESELECTSIZE*i+4); + optionBtn[i]->SetRumble(false); + optionBtn[i]->SetTrigger(trigA); + optionBtn[i]->SetSoundClick(btnSoundClick); + } +} + +/** + * Destructor for the GuiCustomOptionBrowser class. + */ +GuiCustomOptionBrowser::~GuiCustomOptionBrowser() +{ + if (scrollbaron == 1) { + delete arrowUpBtn; + delete arrowDownBtn; + delete scrollbarBoxBtn; + delete scrollbarImg; + delete arrowDownImg; + delete arrowDownOverImg; + delete arrowUpImg; + delete arrowUpOverImg; + delete scrollbarBoxImg; + delete scrollbarBoxOverImg; + delete scrollbar; + delete arrowDown; + delete arrowDownOver; + delete arrowUp; + delete arrowUpOver; + delete scrollbarBox; + delete scrollbarBoxOver; + } + delete bgOptionsImg; + delete bgOptions; + delete bgOptionsEntry; + + delete trigA; + delete btnSoundClick; + +// delete optionBg; + for(int i = 0; i < size; i++) + { + delete optionTxt[i]; + delete optionVal[i]; + delete optionBg[i]; + delete optionBtn[i]; + } + delete [] optionIndex; + delete [] optionVal; + delete [] optionBtn; + delete [] optionTxt; + delete [] optionBg; +} + +void GuiCustomOptionBrowser::SetCol2Position(int x) +{ + for(int i = 0; i < size; i++) + optionVal[i]->SetPosition(x,0); +} + +void GuiCustomOptionBrowser::SetFocus(int f) +{ + focus = f; + + for(int i = 0; i < size; i++) + optionBtn[i]->ResetState(); + + if(f == 1) + optionBtn[selectedItem]->SetState(STATE_SELECTED); +} + +void GuiCustomOptionBrowser::ResetState() +{ + if(state != STATE_DISABLED) + { + state = STATE_DEFAULT; + stateChan = -1; + } + + for(int i = 0; i < size; i++) + { + optionBtn[i]->ResetState(); + } +} + +int GuiCustomOptionBrowser::GetClickedOption() +{ + int found = -1; + for(int i = 0; i < size; i++) + { + if(optionBtn[i]->GetState() == STATE_CLICKED) + { + optionBtn[i]->SetState(STATE_SELECTED); + found = optionIndex[i]; + break; + } + } + return found; +} + +int GuiCustomOptionBrowser::GetSelectedOption() +{ + int found = -1; + for(int i = 0; i < size; i++) + { + if(optionBtn[i]->GetState() == STATE_SELECTED) + { + found = optionIndex[i]; + break; + } + } + return found; +} + +/**************************************************************************** + * FindMenuItem + * + * Help function to find the next visible menu item on the list + ***************************************************************************/ + +int GuiCustomOptionBrowser::FindMenuItem(int currentItem, int direction) +{ + int nextItem = currentItem + direction; + + if(nextItem < 0 || nextItem >= options->length) + return -1; + + if(strlen(options->name[nextItem]) > 0) + return nextItem; + else + return FindMenuItem(nextItem, direction); +} + +/** + * Draw the button on screen + */ +void GuiCustomOptionBrowser::Draw() +{ + if(!this->IsVisible()) + return; + + bgOptionsImg->Draw(); + + int next = listOffset; + + for(int i=0; i < size; i++) + { + if(next >= 0) + { + optionBtn[i]->Draw(); + next = this->FindMenuItem(next, 1); + } + else + break; + } + + if(scrollbaron == 1) { + scrollbarImg->Draw(); + arrowUpBtn->Draw(); + arrowDownBtn->Draw(); + scrollbarBoxBtn->Draw(); + } + this->UpdateEffects(); +} + +void GuiCustomOptionBrowser::Update(GuiTrigger * t) +{ int next, prev, lang = options->length; + + if(state == STATE_DISABLED || !t) + return; + + + // scrolldelay affects how fast the list scrolls + // when the arrows are clicked + float scrolldelay = 3.5; + + + if (scrollbaron == 1) { + // update the location of the scroll box based on the position in the option list + + + arrowUpBtn->Update(t); + arrowDownBtn->Update(t); + scrollbarBoxBtn->Update(t); + } + next = listOffset; + + for(int i=0; i < size; i++) + { + if(next >= 0) + { + if(optionBtn[i]->GetState() == STATE_DISABLED) + { + optionBtn[i]->SetVisible(true); + optionBtn[i]->SetState(STATE_DEFAULT); + } + + optionTxt[i]->SetText(options->name[next]); + optionVal[i]->SetText(options->value[next]); + optionIndex[i] = next; + next = this->FindMenuItem(next, 1); + } + else + { + optionBtn[i]->SetVisible(false); + optionBtn[i]->SetState(STATE_DISABLED); + } + + if(focus) + { + if(i != selectedItem && optionBtn[i]->GetState() == STATE_SELECTED) + optionBtn[i]->ResetState(); + else if(i == selectedItem && optionBtn[i]->GetState() == STATE_DEFAULT) + optionBtn[selectedItem]->SetState(STATE_SELECTED, t->chan); + } + + optionBtn[i]->Update(t); + + if(optionBtn[i]->GetState() == STATE_SELECTED) + { + selectedItem = i; + } + } + + // pad/joystick navigation + if(!focus) + return; // skip navigation + + if (scrollbaron == 1) { + + if (t->Down() || + arrowDownBtn->GetState() == STATE_CLICKED || ////////////////////////////////////////////down + arrowDownBtn->GetState() == STATE_HELD) + { + + next = this->FindMenuItem(optionIndex[selectedItem], 1); + + if(next >= 0) + { + if(selectedItem == size-1) + { + // move list down by 1 + listOffset = this->FindMenuItem(listOffset, 1); + } + else if(optionBtn[selectedItem+1]->IsVisible()) + { + optionBtn[selectedItem]->ResetState(); + optionBtn[selectedItem+1]->SetState(STATE_SELECTED, t->chan); + selectedItem++; + } + scrollbarBoxBtn->Draw(); + usleep(10000 * scrolldelay); + + + }WPAD_ScanPads(); + u8 cnt, buttons = NULL; + /* Get pressed buttons */ + for (cnt = 0; cnt < 4; cnt++) + buttons |= WPAD_ButtonsHeld(cnt); + if (buttons == WPAD_BUTTON_A) { + + } else { + arrowDownBtn->ResetState(); + + } + + } + else if(t->Up() || + arrowUpBtn->GetState() == STATE_CLICKED || ////////////////////////////////////////////up + arrowUpBtn->GetState() == STATE_HELD) + { + prev = this->FindMenuItem(optionIndex[selectedItem], -1); + + if(prev >= 0) + { + if(selectedItem == 0) + { + // move list up by 1 + listOffset = prev; + } + else + { + optionBtn[selectedItem]->ResetState(); + optionBtn[selectedItem-1]->SetState(STATE_SELECTED, t->chan); + selectedItem--; + } + scrollbarBoxBtn->Draw(); + usleep(10000 * scrolldelay); + + + }WPAD_ScanPads(); + u8 cnt, buttons = NULL; + /* Get pressed buttons */ + for (cnt = 0; cnt < 4; cnt++) + buttons |= WPAD_ButtonsHeld(cnt); + if (buttons == WPAD_BUTTON_A) { + + } else { + arrowUpBtn->ResetState(); + + } + } + + if(scrollbarBoxBtn->GetState() == STATE_HELD && + scrollbarBoxBtn->GetStateChan() == t->chan && + t->wpad.ir.valid && options->length > size) + { + scrollbarBoxBtn->SetPosition(width/2-18+7,0); + int position = t->wpad.ir.y - 50 - scrollbarBoxBtn->GetTop(); + + listOffset = (position * lang)/180 - selectedItem; + + if(listOffset <= 0) + { + listOffset = 0; + selectedItem = 0; + } + else if(listOffset+size >= lang) + { + listOffset = lang-size; + selectedItem = size-1; + } + + } + int positionbar = 237*(listOffset + selectedItem) / lang; + + if(positionbar > 216) + positionbar = 216; + scrollbarBoxBtn->SetPosition(width/2-18+7, positionbar+8); + + + if(t->Right()) + { + if(listOffset < lang && lang > size) + { + listOffset =listOffset+ size; + if(listOffset+size >= lang) + listOffset = lang-size; + } + } + else if(t->Left()) + { + if(listOffset > 0) + { + listOffset =listOffset- size; + if(listOffset < 0) + listOffset = 0; + } + } + + } else { + + + if(t->Down()) + { + next = this->FindMenuItem(optionIndex[selectedItem], 1); + + if(next >= 0) + { + if(selectedItem == size-1) + { + // move list down by 1 + listOffset = this->FindMenuItem(listOffset, 1); + } + else if(optionBtn[selectedItem+1]->IsVisible()) + { + optionBtn[selectedItem]->ResetState(); + optionBtn[selectedItem+1]->SetState(STATE_SELECTED, t->chan); + selectedItem++; + } + } + } + else if(t->Up()) + { + prev = this->FindMenuItem(optionIndex[selectedItem], -1); + + if(prev >= 0) + { + if(selectedItem == 0) + { + // move list up by 1 + listOffset = prev; + } + else + { + optionBtn[selectedItem]->ResetState(); + optionBtn[selectedItem-1]->SetState(STATE_SELECTED, t->chan); + selectedItem--; + } + } + } + } + + if(updateCB) + updateCB(this); +} diff --git a/source/libwiigui/gui_customoptionbrowser.h b/source/libwiigui/gui_customoptionbrowser.h new file mode 100644 index 00000000..2ea016bb --- /dev/null +++ b/source/libwiigui/gui_customoptionbrowser.h @@ -0,0 +1,85 @@ +#include "gui.h" + +class customOptionList { + public: + customOptionList(int size) { + name = new char * [size]; + value = new char * [size]; + for (int i = 0; i < size; i++) + { + name[i] = new char[30]; + value[i] = new char[20]; + } + length = size; + }; + ~customOptionList(){ + for (int i = 0; i < length; i++) + { + delete [] name[i]; + delete [] value[i]; + } + delete [] name; + delete [] value; + }; + + public: + int length; + char ** name; + char ** value; +}; + +//!Display a list of menu options +class GuiCustomOptionBrowser : public GuiElement +{ + public: + GuiCustomOptionBrowser(int w, int h, customOptionList * l, const char * themePath, const char *custombg, const u8 *imagebg, int scrollbar); + ~GuiCustomOptionBrowser(); + void SetCol2Position(int x); + int FindMenuItem(int c, int d); + int GetClickedOption(); + int GetSelectedOption(); + void ResetState(); + void SetFocus(int f); + void Draw(); + void Update(GuiTrigger * t); + GuiText ** optionVal; + protected: + int selectedItem; + int listOffset; + int size; + + customOptionList * options; + int * optionIndex; + GuiButton ** optionBtn; + GuiText ** optionTxt; + GuiImage ** optionBg; + + GuiButton * arrowUpBtn; + GuiButton * arrowDownBtn; + GuiButton * scrollbarBoxBtn; + + GuiImage * bgOptionsImg; + GuiImage * scrollbarImg; + GuiImage * arrowDownImg; + GuiImage * arrowDownOverImg; + GuiImage * arrowUpImg; + GuiImage * arrowUpOverImg; + GuiImage * scrollbarBoxImg; + GuiImage * scrollbarBoxOverImg; + + GuiImageData * bgOptions; + GuiImageData * bgOptionsEntry; + GuiImageData * scrollbar; + GuiImageData * arrowDown; + GuiImageData * arrowDownOver; + GuiImageData * arrowUp; + GuiImageData * arrowUpOver; + GuiImageData * scrollbarBox; + GuiImageData * scrollbarBoxOver; + + GuiSound * btnSoundOver; + GuiSound * btnSoundClick; + GuiTrigger * trigA; + GuiTrigger * trigB; + GuiTrigger * trigHeldA; +}; diff --git a/source/libwiigui/gui_element.cpp b/source/libwiigui/gui_element.cpp new file mode 100644 index 00000000..778c70d9 --- /dev/null +++ b/source/libwiigui/gui_element.cpp @@ -0,0 +1,577 @@ +/**************************************************************************** + * libwiigui + * + * Tantric 2009 + * + * gui_element.cpp + * + * GUI class definitions + ***************************************************************************/ + +#include "gui.h" + +/** + * Constructor for the Object class. + */ +GuiElement::GuiElement() +{ + xoffset = 0; + yoffset = 0; + xmin = 0; + xmax = 0; + ymin = 0; + ymax = 0; + width = 0; + height = 0; + alpha = 255; + scale = 1; + state = STATE_DEFAULT; + stateChan = -1; + trigger[0] = NULL; + trigger[1] = NULL; + trigger[2] = NULL; + trigger[3] = NULL; + trigger[4] = NULL; + trigger[6] = NULL; + parentElement = NULL; + rumble = true; + selectable = false; + clickable = false; + holdable = false; + visible = true; + focus = -1; // cannot be focused + updateCB = NULL; + yoffsetDyn = 0; + xoffsetDyn = 0; + alphaDyn = -1; + scaleDyn = 1; + effects = 0; + effectAmount = 0; + effectTarget = 0; + effectsOver = 0; + effectAmountOver = 0; + effectTargetOver = 0; + + // default alignment - align to top left + alignmentVert = ALIGN_TOP; + alignmentHor = ALIGN_LEFT; +} + +/** + * Destructor for the GuiElement class. + */ +GuiElement::~GuiElement() +{ +} + +void GuiElement::SetParent(GuiElement * e) +{ + parentElement = e; +} +/** + * Get the left position of the GuiElement. + * @see SetLeft() + * @return Left position in pixel. + */ +int GuiElement::GetLeft() +{ + int x = 0; + int pWidth = 0; + int pLeft = 0; + + if(parentElement) + { + pWidth = parentElement->GetWidth(); + pLeft = parentElement->GetLeft(); + } + + if(effects & (EFFECT_SLIDE_IN | EFFECT_SLIDE_OUT)) + pLeft += xoffsetDyn; + + switch(alignmentHor) + { + case ALIGN_LEFT: + x = pLeft; + break; + case ALIGN_CENTRE: + x = pLeft + (pWidth/2) - (width/2); + break; + case ALIGN_RIGHT: + x = pLeft + pWidth - width; + break; + } + return x + xoffset; +} + +/** + * Get the top position of the GuiElement. + * @see SetTop() + * @return Top position in pixel. + */ +int GuiElement::GetTop() +{ + int y = 0; + int pHeight = 0; + int pTop = 0; + + if(parentElement) + { + pHeight = parentElement->GetHeight(); + pTop = parentElement->GetTop(); + } + + if(effects & (EFFECT_SLIDE_IN | EFFECT_SLIDE_OUT)) + pTop += yoffsetDyn; + + switch(alignmentVert) + { + case ALIGN_TOP: + y = pTop; + break; + case ALIGN_MIDDLE: + y = pTop + (pHeight/2) - (height/2); + break; + case ALIGN_BOTTOM: + y = pTop + pHeight - height; + break; + } + return y + yoffset; +} + +void GuiElement::SetMinX(int x) +{ + xmin = x; +} + +int GuiElement::GetMinX() +{ + return xmin; +} + +void GuiElement::SetMaxX(int x) +{ + xmax = x; +} + +int GuiElement::GetMaxX() +{ + return xmax; +} + +void GuiElement::SetMinY(int y) +{ + ymin = y; +} + +int GuiElement::GetMinY() +{ + return ymin; +} + +void GuiElement::SetMaxY(int y) +{ + ymax = y; +} + +int GuiElement::GetMaxY() +{ + return ymax; +} + +/** + * Get the width of the GuiElement. + * @see SetWidth() + * @return Width of the GuiElement. + */ +int GuiElement::GetWidth() +{ + return width; +} + +/** + * Get the height of the GuiElement. + * @see SetHeight() + * @return Height of the GuiElement. + */ +int GuiElement::GetHeight() +{ + return height; +} + +/** + * Set the width and height of the GuiElement. + * @param[in] Width Width in pixel. + * @param[in] Height Height in pixel. + * @see SetWidth() + * @see SetHeight() + */ +void GuiElement::SetSize(int w, int h) +{ + + width = w; + height = h; +} + +/** + * Get visible. + * @see SetVisible() + * @return true if visible, false otherwise. + */ +bool GuiElement::IsVisible() +{ + return visible; +} + +/** + * Set visible. + * @param[in] Visible Set to true to show GuiElement. + * @see IsVisible() + */ +void GuiElement::SetVisible(bool v) +{ + visible = v; +} + +void GuiElement::SetAlpha(int a) +{ + alpha = a; +} + +int GuiElement::GetAlpha() +{ + int a; + + if(alphaDyn >= 0) + a = alphaDyn; + else + a = alpha; + + if(parentElement) + a *= parentElement->GetAlpha()/255.0; + + return a; +} + +void GuiElement::SetScale(float s) +{ + scale = s; +} + +float GuiElement::GetScale() +{ + float s = scale * scaleDyn; + + if(parentElement) + s *= parentElement->GetScale(); + + return s; +} + +int GuiElement::GetState() +{ + return state; +} + +int GuiElement::GetStateChan() +{ + return stateChan; +} + +void GuiElement::SetState(int s, int c) +{ + state = s; + stateChan = c; +} + +void GuiElement::ResetState() +{ + if(state != STATE_DISABLED) + { + state = STATE_DEFAULT; + stateChan = -1; + } +} + +void GuiElement::SetClickable(bool c) +{ + clickable = c; +} + +void GuiElement::SetSelectable(bool s) +{ + selectable = s; +} + +void GuiElement::SetHoldable(bool d) +{ + holdable = d; +} + +bool GuiElement::IsSelectable() +{ + if(state == STATE_DISABLED || state == STATE_CLICKED) + return false; + else + return selectable; +} + +bool GuiElement::IsClickable() +{ + if(state == STATE_DISABLED || + state == STATE_CLICKED || + state == STATE_HELD) + return false; + else + return clickable; +} + +bool GuiElement::IsHoldable() +{ + if(state == STATE_DISABLED) + return false; + else + return holdable; +} + +void GuiElement::SetFocus(int f) +{ + focus = f; +} + +int GuiElement::IsFocused() +{ + return focus; +} + +void GuiElement::SetTrigger(GuiTrigger * t) +{ + if(!trigger[0]) + trigger[0] = t; + else if(!trigger[1]) + trigger[1] = t; + else if(!trigger[2]) + trigger[2] = t; + else if(!trigger[3]) + trigger[3] = t; + else if(!trigger[4]) + trigger[4] = t; + else if(!trigger[5]) + trigger[5] = t; + else // both were assigned, so we'll just overwrite the first one + trigger[0] = t; +} + +void GuiElement::SetTrigger(u8 i, GuiTrigger * t) +{ + trigger[i] = t; +} + +bool GuiElement::Rumble() +{ + return rumble; +} + +void GuiElement::SetRumble(bool r) +{ + rumble = r; +} + +int GuiElement::GetEffect() +{ + return effects; +} + +void GuiElement::SetEffect(int eff, int amount, int target) +{ + if(eff & EFFECT_SLIDE_IN) + { + // these calculations overcompensate a little + if(eff & EFFECT_SLIDE_TOP) + yoffsetDyn = -screenheight; + else if(eff & EFFECT_SLIDE_LEFT) + xoffsetDyn = -screenwidth; + else if(eff & EFFECT_SLIDE_BOTTOM) + yoffsetDyn = screenheight; + else if(eff & EFFECT_SLIDE_RIGHT) + xoffsetDyn = screenwidth; + } + if(eff & EFFECT_FADE && amount > 0) + { + alphaDyn = 0; + } + else if(eff & EFFECT_FADE && amount < 0) + { + alphaDyn = alpha; + } + + effects |= eff; + effectAmount = amount; + effectTarget = target; +} + +void GuiElement::SetEffectOnOver(int eff, int amount, int target) +{ + effectsOver |= eff; + effectAmountOver = amount; + effectTargetOver = target; +} + +void GuiElement::SetEffectGrow() +{ + SetEffectOnOver(EFFECT_SCALE, 4, 110); +} + +void GuiElement::UpdateEffects() +{ + if(effects & (EFFECT_SLIDE_IN | EFFECT_SLIDE_OUT)) + { + if(effects & EFFECT_SLIDE_IN) + { + if(effects & EFFECT_SLIDE_LEFT) + { + xoffsetDyn += effectAmount; + + if(xoffsetDyn >= 0) + { + xoffsetDyn = 0; + effects = 0; + } + } + else if(effects & EFFECT_SLIDE_RIGHT) + { + xoffsetDyn -= effectAmount; + + if(xoffsetDyn <= 0) + { + xoffsetDyn = 0; + effects = 0; + } + } + else if(effects & EFFECT_SLIDE_TOP) + { + yoffsetDyn += effectAmount; + + if(yoffsetDyn >= 0) + { + yoffsetDyn = 0; + effects = 0; + } + } + else if(effects & EFFECT_SLIDE_BOTTOM) + { + yoffsetDyn -= effectAmount; + + if(yoffsetDyn <= 0) + { + yoffsetDyn = 0; + effects = 0; + } + } + } + else + { + if(effects & EFFECT_SLIDE_LEFT) + { + xoffsetDyn -= effectAmount; + + if(xoffsetDyn <= -screenwidth) + effects = 0; // shut off effect + } + else if(effects & EFFECT_SLIDE_RIGHT) + { + xoffsetDyn += effectAmount; + + if(xoffsetDyn >= screenwidth) + effects = 0; // shut off effect + } + else if(effects & EFFECT_SLIDE_TOP) + { + yoffsetDyn -= effectAmount; + + if(yoffsetDyn <= -screenheight) + effects = 0; // shut off effect + } + else if(effects & EFFECT_SLIDE_BOTTOM) + { + yoffsetDyn += effectAmount; + + if(yoffsetDyn >= screenheight) + effects = 0; // shut off effect + } + } + } + if(effects & EFFECT_FADE) + { + alphaDyn += effectAmount; + + if(effectAmount < 0 && alphaDyn <= 0) + { + alphaDyn = 0; + effects = 0; // shut off effect + } + else if(effectAmount > 0 && alphaDyn >= alpha) + { + alphaDyn = alpha; + effects = 0; // shut off effect + } + } + if(effects & EFFECT_SCALE) + { + scaleDyn += effectAmount/100.0; + + if((effectAmount < 0 && scaleDyn <= effectTarget/100.0) + || (effectAmount > 0 && scaleDyn >= effectTarget/100.0)) + { + scaleDyn = effectTarget/100.0; + effects = 0; // shut off effect + } + } +} + +void GuiElement::Update(GuiTrigger * t) +{ + if(updateCB) + updateCB(this); +} + +void GuiElement::SetUpdateCallback(UpdateCallback u) +{ + updateCB = u; +} + +void GuiElement::SetPosition(int xoff, int yoff) +{ + xoffset = xoff; + yoffset = yoff; +} + +void GuiElement::SetAlignment(int hor, int vert) +{ + alignmentHor = hor; + alignmentVert = vert; +} + +int GuiElement::GetSelected() +{ + return -1; +} + +/** + * Draw an element on screen. + */ +void GuiElement::Draw() +{ +} + +/** + * Check if a position is inside the GuiElement. + * @param[in] x X position in pixel. + * @param[in] y Y position in pixel. + */ +bool GuiElement::IsInside(int x, int y) +{ + if(x > this->GetLeft() && x < (this->GetLeft()+width) + && y > this->GetTop() && y < (this->GetTop()+height)) + return true; + return false; +} diff --git a/source/libwiigui/gui_gamebrowser.cpp b/source/libwiigui/gui_gamebrowser.cpp new file mode 100644 index 00000000..32ee2f9f --- /dev/null +++ b/source/libwiigui/gui_gamebrowser.cpp @@ -0,0 +1,615 @@ +/**************************************************************************** + * libwiigui + * + * gui_gamebrowser.cpp + * + * GUI class definitions + ***************************************************************************/ + +#include "gui.h" +#include "../wpad.h" + +#include +#include "gui_gamebrowser.h" +#include "cfg.h" + +#define GAMESELECTSIZE 30 +extern const int vol; +/** + * Constructor for the GuiGameBrowser class. + */ +GuiGameBrowser::GuiGameBrowser(int w, int h, struct discHdr * l, int gameCnt, const char *themePath, const u8 *imagebg, int selected, int offset) +{ + width = w; + height = h; + this->gameCnt = gameCnt; + gameList = l; + pagesize = (gameCnt > PAGESIZE) ? PAGESIZE : gameCnt; + scrollbaron = (gameCnt > PAGESIZE) ? 1 : 0; + selectable = true; + listOffset = (offset == 0) ? this->FindMenuItem(-1, 1) : offset; + selectedItem = selected - offset; + focus = 1; // allow focus + char imgPath[100]; + + trigA = new GuiTrigger; + 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, SOUND_PCM, vol); + + snprintf(imgPath, sizeof(imgPath), "%sbg_options.png", themePath); + bgGames = new GuiImageData(imgPath, imagebg); + + bgGameImg = new GuiImage(bgGames); + bgGameImg->SetParent(this); + bgGameImg->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + + snprintf(imgPath, sizeof(imgPath), "%sbg_options_entry.png", themePath); + bgGamesEntry = new GuiImageData(imgPath, bg_options_entry_png); + if (scrollbaron == 1) { + snprintf(imgPath, sizeof(imgPath), "%sscrollbar.png", themePath); + scrollbar = new GuiImageData(imgPath, scrollbar_png); + scrollbarImg = new GuiImage(scrollbar); + scrollbarImg->SetParent(this); + scrollbarImg->SetAlignment(ALIGN_RIGHT, ALIGN_TOP); + scrollbarImg->SetPosition(0, 4); + + snprintf(imgPath, sizeof(imgPath), "%sscrollbar_arrowdown.png", themePath); + arrowDown = new GuiImageData(imgPath, scrollbar_arrowdown_png); + arrowDownImg = new GuiImage(arrowDown); + arrowDownOver = new GuiImageData(imgPath, scrollbar_arrowdown_png); + arrowDownOverImg = new GuiImage(arrowDownOver); + snprintf(imgPath, sizeof(imgPath), "%sscrollbar_arrowup.png", themePath); + arrowUp = new GuiImageData(imgPath, scrollbar_arrowup_png); + arrowUpImg = new GuiImage(arrowUp); + arrowUpOver = new GuiImageData(imgPath, scrollbar_arrowup_png); + arrowUpOverImg = new GuiImage(arrowUpOver); + snprintf(imgPath, sizeof(imgPath), "%sscrollbar_box.png", themePath); + scrollbarBox = new GuiImageData(imgPath, scrollbar_box_png); + scrollbarBoxImg = new GuiImage(scrollbarBox); + scrollbarBoxOver = new GuiImageData(imgPath, scrollbar_box_png); + scrollbarBoxOverImg = new GuiImage(scrollbarBoxOver); + + arrowUpBtn = new GuiButton(arrowUpImg->GetWidth(), arrowUpImg->GetHeight()); + arrowUpBtn->SetParent(this); + arrowUpBtn->SetImage(arrowUpImg); + arrowUpBtn->SetImageOver(arrowUpOverImg); + arrowUpBtn->SetImageHold(arrowUpOverImg); + arrowUpBtn->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + arrowUpBtn->SetPosition(width/2-18+7,-18); + arrowUpBtn->SetSelectable(false); + arrowUpBtn->SetTrigger(trigA); + arrowUpBtn->SetEffectOnOver(EFFECT_SCALE, 50, 130); + arrowUpBtn->SetSoundClick(btnSoundClick); + + arrowDownBtn = new GuiButton(arrowDownImg->GetWidth(), arrowDownImg->GetHeight()); + arrowDownBtn->SetParent(this); + arrowDownBtn->SetImage(arrowDownImg); + arrowDownBtn->SetImageOver(arrowDownOverImg); + arrowDownBtn->SetImageHold(arrowDownOverImg); + arrowDownBtn->SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + arrowDownBtn->SetPosition(width/2-18+7,18); + arrowDownBtn->SetSelectable(false); + arrowDownBtn->SetTrigger(trigA); + arrowDownBtn->SetEffectOnOver(EFFECT_SCALE, 50, 130); + arrowDownBtn->SetSoundClick(btnSoundClick); + + scrollbarBoxBtn = new GuiButton(scrollbarBoxImg->GetWidth(), scrollbarBoxImg->GetHeight()); + scrollbarBoxBtn->SetParent(this); + scrollbarBoxBtn->SetImage(scrollbarBoxImg); + scrollbarBoxBtn->SetImageOver(scrollbarBoxOverImg); + scrollbarBoxBtn->SetImageHold(scrollbarBoxOverImg); + scrollbarBoxBtn->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + scrollbarBoxBtn->SetSelectable(false); + scrollbarBoxBtn->SetEffectOnOver(EFFECT_SCALE, 50, 120); + scrollbarBoxBtn->SetMinY(0); + scrollbarBoxBtn->SetMaxY(height-30); + scrollbarBoxBtn->SetHoldable(true); + scrollbarBoxBtn->SetTrigger(trigHeldA); + } + + gameIndex = new int[pagesize]; + game = new GuiButton * [pagesize]; + gameTxt = new GuiText * [pagesize]; + gameBg = new GuiImage * [pagesize]; + + char buffer[CFG.maxcharacters + 4]; + + for(int i=0; i < pagesize; i++) + { + if (strlen(get_title(&gameList[i])) < (u32)(CFG.maxcharacters + 3)) + { + sprintf(buffer, "%s", get_title(&gameList[i])); + } + else + { + sprintf(buffer, get_title(&gameList[i]), CFG.maxcharacters); + buffer[CFG.maxcharacters] = '\0'; + strncat(buffer, "...", 3); + } + + gameTxt[i] = new GuiText(buffer, 20, (GXColor){0, 0, 0, 0xff}); + gameTxt[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + gameTxt[i]->SetPosition(24,0); + + gameBg[i] = new GuiImage(bgGamesEntry); + + game[i] = new GuiButton(width-28,GAMESELECTSIZE); + game[i]->SetParent(this); + game[i]->SetLabel(gameTxt[i]); + game[i]->SetImageOver(gameBg[i]); + game[i]->SetPosition(5,GAMESELECTSIZE*i+4); + game[i]->SetRumble(false); + game[i]->SetTrigger(trigA); + game[i]->SetSoundClick(btnSoundClick); + } +} + +/** + * Destructor for the GuiGameBrowser class. + */ +GuiGameBrowser::~GuiGameBrowser() +{ + if (scrollbaron == 1) { + delete arrowUpBtn; + delete arrowDownBtn; + delete scrollbarBoxBtn; + delete scrollbarImg; + delete arrowDownImg; + delete arrowDownOverImg; + delete arrowUpImg; + delete arrowUpOverImg; + delete scrollbarBoxImg; + delete scrollbarBoxOverImg; + delete scrollbar; + delete arrowDown; + delete arrowDownOver; + delete arrowUp; + delete arrowUpOver; + delete scrollbarBox; + delete scrollbarBoxOver; + } + delete bgGameImg; + delete bgGames; + delete bgGamesEntry; + + delete trigA; + delete btnSoundClick; + +// delete optionBg; + for(int i=0; iResetState(); + + if(f == 1) + game[selectedItem]->SetState(STATE_SELECTED); +} + +void GuiGameBrowser::ResetState() +{ + if(state != STATE_DISABLED) + { + state = STATE_DEFAULT; + stateChan = -1; + } + + for(int i=0; iResetState(); + } +} + +int GuiGameBrowser::GetOffset() +{ + return listOffset; +} +int GuiGameBrowser::GetClickedOption() +{ + int found = -1; + for(int i=0; iGetState() == STATE_CLICKED) + { + game[i]->SetState(STATE_SELECTED); + found = gameIndex[i]; + break; + } + } + return found; +} + +int GuiGameBrowser::GetSelectedOption() +{ + int found = -1; + for(int i=0; iGetState() == STATE_SELECTED) + { + game[i]->SetState(STATE_SELECTED); + found = gameIndex[i]; + break; + } + } + return found; +} + +/**************************************************************************** + * FindMenuItem + * + * Help function to find the next visible menu item on the list + ***************************************************************************/ + +int GuiGameBrowser::FindMenuItem(int currentItem, int direction) +{ + int nextItem = currentItem + direction; + + if(nextItem < 0 || nextItem >= gameCnt) + return -1; + + if(strlen(get_title(&gameList[nextItem])) > 0) + return nextItem; + else + return FindMenuItem(nextItem, direction); +} + +/** + * Draw the button on screen + */ +void GuiGameBrowser::Draw() +{ + if(!this->IsVisible()) + return; + + bgGameImg->Draw(); + + int next = listOffset; + + for(int i=0; i= 0) + { + game[i]->Draw(); + next = this->FindMenuItem(next, 1); + } + else + break; + } + + if(scrollbaron == 1) { + scrollbarImg->Draw(); + arrowUpBtn->Draw(); + arrowDownBtn->Draw(); + scrollbarBoxBtn->Draw(); + } + this->UpdateEffects(); +} + +void GuiGameBrowser::Update(GuiTrigger * t) +{ + if(state == STATE_DISABLED || !t) + return; + + int next, prev; + static int position2; + // scrolldelay affects how fast the list scrolls + // when the arrows are clicked + float scrolldelay = 3.5; + + + if (scrollbaron == 1) { + // update the location of the scroll box based on the position in the option list + arrowUpBtn->Update(t); + arrowDownBtn->Update(t); + scrollbarBoxBtn->Update(t); + } + + next = listOffset; + char buffer[CFG.maxcharacters + 4]; + + for(int i=0; i= 0) + { + if(game[i]->GetState() == STATE_DISABLED) + { + game[i]->SetVisible(true); + game[i]->SetState(STATE_DEFAULT); + } + + if (strlen(get_title(&gameList[next])) < (u32)(CFG.maxcharacters + 3)) + { + sprintf(buffer, "%s", get_title(&gameList[next])); + } + else + { + sprintf(buffer, get_title(&gameList[next]), CFG.maxcharacters); + buffer[CFG.maxcharacters] = '\0'; + strncat(buffer, "...", 3); + } + + gameTxt[i]->SetText(buffer); + gameIndex[i] = next; + next = this->FindMenuItem(next, 1); + } + else + { + game[i]->SetVisible(false); + game[i]->SetState(STATE_DISABLED); + } + + if(focus) + { + if(i != selectedItem && game[i]->GetState() == STATE_SELECTED) + game[i]->ResetState(); + else if(i == selectedItem && game[i]->GetState() == STATE_DEFAULT) + game[selectedItem]->SetState(STATE_SELECTED, t->chan); + } + + game[i]->Update(t); + + if(game[i]->GetState() == STATE_SELECTED) + { + selectedItem = i; + } + } + + // pad/joystick navigation + if(!focus) + return; // skip navigation + + if (scrollbaron == 1) { + + if (t->Down() || + arrowDownBtn->GetState() == STATE_CLICKED || ////////////////////////////////////////////down + arrowDownBtn->GetState() == STATE_HELD) + { + + + next = this->FindMenuItem(gameIndex[selectedItem], 1); + + if(next >= 0) + { + if(selectedItem == pagesize-1) + { + // move list down by 1 + listOffset = this->FindMenuItem(listOffset, 1); + } + else if(game[selectedItem+1]->IsVisible()) + { + game[selectedItem]->ResetState(); + game[selectedItem+1]->SetState(STATE_SELECTED, t->chan); + selectedItem++; + } + scrollbarBoxBtn->Draw(); + usleep(10000 * scrolldelay); + + + }WPAD_ScanPads(); + u8 cnt, buttons = NULL; + /* Get pressed buttons */ + for (cnt = 0; cnt < 4; cnt++) + buttons |= WPAD_ButtonsHeld(cnt); + if (buttons == WPAD_BUTTON_A) { + + } else { + arrowDownBtn->ResetState(); + + } + + } + else if(t->Up() || + arrowUpBtn->GetState() == STATE_CLICKED || ////////////////////////////////////////////up + arrowUpBtn->GetState() == STATE_HELD) + { + prev = this->FindMenuItem(gameIndex[selectedItem], -1); + + if(prev >= 0) + { + if(selectedItem == 0) + { + // move list up by 1 + listOffset = prev; + } + else + { + game[selectedItem]->ResetState(); + game[selectedItem-1]->SetState(STATE_SELECTED, t->chan); + selectedItem--; + } + scrollbarBoxBtn->Draw(); + usleep(10000 * scrolldelay); + + + } + + WPAD_ScanPads(); + u8 cnt, buttons = NULL; + /* Get pressed buttons */ + for (cnt = 0; cnt < 4; cnt++) + buttons |= WPAD_ButtonsHeld(cnt); + if (buttons == WPAD_BUTTON_A) { + + } else { + arrowUpBtn->ResetState(); + + } + } + + WPAD_ScanPads(); + u8 cnt, buttons = NULL; + int position1 = 0; + + + position1 = t->wpad.ir.y; + + if (position2 == 0 && position1 > 0) { + position2 = position1; + } + + for (cnt = 0; cnt < 4; cnt++) + buttons |= WPAD_ButtonsHeld(cnt); + + if (buttons == WPAD_BUTTON_B && position1 > 0) { + scrollbarBoxBtn->ScrollIsOn(1); + if (position2 > position1) { + + prev = this->FindMenuItem(gameIndex[selectedItem], -1); + + if(prev >= 0) + { + if(selectedItem == 0) + { + // move list up by 1 + listOffset = prev; + } + else + { + game[selectedItem]->ResetState(); + game[selectedItem-1]->SetState(STATE_SELECTED, t->chan); + selectedItem--; + } + scrollbarBoxBtn->Draw(); + usleep(10000 * scrolldelay); + + + } + + } else if (position2 < position1) { + + + next = this->FindMenuItem(gameIndex[selectedItem], 1); + + if(next >= 0) + { + if(selectedItem == pagesize-1) + { + // move list down by 1 + listOffset = this->FindMenuItem(listOffset, 1); + } + else if(game[selectedItem+1]->IsVisible()) + { + game[selectedItem]->ResetState(); + game[selectedItem+1]->SetState(STATE_SELECTED, t->chan); + selectedItem++; + } + scrollbarBoxBtn->Draw(); + usleep(10000 * scrolldelay); + + + } + } + + } else if (!buttons) { + scrollbarBoxBtn->ScrollIsOn(0); + position2 = 0; + } + + if(scrollbarBoxBtn->GetState() == STATE_HELD && + scrollbarBoxBtn->GetStateChan() == t->chan && + t->wpad.ir.valid && gameCnt > pagesize) + { + scrollbarBoxBtn->SetPosition(width/2-18+7,0); + int position = t->wpad.ir.y - 50 - scrollbarBoxBtn->GetTop(); + + listOffset = (position * gameCnt)/180 - selectedItem; + + if(listOffset <= 0) + { + listOffset = 0; + selectedItem = 0; + } + else if(listOffset+pagesize >= gameCnt) + { + listOffset = gameCnt - pagesize; + selectedItem = pagesize-1; + } + + } + int positionbar = 237*(listOffset + selectedItem) / gameCnt; + + if(positionbar > 216) + positionbar = 216; + scrollbarBoxBtn->SetPosition(width/2-18+7, positionbar+8); + + + if(t->Right()) + { + if(listOffset < gameCnt && gameCnt > pagesize) + { + listOffset =listOffset+ pagesize; + if(listOffset+pagesize >= gameCnt) + listOffset = gameCnt-pagesize; + } + } + else if(t->Left()) + { + if(listOffset > 0) + { + listOffset =listOffset- pagesize; + if(listOffset < 0) + listOffset = 0; + } + } + + } else { + + + if(t->Down()) + { + next = this->FindMenuItem(gameIndex[selectedItem], 1); + + if(next >= 0) + { + if(selectedItem == pagesize-1) + { + // move list down by 1 + listOffset = this->FindMenuItem(listOffset, 1); + } + else if(game[selectedItem+1]->IsVisible()) + { + game[selectedItem]->ResetState(); + game[selectedItem+1]->SetState(STATE_SELECTED, t->chan); + selectedItem++; + } + } + } + else if(t->Up()) + { + prev = this->FindMenuItem(gameIndex[selectedItem], -1); + + if(prev >= 0) + { + if(selectedItem == 0) + { + // move list up by 1 + listOffset = prev; + } + else + { + game[selectedItem]->ResetState(); + game[selectedItem-1]->SetState(STATE_SELECTED, t->chan); + selectedItem--; + } + } + } + } + + if(updateCB) + updateCB(this); +} diff --git a/source/libwiigui/gui_gamebrowser.h b/source/libwiigui/gui_gamebrowser.h new file mode 100644 index 00000000..6b6661f8 --- /dev/null +++ b/source/libwiigui/gui_gamebrowser.h @@ -0,0 +1,88 @@ +#ifndef _GUIGAMEBROWSER_H_ +#define _GUIGAMEBROWSER_H_ + +#include "gui.h" +#include "../disc.h" +/* +class GameBrowserList { + public: + GameBrowserList(int size) { + name = new char * [size]; + + for (int i = 0; i < size; i++) + { + name[i] = new char[50]; + } + length = size; + }; + ~GameBrowserList(){ + for (int i = 0; i < length; i++) + { + delete [] name[i]; + } + delete [] name; + }; + + public: + int length; + char ** name; +}; +*/ + +class GuiGameBrowser : public GuiElement +{ + public: + GuiGameBrowser(int w, int h, struct discHdr * l, int gameCnt, const char *themePath, const u8 *imagebg, 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(); + //GuiText * optionVal[PAGESIZE]; + protected: + int selectedItem; + int listOffset; + int scrollbaron; + int pagesize; + + struct discHdr * gameList; + int gameCnt; + + int * gameIndex; + GuiButton ** game; + GuiText ** gameTxt; + GuiImage ** gameBg; + + 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 * scrollbar; + GuiImageData * arrowDown; + GuiImageData * arrowDownOver; + GuiImageData * arrowUp; + GuiImageData * arrowUpOver; + GuiImageData * scrollbarBox; + GuiImageData * scrollbarBoxOver; + + GuiSound * btnSoundOver; + GuiSound * btnSoundClick; + GuiTrigger * trigA; + GuiTrigger * trigHeldA; +}; +#endif diff --git a/source/libwiigui/gui_image.cpp b/source/libwiigui/gui_image.cpp new file mode 100644 index 00000000..e89fcbae --- /dev/null +++ b/source/libwiigui/gui_image.cpp @@ -0,0 +1,239 @@ +/**************************************************************************** + * libwiigui + * + * Tantric 2009 + * + * gui_image.cpp + * + * GUI class definitions + ***************************************************************************/ + +#include "gui.h" +/** + * Constructor for the GuiImage class. + */ +GuiImage::GuiImage() +{ + image = NULL; + width = 0; + height = 0; + imageangle = 0; + tile = -1; + stripe = 0; + widescreen = 0; + imgType = IMAGE_DATA; +} + +GuiImage::GuiImage(GuiImageData * img) +{ + image = img->GetImage(); + width = img->GetWidth(); + height = img->GetHeight(); + imageangle = 0; + tile = -1; + stripe = 0; + widescreen = 0; + imgType = IMAGE_DATA; +} + +GuiImage::GuiImage(u8 * img, int w, int h) +{ + image = img; + width = w; + height = h; + imageangle = 0; + tile = -1; + stripe = 0; + widescreen = 0; + imgType = IMAGE_TEXTURE; +} + +GuiImage::GuiImage(int w, int h, GXColor c) +{ + image = (u8 *)memalign (32, w * h * 4); + width = w; + height = h; + imageangle = 0; + tile = -1; + stripe = 0; + widescreen = 0; + imgType = IMAGE_COLOR; + + if(!image) + return; + + int x, y; + + for(y=0; y < h; y++) + { + for(x=0; x < w; x++) + { + this->SetPixel(x, y, c); + } + } + int len = w*h*4; + if(len%32) len += (32-len%32); + DCFlushRange(image, len); +} + +/** + * Destructor for the GuiImage class. + */ +GuiImage::~GuiImage() +{ + if(imgType == IMAGE_COLOR && image) + free(image); +} + +u8 * GuiImage::GetImage() +{ + return image; +} + +void GuiImage::SetImage(GuiImageData * img) +{ + image = img->GetImage(); + width = img->GetWidth(); + height = img->GetHeight(); + imgType = IMAGE_DATA; +} + +void GuiImage::SetImage(u8 * img, int w, int h) +{ + image = img; + width = w; + height = h; + imgType = IMAGE_TEXTURE; +} + +void GuiImage::SetAngle(float a) +{ + imageangle = a; +} + +void GuiImage::SetTile(int t) +{ + tile = t; +} + +void GuiImage::SetWidescreen(short w) +{ + widescreen = w; +} + +GXColor GuiImage::GetPixel(int x, int y) +{ + if(!image || this->GetWidth() <= 0 || x < 0 || y < 0) + return (GXColor){0, 0, 0, 0}; + + u32 offset = (((y >> 2)<<4)*this->GetWidth()) + ((x >> 2)<<6) + (((y%4 << 2) + x%4 ) << 1); + GXColor color; + color.a = *(image+offset); + color.r = *(image+offset+1); + color.g = *(image+offset+32); + color.b = *(image+offset+33); + return color; +} + +void GuiImage::SetPixel(int x, int y, GXColor color) +{ + if(!image || this->GetWidth() <= 0 || x < 0 || y < 0) + return; + + u32 offset = (((y >> 2)<<4)*this->GetWidth()) + ((x >> 2)<<6) + (((y%4 << 2) + x%4 ) << 1); + *(image+offset) = color.a; + *(image+offset+1) = color.r; + *(image+offset+32) = color.g; + *(image+offset+33) = color.b; +} + +void GuiImage::SetStripe(int s) +{ + stripe = s; +} + +void GuiImage::ColorStripe(int shift) +{ + int x, y; + GXColor color; + int alt = 0; + + for(y=0; y < this->GetHeight(); y++) + { + if(y % 3 == 0) + alt ^= 1; + + for(x=0; x < this->GetWidth(); x++) + { + color = GetPixel(x, y); + + if(alt) + { + if(color.r < 255-shift) + color.r += shift; + else + color.r = 255; + if(color.g < 255-shift) + color.g += shift; + else + color.g = 255; + if(color.b < 255-shift) + color.b += shift; + else + color.b = 255; + + color.a = 255; + } + else + { + if(color.r > shift) + color.r -= shift; + else + color.r = 0; + if(color.g > shift) + color.g -= shift; + else + color.g = 0; + if(color.b > shift) + color.b -= shift; + else + color.b = 0; + + color.a = 255; + } + SetPixel(x, y, color); + } + } +} + +/** + * Draw the button on screen + */ +void GuiImage::Draw() +{ + if(!image || !this->IsVisible() || tile == 0) + return; + + float currScale = this->GetScale(); + int currLeft = this->GetLeft(); + + if(tile > 0) + { + for(int i=0; iGetTop(), width, height, image, imageangle, widescreen ? currScale*0.8 : currScale, currScale, this->GetAlpha()); + } + else + { + // temporary (maybe), used to correct offset for scaled images + if(scale != 1) + currLeft = currLeft - width/2 + (width*scale)/2; + + Menu_DrawImg(currLeft, this->GetTop(), width, height, image, imageangle, widescreen ? currScale*0.8 : currScale, currScale, this->GetAlpha()); + } + + if(stripe > 0) + for(int y=0; y < this->GetHeight(); y+=6) + Menu_DrawRectangle(currLeft,this->GetTop()+y,this->GetWidth(),3,(GXColor){0, 0, 0, stripe},1); + + this->UpdateEffects(); +} diff --git a/source/libwiigui/gui_imagedata.cpp b/source/libwiigui/gui_imagedata.cpp new file mode 100644 index 00000000..21b5f2e3 --- /dev/null +++ b/source/libwiigui/gui_imagedata.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** + * libwiigui + * + * Tantric 2009 + * + * gui_imagedata.cpp + * + * GUI class definitions + ***************************************************************************/ + +#include "gui.h" + +/** + * Constructor for the GuiImageData class. + */ +GuiImageData::GuiImageData(const u8 * img) +{ + data = NULL; + width = 0; + height = 0; + + if(img) + { + PNGUPROP imgProp; + IMGCTX ctx = PNGU_SelectImageFromBuffer(img); + + if(!ctx) + return; + + int res = PNGU_GetImageProperties(ctx, &imgProp); + + if(res == PNGU_OK) + { + int len = imgProp.imgWidth * imgProp.imgHeight * 4; + if(len%32) len += (32-len%32); + data = (u8 *)memalign (32, len); + + if(data) + { + res = PNGU_DecodeTo4x4RGBA8 (ctx, imgProp.imgWidth, imgProp.imgHeight, data, 255); + + if(res == PNGU_OK) + { + width = imgProp.imgWidth; + height = imgProp.imgHeight; + DCFlushRange(data, len); + } + else + { + free(data); + data = NULL; + } + } + } + PNGU_ReleaseImageContext (ctx); + } +} + +/** + * Constructor for the GuiImageData class. + */ +GuiImageData::GuiImageData(const char * imgPath, const u8 * buffer) +{ + data = NULL; + width = 0; + height = 0; + + if(imgPath) + { + PNGUPROP imgProp; + IMGCTX ctx = PNGU_SelectImageFromDevice(imgPath); + + if(ctx) + { + int res = PNGU_GetImageProperties(ctx, &imgProp); + + if(res == PNGU_OK) + { + int len = imgProp.imgWidth * imgProp.imgHeight * 4; + if(len%32) len += (32-len%32); + data = (u8 *)memalign (32, len); + + if(data) + { + res = PNGU_DecodeTo4x4RGBA8 (ctx, imgProp.imgWidth, imgProp.imgHeight, data, 255); + + if(res == PNGU_OK) + { + width = imgProp.imgWidth; + height = imgProp.imgHeight; + DCFlushRange(data, len); + } + else + { + free(data); + data = NULL; + } + } + } + PNGU_ReleaseImageContext (ctx); + } + } + + if (!data) //use buffer data instead + { + width = 0; + height = 0; + if(buffer) + { + PNGUPROP imgProp; + IMGCTX ctx = PNGU_SelectImageFromBuffer(buffer); + + if(!ctx) + return; + + int res = PNGU_GetImageProperties(ctx, &imgProp); + + if(res == PNGU_OK) + { + int len = imgProp.imgWidth * imgProp.imgHeight * 4; + if(len%32) len += (32-len%32); + data = (u8 *)memalign (32, len); + + if(data) + { + res = PNGU_DecodeTo4x4RGBA8 (ctx, imgProp.imgWidth, imgProp.imgHeight, data, 255); + + if(res == PNGU_OK) + { + width = imgProp.imgWidth; + height = imgProp.imgHeight; + DCFlushRange(data, len); + } + else + { + free(data); + data = NULL; + } + } + } + PNGU_ReleaseImageContext (ctx); + } + } +} + +/** + * Destructor for the GuiImageData class. + */ +GuiImageData::~GuiImageData() +{ + if(data) + { + free(data); + data = NULL; + } +} + +u8 * GuiImageData::GetImage() +{ + return data; +} + +int GuiImageData::GetWidth() +{ + return width; +} + +int GuiImageData::GetHeight() +{ + return height; +} diff --git a/source/libwiigui/gui_keyboard.cpp b/source/libwiigui/gui_keyboard.cpp new file mode 100644 index 00000000..066307e4 --- /dev/null +++ b/source/libwiigui/gui_keyboard.cpp @@ -0,0 +1,330 @@ +/**************************************************************************** + * libwiigui + * + * Tantric 2009 + * + * gui_keyboard.cpp + * + * GUI class definitions + ***************************************************************************/ + +#include "gui.h" +/** + * Constructor for the GuiKeyboard class. + */ +extern const int vol; +GuiKeyboard::GuiKeyboard(char * t, u32 max) +{ + width = 540; + height = 400; + shift = 0; + caps = 0; + selectable = true; + focus = 0; // allow focus + alignmentHor = ALIGN_CENTRE; + alignmentVert = ALIGN_MIDDLE; + strncpy(kbtextstr, t, max); + kbtextstr[max] = 0; + kbtextmaxlen = max; + + Key thekeys[4][11] = { + { + {'1','!'}, + {'2','@'}, + {'3','#'}, + {'4','$'}, + {'5','%'}, + {'6','^'}, + {'7','&'}, + {'8','*'}, + {'9','('}, + {'0',')'}, + {'\0','\0'} + }, + { + {'q','Q'}, + {'w','W'}, + {'e','E'}, + {'r','R'}, + {'t','T'}, + {'y','Y'}, + {'u','U'}, + {'i','I'}, + {'o','O'}, + {'p','P'}, + {'-','_'} + }, + { + {'a','A'}, + {'s','S'}, + {'d','D'}, + {'f','F'}, + {'g','G'}, + {'h','H'}, + {'j','J'}, + {'k','K'}, + {'l','L'}, + {':',';'}, + {'\'','"'} + }, + + { + {'z','Z'}, + {'x','X'}, + {'c','C'}, + {'v','V'}, + {'b','B'}, + {'n','N'}, + {'m','M'}, + {',','<'}, + {'.','>'}, + {'/','?'}, + {'\0','\0'} + } + }; + memcpy(keys, thekeys, sizeof(thekeys)); + + keyTextbox = new GuiImageData(keyboard_textbox_png); + keyTextboxImg = new GuiImage(keyTextbox); + keyTextboxImg->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + keyTextboxImg->SetPosition(0, 0); + this->Append(keyTextboxImg); + + kbText = new GuiText(kbtextstr, 20, (GXColor){0, 0, 0, 0xff}); + kbText->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + kbText->SetPosition(0, 13); + this->Append(kbText); + + key = new GuiImageData(keyboard_key_png); + keyOver = new GuiImageData(keyboard_key_over_png); + keyMedium = new GuiImageData(keyboard_mediumkey_png); + keyMediumOver = new GuiImageData(keyboard_mediumkey_over_png); + keyLarge = new GuiImageData(keyboard_largekey_png); + keyLargeOver = new GuiImageData(keyboard_largekey_over_png); + + keySoundOver = new GuiSound(button_over_pcm, button_over_pcm_size, SOUND_PCM, vol); + keySoundClick = new GuiSound(button_click_pcm, button_click_pcm_size, SOUND_PCM, vol); + 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(keyMedium->GetWidth(), keyMedium->GetHeight()); + keyBack->SetImage(keyBackImg); + keyBack->SetImageOver(keyBackOverImg); + keyBack->SetLabel(keyBackText); + keyBack->SetSoundOver(keySoundOver); + keyBack->SetSoundClick(keySoundClick); + keyBack->SetTrigger(trigA); + keyBack->SetTrigger(trigB); + keyBack->SetPosition(10*42+40, 0*42+80); + keyBack->SetEffectGrow(); + this->Append(keyBack); + + keyCapsImg = new GuiImage(keyMedium); + keyCapsOverImg = new GuiImage(keyMediumOver); + keyCapsText = new GuiText("Caps", 20, (GXColor){0, 0, 0, 0xff}); + keyCaps = new GuiButton(keyMedium->GetWidth(), keyMedium->GetHeight()); + keyCaps->SetImage(keyCapsImg); + keyCaps->SetImageOver(keyCapsOverImg); + keyCaps->SetLabel(keyCapsText); + keyCaps->SetSoundOver(keySoundOver); + keyCaps->SetSoundClick(keySoundClick); + keyCaps->SetTrigger(trigA); + keyCaps->SetPosition(0, 2*42+80); + keyCaps->SetEffectGrow(); + this->Append(keyCaps); + + keyShiftImg = new GuiImage(keyMedium); + keyShiftOverImg = new GuiImage(keyMediumOver); + keyShiftText = new GuiText("Shift", 20, (GXColor){0, 0, 0, 0xff}); + keyShift = new GuiButton(keyMedium->GetWidth(), keyMedium->GetHeight()); + keyShift->SetImage(keyShiftImg); + keyShift->SetImageOver(keyShiftOverImg); + keyShift->SetLabel(keyShiftText); + keyShift->SetSoundOver(keySoundOver); + keyShift->SetSoundClick(keySoundClick); + keyShift->SetTrigger(trigA); + keyShift->SetPosition(21, 3*42+80); + keyShift->SetEffectGrow(); + this->Append(keyShift); + + keySpaceImg = new GuiImage(keyLarge); + keySpaceOverImg = new GuiImage(keyLargeOver); + keySpace = new GuiButton(keyLarge->GetWidth(), keyLarge->GetHeight()); + keySpace->SetImage(keySpaceImg); + keySpace->SetImageOver(keySpaceOverImg); + keySpace->SetSoundOver(keySoundOver); + keySpace->SetSoundClick(keySoundClick); + keySpace->SetTrigger(trigA); + keySpace->SetPosition(0, 4*42+80); + keySpace->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + keySpace->SetEffectGrow(); + this->Append(keySpace); + + for(int i=0; i<4; i++) + { + for(int j=0; j<11; j++) + { + if(keys[i][j].ch != '\0') + { + keyImg[i][j] = new GuiImage(key); + keyImgOver[i][j] = new GuiImage(keyOver); + keyTxt[i][j] = new GuiText(NULL, 20, (GXColor){0, 0, 0, 0xff}); + keyTxt[i][j]->SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + keyTxt[i][j]->SetPosition(0, -10); + keyBtn[i][j] = new GuiButton(key->GetWidth(), key->GetHeight()); + keyBtn[i][j]->SetImage(keyImg[i][j]); + keyBtn[i][j]->SetImageOver(keyImgOver[i][j]); + keyBtn[i][j]->SetSoundOver(keySoundOver); + keyBtn[i][j]->SetSoundClick(keySoundClick); + keyBtn[i][j]->SetTrigger(trigA); + keyBtn[i][j]->SetLabel(keyTxt[i][j]); + keyBtn[i][j]->SetPosition(j*42+21*i+40, i*42+80); + keyBtn[i][j]->SetEffectGrow(); + this->Append(keyBtn[i][j]); + } + } + } +} + +/** + * Destructor for the GuiKeyboard class. + */ +GuiKeyboard::~GuiKeyboard() +{ + delete kbText; + delete keyTextbox; + delete keyTextboxImg; + delete keyCapsText; + delete keyCapsImg; + delete keyCapsOverImg; + delete keyCaps; + delete keyShiftText; + delete keyShiftImg; + delete keyShiftOverImg; + delete keyShift; + delete keyBackText; + delete keyBackImg; + delete keyBackOverImg; + delete keyBack; + delete keySpaceImg; + delete keySpaceOverImg; + delete keySpace; + delete key; + delete keyOver; + delete keyMedium; + delete keyMediumOver; + delete keyLarge; + delete keyLargeOver; + delete keySoundOver; + delete keySoundClick; + delete trigA; + delete trigB; + + for(int i=0; i<4; i++) + { + for(int j=0; j<11; j++) + { + if(keys[i][j].ch != '\0') + { + delete keyImg[i][j]; + delete keyImgOver[i][j]; + delete keyTxt[i][j]; + delete keyBtn[i][j]; + } + } + } +} + +void GuiKeyboard::Update(GuiTrigger * t) +{ + 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(keySpace->GetState() == STATE_CLICKED) + { + if(strlen(kbtextstr) < kbtextmaxlen) + { + kbtextstr[strlen(kbtextstr)] = ' '; + kbText->SetText(kbtextstr); + } + keySpace->SetState(STATE_SELECTED, t->chan); + } + else if(keyBack->GetState() == STATE_CLICKED) + { + kbtextstr[strlen(kbtextstr)-1] = 0; + kbText->SetText(kbtextstr); + keyBack->SetState(STATE_SELECTED, t->chan); + } + else if(keyShift->GetState() == STATE_CLICKED) + { + shift ^= 1; + keyShift->SetState(STATE_SELECTED, t->chan); + } + else if(keyCaps->GetState() == STATE_CLICKED) + { + caps ^= 1; + keyCaps->SetState(STATE_SELECTED, t->chan); + } + + char txt[2] = { 0, 0 }; + + for(int i=0; i<4; i++) + { + for(int j=0; j<11; j++) + { + if(keys[i][j].ch != '\0') + { + if(shift || caps) + txt[0] = keys[i][j].chShift; + else + txt[0] = keys[i][j].ch; + + keyTxt[i][j]->SetText(txt); + + if(keyBtn[i][j]->GetState() == STATE_CLICKED) + { + if(strlen(kbtextstr) < kbtextmaxlen) + { + if(shift || caps) + { + kbtextstr[strlen(kbtextstr)] = keys[i][j].chShift; + if(shift) shift ^= 1; + } + else + { + kbtextstr[strlen(kbtextstr)] = keys[i][j].ch; + } + } + kbText->SetText(kbtextstr); + keyBtn[i][j]->SetState(STATE_SELECTED, t->chan); + } + } + } + } + + 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 new file mode 100644 index 00000000..487ca019 --- /dev/null +++ b/source/libwiigui/gui_optionbrowser.cpp @@ -0,0 +1,653 @@ +/**************************************************************************** + * libwiigui + * + * Tantric 2009 + * + * gui_optionbrowser.cpp + * + * GUI class definitions + ***************************************************************************/ + +#include "gui.h" +#include "../wpad.h" + +#include + + +#define GAMESELECTSIZE 30 + +static int scrollbaron, startat, loaded = 0; +/** + * Constructor for the GuiOptionBrowser class. + */ +GuiOptionBrowser::GuiOptionBrowser(int w, int h, OptionList * l, const u8 *imagebg, int scrollon) +{ + width = w; + height = h; + options = l; + scrollbaron = scrollon; + selectable = true; + listOffset = this->FindMenuItem(-1, 1); + selectedItem = 0; + focus = 1; // allow focus + + trigA = new GuiTrigger; + 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, SOUND_PCM); + + bgOptions = new GuiImageData(imagebg); + bgOptionsImg = new GuiImage(bgOptions); + bgOptionsImg->SetParent(this); + bgOptionsImg->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + + bgOptionsEntry = new GuiImageData(bg_options_entry_png); + if (scrollbaron == 1) { + scrollbar = new GuiImageData(scrollbar_png); + scrollbarImg = new GuiImage(scrollbar); + scrollbarImg->SetParent(this); + scrollbarImg->SetAlignment(ALIGN_RIGHT, ALIGN_TOP); + scrollbarImg->SetPosition(0, 4); + + arrowDown = new GuiImageData(scrollbar_arrowdown_png); + arrowDownImg = new GuiImage(arrowDown); + arrowDownOver = new GuiImageData(scrollbar_arrowdown_png); + arrowDownOverImg = new GuiImage(arrowDownOver); + arrowUp = new GuiImageData(scrollbar_arrowup_png); + arrowUpImg = new GuiImage(arrowUp); + arrowUpOver = new GuiImageData(scrollbar_arrowup_png); + arrowUpOverImg = new GuiImage(arrowUpOver); + scrollbarBox = new GuiImageData(scrollbar_box_png); + scrollbarBoxImg = new GuiImage(scrollbarBox); + scrollbarBoxOver = new GuiImageData(scrollbar_box_png); + scrollbarBoxOverImg = new GuiImage(scrollbarBoxOver); + + arrowUpBtn = new GuiButton(arrowUpImg->GetWidth(), arrowUpImg->GetHeight()); + arrowUpBtn->SetParent(this); + arrowUpBtn->SetImage(arrowUpImg); + arrowUpBtn->SetImageOver(arrowUpOverImg); + arrowUpBtn->SetImageHold(arrowUpOverImg); + arrowUpBtn->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + arrowUpBtn->SetPosition(width/2-18+7,-18); + arrowUpBtn->SetSelectable(false); + arrowUpBtn->SetTrigger(trigA); + arrowUpBtn->SetEffectOnOver(EFFECT_SCALE, 50, 130); + arrowUpBtn->SetSoundClick(btnSoundClick); + + arrowDownBtn = new GuiButton(arrowDownImg->GetWidth(), arrowDownImg->GetHeight()); + arrowDownBtn->SetParent(this); + arrowDownBtn->SetImage(arrowDownImg); + arrowDownBtn->SetImageOver(arrowDownOverImg); + arrowDownBtn->SetImageHold(arrowDownOverImg); + arrowDownBtn->SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + arrowDownBtn->SetPosition(width/2-18+7,18); + arrowDownBtn->SetSelectable(false); + arrowDownBtn->SetTrigger(trigA); + arrowDownBtn->SetEffectOnOver(EFFECT_SCALE, 50, 130); + arrowDownBtn->SetSoundClick(btnSoundClick); + + scrollbarBoxBtn = new GuiButton(scrollbarBoxImg->GetWidth(), scrollbarBoxImg->GetHeight()); + scrollbarBoxBtn->SetParent(this); + scrollbarBoxBtn->SetImage(scrollbarBoxImg); + scrollbarBoxBtn->SetImageOver(scrollbarBoxOverImg); + scrollbarBoxBtn->SetImageHold(scrollbarBoxOverImg); + scrollbarBoxBtn->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + scrollbarBoxBtn->SetSelectable(false); + scrollbarBoxBtn->SetEffectOnOver(EFFECT_SCALE, 50, 120); + scrollbarBoxBtn->SetMinY(0); + scrollbarBoxBtn->SetMaxY(height); + scrollbarBoxBtn->SetHoldable(true); + scrollbarBoxBtn->SetTrigger(trigHeldA); + } + +// optionBg = new GuiImage(bgOptionsEntry); + for(int i=0; iname[i], 20, (GXColor){0, 0, 0, 0xff}); + optionTxt[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + optionTxt[i]->SetPosition(24,0); + + optionBg[i] = new GuiImage(bgOptionsEntry); + + optionVal[i] = new GuiText(NULL, 20, (GXColor){0, 0, 0, 0xff}); + optionVal[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + optionVal[i]->SetPosition(250,0); + + optionBtn[i] = new GuiButton(width-28,GAMESELECTSIZE); + optionBtn[i]->SetParent(this); + optionBtn[i]->SetLabel(optionTxt[i], 0); + optionBtn[i]->SetLabel(optionVal[i], 1); + optionBtn[i]->SetImageOver(optionBg[i]); + optionBtn[i]->SetPosition(5,GAMESELECTSIZE*i+4); + optionBtn[i]->SetRumble(false); + optionBtn[i]->SetTrigger(trigA); + optionBtn[i]->SetSoundClick(btnSoundClick); + } +} + +/** + * Constructor for the GuiOptionBrowser class. + */ +GuiOptionBrowser::GuiOptionBrowser(int w, int h, OptionList * l, const char *themePath, const u8 *imagebg, int scrollon, int start) +{ + width = w; + height = h; + options = l; + startat = start; + loaded = 0; + scrollbaron = scrollon; + selectable = true; + listOffset = this->FindMenuItem(-1, 1); + selectedItem = 0; + focus = 1; // allow focus + char imgPath[100]; + + trigA = new GuiTrigger; + 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, SOUND_PCM); + + snprintf(imgPath, sizeof(imgPath), "%sbg_options.png", themePath); + bgOptions = new GuiImageData(imgPath, imagebg); + + bgOptionsImg = new GuiImage(bgOptions); + bgOptionsImg->SetParent(this); + bgOptionsImg->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + + snprintf(imgPath, sizeof(imgPath), "%sbg_options_entry.png", themePath); + bgOptionsEntry = new GuiImageData(imgPath, bg_options_entry_png); + if (scrollbaron == 1) { + snprintf(imgPath, sizeof(imgPath), "%sscrollbar.png", themePath); + scrollbar = new GuiImageData(imgPath, scrollbar_png); + scrollbarImg = new GuiImage(scrollbar); + scrollbarImg->SetParent(this); + scrollbarImg->SetAlignment(ALIGN_RIGHT, ALIGN_TOP); + scrollbarImg->SetPosition(0, 4); + + snprintf(imgPath, sizeof(imgPath), "%sscrollbar_arrowdown.png", themePath); + arrowDown = new GuiImageData(imgPath, scrollbar_arrowdown_png); + arrowDownImg = new GuiImage(arrowDown); + arrowDownOver = new GuiImageData(imgPath, scrollbar_arrowdown_png); + arrowDownOverImg = new GuiImage(arrowDownOver); + snprintf(imgPath, sizeof(imgPath), "%sscrollbar_arrowup.png", themePath); + arrowUp = new GuiImageData(imgPath, scrollbar_arrowup_png); + arrowUpImg = new GuiImage(arrowUp); + arrowUpOver = new GuiImageData(imgPath, scrollbar_arrowup_png); + arrowUpOverImg = new GuiImage(arrowUpOver); + snprintf(imgPath, sizeof(imgPath), "%sscrollbar_box.png", themePath); + scrollbarBox = new GuiImageData(imgPath, scrollbar_box_png); + scrollbarBoxImg = new GuiImage(scrollbarBox); + scrollbarBoxOver = new GuiImageData(imgPath, scrollbar_box_png); + scrollbarBoxOverImg = new GuiImage(scrollbarBoxOver); + + arrowUpBtn = new GuiButton(arrowUpImg->GetWidth(), arrowUpImg->GetHeight()); + arrowUpBtn->SetParent(this); + arrowUpBtn->SetImage(arrowUpImg); + arrowUpBtn->SetImageOver(arrowUpOverImg); + arrowUpBtn->SetImageHold(arrowUpOverImg); + arrowUpBtn->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + arrowUpBtn->SetPosition(width/2-18+7,-18); + arrowUpBtn->SetSelectable(false); + arrowUpBtn->SetTrigger(trigA); + arrowUpBtn->SetEffectOnOver(EFFECT_SCALE, 50, 130); + arrowUpBtn->SetSoundClick(btnSoundClick); + + arrowDownBtn = new GuiButton(arrowDownImg->GetWidth(), arrowDownImg->GetHeight()); + arrowDownBtn->SetParent(this); + arrowDownBtn->SetImage(arrowDownImg); + arrowDownBtn->SetImageOver(arrowDownOverImg); + arrowDownBtn->SetImageHold(arrowDownOverImg); + arrowDownBtn->SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + arrowDownBtn->SetPosition(width/2-18+7,18); + arrowDownBtn->SetSelectable(false); + arrowDownBtn->SetTrigger(trigA); + arrowDownBtn->SetEffectOnOver(EFFECT_SCALE, 50, 130); + arrowDownBtn->SetSoundClick(btnSoundClick); + + scrollbarBoxBtn = new GuiButton(scrollbarBoxImg->GetWidth(), scrollbarBoxImg->GetHeight()); + scrollbarBoxBtn->SetParent(this); + scrollbarBoxBtn->SetImage(scrollbarBoxImg); + scrollbarBoxBtn->SetImageOver(scrollbarBoxOverImg); + scrollbarBoxBtn->SetImageHold(scrollbarBoxOverImg); + scrollbarBoxBtn->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + scrollbarBoxBtn->SetSelectable(false); + scrollbarBoxBtn->SetEffectOnOver(EFFECT_SCALE, 50, 120); + scrollbarBoxBtn->SetMinY(0); + scrollbarBoxBtn->SetMaxY(height-30); + scrollbarBoxBtn->SetHoldable(true); + scrollbarBoxBtn->SetTrigger(trigHeldA); + } + +// optionBg = new GuiImage(bgOptionsEntry); + for(int i=0; iname[i], 20, (GXColor){0, 0, 0, 0xff}); + optionTxt[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + optionTxt[i]->SetPosition(24,0); + + optionBg[i] = new GuiImage(bgOptionsEntry); + + optionVal[i] = new GuiText(NULL, 20, (GXColor){0, 0, 0, 0xff}); + optionVal[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + optionVal[i]->SetPosition(250,0); + + optionBtn[i] = new GuiButton(width-28,GAMESELECTSIZE); + optionBtn[i]->SetParent(this); + optionBtn[i]->SetLabel(optionTxt[i], 0); + optionBtn[i]->SetLabel(optionVal[i], 1); + optionBtn[i]->SetImageOver(optionBg[i]); + optionBtn[i]->SetPosition(5,GAMESELECTSIZE*i+4); + optionBtn[i]->SetTrigger(trigA); + optionBtn[i]->SetSoundClick(btnSoundClick); + } +} + +/** + * Destructor for the GuiOptionBrowser class. + */ +GuiOptionBrowser::~GuiOptionBrowser() +{ + if (scrollbaron == 1) { + delete arrowUpBtn; + delete arrowDownBtn; + delete scrollbarBoxBtn; + delete scrollbarImg; + delete arrowDownImg; + delete arrowDownOverImg; + delete arrowUpImg; + delete arrowUpOverImg; + delete scrollbarBoxImg; + delete scrollbarBoxOverImg; + delete scrollbar; + delete arrowDown; + delete arrowDownOver; + delete arrowUp; + delete arrowUpOver; + delete scrollbarBox; + delete scrollbarBoxOver; + } + delete bgOptionsImg; + delete bgOptions; + delete bgOptionsEntry; + loaded = 0; + + delete trigA; + delete btnSoundClick; + +// delete optionBg; + for(int i=0; iSetPosition(x,0); +} + +void GuiOptionBrowser::SetFocus(int f) +{ + focus = f; + + for(int i=0; iResetState(); + + if(f == 1) + optionBtn[selectedItem]->SetState(STATE_SELECTED); +} + +void GuiOptionBrowser::ResetState() +{ + if(state != STATE_DISABLED) + { + state = STATE_DEFAULT; + stateChan = -1; + } + + for(int i=0; iResetState(); + } +} + +int GuiOptionBrowser::GetClickedOption() +{ + int found = -1; + for(int i=0; iGetState() == STATE_CLICKED) + { + optionBtn[i]->SetState(STATE_SELECTED); + found = optionIndex[i]; + break; + } + } + return found; +} + +int GuiOptionBrowser::GetSelectedOption() +{ + int found = -1; + for(int i=0; iGetState() == STATE_SELECTED) + { + optionBtn[i]->SetState(STATE_SELECTED); + found = optionIndex[i]; + break; + } + } + return found; +} + +/**************************************************************************** + * FindMenuItem + * + * Help function to find the next visible menu item on the list + ***************************************************************************/ + +int GuiOptionBrowser::FindMenuItem(int currentItem, int direction) +{ + int nextItem = currentItem + direction; + + if(nextItem < 0 || nextItem >= options->length) + return -1; + + if(strlen(options->name[nextItem]) > 0) + return nextItem; + else + return FindMenuItem(nextItem, direction); +} + +/** + * Draw the button on screen + */ +void GuiOptionBrowser::Draw() +{ + if(!this->IsVisible()) + return; + + bgOptionsImg->Draw(); + + int next = listOffset; + + for(int i=0; i= 0) + { + optionBtn[i]->Draw(); + next = this->FindMenuItem(next, 1); + } + else + break; + } + + if(scrollbaron == 1) { + scrollbarImg->Draw(); + arrowUpBtn->Draw(); + arrowDownBtn->Draw(); + scrollbarBoxBtn->Draw(); + } + this->UpdateEffects(); +} + +void GuiOptionBrowser::Update(GuiTrigger * t) +{ int next, prev, lang = options->length; + + //go to the last game selected + if ((loaded == 0) && (startat>0)) + { + + if (startat > (lang-9)){ + listOffset= (lang-9); + selectedItem=startat; + optionBtn[selectedItem]->SetState(STATE_SELECTED, t->chan); + } + else if (startat < 9){ + selectedItem=startat; + optionBtn[selectedItem]->SetState(STATE_SELECTED, t->chan); + } + else { + listOffset = (startat-4); + selectedItem=startat; + optionBtn[selectedItem]->SetState(STATE_SELECTED, t->chan);} + this->SetFocus(1); + loaded = 1; + } + + if(state == STATE_DISABLED || !t) + return; + + + // scrolldelay affects how fast the list scrolls + // when the arrows are clicked + float scrolldelay = 3.5; + + + if (scrollbaron == 1) { + // update the location of the scroll box based on the position in the option list + + + arrowUpBtn->Update(t); + arrowDownBtn->Update(t); + scrollbarBoxBtn->Update(t); + } + next = listOffset; + + for(int i=0; i= 0) + { + if(optionBtn[i]->GetState() == STATE_DISABLED) + { + optionBtn[i]->SetVisible(true); + optionBtn[i]->SetState(STATE_DEFAULT); + } + + optionTxt[i]->SetText(options->name[next]); + optionVal[i]->SetText(options->value[next]); + optionIndex[i] = next; + next = this->FindMenuItem(next, 1); + } + else + { + optionBtn[i]->SetVisible(false); + optionBtn[i]->SetState(STATE_DISABLED); + } + + if(focus) + { + if(i != selectedItem && optionBtn[i]->GetState() == STATE_SELECTED) + optionBtn[i]->ResetState(); + else if(i == selectedItem && optionBtn[i]->GetState() == STATE_DEFAULT) + optionBtn[selectedItem]->SetState(STATE_SELECTED, t->chan); + } + + optionBtn[i]->Update(t); + + if(optionBtn[i]->GetState() == STATE_SELECTED) + { + selectedItem = i; + } + } + + // pad/joystick navigation + if(!focus) + return; // skip navigation + + if (scrollbaron == 1) { + + if (t->Down() || + arrowDownBtn->GetState() == STATE_CLICKED || ////////////////////////////////////////////down + arrowDownBtn->GetState() == STATE_HELD) + { + + next = this->FindMenuItem(optionIndex[selectedItem], 1); + + if(next >= 0) + { + if(selectedItem == PAGESIZE-1) + { + // move list down by 1 + listOffset = this->FindMenuItem(listOffset, 1); + } + else if(optionBtn[selectedItem+1]->IsVisible()) + { + optionBtn[selectedItem]->ResetState(); + optionBtn[selectedItem+1]->SetState(STATE_SELECTED, t->chan); + selectedItem++; + } + scrollbarBoxBtn->Draw(); + usleep(10000 * scrolldelay); + + + }WPAD_ScanPads(); + u8 cnt, buttons = NULL; + /* Get pressed buttons */ + for (cnt = 0; cnt < 4; cnt++) + buttons |= WPAD_ButtonsHeld(cnt); + if (buttons == WPAD_BUTTON_A) { + + } else { + arrowDownBtn->ResetState(); + + } + + } + else if(t->Up() || + arrowUpBtn->GetState() == STATE_CLICKED || ////////////////////////////////////////////up + arrowUpBtn->GetState() == STATE_HELD) + { + prev = this->FindMenuItem(optionIndex[selectedItem], -1); + + if(prev >= 0) + { + if(selectedItem == 0) + { + // move list up by 1 + listOffset = prev; + } + else + { + optionBtn[selectedItem]->ResetState(); + optionBtn[selectedItem-1]->SetState(STATE_SELECTED, t->chan); + selectedItem--; + } + scrollbarBoxBtn->Draw(); + usleep(10000 * scrolldelay); + + + }WPAD_ScanPads(); + u8 cnt, buttons = NULL; + /* Get pressed buttons */ + for (cnt = 0; cnt < 4; cnt++) + buttons |= WPAD_ButtonsHeld(cnt); + if (buttons == WPAD_BUTTON_A) { + + } else { + arrowUpBtn->ResetState(); + + } + } + + if(scrollbarBoxBtn->GetState() == STATE_HELD && + scrollbarBoxBtn->GetStateChan() == t->chan && + t->wpad.ir.valid && options->length > PAGESIZE) + { + scrollbarBoxBtn->SetPosition(width/2-18+7,0); + int position = t->wpad.ir.y - 50 - scrollbarBoxBtn->GetTop(); + + listOffset = (position * lang)/180 - selectedItem; + + if(listOffset <= 0) + { + listOffset = 0; + selectedItem = 0; + } + else if(listOffset+PAGESIZE >= lang) + { + listOffset = lang-PAGESIZE; + selectedItem = PAGESIZE-1; + } + + } + int positionbar = 237*(listOffset + selectedItem) / lang; + + if(positionbar > 216) + positionbar = 216; + scrollbarBoxBtn->SetPosition(width/2-18+7, positionbar+8); + + + if(t->Right()) + { + if(listOffset < lang && lang > PAGESIZE) + { + listOffset =listOffset+ PAGESIZE; + if(listOffset+PAGESIZE >= lang) + listOffset = lang-PAGESIZE; + } + } + else if(t->Left()) + { + if(listOffset > 0) + { + listOffset =listOffset- PAGESIZE; + if(listOffset < 0) + listOffset = 0; + } + } + + } else { + + + if(t->Down()) + { + next = this->FindMenuItem(optionIndex[selectedItem], 1); + + if(next >= 0) + { + if(selectedItem == PAGESIZE-1) + { + // move list down by 1 + listOffset = this->FindMenuItem(listOffset, 1); + } + else if(optionBtn[selectedItem+1]->IsVisible()) + { + optionBtn[selectedItem]->ResetState(); + optionBtn[selectedItem+1]->SetState(STATE_SELECTED, t->chan); + selectedItem++; + } + } + } + else if(t->Up()) + { + prev = this->FindMenuItem(optionIndex[selectedItem], -1); + + if(prev >= 0) + { + if(selectedItem == 0) + { + // move list up by 1 + listOffset = prev; + } + else + { + optionBtn[selectedItem]->ResetState(); + optionBtn[selectedItem-1]->SetState(STATE_SELECTED, t->chan); + selectedItem--; + } + } + } + } + + if(updateCB) + updateCB(this); +} diff --git a/source/libwiigui/gui_sound.cpp b/source/libwiigui/gui_sound.cpp new file mode 100644 index 00000000..3db3bf8c --- /dev/null +++ b/source/libwiigui/gui_sound.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** + * libwiigui + * + * Tantric 2009 + * + * gui_sound.cpp + * + * GUI class definitions + ***************************************************************************/ + +#include "gui.h" + +/** + * Constructor for the GuiSound class. + */ +GuiSound::GuiSound(const u8 * snd, s32 len, int t) +{ + sound = snd; + length = len; + type = t; + voice = -1; + volume = 100; + loop = false; +} + +GuiSound::GuiSound(const u8 * snd, s32 len, int t, int v) +{ + sound = snd; + length = len; + type = t; + voice = -1; + volume = v; + loop = false; +} + +/** + * Destructor for the GuiSound class. + */ +GuiSound::~GuiSound() +{ + if(type == SOUND_OGG) + StopOgg(); +} + +void GuiSound::Play() +{ + int vol; + + switch(type) + { + case SOUND_PCM: + vol = 255*(volume/100.0); + voice = ASND_GetFirstUnusedVoice(); + if(voice >= 0) + ASND_SetVoice(voice, VOICE_STEREO_16BIT, 48000, 0, + (u8 *)sound, length, vol, vol, NULL); + break; + + case SOUND_OGG: + voice = 0; + if(loop) + PlayOgg(mem_open((char *)sound, length), 0, OGG_INFINITE_TIME); + else + PlayOgg(mem_open((char *)sound, length), 0, OGG_ONE_TIME); + SetVolumeOgg(255*(volume/100.0)); + break; + } +} + +void GuiSound::Stop() +{ + if(voice < 0) + return; + + switch(type) + { + case SOUND_PCM: + ASND_StopVoice(voice); + break; + + case SOUND_OGG: + StopOgg(); + break; + } +} + +void GuiSound::Pause() +{ + if(voice < 0) + return; + + switch(type) + { + case SOUND_PCM: + ASND_PauseVoice(voice, 1); + break; + + case SOUND_OGG: + PauseOgg(1); + break; + } +} + +void GuiSound::Resume() +{ + if(voice < 0) + return; + + switch(type) + { + case SOUND_PCM: + ASND_PauseVoice(voice, 0); + break; + + case SOUND_OGG: + PauseOgg(0); + break; + } +} + +bool GuiSound::IsPlaying() +{ + if(ASND_StatusVoice(voice) == SND_WORKING || ASND_StatusVoice(voice) == SND_WAITING) + return true; + else + return false; +} + +void GuiSound::SetVolume(int vol) +{ + volume = vol; + + if(voice < 0) + return; + + int newvol = 255*(volume/100.0); + + switch(type) + { + case SOUND_PCM: + ASND_ChangeVolumeVoice(voice, newvol, newvol); + break; + + case SOUND_OGG: + SetVolumeOgg(255*(volume/100.0)); + break; + } +} + +void GuiSound::SetLoop(bool l) +{ + loop = l; +} diff --git a/source/libwiigui/gui_text.cpp b/source/libwiigui/gui_text.cpp new file mode 100644 index 00000000..f3254a36 --- /dev/null +++ b/source/libwiigui/gui_text.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** + * libwiigui + * + * Tantric 2009 + * + * gui_text.cpp + * + * GUI class definitions + ***************************************************************************/ + +#include "gui.h" + +static int currentSize = 0; +static int presetSize = 0; +static int presetMaxWidth = 0; +static int presetAlignmentHor = 0; +static int presetAlignmentVert = 0; +static u16 presetStyle = 0; +static GXColor presetColor = (GXColor){255, 255, 255, 255}; + +/** + * Constructor for the GuiText class. + */ +GuiText::GuiText(const char * t, int s, GXColor c) +{ + text = NULL; + size = s; + color = c; + alpha = c.a; + style = FTGX_JUSTIFY_CENTER | FTGX_ALIGN_MIDDLE; + maxWidth = 0; + + alignmentHor = ALIGN_CENTRE; + alignmentVert = ALIGN_MIDDLE; + + if(t) + text = fontSystem->charToWideChar((char *)t); +} + +/** + * Constructor for the GuiText class, uses presets + */ +GuiText::GuiText(const char * t) +{ + text = NULL; + size = presetSize; + color = presetColor; + alpha = presetColor.a; + style = presetStyle; + maxWidth = presetMaxWidth; + + alignmentHor = presetAlignmentHor; + alignmentVert = presetAlignmentVert; + + if(t) + text = fontSystem->charToWideChar((char *)t); +} + +/** + * Destructor for the GuiText class. + */ +GuiText::~GuiText() +{ + if(text) + { + delete text; + text = NULL; + } +} + +void GuiText::SetText(const char * t) +{ + if(text) + delete text; + + text = NULL; + + if(t) + text = fontSystem->charToWideChar((char *)t); +} + +void GuiText::SetPresets(int sz, GXColor c, int w, u16 s, int h, int v) +{ + presetSize = sz; + presetColor = c; + presetStyle = s; + presetMaxWidth = w; + presetAlignmentHor = h; + presetAlignmentVert = v; +} + +void GuiText::SetFontSize(int s) +{ + size = s; +} + +void GuiText::SetMaxWidth(int w) +{ + maxWidth = w; +} + +void GuiText::SetColor(GXColor c) +{ + color = c; + alpha = c.a; +} + +void GuiText::SetStyle(u16 s) +{ + style = s; +} + +void GuiText::SetAlignment(int hor, int vert) +{ + style = 0; + + switch(hor) + { + case ALIGN_LEFT: + style |= FTGX_JUSTIFY_LEFT; + break; + case ALIGN_RIGHT: + style |= FTGX_JUSTIFY_RIGHT; + break; + default: + style |= FTGX_JUSTIFY_CENTER; + break; + } + switch(vert) + { + case ALIGN_TOP: + style |= FTGX_ALIGN_TOP; + break; + case ALIGN_BOTTOM: + style |= FTGX_ALIGN_BOTTOM; + break; + default: + style |= FTGX_ALIGN_MIDDLE; + break; + } + + alignmentHor = hor; + alignmentVert = vert; +} + +/** + * Draw the text on screen + */ +void GuiText::Draw() +{ + if(!text) + return; + + if(!this->IsVisible()) + return; + + GXColor c = color; + c.a = this->GetAlpha(); + + int newSize = size*this->GetScale(); + + if(newSize != currentSize) + { + fontSystem->changeSize(newSize); + currentSize = newSize; + } + + int voffset = 0; + + if(alignmentVert == ALIGN_MIDDLE) + voffset = -newSize/2 + 2; + + if(maxWidth > 0) // text wrapping + { + int lineheight = newSize + 6; + int strlen = wcslen(text); + int i = 0; + int ch = 0; + int linenum = 0; + int lastSpace = -1; + int lastSpaceIndex = -1; + wchar_t * tmptext[20]; + + while(ch < strlen) + { + if(i == 0) + tmptext[linenum] = new wchar_t[strlen + 1]; + + tmptext[linenum][i] = text[ch]; + tmptext[linenum][i+1] = 0; + + if(text[ch] == ' ' || ch == strlen-1) + { + if(fontSystem->getWidth(tmptext[linenum]) >= maxWidth) + { + if(lastSpace >= 0) + { + tmptext[linenum][lastSpaceIndex] = 0; // discard space, and everything after + ch = lastSpace; // go backwards to the last space + lastSpace = -1; // we have used this space + lastSpaceIndex = -1; + } + linenum++; + i = -1; + } + else if(ch == strlen-1) + { + linenum++; + } + } + if(text[ch] == ' ' && i >= 0) + { + lastSpace = ch; + lastSpaceIndex = i; + } + ch++; + i++; + } + + if(alignmentVert == ALIGN_MIDDLE) + voffset = voffset - (lineheight*linenum)/2 + lineheight/2; + + for(i=0; i < linenum; i++) + { + fontSystem->drawText(this->GetLeft(), this->GetTop()+voffset+i*lineheight, tmptext[i], c, style); + delete tmptext[i]; + } + } + else + { + fontSystem->drawText(this->GetLeft(), this->GetTop()+voffset, text, c, style); + } + this->UpdateEffects(); +} diff --git a/source/libwiigui/gui_trigger.cpp b/source/libwiigui/gui_trigger.cpp new file mode 100644 index 00000000..fe7a4f45 --- /dev/null +++ b/source/libwiigui/gui_trigger.cpp @@ -0,0 +1,255 @@ +/**************************************************************************** + * libwiigui + * + * Tantric 2009 + * + * gui_trigger.cpp + * + * GUI class definitions + ***************************************************************************/ + +#include "gui.h" + +static int scrollDelay = 0; + +/** + * Constructor for the GuiTrigger class. + */ +GuiTrigger::GuiTrigger() +{ + chan = -1; + memset(&wpad, 0, sizeof(WPADData)); + memset(&pad, 0, sizeof(PADData)); +} + +/** + * Destructor for the GuiTrigger class. + */ +GuiTrigger::~GuiTrigger() +{ +} + +/** + * Sets a simple trigger. Requires: + * - Element is selected + * - Trigger button is pressed + */ +void GuiTrigger::SetSimpleTrigger(s32 ch, u32 wiibtns, u16 gcbtns) +{ + type = TRIGGER_SIMPLE; + chan = ch; + wpad.btns_d = wiibtns; + pad.btns_d = gcbtns; +} + +/** + * Sets a held trigger. Requires: + * - Element is selected + * - Trigger button is pressed and held + */ +void GuiTrigger::SetHeldTrigger(s32 ch, u32 wiibtns, u16 gcbtns) +{ + type = TRIGGER_HELD; + chan = ch; + wpad.btns_h = wiibtns; + pad.btns_h = gcbtns; +} + +/** + * Sets a button trigger. Requires: + * - Trigger button is pressed + */ +void GuiTrigger::SetButtonOnlyTrigger(s32 ch, u32 wiibtns, u16 gcbtns) +{ + type = TRIGGER_BUTTON_ONLY; + chan = ch; + wpad.btns_d = wiibtns; + pad.btns_d = gcbtns; +} + +/** + * Sets a button trigger. Requires: + * - Trigger button is pressed + * - Parent window is in focus + */ +void GuiTrigger::SetButtonOnlyInFocusTrigger(s32 ch, u32 wiibtns, u16 gcbtns) +{ + type = TRIGGER_BUTTON_ONLY_IN_FOCUS; + chan = ch; + wpad.btns_d = wiibtns; + pad.btns_d = gcbtns; +} + +/**************************************************************************** + * WPAD_Stick + * + * Get X/Y value from Wii Joystick (classic, nunchuk) input + ***************************************************************************/ + +s8 GuiTrigger::WPAD_Stick(u8 right, int axis) +{ + #ifdef HW_RVL + + float mag = 0.0; + float ang = 0.0; + + switch (wpad.exp.type) + { + case WPAD_EXP_NUNCHUK: + case WPAD_EXP_GUITARHERO3: + if (right == 0) + { + mag = wpad.exp.nunchuk.js.mag; + ang = wpad.exp.nunchuk.js.ang; + } + break; + + case WPAD_EXP_CLASSIC: + if (right == 0) + { + mag = wpad.exp.classic.ljs.mag; + ang = wpad.exp.classic.ljs.ang; + } + else + { + mag = wpad.exp.classic.rjs.mag; + ang = wpad.exp.classic.rjs.ang; + } + break; + + default: + break; + } + + /* calculate x/y value (angle need to be converted into radian) */ + if (mag > 1.0) mag = 1.0; + else if (mag < -1.0) mag = -1.0; + double val; + + if(axis == 0) // x-axis + val = mag * sin((PI * ang)/180.0f); + else // y-axis + val = mag * cos((PI * ang)/180.0f); + + return (s8)(val * 128.0f); + + #else + return 0; + #endif +} + +bool GuiTrigger::Left() +{ + u32 wiibtn = WPAD_BUTTON_LEFT; + + if((wpad.btns_d | wpad.btns_h) & (wiibtn | WPAD_CLASSIC_BUTTON_LEFT) + || (pad.btns_d | pad.btns_h) & PAD_BUTTON_LEFT + || pad.stickX < -PADCAL + || WPAD_Stick(0,0) < -PADCAL) + { + if(wpad.btns_d & (wiibtn | WPAD_CLASSIC_BUTTON_LEFT) + || pad.btns_d & PAD_BUTTON_LEFT) + { + scrollDelay = SCROLL_INITIAL_DELAY; // reset scroll delay. + return true; + } + else if(scrollDelay == 0) + { + scrollDelay = SCROLL_LOOP_DELAY; + return true; + } + else + { + if(scrollDelay > 0) + scrollDelay--; + } + } + return false; +} + +bool GuiTrigger::Right() +{ + u32 wiibtn = WPAD_BUTTON_RIGHT; + + if((wpad.btns_d | wpad.btns_h) & (wiibtn | WPAD_CLASSIC_BUTTON_RIGHT) + || (pad.btns_d | pad.btns_h) & PAD_BUTTON_RIGHT + || pad.stickX > PADCAL + || WPAD_Stick(0,0) > PADCAL) + { + if(wpad.btns_d & (wiibtn | WPAD_CLASSIC_BUTTON_RIGHT) + || pad.btns_d & PAD_BUTTON_RIGHT) + { + scrollDelay = SCROLL_INITIAL_DELAY; // reset scroll delay. + return true; + } + else if(scrollDelay == 0) + { + scrollDelay = SCROLL_LOOP_DELAY; + return true; + } + else + { + if(scrollDelay > 0) + scrollDelay--; + } + } + return false; +} + +bool GuiTrigger::Up() +{ + u32 wiibtn = WPAD_BUTTON_UP; + + if((wpad.btns_d | wpad.btns_h) & (wiibtn | WPAD_CLASSIC_BUTTON_UP) + || (pad.btns_d | pad.btns_h) & PAD_BUTTON_UP + || pad.stickY > PADCAL + || WPAD_Stick(0,1) > PADCAL) + { + if(wpad.btns_d & (wiibtn | WPAD_CLASSIC_BUTTON_UP) + || pad.btns_d & PAD_BUTTON_UP) + { + scrollDelay = SCROLL_INITIAL_DELAY; // reset scroll delay. + return true; + } + else if(scrollDelay == 0) + { + scrollDelay = SCROLL_LOOP_DELAY; + return true; + } + else + { + if(scrollDelay > 0) + scrollDelay--; + } + } + return false; +} + +bool GuiTrigger::Down() +{ + u32 wiibtn = WPAD_BUTTON_DOWN; + + if((wpad.btns_d | wpad.btns_h) & (wiibtn | WPAD_CLASSIC_BUTTON_DOWN) + || (pad.btns_d | pad.btns_h) & PAD_BUTTON_DOWN + || pad.stickY < -PADCAL + || WPAD_Stick(0,1) < -PADCAL) + { + if(wpad.btns_d & (wiibtn | WPAD_CLASSIC_BUTTON_DOWN) + || pad.btns_d & PAD_BUTTON_DOWN) + { + scrollDelay = SCROLL_INITIAL_DELAY; // reset scroll delay. + return true; + } + else if(scrollDelay == 0) + { + scrollDelay = SCROLL_LOOP_DELAY; + return true; + } + else + { + if(scrollDelay > 0) + scrollDelay--; + } + } + return false; +} diff --git a/source/libwiigui/gui_window.cpp b/source/libwiigui/gui_window.cpp new file mode 100644 index 00000000..b8abea49 --- /dev/null +++ b/source/libwiigui/gui_window.cpp @@ -0,0 +1,405 @@ +/**************************************************************************** + * libwiigui + * + * Tantric 2009 + * + * gui_window.cpp + * + * GUI class definitions + ***************************************************************************/ + +#include "gui.h" + +GuiWindow::GuiWindow() +{ + width = 0; + height = 0; + focus = 0; // allow focus +} + +GuiWindow::GuiWindow(int w, int h) +{ + width = w; + height = h; + focus = 0; // allow focus +} + +GuiWindow::~GuiWindow() +{ +} + +void GuiWindow::Append(GuiElement* e) +{ + if (e == NULL) + return; + + Remove(e); + _elements.push_back(e); + e->SetParent(this); +} + +void GuiWindow::Insert(GuiElement* e, u32 index) +{ + if (e == NULL || index > (_elements.size() - 1)) + return; + + Remove(e); + _elements.insert(_elements.begin()+index, e); + e->SetParent(this); +} + +void GuiWindow::Remove(GuiElement* e) +{ + if (e == NULL) + return; + + for (u8 i = 0; i < _elements.size(); i++) + { + if(e == _elements.at(i)) + { + _elements.erase(_elements.begin()+i); + break; + } + } +} + +void GuiWindow::RemoveAll() +{ + _elements.clear(); +} + +GuiElement* GuiWindow::GetGuiElementAt(u32 index) const +{ + if (index >= _elements.size()) + return NULL; + return _elements.at(index); +} + +u32 GuiWindow::GetSize() +{ + return _elements.size(); +} + +void GuiWindow::Draw() +{ + if(_elements.size() == 0 || !this->IsVisible()) + return; + + for (u8 i = 0; i < _elements.size(); i++) + { + try { _elements.at(i)->Draw(); } + catch (const std::exception& e) { } + } + + this->UpdateEffects(); + + if(parentElement && state == STATE_DISABLED) + //Menu_DrawRectangle(0,0,screenwidth,screenheight,(GXColor){0xbe, 0xca, 0xd5, 0x70},1); + Menu_DrawRectangle(0,0,screenwidth,screenheight,(GXColor){0, 0, 0, 0x70},1); +} + +void GuiWindow::ResetState() +{ + if(state != STATE_DISABLED) + state = STATE_DEFAULT; + + for (u8 i = 0; i < _elements.size(); i++) + { + try { _elements.at(i)->ResetState(); } + catch (const std::exception& e) { } + } +} + +void GuiWindow::SetState(int s) +{ + state = s; + + for (u8 i = 0; i < _elements.size(); i++) + { + try { _elements.at(i)->SetState(s); } + catch (const std::exception& e) { } + } +} + +void GuiWindow::SetVisible(bool v) +{ + visible = v; + + for (u8 i = 0; i < _elements.size(); i++) + { + try { _elements.at(i)->SetVisible(v); } + catch (const std::exception& e) { } + } +} + +void GuiWindow::SetFocus(int f) +{ + focus = f; + + if(f == 1) + this->MoveSelectionVert(1); + else + this->ResetState(); +} + +void GuiWindow::ChangeFocus(GuiElement* e) +{ + if(parentElement) + return; // this is only intended for the main window + + for (u8 i = 0; i < _elements.size(); i++) + { + if(e == _elements.at(i)) + _elements.at(i)->SetFocus(1); + else if(_elements.at(i)->IsFocused() == 1) + _elements.at(i)->SetFocus(0); + } +} + +void GuiWindow::ToggleFocus(GuiTrigger * t) +{ + if(parentElement) + return; // this is only intended for the main window + + int found = -1; + int newfocus = -1; + u8 i; + + // look for currently in focus element + for (i = 0; i < _elements.size(); i++) + { + try + { + if(_elements.at(i)->IsFocused() == 1) + { + found = i; + break; + } + } + catch (const std::exception& e) { } + } + + // element with focus not found, try to give focus + if(found == -1) + { + for (i = 0; i < _elements.size(); i++) + { + try + { + if(_elements.at(i)->IsFocused() == 0 && _elements.at(i)->GetState() != STATE_DISABLED) // focus is possible (but not set) + { + _elements.at(i)->SetFocus(1); // give this element focus + break; + } + } + catch (const std::exception& e) { } + } + } + // change focus + else if(t->wpad.btns_d & (WPAD_BUTTON_1 | WPAD_BUTTON_1 | WPAD_CLASSIC_BUTTON_PLUS) + || t->pad.btns_d & PAD_BUTTON_B) + { + for (i = found; i < _elements.size(); i++) + { + try + { + if(_elements.at(i)->IsFocused() == 0 && _elements.at(i)->GetState() != STATE_DISABLED) // focus is possible (but not set) + { + newfocus = i; + _elements.at(i)->SetFocus(1); // give this element focus + _elements.at(found)->SetFocus(0); // disable focus on other element + break; + } + } + catch (const std::exception& e) { } + } + + if(newfocus == -1) + { + for (i = 0; i < found; i++) + { + try + { + if(_elements.at(i)->IsFocused() == 0 && _elements.at(i)->GetState() != STATE_DISABLED) // focus is possible (but not set) + { + _elements.at(i)->SetFocus(1); // give this element focus + _elements.at(found)->SetFocus(0); // disable focus on other element + break; + } + } + catch (const std::exception& e) { } + } + } + } +} + +int GuiWindow::GetSelected() +{ + // find selected element + int found = -1; + for (u8 i = 0; i < _elements.size(); i++) + { + try + { + if(_elements.at(i)->GetState() == STATE_SELECTED) + { + found = i; + break; + } + } + catch (const std::exception& e) { } + } + return found; +} + +// set element to left/right as selected +// there's probably a more clever way to do this, but this way works +void GuiWindow::MoveSelectionHor(int dir) +{ + int found = -1; + u16 left = 0; + u16 top = 0; + u8 i = 0; + + int selected = this->GetSelected(); + + if(selected >= 0) + { + left = _elements.at(selected)->GetLeft(); + top = _elements.at(selected)->GetTop(); + } + + // look for a button on the same row, to the left/right + for (i = 0; i < _elements.size(); i++) + { + try + { + if(_elements.at(i)->IsSelectable()) + { + if(_elements.at(i)->GetLeft()*dir > left*dir && _elements.at(i)->GetTop() == top) + { + if(found == -1) + found = i; + else if(_elements.at(i)->GetLeft()*dir < _elements.at(found)->GetLeft()*dir) + found = i; // this is a better match + } + } + } + catch (const std::exception& e) { } + } + if(found >= 0) + goto matchfound; + + // match still not found, let's try the first button in the next row + for (i = 0; i < _elements.size(); i++) + { + try + { + if(_elements.at(i)->IsSelectable()) + { + if(_elements.at(i)->GetTop()*dir > top*dir) + { + if(found == -1) + found = i; + else if(_elements.at(i)->GetTop()*dir < _elements.at(found)->GetTop()*dir) + found = i; // this is a better match + else if(_elements.at(i)->GetTop()*dir == _elements.at(found)->GetTop()*dir + && + _elements.at(i)->GetLeft()*dir < _elements.at(found)->GetLeft()*dir) + found = i; // this is a better match + } + } + } + catch (const std::exception& e) { } + } + + // match found + matchfound: + if(found >= 0) + { + _elements.at(found)->SetState(STATE_SELECTED); + if(selected >= 0) + _elements.at(selected)->ResetState(); + } +} + +void GuiWindow::MoveSelectionVert(int dir) +{ + int found = -1; + u16 left = 0; + u16 top = 0; + u8 i = 0; + + int selected = this->GetSelected(); + + if(selected >= 0) + { + left = _elements.at(selected)->GetLeft(); + top = _elements.at(selected)->GetTop(); + } + + // look for a button above/below, with the least horizontal difference + for (i = 0; i < _elements.size(); i++) + { + try + { + if(_elements.at(i)->IsSelectable()) + { + if(_elements.at(i)->GetTop()*dir > top*dir) + { + if(found == -1) + found = i; + else if(_elements.at(i)->GetTop()*dir < _elements.at(found)->GetTop()*dir) + found = i; // this is a better match + else if(_elements.at(i)->GetTop()*dir == _elements.at(found)->GetTop()*dir + && + abs(_elements.at(i)->GetLeft() - left) < + abs(_elements.at(found)->GetLeft() - left)) + found = i; + } + } + } + catch (const std::exception& e) { } + } + if(found >= 0) + goto matchfound; + + // match found + matchfound: + if(found >= 0) + { + _elements.at(found)->SetState(STATE_SELECTED); + if(selected >= 0) + _elements.at(selected)->ResetState(); + } +} + +void GuiWindow::Update(GuiTrigger * t) +{ + 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) { } + } + + 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); + } + + if(updateCB) + updateCB(this); +} diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 00000000..c26614a7 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** + * libwiigui Template + * Tantric 2009 + * + * demo.cpp + * Basic template/demonstration of libwiigui capabilities. For a + * full-featured app using many more extensions, check out Snes9x GX. + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "FreeTypeGX.h" +#include "video.h" +#include "audio.h" +#include "menu.h" +#include "input.h" +#include "filelist.h" +#include "main.h" +#include "http.h" +#include "dns.h" + +#include "disc.h" +#include "wbfs.h" +#include "sys.h" +#include "video2.h" +#include "wpad.h" +#include "cfg.h" +#include +#include + + +/* Constants */ +#define CONSOLE_XCOORD 260 +#define CONSOLE_YCOORD 115 +#define CONSOLE_WIDTH 340 +#define CONSOLE_HEIGHT 218 + +FreeTypeGX *fontSystem; +int ExitRequested = 0; +bool netcheck = false; + +/*Networking - Forsaekn*/ +int Net_Init(char *ip){ + + s32 res; + while ((res = net_init()) == -EAGAIN) + { + usleep(100 * 1000); //100ms + } + + if (if_config(ip, NULL, NULL, true) < 0) { + printf(" Error reading IP address, exiting"); + usleep(1000 * 1000 * 1); //1 sec + return FALSE; + } + return TRUE; +} + +void ExitApp() +{ + ShutoffRumble(); + StopGX(); + ShutdownAudio(); + //WPAD_Flush(0); + //WPAD_Disconnect(0); + //WPAD_Shutdown(); + //exit(0); +} + +void +DefaultSettings() +{ + Settings.video = discdefault; + Settings.vpatch = off; + Settings.language = ConsoleLangDefault; + Settings.ocarina = off; + Settings.hddinfo = HDDInfo; + Settings.sinfo = ((THEME.showID) ? GameID : Neither); + Settings.rumble = RumbleOn; + if (THEME.showRegion) + { + Settings.sinfo = ((Settings.sinfo == GameID) ? Both : GameRegion); + } + Settings.volume = v80; + Settings.tooltips = TooltipsOn; + snprintf(Settings.unlockCode, sizeof(Settings.unlockCode), "ab121b"); + Settings.parentalcontrol = 0; + Settings.cios = ios249; + + CFG_LoadGlobal(); +} + + +int +main(int argc, char *argv[]) +{ + + s32 ret2; + + __io_wiisd.startup(); + fatMountSimple("SD", &__io_wiisd); + + CFG_Load(argc, argv); + + DefaultSettings(); + + fatUnmount("SD"); + __io_wiisd.shutdown(); + + /* Load Custom IOS */ + if(Settings.cios == ios222) { + ret2 = IOS_ReloadIOS(222); + if (ret2 < 0) { + Settings.cios = ios249; + ret2 = IOS_ReloadIOS(249); + } + } else { + ret2 = IOS_ReloadIOS(249); + } + + if (ret2 < 0) { + printf("ERROR: cIOS could not be loaded!"); + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + } + + Sys_Init(); + //Video_SetMode(); + //Con_Init(CONSOLE_XCOORD, CONSOLE_YCOORD, CONSOLE_WIDTH, CONSOLE_HEIGHT); + //Wpad_Init(); + + PAD_Init(); + InitVideo(); // Initialise video + InitAudio(); // Initialize audio + + + fontSystem = new FreeTypeGX(); + fontSystem->loadFont(font_ttf, font_ttf_size, 0); + fontSystem->setCompatibilityMode(FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_PASSCLR | FTGX_COMPATIBILITY_DEFAULT_VTXDESC_GX_NONE); + + InitGUIThreads(); + MainMenu(MENU_CHECK); + return 0; +} diff --git a/source/main.h b/source/main.h new file mode 100644 index 00000000..7af49813 --- /dev/null +++ b/source/main.h @@ -0,0 +1,21 @@ +/**************************************************************************** + * libwiigui Template + * Tantric 2009 + * + * demo.h + ***************************************************************************/ + +#ifndef _MAIN_H_ +#define _MAIN_H_ + +#include "FreeTypeGX.h" + +extern struct SSettings Settings; + +void ExitApp(); +extern int ExitRequested; +extern FreeTypeGX *fontSystem; +extern bool netcheck; +extern int Net_Init(char *ip); + +#endif diff --git a/source/menu.cpp b/source/menu.cpp new file mode 100644 index 00000000..fd39eca4 --- /dev/null +++ b/source/menu.cpp @@ -0,0 +1,4080 @@ +/**************************************************************************** + * libwiigui Template + * Tantric 2009 + * + * menu.cpp + * Menu flow routines - handles all menu logic + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include //CLOCK +#include //CLOCK +#include + +#include "libwiigui/gui.h" +#include "menu.h" +#include "main.h" +#include "input.h" +#include "http.h" +#include "dns.h" +#include "partition.h" +#include "wbfs.h" +#include "utils.h" +#include "usbstorage.h" +#include "disc.h" +#include "filelist.h" +#include "wdvd.h" +#include "libwbfs/libwbfs.h" +#include "sys.h" +#include "patchcode.h" +#include "wpad.h" +#include "cfg.h" +#include "libwiigui/gui_customoptionbrowser.h" +#include "libwiigui/gui_gamebrowser.h" + +#define MAX_CHARACTERS 38 + +static GuiImage * coverImg = NULL; +static GuiImageData * cover = NULL; + +//char GamesHDD[320][14]; + +static struct discHdr *gameList = NULL; +static GuiImageData * pointer[4]; +static GuiImage * bgImg = NULL; +static GuiButton * btnLogo = NULL; +static GuiImageData * background = NULL; +static char prozent[10] = "0%"; +static char timet[50] = " "; +static GuiText prTxt(prozent, 26, (GXColor){0, 0, 0, 255}); +static GuiText timeTxt(prozent, 26, (GXColor){0, 0, 0, 255}); +static GuiText *GameIDTxt = NULL; +static GuiText *GameRegionTxt = NULL; +static GuiSound * bgMusic = NULL; +static GuiSound * creditsMusic = NULL; +static wbfs_t *hdd = NULL; +static u32 gameCnt = 0; +static s32 gameSelected = 0, gameStart = 0; +static GuiWindow * mainWindow = NULL; +static lwp_t guithread = LWP_THREAD_NULL; +static bool guiHalt = true; +static GuiImageData progressbar(progressbar_png); +static GuiImage progressbarImg(&progressbar); +static double progressDone = 0; +static double progressTotal = 1; +int godmode = 0; +int height = 224; +int width = 160; +static int startat = 0; +static int offset = 0, networkisinitialized = 0; +int vol = Settings.volume; + +//downloadvariables +static char missingFiles[500][12]; //fixed +static int cntMissFiles = 0; + +int direction = 0; // direction the gameprompt slides in + +static char gameregion[7]; +//power button fix +extern u8 shutdown; + + +//Wiilight stuff +static vu32 *_wiilight_reg = (u32*)0xCD0000C0; +void wiilight(int enable){ // Toggle wiilight (thanks Bool for wiilight source) + u32 val = (*_wiilight_reg&~0x20); + if(enable) val |= 0x20; + *_wiilight_reg=val;} + +//Prototypes +int WindowPrompt(const char *title, const char *msg, const char *btn1Label, const char *btn2Label); +static void HaltGui(); +static void ResumeGui(); + + +//libfat helper functions +int isSdInserted() { return __io_wiisd.isInserted(); } + +//Initialise SD CARD +int SDCard_Init() +{ + __io_wiisd.startup(); + if (!isSdInserted()){ + printf("No SD card inserted!"); + return -1; + + } if (!fatMountSimple ("SD", &__io_wiisd)){ + printf("Failed to mount front SD card!"); + return -1; + } + + return 1; +} + +void SDCARD_deInit() +{ + //First unmount all the devs... + fatUnmount ("SD"); + //...and then shutdown em! + __io_wiisd.shutdown(); +} + +bool findfile(const char * filename, const char * path) +{ +DIR *dir; +struct dirent *file; + +dir = opendir(path); + +char temp[11]; +while ((file = readdir(dir))) +{ + snprintf(temp,sizeof(temp),"%s",file->d_name); + if (!strncmpi(temp,filename,11)) + { + //WindowPrompt(path, filename,"go" ,0); + closedir(dir); + return true; + } + } + closedir(dir); + return false; +} + +/**************************************************************************** + * ResumeGui + * + * Signals the GUI thread to start, and resumes the thread. This is called + * after finishing the removal/insertion of new elements, and after initial + * GUI setup. + ***************************************************************************/ +static void +ResumeGui() +{ + guiHalt = false; + LWP_ResumeThread (guithread); +} + +/**************************************************************************** + * HaltGui + * + * Signals the GUI thread to stop, and waits for GUI thread to stop + * This is necessary whenever removing/inserting new elements into the GUI. + * This eliminates the possibility that the GUI is in the middle of accessing + * an element that is being changed. + ***************************************************************************/ +static void +HaltGui() +{ + guiHalt = true; + + // wait for thread to finish + while(!LWP_ThreadIsSuspended(guithread)) + usleep(50); +} + +/**************************************************************************** + * WindowCredits + * Display credits + ***************************************************************************/ +static void WindowCredits(void * ptr) +{ + int angle = 0; + + if(btnLogo->GetState() != STATE_CLICKED) { + return; + } + + bgMusic->Stop(); + creditsMusic = new GuiSound(credits_music_ogg, credits_music_ogg_size, SOUND_OGG, 40); + creditsMusic->SetVolume(40); + creditsMusic->SetLoop(1); + creditsMusic->Play(); + + btnLogo->ResetState(); + + bool exit = false; + int i = 0; + int y = 95; + + GuiWindow creditsWindow(screenwidth,screenheight); + GuiWindow creditsWindowBox(580,448); + creditsWindowBox.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + + GuiImageData creditsBox(credits_bg_png); + GuiImage creditsBoxImg(&creditsBox); + creditsBoxImg.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + creditsWindowBox.Append(&creditsBoxImg); + + GuiImageData star(little_star_png); + GuiImage starImg(&star); + starImg.SetWidescreen(CFG.widescreen); //added + starImg.SetAlignment(ALIGN_LEFT, ALIGN_TOP); + starImg.SetPosition(500,335); + + int numEntries = 15; + GuiText * txt[numEntries]; + + txt[i] = new GuiText("Official Site: http://code.google.com/p/usbloader-gui/", 20, (GXColor){255, 255, 255, 255}); + txt[i]->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); txt[i]->SetPosition(0,y); i++; y+=26; + + txt[i]->SetPresets(22, (GXColor){255, 255, 255, 255}, 0, + FTGX_JUSTIFY_LEFT | FTGX_ALIGN_TOP, ALIGN_LEFT, ALIGN_TOP); + + txt[i] = new GuiText("Coding:"); + txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(100,y); + i++; + + txt[i] = new GuiText("dimok"); + txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(320,y); + i++; + y+=22; + + txt[i] = new GuiText("nIxx"); + txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(320,y); + i++; + y+=22; + + txt[i] = new GuiText("hungyip84"); + txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(320,y); + i++; + y+=22; + + txt[i] = new GuiText("giantpune"); + txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(320,y); + i++; + y+=24; + + txt[i] = new GuiText("Design:"); + txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(100,y); + i++; + + txt[i] = new GuiText("cyrex"); + txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(320,y); + i++; + y+=22; + + txt[i] = new GuiText("NeoRame"); + txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(320,y); + i++; + y+=22; + + txt[i] = new GuiText("WiiShizza"); + txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(320,y); + i++; + y+=24; + + txt[i] = new GuiText("Ocarina & Vidpatch thanks to:"); + txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(100,y); + i++; + y+=22; + + txt[i] = new GuiText("Fishears & WiiPower"); + txt[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); txt[i]->SetPosition(320,y); + i++; + y+=26; + + txt[i] = new GuiText("Special thanks to Tantric for libwiigui"); + txt[i]->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); txt[i]->SetPosition(0,y); + i++; + y+=22; + + txt[i] = new GuiText("and to Waninkoko & Kwiirk for the USB Loader "); + txt[i]->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); txt[i]->SetPosition(0,y); + i++; + y+=22; + + txt[i] = new GuiText("and releasing the source code ;)"); + txt[i]->SetAlignment(ALIGN_CENTRE, ALIGN_TOP); txt[i]->SetPosition(0,y); + i++; + y+=22; + + for(i=0; i < numEntries; i++) + creditsWindowBox.Append(txt[i]); + + + creditsWindow.Append(&creditsWindowBox); + creditsWindow.Append(&starImg); + + while(!exit) + { + creditsWindow.Draw(); + + angle ++; + angle = int(angle) % 360; + usleep(12000); + starImg.SetAngle(angle); + + for(i=3; i >= 0; i--) + { + #ifdef HW_RVL + if(userInput[i].wpad.ir.valid) + Menu_DrawImg(userInput[i].wpad.ir.x-48, userInput[i].wpad.ir.y-48, + 96, 96, pointer[i]->GetImage(), userInput[i].wpad.ir.angle, CFG.widescreen? 0.8 : 1, 1, 255); + if(Settings.rumble == RumbleOn){ + DoRumble(i); + } + #endif + } + + Menu_Render(); + + for(i=0; i < 4; i++) + { + if(userInput[i].wpad.btns_d || userInput[i].pad.btns_d) + exit = true; + } + } + + // clear buttons pressed + for(i=0; i < 4; i++) + { + userInput[i].wpad.btns_d = 0; + userInput[i].pad.btns_d = 0; + } + creditsMusic->Stop(); + for(i=0; i < numEntries; i++) + delete txt[i]; + + delete creditsMusic; + bgMusic->SetLoop(1); + bgMusic->Play(); +} + +/**************************************************************************** + * WiiMenuWindowPrompt + * Display Menu WindowPrompt + ***************************************************************************/ + +int +WiiMenuWindowPrompt(const char *title, const char *btn1Label, const char *btn2Label, const char *btn3Label) +{ + int choice = -1; + + GuiWindow promptWindow(472,320); + promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetPosition(0, -10); + GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM, vol); + GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, SOUND_PCM, vol); + GuiImageData btnOutline(button_dialogue_box_png); + + + 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); + + GuiImageData dialogBox(dialogue_box_png); + GuiImage dialogBoxImg(&dialogBox); + + GuiText titleTxt(title, 26, (GXColor){0, 0, 0, 255}); + titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetPosition(0,55); + + GuiText btn1Txt(btn1Label, 22, (GXColor){0, 0, 0, 255}); + GuiImage btn1Img(&btnOutline); + GuiButton btn1(btnOutline.GetWidth(), btnOutline.GetHeight()); + btn1.SetAlignment(ALIGN_RIGHT, ALIGN_BOTTOM); + btn1.SetPosition(-50, -120); + btn1.SetImage(&btn1Img); + btn1.SetLabel(&btn1Txt); + btn1.SetSoundOver(&btnSoundOver); + btn1.SetSoundClick(&btnClick); + btn1.SetTrigger(&trigA); + btn1.SetState(STATE_SELECTED); + btn1.SetEffectGrow(); + + GuiText btn2Txt(btn2Label, 22, (GXColor){0, 0, 0, 255}); + GuiImage btn2Img(&btnOutline); + GuiButton btn2(btnOutline.GetWidth(), btnOutline.GetHeight()); + btn2.SetAlignment(ALIGN_LEFT, ALIGN_BOTTOM); + btn2.SetPosition(50, -120); + btn2.SetLabel(&btn2Txt); + btn2.SetImage(&btn2Img); + btn2.SetSoundOver(&btnSoundOver); + btn2.SetSoundClick(&btnClick); + btn2.SetTrigger(&trigA); + btn2.SetEffectGrow(); + + GuiText btn3Txt(btn3Label, 22, (GXColor){0, 0, 0, 255}); + GuiImage btn3Img(&btnOutline); + GuiButton btn3(btnOutline.GetWidth(), btnOutline.GetHeight()); + btn3.SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + btn3.SetPosition(0, -65); + btn3.SetLabel(&btn3Txt); + btn3.SetImage(&btn3Img); + btn3.SetSoundOver(&btnSoundOver); + btn3.SetSoundClick(&btnClick); + btn3.SetTrigger(&trigB); + btn3.SetTrigger(&trigA); + btn3.SetEffectGrow(); + + promptWindow.Append(&dialogBoxImg); + promptWindow.Append(&titleTxt); + promptWindow.Append(&btn1); + promptWindow.Append(&btn2); + promptWindow.Append(&btn3); + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_IN, 50); + + HaltGui(); + mainWindow->SetState(STATE_DISABLED); + mainWindow->Append(&promptWindow); + mainWindow->ChangeFocus(&promptWindow); + ResumeGui(); + + while(choice == -1) + { + VIDEO_WaitVSync(); + if(shutdown == 1) + { + wiilight(0); + Sys_Shutdown(); + } + if(btn1.GetState() == STATE_CLICKED) { + choice = 1; + } + else if(btn2.GetState() == STATE_CLICKED) { + choice = 2; + } + else if(btn3.GetState() == STATE_CLICKED) { + choice = 0; + } + } + + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_OUT, 50); + while(promptWindow.GetEffect() > 0) usleep(50); + HaltGui(); + mainWindow->Remove(&promptWindow); + mainWindow->SetState(STATE_DEFAULT); + ResumeGui(); + return choice; +} + +/**************************************************************************** + * WindowPrompt + * + * Displays a prompt window to user, with information, an error message, or + * presenting a user with a choice + ***************************************************************************/ +int +WindowPrompt(const char *title, const char *msg, const char *btn1Label, const char *btn2Label) +{ + int choice = -1; + + GuiWindow promptWindow(472,320); + promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetPosition(0, -10); + GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM, vol); + GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, SOUND_PCM, vol); + GuiImageData btnOutline(button_dialogue_box_png); + + + 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); + + GuiImageData dialogBox(dialogue_box_png); + GuiImage dialogBoxImg(&dialogBox); + + GuiText titleTxt(title, 26, (GXColor){0, 0, 0, 255}); + titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetPosition(0,55); + GuiText msgTxt(msg, 22, (GXColor){0, 0, 0, 255}); + msgTxt.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + msgTxt.SetPosition(0,-40); + msgTxt.SetMaxWidth(430); + + GuiText btn1Txt(btn1Label, 22, (GXColor){0, 0, 0, 255}); + GuiImage btn1Img(&btnOutline); + GuiButton btn1(btnOutline.GetWidth(), btnOutline.GetHeight()); + + if(btn2Label) + { + btn1.SetAlignment(ALIGN_LEFT, ALIGN_BOTTOM); + btn1.SetPosition(40, -45); + } + else + { + btn1.SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + btn1.SetPosition(0, -45); + } + + btn1.SetLabel(&btn1Txt); + btn1.SetImage(&btn1Img); + btn1.SetSoundOver(&btnSoundOver); + btn1.SetSoundClick(&btnClick); + btn1.SetTrigger(&trigA); + btn1.SetState(STATE_SELECTED); + btn1.SetEffectGrow(); + + GuiText btn2Txt(btn2Label, 22, (GXColor){0, 0, 0, 255}); + GuiImage btn2Img(&btnOutline); + GuiButton btn2(btnOutline.GetWidth(), btnOutline.GetHeight()); + btn2.SetAlignment(ALIGN_RIGHT, ALIGN_BOTTOM); + btn2.SetPosition(-40, -45); + btn2.SetLabel(&btn2Txt); + btn2.SetImage(&btn2Img); + btn2.SetSoundOver(&btnSoundOver); + btn2.SetSoundClick(&btnClick); + btn2.SetTrigger(&trigB); + btn2.SetTrigger(&trigA); + btn2.SetEffectGrow(); + + promptWindow.Append(&dialogBoxImg); + promptWindow.Append(&titleTxt); + promptWindow.Append(&msgTxt); + promptWindow.Append(&btn1); + + if(btn2Label) + promptWindow.Append(&btn2); + + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_IN, 50); + HaltGui(); + mainWindow->SetState(STATE_DISABLED); + mainWindow->Append(&promptWindow); + mainWindow->ChangeFocus(&promptWindow); + ResumeGui(); + + while(choice == -1) + { + VIDEO_WaitVSync(); + if(shutdown == 1) + { + wiilight(0); + Sys_Shutdown(); + } + if(btn1.GetState() == STATE_CLICKED) { + choice = 1; + } + else if(btn2.GetState() == STATE_CLICKED) { + choice = 0; + } + } + + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_OUT, 50); + while(promptWindow.GetEffect() > 0) usleep(50); + HaltGui(); + mainWindow->Remove(&promptWindow); + mainWindow->SetState(STATE_DEFAULT); + ResumeGui(); + return choice; +} + +/**************************************************************************** + * DownloadWindowPrompt + * Display download + ***************************************************************************/ +int +DownloadWindowPrompt() +{ + int choice = -1; + + GuiWindow promptWindow(472,320); + promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetPosition(0, -10); + GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM, vol); + GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, SOUND_PCM, vol); + GuiImageData btnOutline(button_dialogue_box_png); + + + 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); + + GuiImageData dialogBox(dialogue_box_png); + GuiImage dialogBoxImg(&dialogBox); + + GuiText titleTxt("Cover Download", 26, (GXColor){0, 0, 0, 255}); + titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetPosition(0,55); + + GuiText btn1Txt("3D Covers", 22, (GXColor){0, 0, 0, 255}); + GuiImage btn1Img(&btnOutline); + GuiButton btn1(btnOutline.GetWidth(), btnOutline.GetHeight()); + btn1.SetAlignment(ALIGN_RIGHT, ALIGN_BOTTOM); + btn1.SetPosition(-50, -120); + btn1.SetImage(&btn1Img); + btn1.SetLabel(&btn1Txt); + btn1.SetSoundOver(&btnSoundOver); + btn1.SetSoundClick(&btnClick); + btn1.SetTrigger(&trigA); + btn1.SetState(STATE_SELECTED); + btn1.SetEffectGrow(); + + GuiText btn2Txt("Normal Covers", 22, (GXColor){0, 0, 0, 255}); + GuiImage btn2Img(&btnOutline); + GuiButton btn2(btnOutline.GetWidth(), btnOutline.GetHeight()); + btn2.SetAlignment(ALIGN_LEFT, ALIGN_BOTTOM); + btn2.SetPosition(50, -120); + btn2.SetLabel(&btn2Txt); + btn2.SetImage(&btn2Img); + btn2.SetSoundOver(&btnSoundOver); + btn2.SetSoundClick(&btnClick); + btn2.SetTrigger(&trigA); + btn2.SetEffectGrow(); + + GuiText btn3Txt("Back", 22, (GXColor){0, 0, 0, 255}); + GuiImage btn3Img(&btnOutline); + GuiButton btn3(btnOutline.GetWidth(), btnOutline.GetHeight()); + btn3.SetAlignment(ALIGN_RIGHT, ALIGN_BOTTOM); + btn3.SetPosition(-50, -65); + btn3.SetLabel(&btn3Txt); + btn3.SetImage(&btn3Img); + btn3.SetSoundOver(&btnSoundOver); + btn3.SetSoundClick(&btnClick); + btn3.SetTrigger(&trigB); + btn3.SetTrigger(&trigA); + btn3.SetEffectGrow(); + + GuiText btn4Txt("Disc Images", 22, (GXColor){0, 0, 0, 255}); + GuiImage btn4Img(&btnOutline); + GuiButton btn4(btnOutline.GetWidth(), btnOutline.GetHeight()); + btn4.SetAlignment(ALIGN_LEFT, ALIGN_BOTTOM); + btn4.SetPosition(50, -65); + btn4.SetLabel(&btn4Txt); + btn4.SetImage(&btn4Img); + btn4.SetSoundOver(&btnSoundOver); + btn4.SetSoundClick(&btnClick); + btn4.SetTrigger(&trigA); + btn4.SetEffectGrow(); + + promptWindow.Append(&dialogBoxImg); + promptWindow.Append(&titleTxt); + promptWindow.Append(&btn1); + promptWindow.Append(&btn2); + promptWindow.Append(&btn3); + promptWindow.Append(&btn4); + + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_IN, 50); + HaltGui(); + mainWindow->SetState(STATE_DISABLED); + mainWindow->Append(&promptWindow); + mainWindow->ChangeFocus(&promptWindow); + ResumeGui(); + + while(choice == -1) + { + VIDEO_WaitVSync(); + if(btn1.GetState() == STATE_CLICKED) { + choice = 2; + } + else if(btn2.GetState() == STATE_CLICKED) { + choice = 1; + } + else if(btn4.GetState() == STATE_CLICKED) { + choice = 3; + } + else if(btn3.GetState() == STATE_CLICKED) { + choice = 0; + } + } + + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_OUT, 50); + while(promptWindow.GetEffect() > 0) usleep(50); + HaltGui(); + mainWindow->Remove(&promptWindow); + mainWindow->SetState(STATE_DEFAULT); + ResumeGui(); + return choice; +} + +/**************************************************************************** + * DeviceWait + ***************************************************************************/ +int +DeviceWait(const char *title, const char *msg, const char *btn1Label, const char *btn2Label) +{ + int i = 30; + char timer[20]; + + GuiWindow promptWindow(472,320); + promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetPosition(0, -10); + + GuiImageData btnOutline(button_dialogue_box_png); + GuiTrigger trigA; + trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); + + GuiImageData dialogBox(dialogue_box_png); + GuiImage dialogBoxImg(&dialogBox); + + GuiText timerTxt(timer, 26, (GXColor){0, 0, 0, 255}); + timerTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + timerTxt.SetPosition(0,200); + + GuiText titleTxt(title, 26, (GXColor){0, 0, 0, 255}); + titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetPosition(0,60); + + GuiText msgTxt(msg, 22, (GXColor){0, 0, 0, 255}); + msgTxt.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + msgTxt.SetPosition(0,0); + msgTxt.SetMaxWidth(430); + + promptWindow.Append(&dialogBoxImg); + promptWindow.Append(&titleTxt); + promptWindow.Append(&msgTxt); + promptWindow.Append(&timerTxt); + + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_IN, 50); + HaltGui(); + mainWindow->SetState(STATE_DISABLED); + mainWindow->Append(&promptWindow); + mainWindow->ChangeFocus(&promptWindow); + ResumeGui(); + + s32 ret2; + while(i >= 0) + { + sprintf(timer, "%u s left", i); + timerTxt.SetText(timer); + VIDEO_WaitVSync(); + if(Settings.cios == ios222) { + ret2 = IOS_ReloadIOS(222); + } else { + ret2 = IOS_ReloadIOS(249); + } + sleep(1); + ret2 = WBFS_Init(WBFS_DEVICE_USB); + if(ret2>=0) + break; + + i--; + } + + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_OUT, 50); + while(promptWindow.GetEffect() > 0) usleep(50); + HaltGui(); + mainWindow->Remove(&promptWindow); + mainWindow->SetState(STATE_DEFAULT); + ResumeGui(); + return ret2; +} + +/**************************************************************************** + * GameWindowPrompt + * + * Displays a prompt window to user, with information, an error message, or + * presenting a user with a choice + ***************************************************************************/ +int GameWindowPrompt() +{ + int choice = -1, angle = 0; + char sizeText[15]; + f32 size = 0.0; + char ID[4]; + char IDFull[7]; + char gameName[CFG.maxcharacters + 4]; + + GuiWindow promptWindow(472,320); + promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetPosition(0, -10); + GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM, vol); + GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, SOUND_PCM, vol); + GuiImageData btnOutline(button_dialogue_box_png); + GuiImageData imgLeft(startgame_arrow_left_png); + GuiImageData imgRight(startgame_arrow_right_png); + + + 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); + 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 trigPlus; + trigPlus.SetButtonOnlyTrigger(-1, WPAD_BUTTON_PLUS | WPAD_CLASSIC_BUTTON_PLUS, 0); + GuiTrigger trigMinus; + trigMinus.SetButtonOnlyTrigger(-1, WPAD_BUTTON_MINUS | WPAD_CLASSIC_BUTTON_MINUS, 0); + + char imgPath[100]; + + if (CFG.widescreen) + snprintf(imgPath, sizeof(imgPath), "%swdialogue_box_startgame.png", CFG.theme_path); + else + snprintf(imgPath, sizeof(imgPath), "%sdialogue_box_startgame.png", CFG.theme_path); + + GuiImageData dialogBox(imgPath, CFG.widescreen ? wdialogue_box_startgame_png : dialogue_box_startgame_png); + GuiImage dialogBoxImg(&dialogBox); + + GuiText msgTxt("", 22, (GXColor){50, 50, 50, 255}); + GuiButton nameBtn(120,50); + nameBtn.SetLabel(&msgTxt); + nameBtn.SetLabelOver(&msgTxt); + nameBtn.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + nameBtn.SetPosition(0,-122); + nameBtn.SetSoundOver(&btnSoundOver); + nameBtn.SetSoundClick(&btnClick); + + if (CFG.godmode == 1){ + nameBtn.SetTrigger(&trigA); + nameBtn.SetEffectGrow(); + } + + GuiText sizeTxt("", 22, (GXColor){50, 50, 50, 255}); //TODO: get the size here + sizeTxt.SetAlignment(ALIGN_RIGHT, ALIGN_TOP); + sizeTxt.SetPosition(-60,70); + + GuiImage diskImg; + diskImg.SetWidescreen(CFG.widescreen); + diskImg.SetAlignment(ALIGN_LEFT, ALIGN_TOP); + diskImg.SetAngle(angle); + + GuiButton btn1(160, 160); + btn1.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + btn1.SetPosition(0, -20); + btn1.SetImage(&diskImg); + + btn1.SetSoundOver(&btnSoundOver); + btn1.SetSoundClick(&btnClick); + btn1.SetTrigger(&trigA); + btn1.SetState(STATE_SELECTED); + //btn1.SetEffectGrow(); just commented it out if anybody wants to use it again. + + GuiText btn2Txt("Back", 22, (GXColor){0, 0, 0, 255}); + GuiImage btn2Img(&btnOutline); + GuiButton btn2(btnOutline.GetWidth(), btnOutline.GetHeight()); + //check if unlocked + if (CFG.godmode == 1) + { + btn2.SetAlignment(ALIGN_LEFT, ALIGN_BOTTOM); + btn2.SetPosition(40, -40); + } + else + { + btn2.SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + btn2.SetPosition(0, -40); + } + + btn2.SetLabel(&btn2Txt); + btn2.SetImage(&btn2Img); + btn2.SetSoundOver(&btnSoundOver); + btn2.SetSoundClick(&btnClick); + btn2.SetTrigger(&trigB); + btn2.SetTrigger(&trigA); + btn2.SetEffectGrow(); + + GuiText btn3Txt("Settings", 22, (GXColor){0, 0, 0, 255}); + GuiImage btn3Img(&btnOutline); + GuiButton btn3(btnOutline.GetWidth(), btnOutline.GetHeight()); + btn3.SetAlignment(ALIGN_RIGHT, ALIGN_BOTTOM); + btn3.SetPosition(-50, -40); + btn3.SetLabel(&btn3Txt); + btn3.SetImage(&btn3Img); + btn3.SetSoundOver(&btnSoundOver); + btn3.SetSoundClick(&btnClick); + btn3.SetTrigger(&trigA); + btn3.SetEffectGrow(); + + GuiImage btnLeftImg(&imgLeft); + GuiButton btnLeft(imgLeft.GetWidth(), imgLeft.GetHeight()); + btnLeft.SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + btnLeft.SetPosition(20, 0); + btnLeft.SetImage(&btnLeftImg); + btnLeft.SetSoundOver(&btnSoundOver); + btnLeft.SetSoundClick(&btnClick); + btnLeft.SetTrigger(&trigA); + btnLeft.SetTrigger(&trigL); + btnLeft.SetTrigger(&trigMinus); + btnLeft.SetEffectGrow(); + + GuiImage btnRightImg(&imgRight); + GuiButton btnRight(imgRight.GetWidth(), imgRight.GetHeight()); + btnRight.SetAlignment(ALIGN_RIGHT, ALIGN_MIDDLE); + btnRight.SetPosition(-20, 0); + btnRight.SetImage(&btnRightImg); + btnRight.SetSoundOver(&btnSoundOver); + btnRight.SetSoundClick(&btnClick); + btnRight.SetTrigger(&trigA); + btnRight.SetTrigger(&trigR); + btnRight.SetTrigger(&trigPlus); + btnRight.SetEffectGrow(); + + promptWindow.Append(&dialogBoxImg); + promptWindow.Append(&nameBtn); + promptWindow.Append(&sizeTxt); + promptWindow.Append(&btn1); + promptWindow.Append(&btn2); + promptWindow.Append(&btnLeft); + promptWindow.Append(&btnRight); + + //check if unlocked + if (CFG.godmode == 1) + { + promptWindow.Append(&btn3); + } + + short changed = 3; + GuiImageData * diskCover = NULL; + + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_IN, 50); + + while (changed) + { + if (changed == 1){ + promptWindow.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_IN, 50); + } + if (changed == 2){ + promptWindow.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_IN, 50); + } + + changed = 0; + + struct discHdr * header = &gameList[gameSelected]; + WBFS_GameSize(header->id, &size); + + snprintf(sizeText, sizeof(sizeText), "%.2fGB", size); //set size text + + snprintf (ID,sizeof(ID),"%c%c%c", header->id[0], header->id[1], header->id[2]); + snprintf (IDFull,sizeof(IDFull),"%c%c%c%c%c%c", header->id[0], header->id[1], header->id[2],header->id[3], header->id[4], header->id[5]); + + //set name + if (strlen(get_title(header)) < (u32)(CFG.maxcharacters + 3)) { + sprintf(gameName, "%s", get_title(header)); + } + else { + strncpy(gameName, get_title(header), CFG.maxcharacters); + gameName[CFG.maxcharacters] = '\0'; + strncat(gameName, "...", 3); + } + + snprintf(imgPath,sizeof(imgPath),"%s%s.png", CFG.disc_path, ID); //changed to current id + + if (diskCover) + delete diskCover; + + diskCover = new GuiImageData(imgPath,0); + + if (!diskCover->GetImage()) + { + delete diskCover; + snprintf(imgPath, sizeof(imgPath), "%s%s.png", CFG.disc_path, IDFull); //changed to current full id + diskCover = new GuiImageData(imgPath, 0); + if (!diskCover->GetImage()) + { + delete diskCover; + diskCover = new GuiImageData(imgPath,nodisc_png); + } + } + + diskImg.SetImage(diskCover); + sizeTxt.SetText(sizeText); + msgTxt.SetText(gameName); + + HaltGui(); + mainWindow->SetState(STATE_DISABLED); + mainWindow->Append(&promptWindow); + mainWindow->ChangeFocus(&promptWindow); + ResumeGui(); + + float speedup = 1; //speedup increases while disc is selected + + while(choice == -1) + { + VIDEO_WaitVSync(); + //angle++; + angle = int(angle+speedup) % 360; + //disc speedup and slowdown + if (btn1.GetState() == STATE_SELECTED) { //if mouse over + if (speedup < 11) // speed up + { + speedup = (speedup+0.20); + } + } + else //if not mouse over + { + if (speedup > 1) {speedup = (speedup-0.05);} //slow down + } + if (speedup < 1) + { + speedup = 1; + } + diskImg.SetAngle(angle); + + if(shutdown == 1) //for power button + { + wiilight(0); + Sys_Shutdown(); + } + + if(btn1.GetState() == STATE_CLICKED) { //boot + choice = 1; + SDCARD_deInit(); + } + + else if(btn2.GetState() == STATE_CLICKED) { //back + choice = 0; + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_OUT, 50); + mainWindow->SetState(STATE_DEFAULT); + wiilight(0); + } + + else if(btn3.GetState() == STATE_CLICKED) { //settings + choice = 2; + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_OUT, 50); + } + + else if(nameBtn.GetState() == STATE_CLICKED) { //rename + choice = 3; + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_OUT, 50); + } + + else if(btnRight.GetState() == STATE_CLICKED) { + promptWindow.SetEffect(EFFECT_SLIDE_RIGHT | EFFECT_SLIDE_OUT, 50); + changed = 1; + gameSelected = (gameSelected + 1) % gameCnt; + btnRight.ResetState(); + break; + } + + else if(btnLeft.GetState() == STATE_CLICKED) { + promptWindow.SetEffect(EFFECT_SLIDE_LEFT | EFFECT_SLIDE_OUT, 50); + changed = 2; + gameSelected = (gameSelected - 1 + gameCnt) % gameCnt; + btnLeft.ResetState(); + break; + } + } + + + while(promptWindow.GetEffect() > 0) usleep(50); + HaltGui(); + mainWindow->Remove(&promptWindow); + + ResumeGui(); + } + delete diskCover; + + return choice; +} + +/**************************************************************************** + * DiscWait + ***************************************************************************/ +int +DiscWait(const char *title, const char *msg, const char *btn1Label, const char *btn2Label) +{ + int choice = -1; + u32 cover = 0; + s32 ret; + + GuiWindow promptWindow(472,320); + promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetPosition(0, -10); + GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM, vol); + GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, SOUND_PCM, vol); + GuiImageData btnOutline(button_dialogue_box_png); + 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); + + + GuiImageData dialogBox(dialogue_box_png); + GuiImage dialogBoxImg(&dialogBox); + + GuiText titleTxt(title, 26, (GXColor){0, 0, 0, 255}); + titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetPosition(0,60); + GuiText msgTxt(msg, 22, (GXColor){0, 0, 0, 255}); + msgTxt.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + msgTxt.SetPosition(0,-40); + msgTxt.SetMaxWidth(430); + + GuiText btn1Txt(btn1Label, 22, (GXColor){0, 0, 0, 255}); + GuiImage btn1Img(&btnOutline); + GuiButton btn1(btnOutline.GetWidth(), btnOutline.GetHeight()); + + if(btn2Label) + { + btn1.SetAlignment(ALIGN_LEFT, ALIGN_BOTTOM); + btn1.SetPosition(40, -45); + } + else + { + btn1.SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + btn1.SetPosition(0, -45); + } + + btn1.SetLabel(&btn1Txt); + btn1.SetImage(&btn1Img); + btn1.SetSoundOver(&btnSoundOver); + btn1.SetSoundClick(&btnClick); + btn1.SetTrigger(&trigB); + btn1.SetTrigger(&trigA); + btn1.SetState(STATE_SELECTED); + btn1.SetEffectGrow(); + + GuiText btn2Txt(btn2Label, 22, (GXColor){0, 0, 0, 255}); + GuiImage btn2Img(&btnOutline); + GuiButton btn2(btnOutline.GetWidth(), btnOutline.GetHeight()); + btn2.SetAlignment(ALIGN_RIGHT, ALIGN_BOTTOM); + btn2.SetPosition(-20, -25); + btn2.SetLabel(&btn2Txt); + btn2.SetImage(&btn2Img); + btn2.SetSoundOver(&btnSoundOver); + btn2.SetSoundClick(&btnClick); + btn2.SetTrigger(&trigA); + btn2.SetEffectGrow(); + + promptWindow.Append(&dialogBoxImg); + promptWindow.Append(&titleTxt); + promptWindow.Append(&msgTxt); + promptWindow.Append(&btn1); + + if(btn2Label) + promptWindow.Append(&btn2); + + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_IN, 50); + HaltGui(); + mainWindow->SetState(STATE_DISABLED); + mainWindow->Append(&promptWindow); + mainWindow->ChangeFocus(&promptWindow); + ResumeGui(); + + while(!(cover & 0x2)) + { + VIDEO_WaitVSync(); + if(btn1.GetState() == STATE_CLICKED) { + choice = 1; + break; + } + ret = WDVD_GetCoverStatus(&cover); + if (ret < 0) + return ret; + } + + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_OUT, 50); + while(promptWindow.GetEffect() > 0) usleep(50); + HaltGui(); + mainWindow->Remove(&promptWindow); + mainWindow->SetState(STATE_DEFAULT); + ResumeGui(); + return 0; +} + +/**************************************************************************** + * FormatingPartition + ***************************************************************************/ +int +FormatingPartition(const char *title, partitionEntry *entry) +{ + int ret; + GuiWindow promptWindow(472,320); + promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetPosition(0, -10); + + GuiImageData btnOutline(button_dialogue_box_png); + GuiTrigger trigA; + trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); + + GuiImageData dialogBox(dialogue_box_png); + GuiImage dialogBoxImg(&dialogBox); + + GuiText titleTxt(title, 26, (GXColor){0, 0, 0, 255}); + titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetPosition(0,60); + + promptWindow.Append(&dialogBoxImg); + promptWindow.Append(&titleTxt); + + + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_IN, 50); + HaltGui(); + mainWindow->SetState(STATE_DISABLED); + mainWindow->Append(&promptWindow); + mainWindow->ChangeFocus(&promptWindow); + ResumeGui(); + + VIDEO_WaitVSync(); + ret = WBFS_Format(entry->sector, entry->size); + + + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_OUT, 50); + while(promptWindow.GetEffect() > 0) usleep(50); + HaltGui(); + mainWindow->Remove(&promptWindow); + mainWindow->SetState(STATE_DEFAULT); + ResumeGui(); + return ret; +} + + +/**************************************************************************** + * NetworkInit + ***************************************************************************/ +char * NetworkInitPromp(int choice2) +{ + char myIP [16]; + char * IP = 0; + GuiWindow promptWindow(472,320); + promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetPosition(0, -10); + + GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM, vol); + GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, SOUND_PCM, vol); + + GuiImageData btnOutline(button_dialogue_box_png); + GuiTrigger trigA; + trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); + + GuiImageData dialogBox(dialogue_box_png); + GuiImage dialogBoxImg(&dialogBox); + + GuiText titleTxt("Initializing Network", 26, (GXColor){0, 0, 0, 255}); + titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetPosition(0,60); + + char msg[20] = " "; + GuiText msgTxt(msg, 22, (GXColor){0, 0, 0, 255}); + msgTxt.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + msgTxt.SetPosition(0,-40); + + GuiText btn1Txt("Cancel", 22, (GXColor){0, 0, 0, 255}); + GuiImage btn1Img(&btnOutline); + GuiButton btn1(btnOutline.GetWidth(), btnOutline.GetHeight()); + btn1.SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + btn1.SetPosition(0, -45); + btn1.SetLabel(&btn1Txt); + btn1.SetImage(&btn1Img); + btn1.SetSoundOver(&btnSoundOver); + btn1.SetSoundClick(&btnClick); + btn1.SetTrigger(&trigA); + btn1.SetState(STATE_SELECTED); + btn1.SetEffectGrow(); + + promptWindow.Append(&dialogBoxImg); + promptWindow.Append(&titleTxt); + promptWindow.Append(&msgTxt); + promptWindow.Append(&btn1); + + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_IN, 50); + HaltGui(); + mainWindow->SetState(STATE_DISABLED); + mainWindow->Append(&promptWindow); + mainWindow->ChangeFocus(&promptWindow); + ResumeGui(); + + VIDEO_WaitVSync(); + + while (!IP) + { + + Net_Init(myIP); + IP = myIP; + if (IP) { + sprintf(msg, "IP: %s", IP); + msgTxt.SetText(msg); + cntMissFiles = 0; + u32 i = 0; + char filename[11]; + // char filenameshort[10]; + bool found1 = false; + bool found2 = false; + for (i = 0; i < gameCnt && cntMissFiles < 500; i++) + { + struct discHdr* header = &gameList[i]; + if (choice2 != 3) { + + snprintf (filename,sizeof(filename),"%c%c%c.png", header->id[0], header->id[1], header->id[2]); + found2 = findfile(filename, CFG.covers_path); + snprintf(filename,sizeof(filename),"%c%c%c%c%c%c.png",header->id[0], header->id[1], header->id[2], + header->id[3], header->id[4], header->id[5]); //full id + found1 = findfile(filename, CFG.covers_path); + if (!found1 && !found2) //if could not find any image + { + snprintf(missingFiles[cntMissFiles],11,"%s",filename); + cntMissFiles++; + } + } + else if (choice2 == 3) { + snprintf (filename,sizeof(filename),"%c%c%c.png", header->id[0], header->id[1], header->id[2]); + found2 = findfile(filename, CFG.disc_path); + snprintf(filename,sizeof(filename),"%c%c%c%c%c%c.png",header->id[0], header->id[1], header->id[2], + header->id[3], header->id[4], header->id[5]); //full id + found1 = findfile(filename,CFG.disc_path); + if (!found1 && !found2) + { + snprintf(missingFiles[cntMissFiles],11,"%s",filename); + cntMissFiles++; + } + } + } + break; + } + + if(btn1.GetState() == STATE_CLICKED) { //pending: this should be inside the for loop + IP = 0; + break; + } + + } + promptWindow.SetEffect(EFFECT_SLIDE_TOP | EFFECT_SLIDE_OUT, 50); + while(promptWindow.GetEffect() > 0) usleep(50); + HaltGui(); + mainWindow->Remove(&promptWindow); + mainWindow->SetState(STATE_DEFAULT); + ResumeGui(); + return IP; +} + +/**************************************************************************** + * ShowProgress + * + * Updates the variables used by the progress window for drawing a progress + * bar. Also resumes the progress window thread if it is suspended. + ***************************************************************************/ +void +ShowProgress (s32 done, s32 total) +{ + + static time_t start; + static u32 expected; + + f32 percent; //, size; + u32 d, h, m, s; + + //first time + if (!done) { + start = time(0); + expected = 300; + } + + //Elapsed time + d = time(0) - start; + + if (done != total) { + //Expected time + if (d) + expected = (expected * 3 + d * total / done) / 4; + + //Remaining time + d = (expected > d) ? (expected - d) : 0; + } + + //Calculate time values + h = d / 3600; + m = (d / 60) % 60; + s = d % 60; + + //Calculate percentage/size + percent = (done * 100.0) / total; + //size = (hdd->wbfs_sec_sz / GB_SIZE) * total; + + progressTotal = total; + progressDone = done; + + sprintf(prozent, "%0.2f%%", percent); + prTxt.SetText(prozent); + sprintf(timet,"Time left: %d:%02d:%02d",h,m,s); + timeTxt.SetText(timet); + progressbarImg.SetTile(100*progressDone/progressTotal); + +} + +/**************************************************************************** + * ProgressWindow + * + * Opens a window, which displays progress to the user. Can either display a + * progress bar showing % completion, or a throbber that only shows that an + * action is in progress. + ***************************************************************************/ +int +ProgressWindow(const char *title, const char *msg) +{ + + GuiWindow promptWindow(472,320); + promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetPosition(0, -10); + GuiImageData btnOutline(button_dialogue_box_png); + GuiTrigger trigA; + trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); + + GuiImageData dialogBox(dialogue_box_png); + GuiImage dialogBoxImg(&dialogBox); + + GuiImageData progressbarOutline(progressbar_outline_png); + GuiImage progressbarOutlineImg(&progressbarOutline); + progressbarOutlineImg.SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + progressbarOutlineImg.SetPosition(25, 40); + + GuiImageData progressbarEmpty(progressbar_empty_png); + GuiImage progressbarEmptyImg(&progressbarEmpty); + progressbarEmptyImg.SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + progressbarEmptyImg.SetPosition(25, 40); + progressbarEmptyImg.SetTile(100); + + GuiImageData progressbar(progressbar_png); + + progressbarImg.SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + progressbarImg.SetPosition(25, 40); + + GuiText titleTxt(title, 26, (GXColor){70, 70, 10, 255}); + titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetPosition(0,60); + GuiText msgTxt(msg, 26, (GXColor){0, 0, 0, 255}); + msgTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + msgTxt.SetPosition(0,130); + + prTxt.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + prTxt.SetPosition(0, 40); + + timeTxt.SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + timeTxt.SetPosition(0,-30); + + promptWindow.Append(&dialogBoxImg); + promptWindow.Append(&titleTxt); + promptWindow.Append(&msgTxt); + promptWindow.Append(&progressbarEmptyImg); + promptWindow.Append(&progressbarImg); + promptWindow.Append(&progressbarOutlineImg); + promptWindow.Append(&prTxt); + promptWindow.Append(&timeTxt); + + HaltGui(); + mainWindow->SetState(STATE_DISABLED); + mainWindow->Append(&promptWindow); + mainWindow->ChangeFocus(&promptWindow); + ResumeGui(); + + s32 ret; + + ret = wbfs_add_disc(hdd, __WBFS_ReadDVD, NULL, ShowProgress, ONLY_GAME_PARTITION, 0); + + HaltGui(); + mainWindow->Remove(&promptWindow); + mainWindow->SetState(STATE_DEFAULT); + ResumeGui(); + if (ret < 0) { + return ret; + } + return 0; +} + +/**************************************************************************** + * ProgressWindow + * + * Opens a window, which displays progress to the user. Can either display a + * progress bar showing % completion, or a throbber that only shows that an + * action is in progress. + ***************************************************************************/ +int +ProgressDownloadWindow(int choice2) +{ + + int i = 0, cntNotFound = 0; +// char filename[11]; + + GuiWindow promptWindow(472,320); + promptWindow.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + promptWindow.SetPosition(0, -10); + + GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM, vol); + GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, SOUND_PCM, vol); + + GuiImageData btnOutline(button_dialogue_box_png); + GuiTrigger trigA; + trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); + + GuiImageData dialogBox(dialogue_box_png); + GuiImage dialogBoxImg(&dialogBox); + + GuiImageData progressbarOutline(progressbar_outline_png); + GuiImage progressbarOutlineImg(&progressbarOutline); + progressbarOutlineImg.SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + progressbarOutlineImg.SetPosition(25, 40); + + GuiImageData progressbarEmpty(progressbar_empty_png); + GuiImage progressbarEmptyImg(&progressbarEmpty); + progressbarEmptyImg.SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + progressbarEmptyImg.SetPosition(25, 40); + progressbarEmptyImg.SetTile(100); + + GuiImageData progressbar(progressbar_png); + + progressbarImg.SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + progressbarImg.SetPosition(25, 40); + + GuiText titleTxt("Downloading file:", 26, (GXColor){70, 70, 10, 255}); + titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetPosition(0,60); + char msg[25] = " "; + GuiText msgTxt(msg, 26, (GXColor){0, 0, 0, 255}); + msgTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + msgTxt.SetPosition(0,130); + char msg2[15] = " "; + GuiText msg2Txt(msg2, 26, (GXColor){0, 0, 0, 255}); + msg2Txt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + msg2Txt.SetPosition(0,100); + + prTxt.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); + prTxt.SetPosition(0, 40); + + GuiText btn1Txt("Cancel", 22, (GXColor){0, 0, 0, 255}); + GuiImage btn1Img(&btnOutline); + GuiButton btn1(btnOutline.GetWidth(), btnOutline.GetHeight()); + btn1.SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + btn1.SetPosition(0, -45); + btn1.SetLabel(&btn1Txt); + btn1.SetImage(&btn1Img); + btn1.SetSoundOver(&btnSoundOver); + btn1.SetSoundClick(&btnClick); + btn1.SetTrigger(&trigA); + btn1.SetState(STATE_SELECTED); + btn1.SetEffectGrow(); + + promptWindow.Append(&dialogBoxImg); + promptWindow.Append(&titleTxt); + promptWindow.Append(&msgTxt); + promptWindow.Append(&msg2Txt); + promptWindow.Append(&progressbarEmptyImg); + promptWindow.Append(&progressbarImg); + promptWindow.Append(&progressbarOutlineImg); + promptWindow.Append(&prTxt); + promptWindow.Append(&btn1); + + HaltGui(); + mainWindow->SetState(STATE_DISABLED); + mainWindow->Append(&promptWindow); + mainWindow->ChangeFocus(&promptWindow); + ResumeGui(); + + //check if directory exist and if not create one + struct stat st; + if(stat(CFG.covers_path, &st) != 0) { + char dircovers[100]; + snprintf(dircovers,strlen(CFG.covers_path),"%s",CFG.covers_path); + if (mkdir(dircovers, 0777) == -1) { + WindowPrompt("Error:","Can't create directory","OK",0); + cntMissFiles = 0; + } + } + if(stat(CFG.disc_path,&st) != 0) { + char dirdiscs[100]; + snprintf(dirdiscs,strlen(CFG.disc_path),"%s",CFG.disc_path); + if (mkdir(dirdiscs, 0777) == -1) { + WindowPrompt("Error:","Can't create directory","OK",0); + cntMissFiles = 0; + } + } + + while (i < cntMissFiles) { + + + + sprintf(prozent, "%i%%", 100*i/cntMissFiles); + prTxt.SetText(prozent); + progressbarImg.SetTile(100*i/cntMissFiles); + + sprintf(msg, "%i file(s) left", cntMissFiles - i); + msgTxt.SetText(msg); + sprintf(msg2, "%s", missingFiles[i]); + msg2Txt.SetText(msg2); + + //download boxart image + char imgPath[100]; + char URLFile[100]; + if (choice2 == 2) { + sprintf(URLFile,"http://www.theotherzone.com/wii/3d/176/248/%s",missingFiles[i]); // For 3D Covers + sprintf(imgPath,"%s%s", CFG.covers_path, missingFiles[i]); + } + if(choice2 == 3) { + sprintf(URLFile,"http://www.theotherzone.com/wii/diskart/160/160/%s",missingFiles[i]); + sprintf(imgPath,"%s%s", CFG.disc_path, missingFiles[i]); + } + if(choice2 == 1) { + sprintf(URLFile,"http://www.theotherzone.com/wii/resize/160/224/%s",missingFiles[i]); + sprintf(imgPath,"%s%s", CFG.covers_path, missingFiles[i]); + } + + struct block file = downloadfile(URLFile); + + if (file.size == 36864 || file.size <= 1024 || file.size == 7386 || file.data == NULL) { + cntNotFound++; + i++; + } else { + + if(file.data != NULL) + { + // save png to sd card + FILE *pfile; + pfile = fopen(imgPath, "wb"); + if (!pfile) + return -1; + fwrite(file.data,1,file.size,pfile); + fclose (pfile); + free(file.data); + } + + i++; + } + + if(btn1.GetState() == STATE_CLICKED) { + cntNotFound = cntMissFiles-i+cntNotFound; + break; + } + + } + + + HaltGui(); + mainWindow->Remove(&promptWindow); + mainWindow->SetState(STATE_DEFAULT); + ResumeGui(); + + if (cntNotFound != 0) { + return cntNotFound; + } else { + return 0; + } +} + + +/**************************************************************************** + * UpdateGUI + * + * Primary thread to allow GUI to respond to state changes, and draws GUI + ***************************************************************************/ + +static void * +UpdateGUI (void *arg) +{ + while(1) + { + if(guiHalt) + { + LWP_SuspendThread(guithread); + } + else + { + mainWindow->Draw(); + + #ifdef HW_RVL + for(int i=3; i >= 0; i--) // so that player 1's cursor appears on top! + { + if(userInput[i].wpad.ir.valid) + Menu_DrawImg(userInput[i].wpad.ir.x-48, userInput[i].wpad.ir.y-48, + 96, 96, pointer[i]->GetImage(), userInput[i].wpad.ir.angle, CFG.widescreen? 0.8 : 1, 1, 255); + if(Settings.rumble == RumbleOn) + { + DoRumble(i); + } + } + #endif + + Menu_Render(); + + for(int i=0; i < 4; i++) + mainWindow->Update(&userInput[i]); + + if(ExitRequested) + { + for(int a = 0; a < 255; a += 15) + { + mainWindow->Draw(); + Menu_DrawRectangle(0,0,screenwidth,screenheight,(GXColor){0, 0, 0, a},1); + Menu_Render(); + } + ExitApp(); + } + } + } + return NULL; +} + +/**************************************************************************** + * InitGUIThread + * + * Startup GUI threads + ***************************************************************************/ +void +InitGUIThreads() +{ + LWP_CreateThread (&guithread, UpdateGUI, NULL, NULL, 0, 70); +} + +/**************************************************************************** + * EntryCmp + ***************************************************************************/ +s32 __Menu_EntryCmp(const void *a, const void *b) +{ + struct discHdr *hdr1 = (struct discHdr *)a; + struct discHdr *hdr2 = (struct discHdr *)b; + + /* Compare strings */ + return stricmp(get_title(hdr1), get_title(hdr2)); +} + +/**************************************************************************** + * Get Gamelist + ***************************************************************************/ + +s32 __Menu_GetEntries(void) +{ + struct discHdr *buffer = NULL; + struct discHdr *buffer2 = NULL; + struct discHdr *header = NULL; + + u32 cnt, len; + s32 ret; + + /* Get list length */ + ret = WBFS_GetCount(&cnt); + if (ret < 0) + return ret; + + /* Buffer length */ + len = sizeof(struct discHdr) * cnt; + + /* Allocate memory */ + buffer = (struct discHdr *)memalign(32, len); + if (!buffer) + return -1; + + /* Clear buffer */ + memset(buffer, 0, len); + + /* Get header list */ + ret = WBFS_GetHeaders(buffer, cnt, sizeof(struct discHdr)); + if (ret < 0) + goto err; + + if (CFG.parentalcontrol && !CFG.godmode) + { + u32 cnt2 = 0; + + for (u32 i = 0; i < cnt; i++) + { + header = &buffer[i]; + if (get_block(header) < CFG.parentalcontrol) + { + buffer2 = (discHdr *) realloc(buffer2, (cnt2+1) * sizeof(struct discHdr)); + if (!buffer2) + { + free(buffer); + return -1; + } + + memcpy((buffer2 + cnt2), (buffer + i), sizeof(struct discHdr)); + cnt2++; + } + } + free(buffer); + buffer = buffer2; + buffer2 = NULL; + cnt = cnt2; + } + + /* Sort entries */ + qsort(buffer, cnt, sizeof(struct discHdr), __Menu_EntryCmp); + + /* Free memory */ + if (gameList) + free(gameList); + + /* Set values */ + gameList = buffer; + gameCnt = cnt; + + /* Reset variables */ + gameSelected = gameStart = 0; + + return 0; + +err: + /* Free memory */ + if (buffer) + free(buffer); + + return ret; +} + +/**************************************************************************** + * OnScreenKeyboard + * + * Opens an on-screen keyboard window, with the data entered being stored + * into the specified variable. + ***************************************************************************/ +static int OnScreenKeyboard(char * var, u16 maxlen) +{ + int save = -1; + + GuiKeyboard keyboard(var, maxlen); + + GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM, vol); + GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, SOUND_PCM, vol); + + GuiImageData btnOutline(button_dialogue_box_png); + GuiTrigger trigA; + trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); + GuiTrigger trigB; + trigB.SetSimpleTrigger(-1, WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B, PAD_BUTTON_B); + + GuiText okBtnTxt("OK", 22, (GXColor){0, 0, 0, 255}); + GuiImage okBtnImg(&btnOutline); + GuiButton okBtn(btnOutline.GetWidth(), btnOutline.GetHeight()); + + okBtn.SetAlignment(ALIGN_LEFT, ALIGN_BOTTOM); + okBtn.SetPosition(25, -25); + + okBtn.SetLabel(&okBtnTxt); + okBtn.SetImage(&okBtnImg); + okBtn.SetSoundOver(&btnSoundOver); + okBtn.SetSoundClick(&btnClick); + okBtn.SetTrigger(&trigA); + okBtn.SetEffectGrow(); + + GuiText cancelBtnTxt("Cancel", 22, (GXColor){0, 0, 0, 255}); + GuiImage cancelBtnImg(&btnOutline); + GuiButton cancelBtn(btnOutline.GetWidth(), btnOutline.GetHeight()); + cancelBtn.SetAlignment(ALIGN_RIGHT, ALIGN_BOTTOM); + cancelBtn.SetPosition(-25, -25); + cancelBtn.SetLabel(&cancelBtnTxt); + cancelBtn.SetImage(&cancelBtnImg); + cancelBtn.SetSoundOver(&btnSoundOver); + cancelBtn.SetSoundClick(&btnClick); + cancelBtn.SetTrigger(&trigA); + cancelBtn.SetTrigger(&trigB); + cancelBtn.SetEffectGrow(); + + keyboard.Append(&okBtn); + keyboard.Append(&cancelBtn); + + HaltGui(); + mainWindow->SetState(STATE_DISABLED); + mainWindow->Append(&keyboard); + mainWindow->ChangeFocus(&keyboard); + ResumeGui(); + + while(save == -1) + { + VIDEO_WaitVSync(); + + if(okBtn.GetState() == STATE_CLICKED) + save = 1; + else if(cancelBtn.GetState() == STATE_CLICKED) + save = 0; + } + + if(save) + { + snprintf(var, maxlen, "%s", keyboard.kbtextstr); + } + + HaltGui(); + mainWindow->Remove(&keyboard); + mainWindow->SetState(STATE_DEFAULT); + ResumeGui(); + return save; +} + + +/**************************************************************************** + * MenuInstall + ***************************************************************************/ + +static int MenuInstall() +{ + int menu = MENU_NONE; + static struct discHdr headerdisc ATTRIBUTE_ALIGN(32); + + if(Settings.cios == ios222) { + Disc_SetUSB(NULL, 1); + } else { + Disc_SetUSB(NULL, 0); + } + + int ret, choice = 0; + char *name; + static char buffer[MAX_CHARACTERS + 4]; + + + GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM, vol); + + GuiImageData battery(battery_png); + GuiImageData batteryRed(battery_red_png); + GuiImageData batteryBar(battery_bar_png); + + + #ifdef HW_RVL + int i = 0, level; + char txt[3]; + GuiText * batteryTxt[4]; + GuiImage * batteryImg[4]; + GuiImage * batteryBarImg[4]; + GuiButton * batteryBtn[4]; + + for(i=0; i < 4; i++) + { + + if(i == 0) + sprintf(txt, "P%d", i+1); + else + sprintf(txt, "P%d", i+1); + + batteryTxt[i] = new GuiText(txt, 22, (GXColor){THEME.info_r, THEME.info_g, THEME.info_b, 255}); + batteryTxt[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + batteryImg[i] = new GuiImage(&battery); + batteryImg[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + batteryImg[i]->SetPosition(36, 0); + batteryImg[i]->SetTile(0); + batteryBarImg[i] = new GuiImage(&batteryBar); + batteryBarImg[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + batteryBarImg[i]->SetPosition(33, 0); + + batteryBtn[i] = new GuiButton(40, 20); + batteryBtn[i]->SetLabel(batteryTxt[i]); + batteryBtn[i]->SetImage(batteryBarImg[i]); + batteryBtn[i]->SetIcon(batteryImg[i]); + batteryBtn[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); + batteryBtn[i]->SetRumble(false); + batteryBtn[i]->SetAlpha(70); + } + + + batteryBtn[0]->SetPosition(THEME.battery1_x, THEME.battery1_y); + batteryBtn[1]->SetPosition(THEME.battery2_x, THEME.battery2_y); + batteryBtn[2]->SetPosition(THEME.battery3_x, THEME.battery3_y); + batteryBtn[3]->SetPosition(THEME.battery4_x, THEME.battery4_y); + #endif + + HaltGui(); + GuiWindow w(screenwidth, screenheight); + + if (THEME.showBattery) + { + #ifdef HW_RVL + w.Append(batteryBtn[0]); + w.Append(batteryBtn[1]); + w.Append(batteryBtn[2]); + w.Append(batteryBtn[3]); + #endif + } + + mainWindow->Append(&w); + + ResumeGui(); + + while(menu == MENU_NONE) + { + VIDEO_WaitVSync (); + + #ifdef HW_RVL + for(i=0; i < 4; i++) + { + if(WPAD_Probe(i, NULL) == WPAD_ERR_NONE) // controller connected + { + level = (userInput[i].wpad.battery_level / 100.0) * 4; + if(level > 4) level = 4; + batteryImg[i]->SetTile(level); + + if(level == 0) + batteryBarImg[i]->SetImage(&batteryRed); + else + batteryBarImg[i]->SetImage(&batteryBar); + + batteryBtn[i]->SetAlpha(255); + } + else // controller not connected + { + batteryImg[i]->SetTile(0); + batteryImg[i]->SetImage(&battery); + batteryBtn[i]->SetAlpha(70); + } + } + #endif + + ret = DiscWait("Insert Disk","Waiting...","Cancel",0); + if (ret < 0) { + WindowPrompt ("Error reading Disc",0,"Back",0); + menu = MENU_DISCLIST; + break; + } + ret = Disc_Open(); + if (ret < 0) { + WindowPrompt ("Could not open Disc",0,"Back",0); + menu = MENU_DISCLIST; + break; + } + + ret = Disc_IsWii(); + if (ret < 0) { + choice = WindowPrompt ("Not a Wii Disc","Insert a Wii Disc!","OK","Back"); + + if (choice == 1) { + menu = MENU_INSTALL; + break; + } else + menu = MENU_DISCLIST; + break; + } + + Disc_ReadHeader(&headerdisc); + name = headerdisc.title; + if (strlen(name) < (22 + 3)) { + memset(buffer, 0, sizeof(buffer)); + sprintf(name, "%s", name); + } else { + strncpy(buffer, name, MAX_CHARACTERS); + strncat(buffer, "...", 3); + sprintf(name, "%s", buffer); + } + + ret = WBFS_CheckGame(headerdisc.id); + if (ret) { + WindowPrompt ("Game is already installed:",name,"Back",0); + menu = MENU_DISCLIST; + break; + } + hdd = GetHddInfo(); + if (!hdd) { + WindowPrompt ("No HDD found!","Error!!","Back",0); + menu = MENU_DISCLIST; + break; + } + + f32 freespace, used; + + WBFS_DiskSpace(&used, &freespace); + u32 estimation = wbfs_estimate_disc(hdd, __WBFS_ReadDVD, NULL, ONLY_GAME_PARTITION); + f32 gamesize = ((f32) estimation)/1073741824; + char gametxt[50]; + sprintf(gametxt, "Installing game %.2fGB:", gamesize); + if (gamesize > freespace) { + char errortxt[50]; + sprintf(errortxt, "Game Size: %.2fGB, Free Space: %.2fGB", gamesize, freespace); + choice = WindowPrompt("Not enough free space!",errortxt,"Go on", "Return"); + if (choice == 1) { + ret = ProgressWindow(gametxt, name); + if (ret != 0) { + WindowPrompt ("Install error!",0,"Back",0); + menu = MENU_DISCLIST; + break; + } + else { + __Menu_GetEntries(); //get the entries again + wiilight(1); + WindowPrompt ("Successfully installed:",name,"OK",0); + menu = MENU_DISCLIST; + wiilight(0); + break; + } + } else { + menu = MENU_DISCLIST; + break; + } + + } + else { + ret = ProgressWindow(gametxt, name); + if (ret != 0) { + WindowPrompt ("Install error!",0,"Back",0); + menu = MENU_DISCLIST; + break; + } else { + __Menu_GetEntries(); //get the entries again + wiilight(1); + WindowPrompt ("Successfully installed:",name,"OK",0); + menu = MENU_DISCLIST; + wiilight(0); + break; + } + } + + if (shutdown == 1) + Sys_Shutdown(); + + } + + + HaltGui(); + + #ifdef HW_RVL + for(i=0; i < 4; i++) + { + delete batteryTxt[i]; + delete batteryImg[i]; + delete batteryBarImg[i]; + delete batteryBtn[i]; + } + #endif + + mainWindow->Remove(&w); + ResumeGui(); + return menu; +} + +/**************************************************************************** + * MenuDiscList + ***************************************************************************/ + +static int MenuDiscList() +{ + int menu = MENU_NONE; + char imgPath[100]; + + f32 freespace, used, size = 0.0; + u32 nolist; + char text[MAX_CHARACTERS + 4]; //text2[20]; + int choice = 0, selectedold = 100; + s32 ret; + + //CLOCK + struct tm * timeinfo; + char theTime[80]; + int counter = 0; + + WBFS_DiskSpace(&used, &freespace); + + if (!gameCnt) { + nolist = 1; + } + + GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM, vol); + GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, SOUND_PCM, vol); + + snprintf(imgPath, sizeof(imgPath), "%sbutton_install.png", CFG.theme_path); + GuiImageData btnInstall(imgPath, button_install_png); + snprintf(imgPath, sizeof(imgPath), "%sbutton_install_over.png", CFG.theme_path); + GuiImageData btnInstallOver(imgPath, button_install_over_png); + + snprintf(imgPath, sizeof(imgPath), "%ssettings_button.png", CFG.theme_path); + GuiImageData btnSettings(imgPath, settings_button_png); + snprintf(imgPath, sizeof(imgPath), "%ssettings_button_over.png", CFG.theme_path); + GuiImageData btnSettingsOver(imgPath, settings_button_over_png); + + snprintf(imgPath, sizeof(imgPath), "%swiimote_poweroff.png", CFG.theme_path); + GuiImageData btnpwroff(imgPath, wiimote_poweroff_png); + snprintf(imgPath, sizeof(imgPath), "%swiimote_poweroff_over.png", CFG.theme_path); + GuiImageData btnpwroffOver(imgPath, wiimote_poweroff_over_png); + snprintf(imgPath, sizeof(imgPath), "%smenu_button.png", CFG.theme_path); + GuiImageData btnhome(imgPath, menu_button_png); + snprintf(imgPath, sizeof(imgPath), "%smenu_button_over.png", CFG.theme_path); + GuiImageData btnhomeOver(imgPath, menu_button_over_png); + snprintf(imgPath, sizeof(imgPath), "%sSDcard.png", CFG.theme_path); + GuiImageData btnsdcard(imgPath, sdcard_png); + + GuiImageData battery(battery_png); + GuiImageData batteryRed(battery_red_png); + GuiImageData batteryBar(battery_bar_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); + + char spaceinfo[30]; + sprintf(spaceinfo,"%.2fGB of %.2fGB free",freespace,(freespace+used)); + GuiText usedSpaceTxt(spaceinfo, 18, (GXColor){THEME.info_r, THEME.info_g, THEME.info_b, 255}); + usedSpaceTxt.SetAlignment(THEME.hddInfoAlign, ALIGN_TOP); + usedSpaceTxt.SetPosition(THEME.hddInfo_x, THEME.hddInfo_y); + + char GamesCnt[15]; + sprintf(GamesCnt,"Games: %i",gameCnt); + GuiText gamecntTxt(GamesCnt, 18, (GXColor){THEME.info_r, THEME.info_g, THEME.info_b, 255}); + gamecntTxt.SetAlignment(THEME.gameCntAlign, ALIGN_TOP); + gamecntTxt.SetPosition(THEME.gameCnt_x,THEME.gameCnt_y); + + GuiImageData tooltipLarge(tooltip_large_png); + GuiImage tooltipLargeImg(&tooltipLarge); + + GuiText ttinstallTxt("Install a game", 22, (GXColor){0, 0, 0, 255}); //TOOLTIP DATA FOR INSTALL BUTTON + GuiImageData ttinstall(tooltip_medium_png); + GuiImage ttinstallImg(&ttinstall); + + GuiImage installBtnImg(&btnInstall); + GuiImage installBtnImgOver(&btnInstallOver); + installBtnImg.SetWidescreen(CFG.widescreen); //added + installBtnImgOver.SetWidescreen(CFG.widescreen); //added + GuiButton installBtn(btnInstall.GetWidth(), btnInstall.GetHeight()); + installBtn.SetAlignment(ALIGN_LEFT, ALIGN_TOP); + installBtn.SetPosition(THEME.install_x, THEME.install_y); + installBtn.SetImage(&installBtnImg); + installBtn.SetImageOver(&installBtnImgOver); + installBtn.SetSoundOver(&btnSoundOver); + installBtn.SetSoundClick(&btnClick); + installBtn.SetTrigger(&trigA); + installBtn.SetEffectGrow(); + if (Settings.tooltips == TooltipsOn && THEME.showToolTip != 0) + installBtn.SetToolTip(&ttinstallImg,&ttinstallTxt,175,-30); + + GuiText ttsettingsTxt("Settings", 22, (GXColor){0, 0, 0, 255}); //TOOLTIP DATA FOR SETTINGS BUTTON + GuiImageData ttsettings(tooltip_png); + GuiImage ttsettingsImg(&ttsettings); + + GuiImage settingsBtnImg(&btnSettings); + settingsBtnImg.SetWidescreen(CFG.widescreen); //added + GuiImage settingsBtnImgOver(&btnSettingsOver); + settingsBtnImgOver.SetWidescreen(CFG.widescreen); //added + GuiButton settingsBtn(btnSettings.GetWidth(), btnSettings.GetHeight()); + settingsBtn.SetAlignment(ALIGN_LEFT, ALIGN_TOP); + settingsBtn.SetPosition(THEME.setting_x, THEME.setting_y); + settingsBtn.SetImage(&settingsBtnImg); + settingsBtn.SetImageOver(&settingsBtnImgOver); + settingsBtn.SetSoundOver(&btnSoundOver); + settingsBtn.SetSoundClick(&btnClick); + settingsBtn.SetTrigger(&trigA); + settingsBtn.SetEffectGrow(); + if (Settings.tooltips == TooltipsOn && THEME.showToolTip != 0) + settingsBtn.SetToolTip(&ttsettingsImg,&ttsettingsTxt,65,-30); + + GuiText tthomeTxt("Back to HBC or Wii Menu", 22, (GXColor){0, 0, 0, 255}); //TOOLTIP DATA FOR HOME BUTTON + GuiImageData tthome(tooltip_large_png); + GuiImage tthomeImg(&tthome); + + GuiImage homeBtnImg(&btnhome); + homeBtnImg.SetWidescreen(CFG.widescreen); //added + GuiImage homeBtnImgOver(&btnhomeOver); + homeBtnImgOver.SetWidescreen(CFG.widescreen); //added + GuiButton homeBtn(btnhome.GetWidth(), btnhome.GetHeight()); + homeBtn.SetAlignment(ALIGN_LEFT, ALIGN_TOP); + homeBtn.SetPosition(THEME.home_x, THEME.home_y); + homeBtn.SetImage(&homeBtnImg); + homeBtn.SetImageOver(&homeBtnImgOver); + homeBtn.SetSoundOver(&btnSoundOver); + homeBtn.SetSoundClick(&btnClick); + homeBtn.SetTrigger(&trigA); + homeBtn.SetTrigger(&trigHome); + homeBtn.SetEffectGrow(); + if (Settings.tooltips == TooltipsOn && THEME.showToolTip != 0) + homeBtn.SetToolTip(&tthomeImg,&tthomeTxt,15,-30); + + GuiText ttpoweroffTxt("Power off the Wii", 22, (GXColor){0, 0, 0, 255}); //TOOLTIP DATA FOR POWER BUTTON + GuiImageData ttpoweroff(tooltip_medium_png); + GuiImage ttpoweroffImg(&ttpoweroff); + + + GuiImage poweroffBtnImg(&btnpwroff); + GuiImage poweroffBtnImgOver(&btnpwroffOver); + poweroffBtnImg.SetWidescreen(CFG.widescreen); + poweroffBtnImgOver.SetWidescreen(CFG.widescreen); + GuiButton poweroffBtn(btnpwroff.GetWidth(), btnpwroff.GetHeight()); + poweroffBtn.SetAlignment(ALIGN_LEFT, ALIGN_TOP); + poweroffBtn.SetPosition(THEME.power_x, THEME.power_y); + poweroffBtn.SetImage(&poweroffBtnImg); + poweroffBtn.SetImageOver(&poweroffBtnImgOver); + poweroffBtn.SetSoundOver(&btnSoundOver); + poweroffBtn.SetSoundClick(&btnClick); + poweroffBtn.SetTrigger(&trigA); + poweroffBtn.SetEffectGrow(); + if (Settings.tooltips == TooltipsOn && THEME.showToolTip != 0) + poweroffBtn.SetToolTip(&ttpoweroffImg,&ttpoweroffTxt,-10,-30); + + GuiImage sdcardImg(&btnsdcard); + sdcardImg.SetWidescreen(CFG.widescreen); + GuiButton sdcardBtn(btnsdcard.GetWidth(), btnsdcard.GetHeight()); + sdcardBtn.SetAlignment(ALIGN_LEFT, ALIGN_TOP); + sdcardBtn.SetPosition(THEME.sdcard_x, THEME.sdcard_y); + sdcardBtn.SetImage(&sdcardImg); + sdcardBtn.SetSoundOver(&btnSoundOver); + sdcardBtn.SetSoundClick(&btnClick); + sdcardBtn.SetTrigger(&trigA); + sdcardBtn.SetEffectGrow(); + + //Downloading Covers + GuiText ttDownloadTxt("Click to Download Covers", 20, (GXColor){0, 0, 0, 255}); //TOOLTIP DATA FOR DOWNLOAD + GuiImageData ttDownload(tooltip_large_png); + GuiImage ttDownloadImg(&ttDownload); + + GuiButton DownloadBtn(160,224); + DownloadBtn.SetAlignment(ALIGN_LEFT, ALIGN_TOP); + DownloadBtn.SetPosition(THEME.cover_x,THEME.cover_y);//(20, 300); + DownloadBtn.SetSoundOver(&btnSoundOver); + if (CFG.godmode == 1){ + if (Settings.tooltips == TooltipsOn && THEME.showToolTip != 0){ + DownloadBtn.SetToolTip(&ttDownloadImg,&ttDownloadTxt,205,-30); + DownloadBtn.SetTrigger(&trigA); + } + } + + #ifdef HW_RVL + int i = 0, level; + char txt[3]; + GuiText * batteryTxt[4]; + GuiImage * batteryImg[4]; + GuiImage * batteryBarImg[4]; + GuiButton * batteryBtn[4]; + + for(i=0; i < 4; i++) + { + + if(i == 0) + sprintf(txt, "P%d", i+1); + else + sprintf(txt, "P%d", i+1); + + batteryTxt[i] = new GuiText(txt, 22, (GXColor){THEME.info_r, THEME.info_g, THEME.info_b, 255}); + batteryTxt[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + batteryImg[i] = new GuiImage(&battery); + batteryImg[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + batteryImg[i]->SetPosition(36, 0); + batteryImg[i]->SetTile(0); + batteryBarImg[i] = new GuiImage(&batteryBar); + batteryBarImg[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + batteryBarImg[i]->SetPosition(33, 0); + + batteryBtn[i] = new GuiButton(40, 20); + batteryBtn[i]->SetLabel(batteryTxt[i]); + batteryBtn[i]->SetImage(batteryBarImg[i]); + batteryBtn[i]->SetIcon(batteryImg[i]); + batteryBtn[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); + batteryBtn[i]->SetRumble(false); + batteryBtn[i]->SetAlpha(70); + } + + batteryBtn[0]->SetPosition(THEME.battery1_x, THEME.battery1_y); + batteryBtn[1]->SetPosition(THEME.battery2_x, THEME.battery2_y); + batteryBtn[2]->SetPosition(THEME.battery3_x, THEME.battery3_y); + batteryBtn[3]->SetPosition(THEME.battery4_x, THEME.battery4_y); + #endif + + GuiGameBrowser gameBrowser(THEME.selection_w, THEME.selection_h, gameList, gameCnt, CFG.theme_path, bg_options_png, startat, offset); + gameBrowser.SetPosition(THEME.selection_x, THEME.selection_y); + gameBrowser.SetAlignment(ALIGN_LEFT, ALIGN_CENTRE); + + GuiText clockTime(theTime, 30, (GXColor){138, 138, 138, 255}); + clockTime.SetAlignment(THEME.clockAlign, ALIGN_BOTTOM); + clockTime.SetPosition(THEME.clock_x, THEME.clock_y); + + HaltGui(); + GuiWindow w(screenwidth, screenheight); + + if((Settings.hddinfo == HDDInfo && THEME.showHDD == -1) || THEME.showHDD == 1) //force show hdd info + { + w.Append(&usedSpaceTxt); + } + if((Settings.hddinfo == HDDInfo && THEME.showGameCnt == -1) || THEME.showGameCnt == 1) //force show game cnt info + { + w.Append(&gamecntTxt); + } + + w.Append(&sdcardBtn); + w.Append(&poweroffBtn); + if (CFG.godmode) + w.Append(&installBtn); + w.Append(&homeBtn); + w.Append(&settingsBtn); + w.Append(&DownloadBtn); + + if(Settings.hddinfo == Clock) + { + w.Append(&clockTime); + } + + if (THEME.showBattery) + { + #ifdef HW_RVL + w.Append(batteryBtn[0]); + w.Append(batteryBtn[1]); + w.Append(batteryBtn[2]); + w.Append(batteryBtn[3]); + #endif + } + + mainWindow->Append(&gameBrowser); + mainWindow->Append(&w); + + ResumeGui(); + + while(menu == MENU_NONE) + { + + VIDEO_WaitVSync (); + + //CLOCK + if ((Settings.hddinfo == Clock)&&(counter % 2000 == 0)) { + time_t rawtime = time(0); + timeinfo = localtime (&rawtime); + strftime(theTime, sizeof(theTime), "%H:%M", timeinfo); + clockTime.SetText(theTime); + } + counter++; + + #ifdef HW_RVL + for(i=0; i < 4; i++) + { + if(WPAD_Probe(i, NULL) == WPAD_ERR_NONE) // controller connected + { + level = (userInput[i].wpad.battery_level / 100.0) * 4; + if(level > 4) level = 4; + batteryImg[i]->SetTile(level); + + if(level == 0) + batteryBarImg[i]->SetImage(&batteryRed); + else + batteryBarImg[i]->SetImage(&batteryBar); + + batteryBtn[i]->SetAlpha(255); + } + else // controller not connected + { + batteryImg[i]->SetTile(0); + batteryImg[i]->SetImage(&battery); + batteryBtn[i]->SetAlpha(70); + } + } + #endif + + if(shutdown == 1) + { + Sys_Shutdown(); + } + if(poweroffBtn.GetState() == STATE_CLICKED) + { + + choice = WindowPrompt ("Shutdown System","Are you sure?","Yes","No"); + if(choice == 1) + { + WPAD_Flush(0); + WPAD_Disconnect(0); + WPAD_Shutdown(); + Sys_Shutdown(); + //exit(0); + } else { + poweroffBtn.ResetState(); + gameBrowser.SetFocus(1); + } + + } + else if(homeBtn.GetState() == STATE_CLICKED) + { + + choice = WiiMenuWindowPrompt ("Exit USB ISO Loader ?","Wii Menu","Back to Loader","Back"); + if(choice == 1) + { + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); // Back to System Menu + } + else if (choice == 2) + { + if (*(unsigned int*) 0x80001800) exit(0); + // Channel Version + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + //exit(0); //Back to HBC + } else { + homeBtn.ResetState(); + gameBrowser.SetFocus(1); + } + + } + else if(installBtn.GetState() == STATE_CLICKED) + { + choice = WindowPrompt ("Install a game?",0,"Yes","No"); + if (choice == 1) + { + menu = MENU_INSTALL; + break; + } + else + { + installBtn.ResetState(); + gameBrowser.SetFocus(1); + } + } + + else if(sdcardBtn.GetState() == STATE_CLICKED) + { + __io_wiisd.shutdown(); + __io_wiisd.startup(); + break; + } + + else if(DownloadBtn.GetState() == STATE_CLICKED) + { + choice = DownloadWindowPrompt(); // ask for download choice + + if (choice != 0) + { + char * myIP; + int choice2 = choice; + + myIP = NetworkInitPromp(choice2); + networkisinitialized = 1; + if( !myIP ) + { + WindowPrompt("Network init error", 0, "ok",0); + netcheck = false; + } + else netcheck = true; + + if (netcheck) + { + + if (missingFiles != NULL && cntMissFiles > 0) + + { + char tempCnt[40]; + i = 0; + + sprintf(tempCnt,"Missing %i files",cntMissFiles); + choice = WindowPrompt("Download Boxart image?",tempCnt,"Yes","No"); + //WindowPrompt("Downloading","Please Wait Downloading Covers",0,0); + if (choice == 1) + { + ret = ProgressDownloadWindow(choice2); + if (ret == 0) { + WindowPrompt("Download finished",0,"OK",0); + } else { + sprintf(tempCnt,"%i files not found on the server!",ret); + WindowPrompt("Download finished",tempCnt,"OK",0); + } + } + } + else + { + WindowPrompt("No file missing!",0,"OK",0); + } + } + } + DownloadBtn.ResetState(); + gameBrowser.SetFocus(1); + }//end download + + else if(settingsBtn.GetState() == STATE_CLICKED) + { startat = gameBrowser.GetSelectedOption(); + offset = gameBrowser.GetOffset(); + menu = MENU_SETTINGS; + break; + + } + + + //Get selected game under cursor + int selectimg, promptnumber; + promptnumber = 0; + char ID[4]; + char IDfull[7]; + selectimg = gameBrowser.GetSelectedOption(); + gameSelected = gameBrowser.GetClickedOption(); + + + + if (gameSelected > 0) //if click occured + selectimg = gameSelected; + + if ((selectimg >= 0) && (selectimg < (s32) gameCnt)) + { + if (selectimg != selectedold) + { + selectedold = selectimg; + struct discHdr *header = &gameList[selectimg]; + snprintf (ID,sizeof(ID),"%c%c%c", header->id[0], header->id[1], header->id[2]); + snprintf (IDfull,sizeof(IDfull),"%c%c%c%c%c%c", header->id[0], header->id[1], header->id[2],header->id[3], header->id[4], header->id[5]); + w.Remove(&DownloadBtn); + w.Remove(coverImg); + + if (GameIDTxt) + { + w.Remove(GameIDTxt); + delete GameIDTxt; + GameIDTxt = NULL; + } + if(GameRegionTxt) + { + w.Remove(GameRegionTxt); + delete GameRegionTxt; + GameRegionTxt = NULL; + } + + switch(header->id[3]) + { + case 'E': + sprintf(gameregion,"NTSC U"); + break; + + case 'J': + sprintf(gameregion,"NTSC J"); + break; + + case 'P': + case 'D': + case 'F': + case 'X': + case 'S': + case 'Y': + sprintf(gameregion," PAL "); + break; + } + + //load game cover + if (cover) + { + delete cover; + cover = NULL; + } + + snprintf(imgPath, sizeof(imgPath), "%s%s.png", CFG.covers_path, ID); + cover = new GuiImageData(imgPath,0); //load short id + if (!cover->GetImage()) //if could not load the short id image + { + delete cover; + snprintf(imgPath, sizeof(imgPath), "%s%s.png", CFG.covers_path, IDfull); + cover = new GuiImageData(imgPath, 0); //load full id image + if (!cover->GetImage()) + { + delete cover; + snprintf(imgPath, sizeof(imgPath), "%snoimage.png", CFG.covers_path); + cover = new GuiImageData(imgPath, nocover_png); //load no image + } + } + + if (coverImg) + { + delete coverImg; + coverImg = NULL; + } + coverImg = new GuiImage(cover); + coverImg->SetWidescreen(CFG.widescreen); + + DownloadBtn.SetImage(coverImg); + w.Append(&DownloadBtn); + + if ((Settings.sinfo == GameID) || (Settings.sinfo == Both)){ + GameIDTxt = new GuiText(IDfull, 22, (GXColor){THEME.info_r, THEME.info_g, THEME.info_b, 255}); + GameIDTxt->SetAlignment(ALIGN_LEFT, ALIGN_TOP); + GameIDTxt->SetPosition(THEME.id_x,THEME.id_y); + GameIDTxt->SetEffect(EFFECT_FADE, 20); + w.Append(GameIDTxt); + } + + if ((Settings.sinfo == GameRegion) || (Settings.sinfo == Both)){ + GameRegionTxt = new GuiText(gameregion, 22, (GXColor){THEME.info_r, THEME.info_g, THEME.info_b, 255}); + GameRegionTxt->SetAlignment(ALIGN_LEFT, ALIGN_TOP); + GameRegionTxt->SetPosition(THEME.region_x, THEME.region_y); + GameRegionTxt->SetEffect(EFFECT_FADE, 20); + w.Append(GameRegionTxt); + } + } + } + + if ((gameSelected >= 0) && (gameSelected < (s32)gameCnt)) + { + struct discHdr *header = &gameList[gameSelected]; + WBFS_GameSize(header->id, &size); + if (strlen(get_title(header)) < (MAX_CHARACTERS + 3)) { + sprintf(text, "%s", get_title(header)); + } + else { + strncpy(text, get_title(header), MAX_CHARACTERS); + text[MAX_CHARACTERS] = '\0'; + strncat(text, "...", 3); + } + + + bool returnHere = true; + while (returnHere) + { + returnHere = false; + wiilight(1); + choice = GameWindowPrompt(); + header = &gameList[gameSelected]; //reset header + + if(choice == 1) + { + + wiilight(0); + + int ios2 = 0; + switch(iosChoice) + { + case i249: + ios2 = 0; + break; + + case i222: + ios2 = 1; + break; + + default: + ios2 = 0; + break; + } + + if(networkisinitialized == 1 || ios2 == 1 || Settings.cios == ios222) { + + WPAD_Flush(0); + WPAD_Disconnect(0); + WPAD_Shutdown(); + + WDVD_Close(); + + USBStorage_Deinit(); + + if(ios2 == 1) { + IOS_ReloadIOS(222); + } else { + IOS_ReloadIOS(249); + } + + ret = WBFS_Init(WBFS_DEVICE_USB); + + PAD_Init(); + Wpad_Init(); + WPAD_SetDataFormat(WPAD_CHAN_ALL,WPAD_FMT_BTNS_ACC_IR); + WPAD_SetVRes(WPAD_CHAN_ALL, screenwidth, screenheight); + + ret = Disc_Init(); + ret = WBFS_Open(); + + } + + /* Set USB mode */ + ret = Disc_SetUSB(header->id, ios2); + if (ret < 0) { + sprintf(text, "Error: %i", ret); + WindowPrompt( + "Failed to set USB:", + text, + "OK",0); + } + else { + /* Open disc */ + ret = Disc_Open(); + if (ret < 0) { + sprintf(text, "Error: %i", ret); + WindowPrompt( + "Failed to boot:", + text, + "OK",0); + } + else { + menu = MENU_EXIT; + } + } + } + else if (choice == 2) + { + wiilight(0); + if (GameSettings(header) == 1) //if deleted + { + menu = MENU_DISCLIST; + break; + } + returnHere = true; + } + + else if (choice == 3) //&& (CFG.godmode == 1)) + { + wiilight(0); + //enter new game title + char entered[40]; + snprintf(entered, sizeof(entered), "%s", get_title(header)); + entered[39] = '\0'; + OnScreenKeyboard(entered, 40); + WBFS_RenameGame(header->id, entered); + __Menu_GetEntries(); + menu = MENU_DISCLIST; + } + + + else if(choice == 0) + gameBrowser.SetFocus(1); + } + } + } + + HaltGui(); + + #ifdef HW_RVL + for(i=0; i < 4; i++) + { + delete batteryTxt[i]; + delete batteryImg[i]; + delete batteryBarImg[i]; + delete batteryBtn[i]; + } + #endif + + mainWindow->Remove(&gameBrowser); + mainWindow->Remove(&w); + ResumeGui(); + return menu; +} + +/**************************************************************************** + * MenuFormat + ***************************************************************************/ + +static int MenuFormat() +{ + int menu = MENU_NONE; + char imgPath[100]; + + OptionList options; + partitionEntry partitions[MAX_PARTITIONS]; + + u32 cnt, sector_size, selected = 2000; + int choice, ret; + char text[ISFS_MAXPATH]; + + s32 ret2; + ret2 = Partition_GetEntries(partitions, §or_size); + + //create the partitionlist + for (cnt = 0; cnt < MAX_PARTITIONS; cnt++) { + partitionEntry *entry = &partitions[cnt]; + + /* Calculate size in gigabytes */ + f32 size = entry->size * (sector_size / GB_SIZE); + + if (size) { + sprintf(options.name[cnt], "Partition %d:", cnt+1); + sprintf (options.value[cnt],"%.2fGB", size); + } else { + sprintf(options.name[cnt], "Partition %d:", cnt+1); + sprintf (options.value[cnt],"(Can't be formated)"); + } + } + + options.length = cnt; + + GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM, vol); + GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, SOUND_PCM, vol); + //btnClick.SetVolume(vol); + //btnSoundOver.SetVolume(vol); + snprintf(imgPath, sizeof(imgPath), "%swiimote_poweroff.png", CFG.theme_path); + GuiImageData btnpwroff(imgPath, wiimote_poweroff_png); + snprintf(imgPath, sizeof(imgPath), "%swiimote_poweroff_over.png", CFG.theme_path); + GuiImageData btnpwroffOver(imgPath, wiimote_poweroff_over_png); + snprintf(imgPath, sizeof(imgPath), "%smenu_button.png", CFG.theme_path); + GuiImageData btnhome(imgPath, menu_button_png); + snprintf(imgPath, sizeof(imgPath), "%smenu_button_over.png", CFG.theme_path); + GuiImageData btnhomeOver(imgPath, menu_button_over_png); + GuiImageData battery(battery_png); + GuiImageData batteryRed(battery_red_png); + GuiImageData batteryBar(battery_bar_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); + + GuiText titleTxt("Select the Partition", 18, (GXColor){0, 0, 0, 255}); + titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetPosition(10,40); + + GuiText titleTxt2("you want to format:", 18, (GXColor){0, 0, 0, 255}); + titleTxt2.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt2.SetPosition(20,60); + + GuiImage poweroffBtnImg(&btnpwroff); + GuiImage poweroffBtnImgOver(&btnpwroffOver); + poweroffBtnImg.SetWidescreen(CFG.widescreen); + poweroffBtnImgOver.SetWidescreen(CFG.widescreen); + GuiButton poweroffBtn(btnpwroff.GetWidth(), btnpwroff.GetHeight()); + poweroffBtn.SetAlignment(ALIGN_LEFT, ALIGN_TOP); + poweroffBtn.SetPosition(THEME.power_x, THEME.power_y); + poweroffBtn.SetImage(&poweroffBtnImg); + poweroffBtn.SetImageOver(&poweroffBtnImgOver); + poweroffBtn.SetSoundOver(&btnSoundOver); + poweroffBtn.SetSoundClick(&btnClick); + poweroffBtn.SetTrigger(&trigA); + poweroffBtn.SetEffectGrow(); + + GuiImage exitBtnImg(&btnhome); + GuiImage exitBtnImgOver(&btnhomeOver); + exitBtnImg.SetWidescreen(CFG.widescreen); + exitBtnImgOver.SetWidescreen(CFG.widescreen); + GuiButton exitBtn(btnhome.GetWidth(), btnhome.GetHeight()); + exitBtn.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + exitBtn.SetPosition(240, 367); + exitBtn.SetImage(&exitBtnImg); + exitBtn.SetImageOver(&exitBtnImgOver); + exitBtn.SetSoundOver(&btnSoundOver); + exitBtn.SetSoundClick(&btnClick); + exitBtn.SetTrigger(&trigA); + exitBtn.SetTrigger(&trigHome); + exitBtn.SetEffectGrow(); + + #ifdef HW_RVL + int i = 0, level; + char txt[3]; + GuiText * batteryTxt[4]; + GuiImage * batteryImg[4]; + GuiImage * batteryBarImg[4]; + GuiButton * batteryBtn[4]; + + for(i=0; i < 4; i++) + { + + if(i == 0) + sprintf(txt, "P%d", i+1); + else + sprintf(txt, "P%d", i+1); + + batteryTxt[i] = new GuiText(txt, 22, (GXColor){THEME.info_r, THEME.info_g, THEME.info_b, 255}); + batteryTxt[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + batteryImg[i] = new GuiImage(&battery); + batteryImg[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + batteryImg[i]->SetPosition(36, 0); + batteryImg[i]->SetTile(0); + batteryBarImg[i] = new GuiImage(&batteryBar); + batteryBarImg[i]->SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); + batteryBarImg[i]->SetPosition(33, 0); + + batteryBtn[i] = new GuiButton(40, 20); + batteryBtn[i]->SetLabel(batteryTxt[i]); + batteryBtn[i]->SetImage(batteryBarImg[i]); + batteryBtn[i]->SetIcon(batteryImg[i]); + batteryBtn[i]->SetAlignment(ALIGN_LEFT, ALIGN_TOP); + batteryBtn[i]->SetRumble(false); + batteryBtn[i]->SetAlpha(70); + } + + batteryBtn[0]->SetPosition(THEME.battery1_x, THEME.battery1_y); + batteryBtn[1]->SetPosition(THEME.battery2_x, THEME.battery2_y); + batteryBtn[2]->SetPosition(THEME.battery3_x, THEME.battery3_y); + batteryBtn[3]->SetPosition(THEME.battery4_x, THEME.battery4_y); + #endif + + GuiOptionBrowser optionBrowser(THEME.selection_w, THEME.selection_h, &options, CFG.theme_path, bg_options_png, 1, 0); + optionBrowser.SetPosition(THEME.selection_x, THEME.selection_y); + optionBrowser.SetAlignment(ALIGN_LEFT, ALIGN_CENTRE); + + HaltGui(); + GuiWindow w(screenwidth, screenheight); + w.Append(&titleTxt); + w.Append(&titleTxt2); + w.Append(&poweroffBtn); + w.Append(&exitBtn); + + if (THEME.showBattery) + { + #ifdef HW_RVL + w.Append(batteryBtn[0]); + w.Append(batteryBtn[1]); + w.Append(batteryBtn[2]); + w.Append(batteryBtn[3]); + #endif + } + + mainWindow->Append(&w); + mainWindow->Append(&optionBrowser); + + ResumeGui(); + + while(menu == MENU_NONE) + { + VIDEO_WaitVSync (); + + #ifdef HW_RVL + for(i=0; i < 4; i++) + { + if(WPAD_Probe(i, NULL) == WPAD_ERR_NONE) // controller connected + { + level = (userInput[i].wpad.battery_level / 100.0) * 4; + if(level > 4) level = 4; + batteryImg[i]->SetTile(level); + + if(level == 0) + batteryBarImg[i]->SetImage(&batteryRed); + else + batteryBarImg[i]->SetImage(&batteryBar); + + batteryBtn[i]->SetAlpha(255); + } + else // controller not connected + { + batteryImg[i]->SetTile(0); + batteryImg[i]->SetImage(&battery); + batteryBtn[i]->SetAlpha(70); + } + } + #endif + + selected = optionBrowser.GetClickedOption(); + + for (cnt = 0; cnt < MAX_PARTITIONS; cnt++) { + if (cnt == selected) { + partitionEntry *entry = &partitions[selected]; + if (entry->size) { + sprintf(text, "Partition %d : %.2fGB", selected+1, entry->size * (sector_size / GB_SIZE)); + choice = WindowPrompt( + "Do you want to format:", + text, + "Yes", + "No"); + if(choice == 1) { + ret = FormatingPartition("Formatting, please wait...", entry); + if (ret < 0) { + WindowPrompt("Error:","Failed formating","Return",0); + menu = MENU_SETTINGS; + + } else { + WBFS_Open(); + sprintf(text, "%s formated!", text); + WindowPrompt("Success:",text,"OK",0); + menu = MENU_DISCLIST; + } + } + } + } + } + if (shutdown == 1) + Sys_Shutdown(); + + if(poweroffBtn.GetState() == STATE_CLICKED) + { + choice = WindowPrompt ("Shutdown System","Are you sure?","Yes","No"); + if(choice == 1) + { + WPAD_Flush(0); + WPAD_Disconnect(0); + WPAD_Shutdown(); + Sys_Shutdown(); + } + + } else if(exitBtn.GetState() == STATE_CLICKED) + { + choice = WindowPrompt ("Return to Wii Menu","Are you sure?","Yes","No"); + if(choice == 1) + { + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + //exit(0); //zum debuggen schneller + } + } + } + + + HaltGui(); + + #ifdef HW_RVL + for(i=0; i < 4; i++) + { + delete batteryTxt[i]; + delete batteryImg[i]; + delete batteryBarImg[i]; + delete batteryBtn[i]; + } + #endif + + mainWindow->Remove(&optionBrowser); + mainWindow->Remove(&w); + ResumeGui(); + return menu; +} + +/**************************************************************************** + * MenuSettings + ***************************************************************************/ + +static int MenuSettings() +{ + //__Disc_SetLowMem(); + int menu = MENU_NONE; + int ret; + + GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM, vol); + GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, SOUND_PCM, vol); + + char imgPath[100]; + + GuiImageData btnOutline(settings_menu_button_png); + snprintf(imgPath, sizeof(imgPath), "%ssettings_background.png", CFG.theme_path); + GuiImageData settingsbg(imgPath, settings_background_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); + + GuiText titleTxt("Settings", 28, (GXColor){0, 0, 0, 255}); + titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetPosition(0,40); + + GuiImage settingsbackground(&settingsbg); + GuiButton settingsbackgroundbtn(settingsbackground.GetWidth(), settingsbackground.GetHeight()); + settingsbackgroundbtn.SetAlignment(ALIGN_LEFT, ALIGN_TOP); + settingsbackgroundbtn.SetPosition(0, 0); + settingsbackgroundbtn.SetImage(&settingsbackground); + + GuiText backBtnTxt("Go Back", 22, (GXColor){0, 0, 0, 255}); + backBtnTxt.SetMaxWidth(btnOutline.GetWidth()-30); + GuiImage backBtnImg(&btnOutline); + GuiButton backBtn(btnOutline.GetWidth(), btnOutline.GetHeight()); + backBtn.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + backBtn.SetPosition(-180, 400); + backBtn.SetLabel(&backBtnTxt); + backBtn.SetImage(&backBtnImg); + backBtn.SetSoundOver(&btnSoundOver); + backBtn.SetSoundClick(&btnClick); + backBtn.SetTrigger(&trigA); + backBtn.SetTrigger(&trigB); + backBtn.SetEffectGrow(); + + const char * text = "Unlock"; + if (CFG.godmode == 1) + text = "Lock"; + GuiText lockBtnTxt(text, 22, (GXColor){0, 0, 0, 255}); + lockBtnTxt.SetMaxWidth(btnOutline.GetWidth()-30); + GuiImage lockBtnImg(&btnOutline); + GuiButton lockBtn(btnOutline.GetWidth(), btnOutline.GetHeight()); + lockBtn.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + lockBtn.SetPosition(180, 400); + lockBtn.SetLabel(&lockBtnTxt); + lockBtn.SetImage(&lockBtnImg); + lockBtn.SetSoundOver(&btnSoundOver); + lockBtn.SetSoundClick(&btnClick); + lockBtn.SetTrigger(&trigA); + lockBtn.SetEffectGrow(); + + GuiImageData logo(credits_button_png); + GuiImage logoImg(&logo); + GuiImageData logoOver(credits_button_over_png); + GuiImage logoImgOver(&logoOver); + btnLogo = new GuiButton(logoImg.GetWidth(), logoImg.GetHeight()); + btnLogo->SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); + btnLogo->SetPosition(0, -35); + btnLogo->SetImage(&logoImg); + btnLogo->SetImageOver(&logoImgOver); + btnLogo->SetEffectGrow(); + btnLogo->SetSoundOver(&btnSoundOver); + btnLogo->SetSoundClick(&btnClick); + btnLogo->SetTrigger(&trigA); + btnLogo->SetUpdateCallback(WindowCredits); + + customOptionList options2(9); + GuiCustomOptionBrowser optionBrowser2(396, 280, &options2, CFG.theme_path, "bg_options_settings", bg_options_settings_png, 0); + optionBrowser2.SetPosition(0, 90); + optionBrowser2.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + optionBrowser2.SetCol2Position(150); + GuiWindow w(screenwidth, screenheight); + + int pageToDisplay = 1; + while ( pageToDisplay > 0) //set pageToDisplay to 0 to quit + { + menu = MENU_NONE; + if ( pageToDisplay == 1) + { + sprintf(options2.name[0], "Video Mode"); + sprintf(options2.name[1], "VIDTV Patch"); + sprintf(options2.name[2], "Language"); + sprintf(options2.name[3], "Ocarina"); + sprintf(options2.name[4], "Display"); + sprintf(options2.name[5], "Clock"); //CLOCK + sprintf(options2.name[6], "Rumble"); //RUMBLE + sprintf(options2.name[7], "Volume"); + sprintf(options2.name[8], " "); + + HaltGui(); + w.Append(&settingsbackgroundbtn); + w.Append(&titleTxt); + w.Append(&backBtn); + w.Append(&lockBtn); + w.Append(btnLogo); + + mainWindow->Append(&w); + mainWindow->Append(&optionBrowser2); + + ResumeGui(); + } + else if ( pageToDisplay == 2 ) + { + sprintf(options2.name[0], "Tooltips"); + sprintf(options2.name[1], "Password"); + sprintf(options2.name[2], "Parental Ctrl"); + sprintf(options2.name[3], "Standard cIOS"); + sprintf(options2.name[4], " "); + sprintf(options2.name[5], " "); + sprintf(options2.name[6], " "); + sprintf(options2.name[7], " "); + sprintf(options2.name[8], " "); + + } + while(menu == MENU_NONE) + { + VIDEO_WaitVSync (); + + if ( pageToDisplay == 1 ) + { + if(Settings.video > 5) + Settings.video = 0; + if(Settings.language > 10) + Settings.language = 0; + if(Settings.ocarina > 1) + Settings.ocarina = 0; + if(Settings.vpatch > 1) + Settings.vpatch = 0; + if(Settings.sinfo > 3) + Settings.sinfo = 0; + if(Settings.hddinfo > 1) + Settings.hddinfo = 0; //CLOCK + if(Settings.rumble > 1) + Settings.rumble = 0; //RUMBLE + if(Settings.volume > 10) + Settings.volume = 0; + + if (Settings.video == discdefault) sprintf (options2.value[0],"Disc Default"); + else if (Settings.video == systemdefault) sprintf (options2.value[0],"System Default"); + else if (Settings.video == patch) sprintf (options2.value[0],"Auto Patch"); + else if (Settings.video == pal50) sprintf (options2.value[0],"Force PAL50"); + else if (Settings.video == pal60) sprintf (options2.value[0],"Force PAL60"); + else if (Settings.video == ntsc) sprintf (options2.value[0],"Force NTSC"); + + if (Settings.vpatch == on) sprintf (options2.value[1],"On"); + else if (Settings.vpatch == off) sprintf (options2.value[1],"Off"); + + if (Settings.language == ConsoleLangDefault) sprintf (options2.value[2],"Console Default"); + else if (Settings.language == jap) sprintf (options2.value[2],"Japanese"); + else if (Settings.language == ger) sprintf (options2.value[2],"German"); + else if (Settings.language == eng) sprintf (options2.value[2],"English"); + else if (Settings.language == fren) sprintf (options2.value[2],"French"); + else if (Settings.language == esp) sprintf (options2.value[2],"Spanish"); + else if (Settings.language == it) sprintf (options2.value[2],"Italian"); + else if (Settings.language == dut) sprintf (options2.value[2],"Dutch"); + else if (Settings.language == schin) sprintf (options2.value[2],"S. Chinese"); + else if (Settings.language == tchin) sprintf (options2.value[2],"T. Chinese"); + else if (Settings.language == kor) sprintf (options2.value[2],"Korean"); + + if (Settings.ocarina == on) sprintf (options2.value[3],"On"); + else if (Settings.ocarina == off) sprintf (options2.value[3],"Off"); + + if (Settings.sinfo == GameID) sprintf (options2.value[4],"Game ID"); + else if (Settings.sinfo == GameRegion) sprintf (options2.value[4],"Game Region"); + else if (Settings.sinfo == Both) sprintf (options2.value[4],"Both"); + else if (Settings.sinfo == Neither) sprintf (options2.value[4],"Neither"); + + if (Settings.hddinfo == HDDInfo) sprintf (options2.value[5],"Off"); + else if (Settings.hddinfo == Clock) sprintf (options2.value[5],"On"); + + if (Settings.rumble == RumbleOn) sprintf (options2.value[6],"On"); + else if (Settings.rumble == RumbleOff) sprintf (options2.value[6],"Off"); + + if (Settings.volume == v10) sprintf (options2.value[7],"10"); + else if (Settings.volume == v20) sprintf (options2.value[7],"20"); + else if (Settings.volume == v30) sprintf (options2.value[7],"30"); + else if (Settings.volume == v40) sprintf (options2.value[7],"40"); + else if (Settings.volume == v50) sprintf (options2.value[7],"50"); + else if (Settings.volume == v60) sprintf (options2.value[7],"60"); + else if (Settings.volume == v70) sprintf (options2.value[7],"70"); + else if (Settings.volume == v80) sprintf (options2.value[7],"80"); + else if (Settings.volume == v90) sprintf (options2.value[7],"90"); + else if (Settings.volume == v100) sprintf (options2.value[7],"100"); + else if (Settings.volume == v0) sprintf (options2.value[7],"Off"); + + sprintf(options2.value[8], "Go to page 2"); + + ret = optionBrowser2.GetClickedOption(); + + switch (ret) + { + case 0: + Settings.video++; + break; + case 1: + Settings.vpatch++; + break; + case 2: + Settings.language++; + break; + case 3: + Settings.ocarina++; + break; + case 4: // Game Code and Region + Settings.sinfo++; + break; + case 5: //CLOCK + Settings.hddinfo++; + break; + case 6: //Rumble + Settings.rumble++; + break; + case 7: + Settings.volume++; + break; + case 8: + pageToDisplay = 2; + menu = MENU_EXIT; + break; + } + } + + if ( pageToDisplay == 2 ) + { + if ( Settings.tooltips > 1 ) + Settings.tooltips = 0; + if ( Settings.parentalcontrol > 3 ) + Settings.parentalcontrol = 0; + if ( Settings.cios > 1 ) + Settings.cios = 0; + + if (Settings.tooltips == TooltipsOn) sprintf (options2.value[0],"On"); + else if (Settings.tooltips == TooltipsOff) sprintf (options2.value[0],"Off"); + + if ( CFG.godmode != 1) sprintf(options2.value[1], "********"); + else if (!strcmp("", Settings.unlockCode)) sprintf(options2.value[1], ""); + else sprintf(options2.value[1], Settings.unlockCode); + + + if (Settings.parentalcontrol == ParentalControlOff) sprintf (options2.value[2],"Not yet"); + else if (Settings.parentalcontrol == ParentalControlLevel1) sprintf (options2.value[2],"not working"); + else if (Settings.parentalcontrol == ParentalControlLevel2) sprintf (options2.value[2],"broken"); + else if (Settings.parentalcontrol == ParentalControlLevel3) sprintf (options2.value[2],"coming soon"); + + if (Settings.cios == ios249) sprintf (options2.value[3],"249"); + else if (Settings.cios == ios222) sprintf (options2.value[3],"222"); + + sprintf(options2.value[4], " "); + sprintf(options2.value[5], " "); + sprintf(options2.value[6], " "); + sprintf(options2.value[7], " "); + sprintf(options2.value[8], "Go to page 1"); + + ret = optionBrowser2.GetClickedOption(); + + switch (ret) + { + case 0: + Settings.tooltips++; + break; + case 1: // Modify Password + if ( CFG.godmode == 1) + { + char entered[20] = ""; + strncpy(entered, Settings.unlockCode, sizeof(entered)); + int result = OnScreenKeyboard(entered, 20); + if ( result == 1 ) + { + strncpy(Settings.unlockCode, entered, sizeof(Settings.unlockCode)); + WindowPrompt("Password Changed","Password has been changed","OK",0); + cfg_save_global(); + } + } + else + { + WindowPrompt("Password change","Console should be unlocked to modify it.","OK",0); + } + break; + case 2: + if ( CFG.godmode == 1) + { + Settings.parentalcontrol++; + } + else + { + WindowPrompt("Parental Control","Console should be unlocked to modify it.","OK",0); + } + break; + case 3: + Settings.cios++; + break; + case 4: + break; + case 5: + break; + case 6: + break; + case 7: + + break; + case 8: + pageToDisplay = 1; + menu = MENU_EXIT; + break; + } + } + + if(shutdown == 1) + Sys_Shutdown(); + + if(backBtn.GetState() == STATE_CLICKED) + { + //Add the procedure call to save the global configuration + cfg_save_global(); + menu = MENU_DISCLIST; + pageToDisplay = 0; + break; + } + + if(lockBtn.GetState() == STATE_CLICKED) + { + if (!strcmp("", Settings.unlockCode)) + { + CFG.godmode = !CFG.godmode; + } + else if ( CFG.godmode == 0 ) + { + //password check to unlock Install,Delete and Format + char entered[20] = ""; + int result = OnScreenKeyboard(entered, 20); + if ( result == 1 ) + { + if (!strcmp(entered, Settings.unlockCode)) //if password correct + { + if (CFG.godmode == 0) + { + WindowPrompt("Correct Password","Install, Rename, and Delete are unlocked.","OK",0); + CFG.godmode = 1; + __Menu_GetEntries(); + menu = MENU_DISCLIST; + } + } + else + { + WindowPrompt("Wrong Password","USB Loader is protected.","OK",0); + } + } + } + else + { + int choice = WindowPrompt ("Lock Console","Are you sure?","Yes","No"); + if(choice == 1) + { + WindowPrompt("Console Locked","USB Loader is now protected.","OK",0); + CFG.godmode = 0; + __Menu_GetEntries(); + menu = MENU_DISCLIST; + } + } + if ( CFG.godmode == 1) + { + lockBtnTxt.SetText("Lock"); + } + else + { + lockBtnTxt.SetText("Unlock"); + } + lockBtn.ResetState(); + } + } + + } + HaltGui(); + delete btnLogo; + btnLogo = NULL; + mainWindow->Remove(&optionBrowser2); + mainWindow->Remove(&w); + ResumeGui(); + return menu; +} + + + +/******************************************************************************** +*Game specific settings +*********************************************************************************/ +int GameSettings(struct discHdr * header) +{ + bool exit = false; + int ret; + int retVal = 0; + + char gameName[31]; + + if (strlen(get_title(header)) < (27 + 3)) { + sprintf(gameName, "%s", get_title(header)); + } + else { + strncpy(gameName, get_title(header), 27); + gameName[27] = '\0'; + strncat(gameName, "...", 3); + } + + customOptionList options3(5); + sprintf(options3.name[0], "Video Mode"); + sprintf(options3.name[1], "VIDTV Patch"); + sprintf(options3.name[2], "Language"); + sprintf(options3.name[3], "Ocarina"); + sprintf(options3.name[4], "IOS"); + + GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM, vol); + + char imgPath[100]; + + GuiImageData btnOutline(settings_menu_button_png); + snprintf(imgPath, sizeof(imgPath), "%ssettings_background.png", CFG.theme_path); + GuiImageData settingsbg(imgPath, settings_background_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); + + GuiText titleTxt(gameName, 28, (GXColor){0, 0, 0, 255}); + titleTxt.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + titleTxt.SetPosition(0,40); + + GuiImage settingsbackground(&settingsbg); + GuiButton settingsbackgroundbtn(settingsbackground.GetWidth(), settingsbackground.GetHeight()); + settingsbackgroundbtn.SetAlignment(ALIGN_LEFT, ALIGN_TOP); + settingsbackgroundbtn.SetPosition(0, 0); + settingsbackgroundbtn.SetImage(&settingsbackground); + + GuiText saveBtnTxt("Save", 22, (GXColor){0, 0, 0, 255}); + saveBtnTxt.SetMaxWidth(btnOutline.GetWidth()-30); + GuiImage saveBtnImg(&btnOutline); + GuiButton saveBtn(btnOutline.GetWidth(), btnOutline.GetHeight()); + saveBtn.SetScale(0.9); + saveBtn.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + saveBtn.SetPosition(-180, 400); + saveBtn.SetLabel(&saveBtnTxt); + saveBtn.SetImage(&saveBtnImg); + saveBtn.SetSoundOver(&btnSoundOver); + saveBtn.SetTrigger(&trigA); + saveBtn.SetEffectGrow(); + + GuiText cancelBtnTxt("Back", 22, (GXColor){0, 0, 0, 255}); + cancelBtnTxt.SetMaxWidth(btnOutline.GetWidth()-30); + GuiImage cancelBtnImg(&btnOutline); + GuiButton cancelBtn(btnOutline.GetWidth(), btnOutline.GetHeight()); + cancelBtn.SetScale(0.9); + cancelBtn.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + cancelBtn.SetPosition(180, 400); + cancelBtn.SetLabel(&cancelBtnTxt); + cancelBtn.SetImage(&cancelBtnImg); + cancelBtn.SetSoundOver(&btnSoundOver); + cancelBtn.SetTrigger(&trigA); + cancelBtn.SetTrigger(&trigB); + cancelBtn.SetEffectGrow(); + + GuiText deleteBtnTxt("Uninstall", 22, (GXColor){0, 0, 0, 255}); + deleteBtnTxt.SetMaxWidth(btnOutline.GetWidth()-30); + GuiImage deleteBtnImg(&btnOutline); + GuiButton deleteBtn(btnOutline.GetWidth(), btnOutline.GetHeight()); + deleteBtn.SetScale(0.9); + deleteBtn.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + deleteBtn.SetPosition(0, 400); + deleteBtn.SetLabel(&deleteBtnTxt); + deleteBtn.SetImage(&deleteBtnImg); + deleteBtn.SetSoundOver(&btnSoundOver); + deleteBtn.SetTrigger(&trigA); + deleteBtn.SetEffectGrow(); + + GuiCustomOptionBrowser optionBrowser3(396, 280, &options3, CFG.theme_path, "bg_options_settings", bg_options_settings_png, 0); + optionBrowser3.SetPosition(0, 90); + optionBrowser3.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); + optionBrowser3.SetCol2Position(150); + + HaltGui(); + GuiWindow w(screenwidth, screenheight); + w.Append(&settingsbackgroundbtn); + w.Append(&titleTxt); + w.Append(&deleteBtn); + w.Append(&saveBtn); + w.Append(&cancelBtn); + + mainWindow->Append(&w); + mainWindow->Append(&optionBrowser3); + + ResumeGui(); + + struct Game_CFG* game_cfg = CFG_get_game_opt(header->id); + + if (game_cfg) + { + videoChoice = game_cfg->video; + languageChoice = game_cfg->language; + ocarinaChoice = game_cfg->ocarina; + viChoice = game_cfg->vipatch; + iosChoice = game_cfg->ios; + } + else + { + videoChoice = Settings.video; + languageChoice = Settings.language; + ocarinaChoice = Settings.ocarina; + viChoice = Settings.vpatch; + if(Settings.cios == ios222) { + iosChoice = i222; + } else { + iosChoice = i249; + } + } + + while(!exit) + { + + VIDEO_WaitVSync (); + + if (videoChoice == discdefault) sprintf (options3.value[0],"Disc Default"); + else if (videoChoice == systemdefault) sprintf (options3.value[0],"System Default"); + else if (videoChoice == patch) sprintf (options3.value[0],"Auto Patch"); + else if (videoChoice == pal50) sprintf (options3.value[0],"Force PAL50"); + else if (videoChoice == pal60) sprintf (options3.value[0],"Force PAL60"); + else if (videoChoice == ntsc) sprintf (options3.value[0],"Force NTSC"); + + if (viChoice == on) sprintf (options3.value[1],"ON"); + else if (viChoice == off) sprintf (options3.value[1],"OFF"); + + if (languageChoice == ConsoleLangDefault) sprintf (options3.value[2],"Console Default"); + else if (languageChoice == jap) sprintf (options3.value[2],"Japanese"); + else if (languageChoice == ger) sprintf (options3.value[2],"German"); + else if (languageChoice == eng) sprintf (options3.value[2],"English"); + else if (languageChoice == fren) sprintf (options3.value[2],"French"); + else if (languageChoice == esp) sprintf (options3.value[2],"Spanish"); + else if (languageChoice == it) sprintf (options3.value[2],"Italian"); + else if (languageChoice == dut) sprintf (options3.value[2],"Dutch"); + else if (languageChoice == schin) sprintf (options3.value[2],"S. Chinese"); + else if (languageChoice == tchin) sprintf (options3.value[2],"T. Chinese"); + else if (languageChoice == kor) sprintf (options3.value[2],"Korean"); + + if (ocarinaChoice == on) sprintf (options3.value[3],"ON"); + else if (ocarinaChoice == off) sprintf (options3.value[3],"OFF"); + + if (iosChoice == i249) sprintf (options3.value[4],"249"); + else if (iosChoice == i222) sprintf (options3.value[4],"222"); + + if(shutdown == 1) + Sys_Shutdown(); + + ret = optionBrowser3.GetClickedOption(); + + switch (ret) + { + case 0: + videoChoice = (videoChoice + 1) % CFG_VIDEO_COUNT; + break; + case 1: + viChoice = (viChoice + 1) % 2; + break; + case 2: + languageChoice = (languageChoice + 1) % CFG_LANG_COUNT; + break; + case 3: + ocarinaChoice = (ocarinaChoice + 1) % 2; + break; + case 4: + iosChoice = (iosChoice + 1) % 2; + break; + } + + if(saveBtn.GetState() == STATE_CLICKED) + { + if (CFG_save_game_opt(header->id)) + { + WindowPrompt("Successfully Saved", 0, "OK", 0); + } + else + { + WindowPrompt("Save Failed", 0, "OK", 0); + } + + saveBtn.ResetState(); + optionBrowser3.SetFocus(1); + } + + if (cancelBtn.GetState() == STATE_CLICKED) + { + exit = true; + break; + } + + if (deleteBtn.GetState() == STATE_CLICKED) + { + int choice = WindowPrompt( + "Do you really want to delete:", + gameName, + "Yes","Cancel"); + + if (choice == 1) + { + ret = WBFS_RemoveGame(header->id); + if (ret < 0) + { + WindowPrompt( + "Can't delete:", + gameName, + "OK",0); + } + else { + __Menu_GetEntries(); + WindowPrompt( + "Successfully deleted:", + gameName, + "OK",0); + retVal = 1; + } + break; + } + else if (choice == 0) + { + deleteBtn.ResetState(); + optionBrowser3.SetFocus(1); + } + + } + } + + HaltGui(); + mainWindow->Remove(&optionBrowser3); + mainWindow->Remove(&w); + ResumeGui(); + return retVal; +} + +/**************************************************************************** + * MenuCheck + ***************************************************************************/ +static int MenuCheck() +{ + int menu = MENU_NONE; + int i = 0; + int choice; + s32 ret2; + OptionList options; + options.length = i; + partitionEntry partitions[MAX_PARTITIONS]; + + VIDEO_WaitVSync (); + + + ret2 = WBFS_Init(WBFS_DEVICE_USB); + if (ret2 < 0) + { + fatUnmount("SD"); + __io_wiisd.shutdown(); + ret2 = DeviceWait("No USB Device:", "Waiting for USB Device 30 secs", 0, 0); + PAD_Init(); + Wpad_Init(); + WPAD_SetDataFormat(WPAD_CHAN_ALL,WPAD_FMT_BTNS_ACC_IR); + WPAD_SetVRes(WPAD_CHAN_ALL, screenwidth, screenheight); + __io_wiisd.startup(); + fatMountSimple("SD", &__io_wiisd); + } + if (ret2 < 0) { + WindowPrompt ("ERROR:","USB-Device not found!", "ok", 0); + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + } else { + PAD_Init(); + Wpad_Init(); + WPAD_SetDataFormat(WPAD_CHAN_ALL,WPAD_FMT_BTNS_ACC_IR); + WPAD_SetVRes(WPAD_CHAN_ALL, screenwidth, screenheight); + __io_wiisd.startup(); + fatMountSimple("SD", &__io_wiisd); + } + + ret2 = Disc_Init(); + if (ret2 < 0) { + WindowPrompt ("Error","Could not initialize DIP module!", "ok", 0); + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + } + + ret2 = WBFS_Open(); + + if (ret2 < 0) { + + choice = WindowPrompt("No WBFS partition found!", + "You need to format a partition.", + "Format", + "Return"); + if(choice == 0) + { + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + + } else { + /* Get partition entries */ + u32 sector_size; + ret2 = Partition_GetEntries(partitions, §or_size); + if (ret2 < 0) { + + WindowPrompt ("No partitions found!",0, "Restart", 0); + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + + } + menu = MENU_FORMAT; + + } + } + + if(shutdown == 1) + Sys_Shutdown(); + + //Spieleliste laden + __Menu_GetEntries(); + + + menu = MENU_DISCLIST; + + + + return menu; +} + +/**************************************************************************** + * MainMenu + ***************************************************************************/ +int MainMenu(int menu) +{ + + int currentMenu = menu; + char imgPath[100]; + + #ifdef HW_RVL + snprintf(imgPath, sizeof(imgPath), "%splayer1_point.png", CFG.theme_path); + pointer[0] = new GuiImageData(imgPath, player1_point_png); + snprintf(imgPath, sizeof(imgPath), "%splayer2_point.png", CFG.theme_path); + pointer[1] = new GuiImageData(imgPath, player2_point_png); + snprintf(imgPath, sizeof(imgPath), "%splayer3_point.png", CFG.theme_path); + pointer[2] = new GuiImageData(imgPath, player3_point_png); + snprintf(imgPath, sizeof(imgPath), "%splayer4_point.png", CFG.theme_path); + pointer[3] = new GuiImageData(imgPath, player4_point_png); + #endif + + mainWindow = new GuiWindow(screenwidth, screenheight); + + if (CFG.widescreen) + snprintf(imgPath, sizeof(imgPath), "%swbackground.png", CFG.theme_path); + else + snprintf(imgPath, sizeof(imgPath), "%sbackground.png", CFG.theme_path); + + background = new GuiImageData(imgPath, CFG.widescreen? wbackground_png : background_png); + + bgImg = new GuiImage(background); + mainWindow->Append(bgImg); + + GuiTrigger trigA; + trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); + + ResumeGui(); + + bgMusic = new GuiSound(bg_music_ogg, bg_music_ogg_size, SOUND_OGG, vol); + bgMusic->SetVolume(vol); + bgMusic->SetLoop(1); //loop music + bgMusic->Play(); // startup music + + while(currentMenu != MENU_EXIT) + { + bgMusic->SetVolume(vol); + switch (currentMenu) + { + case MENU_CHECK: + currentMenu = MenuCheck(); + break; + case MENU_FORMAT: + currentMenu = MenuFormat(); + break; + case MENU_INSTALL: + currentMenu = MenuInstall(); + break; + case MENU_SETTINGS: + currentMenu = MenuSettings(); + break; + case MENU_DISCLIST: + currentMenu = MenuDiscList(); + break; + default: // unrecognized menu + currentMenu = MenuCheck(); + break; + } + + switch (Settings.volume) + { + case v10: + vol = 10; + break; + case v20: + vol = 20; + break; + case v30: + vol = 30; + break; + case v40: + vol = 40; + break; + case v50: + vol = 50; + break; + case v60: + vol = 60; + break; + case v70: + vol = 70; + break; + case v80: + vol = 80; + break; + case v90: + vol = 90; + break; + case v100: + vol = 100; + break; + case v0: + vol = 0; + break; + default: + vol = 80; + break; + } + + } + + + bgMusic->Stop(); + delete bgMusic; + delete background; + delete bgImg; + delete mainWindow; + delete pointer[0]; + delete pointer[1]; + delete pointer[2]; + delete pointer[3]; + + delete cover; + delete coverImg; + + mainWindow = NULL; + fatUnmount("SD"); + __io_wiisd.shutdown(); + ExitApp(); + + struct discHdr *header = &gameList[gameSelected]; + struct Game_CFG* game_cfg = CFG_get_game_opt(header->id); + + if (game_cfg) + { + videoChoice = game_cfg->video; + languageChoice = game_cfg->language; + ocarinaChoice = game_cfg->ocarina; + viChoice = game_cfg->vipatch; + iosChoice = game_cfg->ios; + } + else + { + videoChoice = Settings.video; + languageChoice = Settings.language; + ocarinaChoice = Settings.ocarina; + viChoice = Settings.vpatch; + iosChoice = i249; + } + + switch(languageChoice) + { + case ConsoleLangDefault: + configbytes[0] = 0xCD; + break; + + case jap: + configbytes[0] = 0x00; + break; + + case eng: + configbytes[0] = 0x01; + break; + + case ger: + configbytes[0] = 0x02; + break; + + case fren: + configbytes[0] = 0x03; + break; + + case esp: + configbytes[0] = 0x04; + break; + + case it: + configbytes[0] = 0x05; + break; + + case dut: + configbytes[0] = 0x06; + break; + + case schin: + configbytes[0] = 0x07; + break; + + case tchin: + configbytes[0] = 0x08; + break; + + case kor: + configbytes[0] = 0x09; + break; + //wenn nicht genau klar ist welches + default: + configbytes[0] = 0xCD; + break; + } + + u32 videoselected = 0; + + switch(videoChoice) + { + case discdefault: + videoselected = 0; + break; + + case pal50: + videoselected = 1; + break; + + case pal60: + videoselected = 2; + break; + + case ntsc: + videoselected = 3; + + case systemdefault: + + videoselected = 4; + break; + case patch: + + videoselected = 5; + break; + default: + videoselected = 3; + break; + } + + u32 cheat = 0; + switch(ocarinaChoice) + { + case on: + cheat = 1; + break; + + case off: + cheat = 0; + break; + + default: + cheat = 1; + break; + } + + u8 vipatch = 0; + switch(viChoice) + { + case on: + vipatch = 1; + break; + + case off: + vipatch = 0; + break; + + default: + vipatch = 0; + break; + } + + int ret = 0; + ret = Disc_WiiBoot(videoselected, cheat, vipatch); + if (ret < 0) { + printf(" ERROR: BOOT ERROR! (ret = %d)\n", ret); + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + } + + return 0; +} diff --git a/source/menu.h b/source/menu.h new file mode 100644 index 00000000..7e8bd317 --- /dev/null +++ b/source/menu.h @@ -0,0 +1,30 @@ +/**************************************************************************** + * libwiigui Template + * Tantric 2009 + * + * menu.h + * Menu flow routines - handles all menu logic + ***************************************************************************/ + +#ifndef _MENU_H_ +#define _MENU_H_ + +#include + +void InitGUIThreads(); +int MainMenu (int menuitem); +void wiilight(int enable); +int GameSettings(struct discHdr *); +enum +{ + MENU_EXIT = -1, + MENU_NONE, + MENU_SETTINGS, + MENU_DISCLIST, + MENU_FORMAT, + MENU_INSTALL, + MENU_CHECK, + MENU_GAME_SETTINGS +}; + +#endif diff --git a/source/oggplayer.c b/source/oggplayer.c new file mode 100644 index 00000000..6e562e17 --- /dev/null +++ b/source/oggplayer.c @@ -0,0 +1,350 @@ +/* + Copyright (c) 2008 Francisco Muñoz 'Hermes' + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are + permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. + - The names of the contributors may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "oggplayer.h" +#include + +/* OGG control */ + +#define READ_SAMPLES 4096 // samples that it must read before to send +#define MAX_PCMOUT 4096 // minimum size to read ogg samples +typedef struct +{ + OggVorbis_File vf; + vorbis_info *vi; + int current_section; + + // OGG file operation + int fd; + int mode; + int eof; + int flag; + int volume; + int seek_time; + + /* OGG buffer control */ + short pcmout[2][READ_SAMPLES + MAX_PCMOUT * 2]; /* take 4k out of the data segment, not the stack */ + int pcmout_pos; + int pcm_indx; + +} private_data_ogg; + +static private_data_ogg private_ogg; + +// OGG thread control + +#define STACKSIZE 8192 + +static u8 oggplayer_stack[STACKSIZE]; +static lwpq_t oggplayer_queue; +static lwp_t h_oggplayer; +static int ogg_thread_running = 0; + +static void ogg_add_callback(int voice) +{ + if (ogg_thread_running <= 0) + { + SND_StopVoice(0); + return; + } + + if (private_ogg.flag & 128) + return; // Ogg is paused + + if (private_ogg.pcm_indx >= READ_SAMPLES) + { + if (SND_AddVoice(0, + (void *) private_ogg.pcmout[private_ogg.pcmout_pos], + private_ogg.pcm_indx << 1) == 0) + { + private_ogg.pcmout_pos ^= 1; + private_ogg.pcm_indx = 0; + private_ogg.flag = 0; + LWP_ThreadSignal(oggplayer_queue); + } + } + else + { + if (private_ogg.flag & 64) + { + private_ogg.flag &= ~64; + LWP_ThreadSignal(oggplayer_queue); + } + } +} + +static void * ogg_player_thread(private_data_ogg * priv) +{ + int first_time = 1; + + ogg_thread_running = 0; + //init + LWP_InitQueue(&oggplayer_queue); + + priv[0].vi = ov_info(&priv[0].vf, -1); + + SND_Pause(0); + + priv[0].pcm_indx = 0; + priv[0].pcmout_pos = 0; + priv[0].eof = 0; + priv[0].flag = 0; + priv[0].current_section = 0; + + ogg_thread_running = 1; + + while (!priv[0].eof) + { + long ret; + if (ogg_thread_running <= 0) + break; + + if (priv[0].flag) + LWP_ThreadSleep(oggplayer_queue); // wait only when i have samples to send + + if (ogg_thread_running <= 0) + break; + + if (priv[0].flag == 0) // wait to all samples are sended + { + if (SND_TestPointer(0, priv[0].pcmout[priv[0].pcmout_pos]) + && SND_StatusVoice(0) != SND_UNUSED) + { + priv[0].flag |= 64; + continue; + } + if (priv[0].pcm_indx < READ_SAMPLES) + { + priv[0].flag = 3; + + if (priv[0].seek_time >= 0) + { + ov_time_seek(&priv[0].vf, priv[0].seek_time); + priv[0].seek_time = -1; + } + + ret + = ov_read( + &priv[0].vf, + (void *) &priv[0].pcmout[priv[0].pcmout_pos][priv[0].pcm_indx], + MAX_PCMOUT,/*0,2,1,*/&priv[0].current_section); + priv[0].flag &= 192; + if (ret == 0) + { + /* EOF */ + if (priv[0].mode & 1) + ov_time_seek(&priv[0].vf, 0); // repeat + else + priv[0].eof = 1; // stops + // + } + else 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) + { + if (priv[0].mode & 1) + ov_time_seek(&priv[0].vf, 0); // repeat + else + priv[0].eof = 1; // stops + } + } + else + { + /* we don't bother dealing with sample rate changes, etc, but + you'll have to*/ + priv[0].pcm_indx += ret >> 1; //get 16 bits samples + } + } + else + priv[0].flag = 1; + } + + if (priv[0].flag == 1) + { + if (SND_StatusVoice(0) == SND_UNUSED || first_time) + { + first_time = 0; + if (priv[0].vi->channels == 2) + { + SND_SetVoice(0, VOICE_STEREO_16BIT, priv[0].vi->rate, 0, + (void *) priv[0].pcmout[priv[0].pcmout_pos], + priv[0].pcm_indx << 1, priv[0].volume, + priv[0].volume, ogg_add_callback); + priv[0].pcmout_pos ^= 1; + priv[0].pcm_indx = 0; + priv[0].flag = 0; + } + else + { + SND_SetVoice(0, VOICE_MONO_16BIT, priv[0].vi->rate, 0, + (void *) priv[0].pcmout[priv[0].pcmout_pos], + priv[0].pcm_indx << 1, priv[0].volume, + priv[0].volume, ogg_add_callback); + priv[0].pcmout_pos ^= 1; + priv[0].pcm_indx = 0; + priv[0].flag = 0; + } + } + else + { + // if(priv[0].pcm_indx==0) priv[0].flag=0; // all samples sended + } + + } + + } + ov_clear(&priv[0].vf); + priv[0].fd = -1; + priv[0].pcm_indx = 0; + ogg_thread_running = 0; + + return 0; +} + +void StopOgg() +{ + SND_StopVoice(0); + if (ogg_thread_running > 0) + { + ogg_thread_running = -2; + LWP_ThreadSignal(oggplayer_queue); + LWP_JoinThread(h_oggplayer, NULL); + + while (((volatile int) ogg_thread_running) != 0) + { + ;;; + } + } +} + +int PlayOgg(int fd, int time_pos, int mode) +{ + StopOgg(); + + ogg_thread_running = 0; + + private_ogg.fd = fd; + private_ogg.mode = mode; + private_ogg.eof = 0; + private_ogg.volume = 127; + private_ogg.flag = 0; + private_ogg.seek_time = -1; + + if (time_pos > 0) + private_ogg.seek_time = time_pos; + + if (fd < 0) + { + private_ogg.fd = -1; + return -1; + } + if (ov_open((void *) &private_ogg.fd, &private_ogg.vf, NULL, 0) < 0) + { + mem_close(private_ogg.fd); // mem_close() can too close files from devices + private_ogg.fd = -1; + ogg_thread_running = -1; + return -1; + } + + if (LWP_CreateThread(&h_oggplayer, (void *) ogg_player_thread, + &private_ogg, oggplayer_stack, STACKSIZE, 80) == -1) + { + ogg_thread_running = -1; + ov_clear(&private_ogg.vf); + private_ogg.fd = -1; + return -1; + } + LWP_ThreadSignal(oggplayer_queue); + while (((volatile int) ogg_thread_running) == 0) + { + ;;; + } + return 0; +} + +void PauseOgg(int pause) +{ + if (pause) + { + private_ogg.flag |= 128; + } + else + { + if (private_ogg.flag & 128) + { + private_ogg.flag |= 64; + private_ogg.flag &= ~128; + if (ogg_thread_running > 0) + { + LWP_ThreadSignal(oggplayer_queue); + // while(((volatile int )private_ogg.flag)!=1 && ((volatile int )ogg_thread_running)>0) {;;;} + } + } + + } +} + +int StatusOgg() +{ + if (ogg_thread_running <= 0) + return -1; // Error + + if (private_ogg.eof) + return 255; // EOF + + if (private_ogg.flag & 128) + return 2; // paused + return 1; // running +} + +void SetVolumeOgg(int volume) +{ + private_ogg.volume = volume; + + SND_ChangeVolumeVoice(0, volume, volume); +} + +s32 GetTimeOgg() +{ + int ret; + if (ogg_thread_running <= 0) + return 0; + if (private_ogg.fd < 0) + return 0; + ret = ((s32) ov_time_tell(&private_ogg.vf)); + if (ret < 0) + ret = 0; + + return ret; +} + +void SetTimeOgg(s32 time_pos) +{ + if (time_pos >= 0) + private_ogg.seek_time = time_pos; +} diff --git a/source/oggplayer.h b/source/oggplayer.h new file mode 100644 index 00000000..5c4610d1 --- /dev/null +++ b/source/oggplayer.h @@ -0,0 +1,170 @@ +/* + Copyright (c) 2008 Francisco Muñoz 'Hermes' + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are + permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, this list of + conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, this list + of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. + - The names of the contributors may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __OGGPLAYER_H__ +#define __OGGPLAYER_H__ + +#include +#include "tremor/ivorbiscodec.h" +#include "tremor/ivorbisfile.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define OGG_ONE_TIME 0 +#define OGG_INFINITE_TIME 1 + +#define OGG_STATUS_RUNNING 1 +#define OGG_STATUS_ERR -1 +#define OGG_STATUS_PAUSED 2 +#define OGG_STATUS_EOF 255 + +/*------------------------------------------------------------------------------------------------------------------------------------------------------*/ +/* Player OGG functions */ +/*------------------------------------------------------------------------------------------------------------------------------------------------------*/ + +/* int PlayOgg(int fd, int time_pos, int mode); + + Play an Ogg file. This file can be loaded from memory (mem_open(void *ogg, int size_ogg)) or from device with open("device:file.ogg",O_RDONLY,0); + + NOTE: The file is closed by the player when you call PlayOgg(), StopOgg() or if it fail. + + -- Params --- + + fd: file descriptor from open() or mem_open() + + time_pos: initial time position in the file (in milliseconds). For example, use 30000 to advance 30 seconds + + mode: Use OGG_ONE_TIME or OGG_INFINITE_TIME. When you use OGG_ONE_TIME the sound stops and StatusOgg() return OGG_STATUS_EOF + + return: 0- Ok, -1 Error + + */ + +int PlayOgg(int fd, int time_pos, int mode); + +/*------------------------------------------------------------------------------------------------------------------------------------------------------*/ + +/* void StopOgg(); + + Stop an Ogg file. + + NOTE: The file is closed and the player thread is released + + -- Params --- + + + */ + +void StopOgg(); + +/*------------------------------------------------------------------------------------------------------------------------------------------------------*/ + +/* void PauseOgg(int pause); + + Pause an Ogg file. + + -- Params --- + + pause: 0 -> continue, 1-> pause + + */ + +void PauseOgg(int pause); + +/*------------------------------------------------------------------------------------------------------------------------------------------------------*/ + +/* int StatusOgg(); + + Return the Ogg status + + -- Params --- + + + return: OGG_STATUS_RUNNING + OGG_STATUS_ERR -> not initialized? + OGG_STATUS_PAUSED + OGG_STATUS_EOF -> player stopped by End Of File + + */ + +int StatusOgg(); + +/*------------------------------------------------------------------------------------------------------------------------------------------------------*/ + +/* void SetVolumeOgg(int volume); + + Set the Ogg playing volume. + NOTE: it change the volume of voice 0 (used for the Ogg player) + + -- Params --- + + volume: 0 to 255 (max) + + */ + +void SetVolumeOgg(int volume); + +/*------------------------------------------------------------------------------------------------------------------------------------------------------*/ + +/* s32 GetTimeOgg(); + + Return the Ogg time from the starts of the file + + -- Params --- + + return: 0 -> Ok or error condition (you must ignore this value) + >0 -> time in milliseconds from the starts + + */ + +s32 GetTimeOgg(); + +/*------------------------------------------------------------------------------------------------------------------------------------------------------*/ + +/* void SetTimeOgg(s32 time_pos); + + Set the time position + + NOTE: The file is closed by the player when you call PlayOgg(), StopOgg() or if it fail. + + -- Params --- + + time_pos: time position in the file (in milliseconds). For example, use 30000 to advance 30 seconds + + */ + +void SetTimeOgg(s32 time_pos); + +/*------------------------------------------------------------------------------------------------------------------------------------------------------*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/partition.c b/source/partition.c new file mode 100644 index 00000000..32dc5711 --- /dev/null +++ b/source/partition.c @@ -0,0 +1,51 @@ +#include +#include +#include + +#include "partition.h" +#include "usbstorage.h" +#include "utils.h" + +/* 'partition table' structure */ +typedef struct { + /* Zero bytes */ + u8 padding[446]; + + /* Partition table entries */ + partitionEntry entries[MAX_PARTITIONS]; +} ATTRIBUTE_PACKED partitionTable; + + +s32 Partition_GetEntries(partitionEntry *outbuf, u32 *outval) +{ + static partitionTable table ATTRIBUTE_ALIGN(32); + + u32 cnt, sector_size; + s32 ret; + + /* Get sector size */ + ret = USBStorage_GetCapacity(§or_size); + if (ret < 0) + return ret; + + /* Read partition table */ + ret = USBStorage_ReadSectors(0, 1, &table); + if (ret < 0) + return ret; + + /* Swap endianess */ + for (cnt = 0; cnt < 4; cnt++) { + partitionEntry *entry = &table.entries[cnt]; + + entry->sector = swap32(entry->sector); + entry->size = swap32(entry->size); + } + + /* Set partition entries */ + memcpy(outbuf, table.entries, sizeof(table.entries)); + + /* Set sector size */ + *outval = sector_size; + + return 0; +} diff --git a/source/partition.h b/source/partition.h new file mode 100644 index 00000000..113fe6c0 --- /dev/null +++ b/source/partition.h @@ -0,0 +1,42 @@ +#ifndef _PARTITION_H_ +#define _PARTITION_H_ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* 'partition entry' structure */ +typedef struct { + /* Boot indicator */ + u8 boot; + + /* Starting CHS */ + u8 start[3]; + + /* Partition type */ + u8 type; + + /* Ending CHS */ + u8 end[3]; + + /* Partition sector */ + u32 sector; + + /* Partition size */ + u32 size; +} ATTRIBUTE_PACKED partitionEntry; + +/* Constants */ +#define MAX_PARTITIONS 4 + +/* Prototypes */ +s32 Partition_GetEntries(partitionEntry *, u32 *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/patchcode.c b/source/patchcode.c new file mode 100644 index 00000000..51a6a1bc --- /dev/null +++ b/source/patchcode.c @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2008 Nuke (wiinuke@gmail.com) + * + * this file is part of GeckoOS for USB Gecko + * http://www.usbgecko.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "geckomenu.h" +#include "apploader.h" +#include "patchcode.h" +#include "fwrite_patch.h" + +extern void patchhook(u32 address, u32 len); +extern void patchhook2(u32 address, u32 len); +extern void patchhook3(u32 address, u32 len); +extern void patchhook4(u32 address, u32 len); +extern void multidolpatchone(u32 address, u32 len); +extern void multidolpatchtwo(u32 address, u32 len); + +extern void regionfreejap(u32 address, u32 len); +extern void regionfreeusa(u32 address, u32 len); +extern void regionfreepal(u32 address, u32 len); + +extern void removehealthcheck(u32 address, u32 len); + +extern void copyflagcheck1(u32 address, u32 len); +extern void copyflagcheck2(u32 address, u32 len); +extern void copyflagcheck3(u32 address, u32 len); +extern void copyflagcheck4(u32 address, u32 len); +extern void copyflagcheck5(u32 address, u32 len); + +extern void patchupdatecheck(u32 address, u32 len); + +extern void movedvdhooks(u32 address, u32 len); + + +extern void patchhookdol(u32 address, u32 len); +extern void langvipatch(u32 address, u32 len, u8 langbyte); +extern void vipatch(u32 address, u32 len); +extern u32 regionfreeselect; + +static const u32 viwiihooks[4] = { + 0x7CE33B78,0x38870034,0x38A70038,0x38C7004C +}; + +static const u32 multidolpatch1[2] = { + 0x3C03FFB4,0x28004F43 +}; + +static const u32 healthcheckhook[2] = { + 0x41810010,0x881D007D +}; + +static const u32 updatecheckhook[3] = { + 0x80650050,0x80850054,0xA0A50058 +}; + +static const u32 multidolpatch2[2] = { + 0x3F608000, 0x807B0018 +}; + +static const u32 recoveryhooks[3] = { + 0xA00100AC,0x5400073E,0x2C00000F +}; + +static const u32 nocopyflag1[3] = { + 0x540007FF, 0x4182001C, 0x80630068 +}; + +static const u32 nocopyflag2[3] = { + 0x540007FF, 0x41820024, 0x387E12E2 +}; + +// this one is for the GH3 and VC saves +//static const u32 nocopyflag3[5] = { +// 0x2C030000, 0x40820010, 0x88010020, 0x28000002, 0x41820234 +//}; + +static const u32 nocopyflag3[5] = { + 0x2C030000, 0x41820200,0x48000058,0x38610100 +}; +// this removes the display warning for no copy VC and GH3 saves +static const u32 nocopyflag4[4] = { + 0x80010008, 0x2C000000, 0x4182000C, 0x3BE00001 +}; + +static const u32 nocopyflag5[3] = { + 0x801D0024,0x540007FF,0x41820024 +}; + +static const u32 movedvdpatch[3] = { + 0x2C040000, 0x41820120, 0x3C608109 +}; + + + +static const u32 regionfreehooks[5] = { + 0x7C600774, 0x2C000001, 0x41820030,0x40800010,0x2C000000 +}; + +static const u32 fwritepatch[8] = { + 0x9421FFD0,0x7C0802A6,0x90010034,0xBF210014,0x7C9B2378,0x7CDC3378,0x7C7A1B78,0x7CB92B78 // bushing fwrite +}; + +static const u32 kpadhooks[4] = { + 0x9A3F005E,0x38AE0080,0x389FFFFC,0x7E0903A6 +}; + +static const u32 kpadoldhooks[6] = { + 0x801D0060, 0x901E0060, 0x801D0064, 0x901E0064, 0x801D0068, 0x901E0068 +}; + +static const u32 joypadhooks[4] = { + 0x3AB50001, 0x3A73000C, 0x2C150004, 0x3B18000C +}; + +static const u32 langpatch[3] = { + 0x7C600775, 0x40820010, 0x38000000 +}; + +static const u32 vipatchcode[3] = { + 0x4182000C,0x4180001C,0x48000018 +}; + +static const u32 wpadlibogc[5] = { +// 0x38A00140, 0x7C095878, 0x7D600078, 0x901F0010,0x913F0014 +// 0x7FA00124, 0x8001001C, 0x83810008, 0x83A1000C,0x7C0803A6 + 0x90A402E0,0x806502E4,0x908502E4,0x2C030000,0x906402E4 +}; + +void dogamehooks(void *addr, u32 len) +{ + void *addr_start = addr; + void *addr_end = addr+len; + + while(addr_start < addr_end) + { + + switch(hooktype) + { + + case 0: + + break; + + case 1: + if(memcmp(addr_start, viwiihooks, sizeof(viwiihooks))==0){ + // printf("\n\n\n"); + // printf("found at address %x\n", addr_start); + // sleep(2); + patchhook((u32)addr_start, len); + patched = 1; + hooktype = 1; + } + break; + +/* + case 2: + if(memcmp(addr_start, kpadhooks, sizeof(kpadhooks))==0){ + patchhook((u32)addr_start, len); + patched = 1; + } + + if(memcmp(addr_start, kpadoldhooks, sizeof(kpadoldhooks))==0){ + patchhook((u32)addr_start, len); + patched = 1; + } + break; + + case 3: + if(memcmp(addr_start, joypadhooks, sizeof(joypadhooks))==0){ + patchhook((u32)addr_start, len); + patched = 1; + } + break; + + case 4: + if(memcmp(addr_start, recoveryhooks, sizeof(recoveryhooks))==0){ + patchhook3((u32)addr_start, len); + } + break; +*/ + case 2: + + if(memcmp(addr_start, viwiihooks, sizeof(viwiihooks))==0){ + patchhook2((u32)addr_start, len); + } + + break; + +/* + case 6: + // jap region free + if(memcmp(addr_start, regionfreehooks, sizeof(regionfreehooks))==0){ + regionfreejap((u32)addr_start, len); + } + + // usa region free + if(memcmp(addr_start, regionfreehooks, sizeof(regionfreehooks))==0){ + regionfreeusa((u32)addr_start, len); + } + + // pal region free + if(memcmp(addr_start, regionfreehooks, sizeof(regionfreehooks))==0){ + regionfreepal((u32)addr_start, len); + } + + // skip disc update + if(memcmp(addr_start, updatecheckhook, sizeof(updatecheckhook))==0){ + patchupdatecheck((u32)addr_start, len); + } + break; + + + case 7: + if(memcmp(addr_start, healthcheckhook, sizeof(healthcheckhook))==0){ + removehealthcheck((u32)addr_start, len); + } + break; + + // no copy flags + case 8: + // Remove the actual flag so can copy back + if(memcmp(addr_start, nocopyflag5, sizeof(nocopyflag5))==0){ + copyflagcheck5((u32)addr_start, len); + } + + + if(memcmp(addr_start, nocopyflag1, sizeof(nocopyflag1))==0){ + copyflagcheck1((u32)addr_start, len); + } + + if(memcmp(addr_start, nocopyflag2, sizeof(nocopyflag2))==0){ + copyflagcheck2((u32)addr_start, len); + } + + // no VC and GH3 save + if(memcmp(addr_start, nocopyflag3, sizeof(nocopyflag2))==0){ + copyflagcheck3((u32)addr_start, len); + } + // no VC and GH3 save display remove + if(memcmp(addr_start, nocopyflag4, sizeof(nocopyflag4))==0){ + copyflagcheck4((u32)addr_start, len); + } + + break; + + case 9: + if(memcmp(addr_start, movedvdpatch, sizeof(movedvdpatch))==0){ + movedvdhooks((u32)addr_start, len); + } + break; +*/ + // multidol + case 3: + + if(memcmp(addr_start, multidolpatch1, sizeof(multidolpatch1))==0){ + multidolpatchone((u32)addr_start, len); + } + if(memcmp(addr_start, multidolpatch2, sizeof(multidolpatch2))==0){ + multidolpatchtwo((u32)addr_start, len); + } + break; + } + addr_start += 4; + } +} + +// Not used yet, for patching DOL once loaded into memory and befor execution +void patchdol(void *addr, u32 len) +{ + + void *addr_start = addr; + void *addr_end = addr+len; + + while(addr_start < addr_end) + { + if(memcmp(addr_start, wpadlibogc, sizeof(wpadlibogc))==0) { + // printf("\n\n\n"); + // printf("found at address %x\n", addr_start); + // sleep(10); + // patchhookdol((u32)addr_start, len); + patched = 1; + break; + } + addr_start += 4; + } +} + +void langpatcher(void *addr, u32 len) +{ + + void *addr_start = addr; + void *addr_end = addr+len; + + while(addr_start < addr_end) + { + + if(memcmp(addr_start, langpatch, sizeof(langpatch))==0) { + if(configbytes[0] != 0xCD){ + langvipatch((u32)addr_start, len, configbytes[0]); + } + } + addr_start += 4; + } +} + +void patchdebug(void *addr, u32 len) +{ + + void *addr_start = addr; + void *addr_end = addr+len; + + while(addr_start < addr_end) + { + + if(memcmp(addr_start, fwritepatch, sizeof(fwritepatch))==0) { + + memcpy(addr_start,fwrite_patch_bin,fwrite_patch_bin_len); + // apply patch + } + addr_start += 4; + } +} + +void vidolpatcher(void *addr, u32 len) +{ + + void *addr_start = addr; + void *addr_end = addr+len; + + while(addr_start < addr_end) + { + if(memcmp(addr_start, vipatchcode, sizeof(vipatchcode))==0) { + vipatch((u32)addr_start, len); + } + addr_start += 4; + } +} + + diff --git a/source/patchcode.h b/source/patchcode.h new file mode 100644 index 00000000..381fcaa9 --- /dev/null +++ b/source/patchcode.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2008 Nuke (wiinuke@gmail.com) + * + * this file is part of GeckoOS for USB Gecko + * http://www.usbgecko.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __PATCHCODE_H__ +#define __PATCHCODE_H__ +#ifdef __cplusplus +extern "C" +{ +#endif +// Globals +u32 hooktype; +int patched; +u8 configbytes[2]; +u32 regionfree; + +// Function prototypes +void dogamehooks(void *addr, u32 len); +void langpatcher(void *addr, u32 len); +void vidolpatcher(void *addr, u32 len); +void patchdebug(void *addr, u32 len); + + + +#ifdef __cplusplus +} +#endif + +#endif // __PATCHCODE_H__ diff --git a/source/patchhook.S b/source/patchhook.S new file mode 100644 index 00000000..bddd9b7c --- /dev/null +++ b/source/patchhook.S @@ -0,0 +1,508 @@ + + # (c) Nuke www.usbgecko.com. Licensed under GPL V2 +.text +#include "ppc.h" + + +.globl patchhook # r3 address +patchhook: + mtctr r4 + lis r6, 0x4E80 + ori r6, r6, 0x0020 # blr +findblr: + lwz r5, 0(r3) + cmpw r6, r5 + beq writebranch + addi r3, r3, 4 # next word + bdnz findblr # loop length + b exit # stop unhooked game hanging + +writebranch: + lis r4, 0x8000 # 800018A0 hook location (source) + ori r4, r4, 0x18A8 + subf r4, r3, r4 # subtract r3 from r4 and place in r4 + lis r5, 0x3FF + ori r5, r5, 0xFFFF # 0x3FFFFFF + and r4, r4, r5 + lis r5, 0x4800 # 0x48000000 + or r4, r4, r5 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exit: + blr # return + +.globl patchhook2 # r3 address +patchhook2: + mtctr r4 + lis r6, 0x4E80 + ori r6, r6, 0x0020 # blr +findblr2: + lwz r5, 0(r3) + cmpw r6, r5 + beq writebranch2 + addi r3, r3, 4 # next word + bdnz findblr2 # loop length + b exit2 # stop unhooked game hanging + +writebranch2: + lis r4, 0x8000 # 81700000 our temp patcher + ori r4, r4, 0x18a8 + subf r4, r3, r4 # subtract r3 from r4 and place in r4 + lis r5, 0x3FF + ori r5, r5, 0xFFFF # 0x3FFFFFF + and r4, r4, r5 + lis r5, 0x4800 # 0x48000000 + or r4, r4, r5 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exit2: + blr # return + +.globl patchhook3 # r3 address +patchhook3: + mtctr r4 + lis r6, 0x4BFF + ori r6, r6, 0xE955 # blr +findbne: + lwz r5, 0(r3) + cmpw r6, r5 + beq writebl + addi r3, r3, 4 # next word + bdnz findbne # loop length + b exit3 # stop unhooked game hanging + +writebl: + lis r4, 0x4BFF # 81700000 our temp patcher + ori r4, r4, 0xEA91 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exit3: + blr # return + +.globl patchhook4 # r3 address +patchhook4: + mtctr r4 + lis r6, 0x4082 + ori r6, r6, 0x001C # blr +findregion: + lwz r5, 0(r3) + cmpw r6, r5 + beq writebr + addi r3, r3, 4 # next word + bdnz findregion # loop length + b exit4 # stop unhooked game hanging + +writebr: + lis r4, 0x4800 + ori r4, r4, 0x001C + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exit4: + blr # return + +.globl multidolpatchone # r3 address +multidolpatchone: + mtctr r4 + lis r6, 0x3800 + ori r6, r6, 0x0001 # (li r0,1) +findmulti: + lwz r5, 0(r3) + cmpw r6, r5 + beq writemulti + subi r3, r3, 4 # go back + bdnz findmulti # loop length + b exit5 # stop unhooked game hanging + +writemulti: + lis r4, 0x8170 # 81700000 + ori r4, r4, 0x0020 + subf r18, r3, r4 # subf r18,(source),(dest) + lis r6, 0x4800 + ori r6,r6,1 + rlwimi r6,r18,0,6,29 + stw r6,0(r3) + stw r6,0(r19) + stw r3,4(r19) + dcbf r0, r3 + sync + icbi r0, r3 + isync +exit5: + blr # return + +.globl multidolpatchtwo # r3 address +multidolpatchtwo: + mtctr r4 + lis r6, 0x3F60 + ori r6, r6, 0x8000 # (lis r27,-32768) +findmulti2: + lwz r5, 0(r3) + cmpw r6, r5 + beq writemulti2 + addi r3, r3, 4 # go forward + bdnz findmulti2 # loop length + b exit6 # stop unhooked game hanging + +writemulti2: + lis r4, 0x8170 # 81700020 + ori r4, r4, 0x0000 + subf r18, r3, r4 # subf r18,(source),(dest) + lis r6, 0x4800 + ori r6,r6,1 + rlwimi r6,r18,0,6,29 + stw r6,0(r3) + stw r6,0(r19) + stw r3,4(r19) + dcbf r0, r3 + sync + icbi r0, r3 + isync +exit6: + blr # return + +.globl langvipatch # r3 address, r4 len, r5 lang byte +langvipatch: + mtctr r4 + lis r6, 0x8861 + ori r6, r6, 0x0008 # lbz r3, 8(sp) +findlang: + lwz r7, 0(r3) + cmpw r6, r7 + beq patchlang + addi r3, r3, 4 # next word + bdnz findlang # loop length + b exitlang # stop unhooked game hanging + +patchlang: + + lis r4, 0x3860 # 0x38600001 li %r3, 1 # eng + add r4, r4, r5 +gofinal: + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitlang: + blr # return + +.globl vipatch # r3 address +vipatch: + mtctr r4 + lis r6, 0x5400 + ori r6, r6, 0xFFFE +findvi: + lwz r5, 0(r3) + cmpw r6, r5 + beq patchvi + addi r3, r3, 4 # next word + bdnz findvi # loop length + b exitvi # stop unhooked game hanging + +patchvi: + lis r4, 0x8000 + ori r4, r4, 0x0003 + lbz r5, 0(r4) + cmpwi r5, 0x45 # USA + beq patchusa + cmpwi r5, 0x4A + beq patchjap2 # JAP + b exitvi +patchjap2: + lis r4, 0x3800 + ori r4, r4, 0x0001 + b gofinal2 +patchusa: + lis r4, 0x3800 + ori r4, r4, 0x0000 +gofinal2: + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitvi: + blr # return + +.globl regionfreejap # r3 address +regionfreejap: + mtctr r4 + lis r6, 0x2C1B + ori r6, r6, 0x0000 # blr +findjap: + lwz r5, 0(r3) + cmpw r6, r5 + beq writenop + addi r3, r3, 4 # next word + bdnz findjap # loop length + b exitjap # stop unhooked game hanging + +writenop: + addi r3, r3, 4 # next word + lis r4, 0x6000 # nop + ori r4, r4, 0x0000 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitjap: + blr # return + +.globl regionfreeusa # r3 address +regionfreeusa: + mtctr r4 + lis r6, 0x281B + ori r6, r6, 0x0001 # blr +findusa: + lwz r5, 0(r3) + cmpw r6, r5 + beq writenop1 + addi r3, r3, 4 # next word + bdnz findusa # loop length + b exitusa # stop unhooked game hanging + +writenop1: + addi r3, r3, 4 # next word + lis r4, 0x6000 # nop + ori r4, r4, 0x0000 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitusa: + blr # return + +.globl regionfreepal # r3 address +regionfreepal: + mtctr r4 + lis r6, 0x281B + ori r6, r6, 0x0002 # blr +findpal: + lwz r5, 0(r3) + cmpw r6, r5 + beq writenop2 + addi r3, r3, 4 # next word + bdnz findpal # loop length + b exitpal # stop unhooked game hanging + +writenop2: + addi r3, r3, 4 # next word + lis r4, 0x6000 # nop + ori r4, r4, 0x0000 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 + + lis r6, 0x4082 + ori r6, r6, 0x001C # bne loc_81377A2C +findextra: #this is just the bne to b patch + lwz r5, 0(r3) + cmpw r6, r5 + beq writeb + addi r3, r3, 4 # next word + bdnz findextra # loop length + b exitpal # stop unhooked game hanging + +writeb: + addi r3, r3, 4 # next word + lis r4, 0x4800 + ori r4, r4, 0x001c # b loc_81377A2C + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitpal: + blr # return + +.globl removehealthcheck # r3 address +removehealthcheck: + mtctr r4 + lis r6, 0x4182 + ori r6, r6, 0x004C # blr +findhe: + lwz r5, 0(r3) + cmpw r6, r5 + beq writebhe + addi r3, r3, 4 # next word + bdnz findhe # loop length + b exithe # stop unhooked game hanging + +writebhe: + lis r4, 0x6000 + ori r4, r4, 0x0000 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exithe: + blr # return + + + +.globl patchupdatecheck # r3 address +patchupdatecheck: + mtctr r4 + lis r6, 0x4082 + ori r6, r6, 0x0020 # blr +finduc: + lwz r5, 0(r3) + cmpw r6, r5 + beq writenopuc + addi r3, r3, 4 # next word + bdnz finduc # loop length + b exituc # stop unhooked game hanging + +writenopuc: + lis r4, 0x6000 + ori r4, r4, 0x0000 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exituc: + blr # return + + + + +.globl copyflagcheck1 # r3 address +copyflagcheck1: + mtctr r4 + lis r6, 0x5400 + ori r6, r6, 0x07FF +findncf1: + lwz r5, 0(r3) + cmpw r6, r5 + beq writencf1 + subi r3, r3, 4 # next word + bdnz findncf1 # loop length + b exitncf1 # stop unhooked game hanging + +writencf1: + lis r4, 0x7C00 + ori r4, r4, 0x0000 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitncf1: + blr # return + +.globl copyflagcheck2 # r3 address +copyflagcheck2: + mtctr r4 + lis r6, 0x5400 + ori r6, r6, 0x07FF +findncf2: + lwz r5, 0(r3) + cmpw r6, r5 + beq writencf2 + subi r3, r3, 4 # next word + bdnz findncf2 # loop length + b exitncf2 # stop unhooked game hanging + +writencf2: + lis r4, 0x7C00 + ori r4, r4, 0x0000 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitncf2: + blr # return + + +.globl copyflagcheck3 # r3 address +copyflagcheck3: +findncf3: + addi r3, r3, 20 # go back one dword (4 bytes) + lwz r5, 0(r3) +writencf3: + lis r4, 0x3860 + ori r4, r4, 0x0001 # li r3,1 + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitncf3: + blr # return + + +.globl copyflagcheck4 # r3 address +copyflagcheck4: + mtctr r4 + lis r6, 0x3BE0 + ori r6, r6, 0x0001 # li r31,1 +findncf4: + lwz r5, 0(r3) + cmpw r6, r5 + beq writencf4 + addi r3, r3, 4 # next word + bdnz findncf4 # loop length + b exitncf4 # stop unhooked game hanging + +writencf4: + lis r4, 0x3BE0 + ori r4, r4, 0x0000 # change this to 3BE00000 (li r31,0) + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitncf4: + blr # return + +.globl copyflagcheck5 # r3 address +copyflagcheck5: + mtctr r4 + lis r6, 0x4182 + ori r6, r6, 0x0024 # beq loc_8134AA60 +findncf5: + lwz r5, 0(r3) + cmpw r6, r5 + beq writencf5 + addi r3, r3, 4 # next word + bdnz findncf5 # loop length + b exitncf5 # stop unhooked game hanging + +writencf5: + #addi r3, r3, 8 # skip 2 + + lis r4, 0x801D + ori r4, r4, 0x0024 # change to 801D0024 (lwz r0,36(r29)) + stw r4, 0(r3) + dcbf r0, r3 + icbi r0, r3 + + addi r3, r3, 4 # next word + + lis r4, 0x5400 + ori r4, r4, 0x003C # change to 5400003C (rlwinm r0,r0,0,0,30) + stw r4, 0(r3) + dcbf r0, r3 + icbi r0, r3 + + addi r3, r3, 4 # next word + + lis r4, 0x901D + ori r4, r4, 0x0024 # change to 901D0024 (stw r0,36(r29)) + stw r4, 0(r3) + dcbf r0, r3 + icbi r0, r3 + + addi r3, r3, 4 # next word + + lis r4, 0x4800 + ori r4, r4, 0x0018 # change to 48000018 (b 0x8134aa60) + stw r4, 0(r3) + dcbf r0, r3 + icbi r0, r3 +exitncf5: + blr # return + +.globl movedvdhooks # r3 address +movedvdhooks: + lis r6, 0x4182 + ori r6, r6, 0x0120 # beq loc_813A7938 +findmd1: + addi r3, r3, 4 # next word + lwz r5, 0(r3) +writemd1: + lis r4, 0x6000 + ori r4, r4, 0x0000 # nop + stw r4, 0(r3) # result in r3 + dcbf r0, r3 # data cache block flush + icbi r0, r3 +exitmd1: + blr # return + diff --git a/source/ppc.h b/source/ppc.h new file mode 100644 index 00000000..ae90eb10 --- /dev/null +++ b/source/ppc.h @@ -0,0 +1,83 @@ + + +/* Condition Register Bit Fields */ + +#define cr0 0 +#define cr1 1 +#define cr2 2 +#define cr3 3 +#define cr4 4 +#define cr5 5 +#define cr6 6 +#define cr7 7 + + +/* General Purpose Registers */ + +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + +/* Define Floating Point Registers */ + +#define f0 0 +#define f1 1 +#define f2 2 +#define f3 3 +#define f4 4 +#define f5 5 +#define f6 6 +#define f7 7 +#define f8 8 +#define f9 9 +#define f10 10 +#define f11 11 +#define f12 12 +#define f13 13 +#define f14 14 +#define f15 15 +#define f16 16 +#define f17 17 +#define f18 18 +#define f19 19 +#define f20 20 +#define f21 21 +#define f22 22 +#define f23 23 +#define f24 24 +#define f25 25 +#define f26 26 +#define f27 27 +#define f28 28 +#define f29 29 +#define f30 30 +#define f31 31 diff --git a/source/sdhc.c b/source/sdhc.c new file mode 100644 index 00000000..a7935ec7 --- /dev/null +++ b/source/sdhc.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include + +#include "sdhc.h" + +/* IOCTL comamnds */ +#define IOCTL_SDHC_INIT 0x01 +#define IOCTL_SDHC_READ 0x02 +#define IOCTL_SDHC_WRITE 0x03 +#define IOCTL_SDHC_ISINSERTED 0x04 + +#define SDHC_HEAPSIZE 0x8000 + +/* Variables */ +static char fs[] ATTRIBUTE_ALIGN(32) = "/dev/sdio/sdhc"; + +static s32 hid = -1, fd = -1; +static u32 sector_size = SDHC_SECTOR_SIZE; + + +bool SDHC_Init(void) +{ + s32 ret; + + /* Already open */ + if (fd > 0) + return true; + + /* Create heap */ + if (hid < 0) { + hid = iosCreateHeap(SDHC_HEAPSIZE); + if (hid < 0) + goto err; + } + + /* Open SDHC device */ + fd = IOS_Open(fs, 0); + if (fd < 0) + goto err; + + /* Initialize SDHC */ + ret = IOS_IoctlvFormat(hid, fd, IOCTL_SDHC_INIT, ":"); + if (ret) + goto err; + + return true; + +err: + /* Close SDHC device */ + if (fd > 0) { + IOS_Close(fd); + fd = -1; + } + + return false; +} + +bool SDHC_Close(void) +{ + /* Close SDHC device */ + if (fd > 0) { + IOS_Close(fd); + fd = -1; + } + + return true; +} + +bool SDHC_IsInserted(void) +{ + s32 ret; + + /* Check if SD card is inserted */ + ret = IOS_IoctlvFormat(hid, fd, IOCTL_SDHC_ISINSERTED, ":"); + + return (!ret) ? true : false; +} + +bool SDHC_ReadSectors(u32 sector, u32 count, void *buffer) +{ + void *buf = (void *)buffer; + u32 len = (sector_size * count); + + s32 ret; + + /* Device not opened */ + if (fd < 0) + return false; + + /* Buffer not aligned */ + if ((u32)buffer & 0x1F) { + /* Allocate memory */ + buf = iosAlloc(hid, len); + if (!buf) + return false; + } + + /* Read data */ + ret = IOS_IoctlvFormat(hid, fd, IOCTL_SDHC_READ, "ii:d", sector, count, buf, len); + + /* Copy data */ + if (buf != buffer) { + memcpy(buffer, buf, len); + iosFree(hid, buf); + } + + return (!ret) ? true : false; +} + +bool SDHC_WriteSectors(u32 sector, u32 count, void *buffer) +{ + void *buf = (void *)buffer; + u32 len = (sector_size * count); + + s32 ret; + + /* Device not opened */ + if (fd < 0) + return false; + + /* Buffer not aligned */ + if ((u32)buffer & 0x1F) { + /* Allocate memory */ + buf = iosAlloc(hid, len); + if (!buf) + return false; + + /* Copy data */ + memcpy(buf, buffer, len); + } + + /* Read data */ + ret = IOS_IoctlvFormat(hid, fd, IOCTL_SDHC_WRITE, "ii:d", sector, count, buf, len); + + /* Free memory */ + if (buf != buffer) + iosFree(hid, buf); + + return (!ret) ? true : false; +} + +bool SDHC_ClearStatus(void) +{ + return true; +} + + +const DISC_INTERFACE __io_sdhc = { + DEVICE_TYPE_WII_SD, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_SD, + (FN_MEDIUM_STARTUP)&SDHC_Init, + (FN_MEDIUM_ISINSERTED)&SDHC_IsInserted, + (FN_MEDIUM_READSECTORS)&SDHC_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&SDHC_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&SDHC_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&SDHC_Close +}; diff --git a/source/sdhc.h b/source/sdhc.h new file mode 100644 index 00000000..9f6f3c56 --- /dev/null +++ b/source/sdhc.h @@ -0,0 +1,22 @@ +#ifndef _SDHC_H_ +#define _SDHC_H_ + +/* Constants */ +#define SDHC_SECTOR_SIZE 0x200 + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Prototypes */ +bool SDHC_Init(void); +bool SDHC_Close(void); +bool SDHC_ReadSectors(u32, u32, void *); +bool SDHC_WriteSectors(u32, u32, void *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/sounds/bg_music.ogg b/source/sounds/bg_music.ogg new file mode 100644 index 00000000..229f42d7 Binary files /dev/null and b/source/sounds/bg_music.ogg differ diff --git a/source/sounds/button_click.pcm b/source/sounds/button_click.pcm new file mode 100644 index 00000000..de3d55e3 Binary files /dev/null and b/source/sounds/button_click.pcm differ diff --git a/source/sounds/button_click2.pcm b/source/sounds/button_click2.pcm new file mode 100644 index 00000000..113b4321 Binary files /dev/null and b/source/sounds/button_click2.pcm differ diff --git a/source/sounds/button_over.pcm b/source/sounds/button_over.pcm new file mode 100644 index 00000000..64f519cf Binary files /dev/null and b/source/sounds/button_over.pcm differ diff --git a/source/sounds/credits_music.ogg b/source/sounds/credits_music.ogg new file mode 100644 index 00000000..6e3efeda Binary files /dev/null and b/source/sounds/credits_music.ogg differ diff --git a/source/sys.c b/source/sys.c new file mode 100644 index 00000000..10d02d3c --- /dev/null +++ b/source/sys.c @@ -0,0 +1,91 @@ +#include +#include + +#include "sys.h" + +/* Constants */ +#define CERTS_LEN 0x280 + +/* Variables */ +static const char certs_fs[] ATTRIBUTE_ALIGN(32) = "/sys/cert.sys"; +u8 shutdown = 0; + +void __Sys_ResetCallback(void) +{ + /* Reboot console */ + Sys_Reboot(); +} + +void __Sys_PowerCallback(void) +{ + /* Poweroff console */ + shutdown = 1; +} + + +void Sys_Init(void) +{ + /* Initialize video subsytem */ + //VIDEO_Init(); + + /* Set RESET/POWER button callback */ + SYS_SetResetCallback(__Sys_ResetCallback); + SYS_SetPowerCallback(__Sys_PowerCallback); +} + +void Sys_Reboot(void) +{ + /* Restart console */ + STM_RebootSystem(); +} + +void Sys_Shutdown(void) +{ + /* Poweroff console */ + if(CONF_GetShutdownMode() == CONF_SHUTDOWN_IDLE) { + s32 ret; + + /* Set LED mode */ + ret = CONF_GetIdleLedMode(); + if(ret >= 0 && ret <= 2) + STM_SetLedMode(ret); + + /* Shutdown to idle */ + STM_ShutdownToIdle(); + } else { + /* Shutdown to standby */ + STM_ShutdownToStandby(); + } +} + +void Sys_LoadMenu(void) +{ + /* Return to the Wii system menu */ + SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); +} + +s32 Sys_GetCerts(signed_blob **certs, u32 *len) +{ + static signed_blob certificates[CERTS_LEN] ATTRIBUTE_ALIGN(32); + + s32 fd, ret; + + /* Open certificates file */ + fd = IOS_Open(certs_fs, 1); + if (fd < 0) + return fd; + + /* Read certificates */ + ret = IOS_Read(fd, certificates, sizeof(certificates)); + + /* Close file */ + IOS_Close(fd); + + /* Set values */ + if (ret > 0) { + *certs = certificates; + *len = sizeof(certificates); + } + + return ret; +} diff --git a/source/sys.h b/source/sys.h new file mode 100644 index 00000000..fbfdcac1 --- /dev/null +++ b/source/sys.h @@ -0,0 +1,20 @@ +#ifndef _SYS_H_ +#define _SYS_H_ +#include + +#ifdef __cplusplus +extern "C" +{ +#endif +/* Prototypes */ +void Sys_Init(void); +void Sys_Reboot(void); +void Sys_Shutdown(void); +void Sys_LoadMenu(void); +s32 Sys_GetCerts(signed_blob **, u32 *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/usbstorage.c b/source/usbstorage.c new file mode 100644 index 00000000..de46e74d --- /dev/null +++ b/source/usbstorage.c @@ -0,0 +1,186 @@ +/*------------------------------------------------------------- + +usbstorage_starlet.c -- USB mass storage support, inside starlet +Copyright (C) 2009 Kwiirk + +If this driver is linked before libogc, this will replace the original +usbstorage driver by svpe from libogc +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +-------------------------------------------------------------*/ + +#include +#include +#include +#include + +/* IOCTL commands */ +#define UMS_BASE (('U'<<24)|('M'<<16)|('S'<<8)) +#define USB_IOCTL_UMS_INIT (UMS_BASE+0x1) +#define USB_IOCTL_UMS_GET_CAPACITY (UMS_BASE+0x2) +#define USB_IOCTL_UMS_READ_SECTORS (UMS_BASE+0x3) +#define USB_IOCTL_UMS_WRITE_SECTORS (UMS_BASE+0x4) +#define USB_IOCTL_UMS_READ_STRESS (UMS_BASE+0x5) +#define USB_IOCTL_UMS_SET_VERBOSE (UMS_BASE+0x6) + +#define UMS_HEAPSIZE 0x8000 + +/* Variables */ +static char fs[] ATTRIBUTE_ALIGN(32) = "/dev/usb/ehc"; + +static s32 hid = -1, fd = -1; +static u32 sector_size; + + +inline s32 __USBStorage_isMEM2Buffer(const void *buffer) +{ + u32 high_addr = ((u32)buffer) >> 24; + + return (high_addr == 0x90) || (high_addr == 0xD0); +} + + +s32 USBStorage_GetCapacity(u32 *_sector_size) +{ + if (fd > 0) { + s32 ret; + + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_GET_CAPACITY, ":i", §or_size); + + if (ret && _sector_size) + *_sector_size = sector_size; + + return ret; + } + + return IPC_ENOENT; +} + +s32 USBStorage_Init(void) +{ + s32 ret; + + /* Already open */ + if (fd > 0) + return 0; + + /* Create heap */ + if (hid < 0) { + hid = iosCreateHeap(UMS_HEAPSIZE); + if (hid < 0) + return IPC_ENOMEM; + } + + /* Open USB device */ + fd = IOS_Open(fs, 0); + if (fd < 0) + return fd; + + /* Initialize USB storage */ + IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_INIT, ":"); + + /* Get device capacity */ + ret = USBStorage_GetCapacity(NULL); + if (!ret) + goto err; + + return 0; + +err: + /* Close USB device */ + if (fd > 0) { + IOS_Close(fd); + fd = -1; + } + + return -1; +} + +void USBStorage_Deinit(void) +{ + /* Close USB device */ + if (fd > 0) { + IOS_Close(fd); + fd = -1; + } +} + +s32 USBStorage_ReadSectors(u32 sector, u32 numSectors, void *buffer) +{ + void *buf = (void *)buffer; + u32 len = (sector_size * numSectors); + + s32 ret; + + /* Device not opened */ + if (fd < 0) + return fd; + + /* MEM1 buffer */ + if (!__USBStorage_isMEM2Buffer(buffer)) { + /* Allocate memory */ + buf = iosAlloc(hid, len); + if (!buf) + return IPC_ENOMEM; + } + + /* Read data */ + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_READ_SECTORS, "ii:d", sector, numSectors, buf, len); + + /* Copy data */ + if (buf != buffer) { + memcpy(buffer, buf, len); + iosFree(hid, buf); + } + + return ret; +} + +s32 USBStorage_WriteSectors(u32 sector, u32 numSectors, const void *buffer) +{ + void *buf = (void *)buffer; + u32 len = (sector_size * numSectors); + + s32 ret; + + /* Device not opened */ + if (fd < 0) + return fd; + + /* MEM1 buffer */ + if (!__USBStorage_isMEM2Buffer(buffer)) { + /* Allocate memory */ + buf = iosAlloc(hid, len); + if (!buf) + return IPC_ENOMEM; + + /* Copy data */ + memcpy(buf, buffer, len); + } + + /* Write data */ + ret = IOS_IoctlvFormat(hid, fd, USB_IOCTL_UMS_WRITE_SECTORS, "ii:d", sector, numSectors, buf, len); + + /* Free memory */ + if (buf != buffer) + iosFree(hid, buf); + + return ret; +} diff --git a/source/usbstorage.h b/source/usbstorage.h new file mode 100644 index 00000000..6f8fcbf2 --- /dev/null +++ b/source/usbstorage.h @@ -0,0 +1,21 @@ +#ifndef _USBSTORAGE_H_ +#define _USBSTORAGE_H_ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif +/* Prototypes */ +s32 USBStorage_GetCapacity(u32 *); +s32 USBStorage_Init(void); +void USBStorage_Deinit(void); +s32 USBStorage_ReadSectors(u32, u32, void *); +s32 USBStorage_WriteSectors(u32, u32, void *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/utils.c b/source/utils.c new file mode 100644 index 00000000..a29f37e3 --- /dev/null +++ b/source/utils.c @@ -0,0 +1,8 @@ +#include +#include + + +u32 swap32(u32 x) +{ + return (x >> 24) | ((x << 8) & 0x00FF0000UL) | ((x >> 8) & 0x0000FF00UL) | (x << 24); +} diff --git a/source/utils.h b/source/utils.h new file mode 100644 index 00000000..3a4862bf --- /dev/null +++ b/source/utils.h @@ -0,0 +1,15 @@ +#ifndef _UTILS_H_ +#define _UTILS_H_ + +/* Constants */ +#define KB_SIZE 1024.0 +#define MB_SIZE 1048576.0 +#define GB_SIZE 1073741824.0 + +/* Macros */ +#define round_up(x,n) (-(-(x) & -(n))) + +/* Prototypes */ +u32 swap32(u32); + +#endif diff --git a/source/video.cpp b/source/video.cpp new file mode 100644 index 00000000..6a2ff0d5 --- /dev/null +++ b/source/video.cpp @@ -0,0 +1,327 @@ +/**************************************************************************** + * libwiigui Template + * Tantric 2009 + * + * video.cpp + * Video routines + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include "input.h" +#include "libwiigui/gui.h" + +#include "cfg.h" + +#define DEFAULT_FIFO_SIZE 256 * 1024 +static unsigned int *xfb[2] = { NULL, NULL }; // Double buffered +static int whichfb = 0; // Switch +static GXRModeObj *vmode; // Menu video mode +static unsigned char gp_fifo[DEFAULT_FIFO_SIZE] ATTRIBUTE_ALIGN (32); +static Mtx GXmodelView2D; +int screenheight; +int screenwidth; + +/**************************************************************************** + * UpdatePadsCB + * + * called by postRetraceCallback in InitGCVideo - scans gcpad and wpad + ***************************************************************************/ +static void +UpdatePadsCB () +{ + #ifdef HW_RVL + WPAD_ScanPads(); + #endif + PAD_ScanPads(); + + for(int i=3; i >= 0; i--) + { + #ifdef HW_RVL + memcpy(&userInput[i].wpad, WPAD_Data(i), sizeof(WPADData)); + #endif + + userInput[i].chan = i; + userInput[i].pad.btns_d = PAD_ButtonsDown(i); + userInput[i].pad.btns_u = PAD_ButtonsUp(i); + userInput[i].pad.btns_h = PAD_ButtonsHeld(i); + userInput[i].pad.stickX = PAD_StickX(i); + userInput[i].pad.stickY = PAD_StickY(i); + userInput[i].pad.substickX = PAD_SubStickX(i); + userInput[i].pad.substickY = PAD_SubStickY(i); + userInput[i].pad.triggerL = PAD_TriggerL(i); + userInput[i].pad.triggerR = PAD_TriggerR(i); + } +} + +/**************************************************************************** + * StartGX + * + * Initialises GX and sets it up for use + ***************************************************************************/ +static void +StartGX () +{ + GXColor background = { 0, 0, 0, 0xff }; + + /*** Clear out FIFO area ***/ + memset (&gp_fifo, 0, DEFAULT_FIFO_SIZE); + + /*** Initialise GX ***/ + GX_Init (&gp_fifo, DEFAULT_FIFO_SIZE); + GX_SetCopyClear (background, 0x00ffffff); + + GX_SetDispCopyGamma (GX_GM_1_0); + GX_SetCullMode (GX_CULL_NONE); +} + +/**************************************************************************** + * ResetVideo_Menu + * + * Reset the video/rendering mode for the menu +****************************************************************************/ +void +ResetVideo_Menu() +{ + Mtx44 p; + f32 yscale; + u32 xfbHeight; + + VIDEO_Configure (vmode); + VIDEO_Flush(); + VIDEO_WaitVSync(); + if (vmode->viTVMode & VI_NON_INTERLACE) + VIDEO_WaitVSync(); + else + while (VIDEO_GetNextField()) + VIDEO_WaitVSync(); + + // clears the bg to color and clears the z buffer + GXColor background = {0, 0, 0, 255}; + GX_SetCopyClear (background, 0x00ffffff); + + yscale = GX_GetYScaleFactor(vmode->efbHeight,vmode->xfbHeight); + xfbHeight = GX_SetDispCopyYScale(yscale); + GX_SetScissor(0,0,vmode->fbWidth,vmode->efbHeight); + GX_SetDispCopySrc(0,0,vmode->fbWidth,vmode->efbHeight); + GX_SetDispCopyDst(vmode->fbWidth,xfbHeight); + GX_SetCopyFilter(vmode->aa,vmode->sample_pattern,GX_TRUE,vmode->vfilter); + GX_SetFieldMode(vmode->field_rendering,((vmode->viHeight==2*vmode->xfbHeight)?GX_ENABLE:GX_DISABLE)); + + if (vmode->aa) + GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR); + else + GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR); + + // setup the vertex descriptor + // tells the flipper to expect direct data + GX_ClearVtxDesc(); + GX_InvVtxCache (); + GX_InvalidateTexAll(); + + GX_SetVtxDesc(GX_VA_TEX0, GX_NONE); + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxDesc (GX_VA_CLR0, GX_DIRECT); + + GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0); + GX_SetVtxAttrFmt (GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + GX_SetZMode (GX_FALSE, GX_LEQUAL, GX_TRUE); + + GX_SetNumChans(1); + GX_SetNumTexGens(1); + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + + guMtxIdentity(GXmodelView2D); + guMtxTransApply (GXmodelView2D, GXmodelView2D, 0.0F, 0.0F, -50.0F); + GX_LoadPosMtxImm(GXmodelView2D,GX_PNMTX0); + + guOrtho(p,0,479,0,639,0,300); + GX_LoadProjectionMtx(p, GX_ORTHOGRAPHIC); + + GX_SetViewport(0,0,vmode->fbWidth,vmode->efbHeight,0,1); + GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR); + GX_SetAlphaUpdate(GX_TRUE); +} + +/**************************************************************************** + * InitVideo + * + * This function MUST be called at startup. + * - also sets up menu video mode + ***************************************************************************/ + +void +InitVideo () +{ + VIDEO_Init(); + vmode = VIDEO_GetPreferredMode(NULL); // get default video mode + + // widescreen fix + if(CFG.widescreen) + { + vmode->viWidth = VI_MAX_WIDTH_PAL-12; + vmode->viXOrigin = ((VI_MAX_WIDTH_PAL - vmode->viWidth) / 2) + 2; + } + + VIDEO_Configure (vmode); + + screenheight = 480; + screenwidth = vmode->fbWidth; + + // Allocate the video buffers + xfb[0] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode)); + xfb[1] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode)); + + // A console is always useful while debugging + console_init (xfb[0], 20, 64, vmode->fbWidth, vmode->xfbHeight, vmode->fbWidth * 2); + + // Clear framebuffers etc. + VIDEO_ClearFrameBuffer (vmode, xfb[0], COLOR_BLACK); + VIDEO_ClearFrameBuffer (vmode, xfb[1], COLOR_BLACK); + VIDEO_SetNextFramebuffer (xfb[0]); + + // video callback + VIDEO_SetPostRetraceCallback ((VIRetraceCallback)UpdatePadsCB); + + VIDEO_SetBlack (FALSE); + VIDEO_Flush (); + VIDEO_WaitVSync (); + if (vmode->viTVMode & VI_NON_INTERLACE) + VIDEO_WaitVSync (); + + StartGX(); + ResetVideo_Menu(); + // Finally, the video is up and ready for use :) +} + +/**************************************************************************** + * StopGX + * + * Stops GX (when exiting) + ***************************************************************************/ +void StopGX() +{ + GX_AbortFrame(); + GX_Flush(); + + VIDEO_SetBlack(TRUE); + VIDEO_Flush(); +} + +/**************************************************************************** + * Menu_Render + * + * Renders everything current sent to GX, and flushes video + ***************************************************************************/ +void Menu_Render() +{ + GX_DrawDone (); + + whichfb ^= 1; // flip framebuffer + GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE); + GX_SetColorUpdate(GX_TRUE); + GX_CopyDisp(xfb[whichfb],GX_TRUE); + VIDEO_SetNextFramebuffer(xfb[whichfb]); + VIDEO_Flush(); + VIDEO_WaitVSync(); +} + +/**************************************************************************** + * Menu_DrawImg + * + * Draws the specified image on screen using GX + ***************************************************************************/ +void Menu_DrawImg(f32 xpos, f32 ypos, u16 width, u16 height, u8 data[], + f32 degrees, f32 scaleX, f32 scaleY, u8 alpha) +{ + if(data == NULL) + return; + + GXTexObj texObj; + + GX_InitTexObj(&texObj, data, width,height, GX_TF_RGBA8,GX_CLAMP, GX_CLAMP,GX_FALSE); + GX_LoadTexObj(&texObj, GX_TEXMAP0); + GX_InvalidateTexAll(); + + GX_SetTevOp (GX_TEVSTAGE0, GX_MODULATE); + GX_SetVtxDesc (GX_VA_TEX0, GX_DIRECT); + + Mtx m,m1,m2, mv; + width *=.5; + height*=.5; + guMtxIdentity (m1); + guMtxScaleApply(m1,m1,scaleX,scaleY,1.0); + Vector axis = (Vector) {0 , 0, 1 }; + guMtxRotAxisDeg (m2, &axis, degrees); +// guMtxConcat(m2,m1,m); + guMtxConcat(m1,m2,m); + + guMtxTransApply(m,m, xpos+width,ypos+height,0); + guMtxConcat (GXmodelView2D, m, mv); + GX_LoadPosMtxImm (mv, GX_PNMTX0); + + GX_Begin(GX_QUADS, GX_VTXFMT0,4); + GX_Position3f32(-width, -height, 0); + GX_Color4u8(0xFF,0xFF,0xFF,alpha); + GX_TexCoord2f32(0, 0); + + GX_Position3f32(width, -height, 0); + GX_Color4u8(0xFF,0xFF,0xFF,alpha); + GX_TexCoord2f32(1, 0); + + GX_Position3f32(width, height, 0); + GX_Color4u8(0xFF,0xFF,0xFF,alpha); + GX_TexCoord2f32(1, 1); + + GX_Position3f32(-width, height, 0); + GX_Color4u8(0xFF,0xFF,0xFF,alpha); + GX_TexCoord2f32(0, 1); + GX_End(); + GX_LoadPosMtxImm (GXmodelView2D, GX_PNMTX0); + + GX_SetTevOp (GX_TEVSTAGE0, GX_PASSCLR); + GX_SetVtxDesc (GX_VA_TEX0, GX_NONE); +} + +/**************************************************************************** + * Menu_DrawRectangle + * + * Draws a rectangle at the specified coordinates using GX + ***************************************************************************/ +void Menu_DrawRectangle(f32 x, f32 y, f32 width, f32 height, GXColor color, u8 filled) +{ + u8 fmt; + long n; + int i; + f32 x2 = x+width; + f32 y2 = y+height; + Vector v[] = {{x,y,0.0f}, {x2,y,0.0f}, {x2,y2,0.0f}, {x,y2,0.0f}, {x,y,0.0f}}; + + if(!filled) + { + fmt = GX_LINESTRIP; + n = 5; + } + else + { + fmt = GX_TRIANGLEFAN; + n = 4; + } + + GX_Begin(fmt, GX_VTXFMT0, n); + for(i=0; i + +void InitVideo (); +void StopGX(); +void ResetVideo_Menu(); +void Menu_Render(); +void Menu_DrawImg(f32 xpos, f32 ypos, u16 width, u16 height, u8 data[], f32 degrees, f32 scaleX, f32 scaleY, u8 alphaF ); +void Menu_DrawRectangle(f32 x, f32 y, f32 width, f32 height, GXColor color, u8 filled); + +extern int screenheight; +extern int screenwidth; + +#endif diff --git a/source/video2.c b/source/video2.c new file mode 100644 index 00000000..95b54103 --- /dev/null +++ b/source/video2.c @@ -0,0 +1,136 @@ +#include +#include + +#include "sys.h" +#include "video2.h" + +/* Video variables */ +static void *framebuffer = NULL; +static GXRModeObj *vmode = NULL; + + +void Con_Init(u32 x, u32 y, u32 w, u32 h) +{ + /* Create console in the framebuffer */ + CON_InitEx(vmode, x, y, w, h); +} + +void Con_Clear(void) +{ + /* Clear console */ + printf("\x1b[2J"); + fflush(stdout); +} + +void Con_ClearLine(void) +{ + s32 cols, rows; + u32 cnt; + + printf("\r"); + fflush(stdout); + + /* Get console metrics */ + CON_GetMetrics(&cols, &rows); + + /* Erase line */ + for (cnt = 1; cnt < cols; cnt++) { + printf(" "); + fflush(stdout); + } + + printf("\r"); + fflush(stdout); +} + +void Con_FgColor(u32 color, u8 bold) +{ + /* Set foreground color */ + printf("\x1b[%u;%um", color + 30, bold); + fflush(stdout); +} + +void Con_BgColor(u32 color, u8 bold) +{ + /* Set background color */ + printf("\x1b[%u;%um", color + 40, bold); + fflush(stdout); +} + +void Con_FillRow(u32 row, u32 color, u8 bold) +{ + s32 cols, rows; + u32 cnt; + + /* Set color */ + printf("\x1b[%u;%um", color + 40, bold); + fflush(stdout); + + /* Get console metrics */ + CON_GetMetrics(&cols, &rows); + + /* Save current row and col */ + printf("\x1b[s"); + fflush(stdout); + + /* Move to specified row */ + printf("\x1b[%u;0H", row); + fflush(stdout); + + /* Fill row */ + for (cnt = 0; cnt < cols; cnt++) { + printf(" "); + fflush(stdout); + } + + /* Load saved row and col */ + printf("\x1b[u"); + fflush(stdout); + + /* Set default color */ + Con_BgColor(0, 0); + Con_FgColor(7, 1); +} + +void Video_Configure(GXRModeObj *rmode) +{ + /* Configure the video subsystem */ + VIDEO_Configure(rmode); + + /* Setup video */ + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + + if (rmode->viTVMode & VI_NON_INTERLACE) + VIDEO_WaitVSync(); +} + +void Video_SetMode(void) +{ + /* Select preferred video mode */ + vmode = VIDEO_GetPreferredMode(NULL); + + /* Allocate memory for the framebuffer */ + framebuffer = MEM_K0_TO_K1(SYS_AllocateFramebuffer(vmode)); + + /* Configure the video subsystem */ + VIDEO_Configure(vmode); + + /* Setup video */ + VIDEO_SetNextFramebuffer(framebuffer); + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + + if (vmode->viTVMode & VI_NON_INTERLACE) + VIDEO_WaitVSync(); + + /* Clear the screen */ + Video_Clear(COLOR_BLACK); +} + +void Video_Clear(s32 color) +{ + VIDEO_ClearFrameBuffer(vmode, framebuffer, color); +} diff --git a/source/video2.h b/source/video2.h new file mode 100644 index 00000000..d7c916d6 --- /dev/null +++ b/source/video2.h @@ -0,0 +1,27 @@ +#ifndef _VIDEO2_H_ +#define _VIDEO2_H_ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Prototypes */ +void Con_Init(u32, u32, u32, u32); +void Con_Clear(void); +void Con_ClearLine(void); +void Con_FgColor(u32, u8); +void Con_BgColor(u32, u8); +void Con_FillRow(u32, u32, u8); + +void Video_Configure(GXRModeObj *); +void Video_SetMode(void); +void Video_Clear(s32); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/wbfs.c b/source/wbfs.c new file mode 100644 index 00000000..0554bb0c --- /dev/null +++ b/source/wbfs.c @@ -0,0 +1,531 @@ +#include +#include +#include +#include +#include + +#include "sdhc.h" +#include "usbstorage.h" +#include "utils.h" +#include "video.h" +#include "wdvd.h" +#include "wbfs.h" + +#include "libwbfs/libwbfs.h" + +/* Constants */ +#define MAX_NB_SECTORS 32 + +/* WBFS HDD */ +static wbfs_t *hdd = NULL; + +/* WBFS callbacks */ +static rw_sector_callback_t readCallback = NULL; +static rw_sector_callback_t writeCallback = NULL; + +/* Variables */ + +static u32 nb_sectors, sector_size; +void __WBFS_Spinner(s32 x, s32 max) +{ + static time_t start; + static u32 expected; + + f32 percent, size; + u32 d, h, m, s; + + /* First time */ + if (!x) { + start = time(0); + expected = 300; + } + + /* Elapsed time */ + d = time(0) - start; + + if (x != max) { + /* Expected time */ + if (d) + expected = (expected * 3 + d * max / x) / 4; + + /* Remaining time */ + d = (expected > d) ? (expected - d) : 0; + } + + /* Calculate time values */ + h = d / 3600; + m = (d / 60) % 60; + s = d % 60; + + /* Calculate percentage/size */ + percent = (x * 100.0) / max; + size = (hdd->wii_sec_sz / GB_SIZE) * max; + + //Con_ClearLine(); + + /* Show progress */ + if (x != max) { + printf(" %.2f%% of %.2fGB (%c) ETA: %d:%02d:%02d\r", percent, size, "/|\\-"[(x / 10) % 4], h, m, s); + fflush(stdout); + } else + printf(" %.2fGB copied in %d:%02d:%02d\n", size, h, m, s); +} + +wbfs_t *GetHddInfo(void) +{ + return hdd; +} + +s32 __WBFS_ReadDVD(void *fp, u32 lba, u32 len, void *iobuf) +{ + void *buffer = NULL; + + u64 offset; + u32 mod, size; + s32 ret; + + /* Calculate offset */ + offset = ((u64)lba) << 2; + + /* Calcualte sizes */ + mod = len % 32; + size = len - mod; + + /* Read aligned data */ + if (size) { + ret = WDVD_UnencryptedRead(iobuf, size, offset); + if (ret < 0) + goto out; + } + + /* Read non-aligned data */ + if (mod) { + /* Allocate memory */ + buffer = memalign(32, 0x20); + if (!buffer) + return -1; + + /* Read data */ + ret = WDVD_UnencryptedRead(buffer, 0x20, offset + size); + if (ret < 0) + goto out; + + /* Copy data */ + memcpy(iobuf + size, buffer, mod); + } + + /* Success */ + ret = 0; + +out: + /* Free memory */ + if (buffer) + free(buffer); + + return ret; +} + +s32 __WBFS_ReadUSB(void *fp, u32 lba, u32 count, void *iobuf) +{ + u32 cnt = 0; + s32 ret; + + /* Do reads */ + while (cnt < count) { + void *ptr = ((u8 *)iobuf) + (cnt * sector_size); + u32 sectors = (count - cnt); + + /* Read sectors is too big */ + if (sectors > MAX_NB_SECTORS) + sectors = MAX_NB_SECTORS; + + /* USB read */ + ret = USBStorage_ReadSectors(lba + cnt, sectors, ptr); + if (ret < 0) + return ret; + + /* Increment counter */ + cnt += sectors; + } + + return 0; +} + +s32 __WBFS_WriteUSB(void *fp, u32 lba, u32 count, void *iobuf) +{ + u32 cnt = 0; + s32 ret; + + /* Do writes */ + while (cnt < count) { + void *ptr = ((u8 *)iobuf) + (cnt * sector_size); + u32 sectors = (count - cnt); + + /* Write sectors is too big */ + if (sectors > MAX_NB_SECTORS) + sectors = MAX_NB_SECTORS; + + /* USB write */ + ret = USBStorage_WriteSectors(lba + cnt, sectors, ptr); + if (ret < 0) + return ret; + + /* Increment counter */ + cnt += sectors; + } + + return 0; +} + +s32 __WBFS_ReadSDHC(void *fp, u32 lba, u32 count, void *iobuf) +{ + u32 cnt = 0; + s32 ret; + + /* Do reads */ + while (cnt < count) { + void *ptr = ((u8 *)iobuf) + (cnt * sector_size); + u32 sectors = (count - cnt); + + /* Read sectors is too big */ + if (sectors > MAX_NB_SECTORS) + sectors = MAX_NB_SECTORS; + + /* SDHC read */ + ret = SDHC_ReadSectors(lba + cnt, sectors, ptr); + if (!ret) + return -1; + + /* Increment counter */ + cnt += sectors; + } + + return 0; +} + +s32 __WBFS_WriteSDHC(void *fp, u32 lba, u32 count, void *iobuf) +{ + u32 cnt = 0; + s32 ret; + + /* Do writes */ + while (cnt < count) { + void *ptr = ((u8 *)iobuf) + (cnt * sector_size); + u32 sectors = (count - cnt); + + /* Write sectors is too big */ + if (sectors > MAX_NB_SECTORS) + sectors = MAX_NB_SECTORS; + + /* SDHC write */ + ret = SDHC_WriteSectors(lba + cnt, sectors, ptr); + if (!ret) + return -1; + + /* Increment counter */ + cnt += sectors; + } + + return 0; +} + +s32 WBFS_Init(u32 device) +{ + s32 ret; + + switch (device) { + case WBFS_DEVICE_USB: + /* Initialize USB storage */ + ret = USBStorage_Init(); + if (ret >= 0) { + /* Setup callbacks */ + readCallback = __WBFS_ReadUSB; + writeCallback = __WBFS_WriteUSB; + /* Device info */ + /* Get USB capacity */ + nb_sectors = USBStorage_GetCapacity(§or_size); + if (!nb_sectors) + return -1; + } + else + return ret; + break; + case WBFS_DEVICE_SDHC: + /* Initialize SDHC */ + ret = SDHC_Init(); + + if (ret) { + /* Setup callbacks */ + readCallback = __WBFS_ReadSDHC; + writeCallback = __WBFS_WriteSDHC; + + /* Device info */ + nb_sectors = 0; + sector_size = SDHC_SECTOR_SIZE; + } + else + return -1; + break; + } + + return 0; +} +//s32 WBFS_Init(void) +//{ +// s32 ret; +// +// /* Initialize USB storage */ +// ret = USBStorage_Init(); +// if (ret < 0) +// return ret; +// +// /* Get USB capacity */ +// nb_sectors = USBStorage_GetCapacity(§or_size); +// if (!nb_sectors) +// return -1; +// +// return 0; +//} + +/* +s32 WBFS_Init(u32 device, u32 timeout) +{ + u32 cnt; + s32 ret; + + // Wrong timeout + if (!timeout) + return -1; + + // Try to mount device + for (cnt = 0; cnt < timeout; cnt++) { + switch (device) { + case WBFS_DEVICE_USB: { + // Initialize USB storage + ret = USBStorage_Init(); + + if (ret >= 0) { + // Setup callbacks + readCallback = __WBFS_ReadUSB; + writeCallback = __WBFS_WriteUSB; + + // Device info + nb_sectors = USBStorage_GetCapacity(§or_size); + + goto out; + } + } + + case WBFS_DEVICE_SDHC: { + // Initialize SDHC + ret = SDHC_Init(); + + if (ret) { + // Setup callbacks + readCallback = __WBFS_ReadSDHC; + writeCallback = __WBFS_WriteSDHC; + + // Device info + nb_sectors = 0; + sector_size = SDHC_SECTOR_SIZE; + + goto out; + } else + ret = -1; + } + + default: + return -1; + } + + // Sleep 1 second + sleep(1); + } + +out: + return ret; +} +*/ + +s32 WBFS_Open(void) +{ + /* Close hard disk */ + if (hdd) + wbfs_close(hdd); + + /* Open hard disk */ + hdd = wbfs_open_hd(readCallback, writeCallback, NULL, sector_size, nb_sectors, 0); + if (!hdd) + return -1; + + return 0; +} + +s32 WBFS_Close(void) + +{ + /* Close hard disk */ + if (hdd) + wbfs_close(hdd); + + return 0; +} + +s32 WBFS_Format(u32 lba, u32 size) +{ + wbfs_t *partition = NULL; + + /* Reset partition */ + partition = wbfs_open_partition(readCallback, writeCallback, NULL, sector_size, size, lba, 1); + if (!partition) + return -1; + + /* Free memory */ + wbfs_close(partition); + + return 0; +} + +s32 WBFS_GetCount(u32 *count) +{ + /* No device open */ + if (!hdd) + return -1; + + /* Get list length */ + *count = wbfs_count_discs(hdd); + + return 0; +} + +s32 WBFS_GetHeaders(void *outbuf, u32 cnt, u32 len) +{ + u32 idx, size; + s32 ret; + + /* No device open */ + if (!hdd) + return -1; + + for (idx = 0; idx < cnt; idx++) { + u8 *ptr = ((u8 *)outbuf) + (idx * len); + + /* Get header */ + ret = wbfs_get_disc_info(hdd, idx, ptr, len, &size); + if (ret < 0) + return ret; + } + + return 0; +} + +s32 WBFS_CheckGame(u8 *discid) +{ + wbfs_disc_t *disc = NULL; + + /* Try to open game disc */ + disc = wbfs_open_disc(hdd, discid); + if (disc) { + /* Close disc */ + wbfs_close_disc(disc); + + return 1; + } + + return 0; +} + +s32 WBFS_AddGame(void) +{ + s32 ret; + + /* No device open */ + if (!hdd) + return -1; + + /* Add game to device */ + ret = wbfs_add_disc(hdd, __WBFS_ReadDVD, NULL, __WBFS_Spinner, ALL_PARTITIONS, 0); + if (ret < 0) + return ret; + + return 0; +} + +s32 WBFS_RemoveGame(u8 *discid) +{ + s32 ret; + + /* No device open */ + if (!hdd) + return -1; + + /* Remove game from USB device */ + ret = wbfs_rm_disc(hdd, discid); + if (ret < 0) + return ret; + + return 0; +} + +s32 WBFS_GameSize(u8 *discid, f32 *size) +{ + wbfs_disc_t *disc = NULL; + + u32 sectors; + + /* No device open */ + if (!hdd) + return -1; + + /* Open disc */ + disc = wbfs_open_disc(hdd, discid); + if (!disc) + return -2; + + /* Get game size in sectors */ + sectors = wbfs_sector_used(hdd, disc->header); + + /* Close disc */ + wbfs_close_disc(disc); + + /* Copy value */ + *size = (hdd->wbfs_sec_sz / GB_SIZE) * sectors; + + return 0; +} + +s32 WBFS_DiskSpace(f32 *used, f32 *free) +{ + f32 ssize; + u32 cnt; + + /* No device open */ + if (!hdd) + return -1; + + /* Count used blocks */ + cnt = wbfs_count_usedblocks(hdd); + + /* Sector size in GB */ + ssize = hdd->wbfs_sec_sz / GB_SIZE; + + /* Copy values */ + *free = ssize * cnt; + *used = ssize * (hdd->n_wbfs_sec - cnt); + + return 0; +} + +s32 WBFS_RenameGame(u8 *discid, const void *newname) +{ + s32 ret; + + /* No USB device open */ + if (!hdd) + return -1; + ret = wbfs_ren_disc(hdd, discid,(u8*)newname); + if (ret < 0) + return ret; + + return 0; +} diff --git a/source/wbfs.h b/source/wbfs.h new file mode 100644 index 00000000..7c177e47 --- /dev/null +++ b/source/wbfs.h @@ -0,0 +1,43 @@ +#ifndef _WBFS_H_ +#define _WBFS_H_ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "libwbfs/libwbfs.h" +/* Device list */ +enum { + WBFS_DEVICE_USB = 1, /* USB device */ + WBFS_DEVICE_SDHC /* SDHC device */ +}; + +/* Macros */ +#define WBFS_MIN_DEVICE 1 +#define WBFS_MAX_DEVICE 2 + +/* Prototypes */ + +s32 WBFS_Init(u32); +s32 WBFS_Open(void); +s32 WBFS_Close(void); +s32 WBFS_Format(u32, u32); +s32 WBFS_GetCount(u32 *); +s32 WBFS_GetHeaders(void *, u32, u32); +s32 __WBFS_ReadDVD(void *fp, u32 lba, u32 len, void *iobuf); +wbfs_t *GetHddInfo(void); +s32 WBFS_CheckGame(u8 *); +s32 WBFS_AddGame(void); +s32 WBFS_RemoveGame(u8 *); +s32 WBFS_GameSize(u8 *, f32 *); +s32 WBFS_DiskSpace(f32 *, f32 *); +s32 WBFS_RenameGame(u8 *, const void *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/wdvd.c b/source/wdvd.c new file mode 100644 index 00000000..352ae5ba --- /dev/null +++ b/source/wdvd.c @@ -0,0 +1,338 @@ +#include +#include +#include +#include + +/* Constants */ +#define IOCTL_DI_READID 0x70 +#define IOCTL_DI_READ 0x71 +#define IOCTL_DI_WAITCVRCLOSE 0x79 +#define IOCTL_DI_GETCOVER 0x88 +#define IOCTL_DI_RESET 0x8A +#define IOCTL_DI_OPENPART 0x8B +#define IOCTL_DI_CLOSEPART 0x8C +#define IOCTL_DI_UNENCREAD 0x8D +#define IOCTL_DI_SEEK 0xAB +#define IOCTL_DI_STOPLASER 0xD2 +#define IOCTL_DI_OFFSET 0xD9 +#define IOCTL_DI_STOPMOTOR 0xE3 +#define IOCTL_DI_SETUSBMODE 0xF4 +#define IOCTL_DI_SETWBFSMODE 0xfe +#define IOCTL_DI_DISABLERESET 0xF6 + +/* Variables */ +static u32 inbuf[8] ATTRIBUTE_ALIGN(32); +static u32 outbuf[8] ATTRIBUTE_ALIGN(32); + +static const char di_fs[] ATTRIBUTE_ALIGN(32) = "/dev/di"; +static s32 di_fd = -1; + + +s32 WDVD_Init(void) +{ + /* Open "/dev/di" */ + if (di_fd < 0) { + di_fd = IOS_Open(di_fs, 0); + if (di_fd < 0) + return di_fd; + } + + return 0; +} + +s32 WDVD_Close(void) +{ + /* Close "/dev/di" */ + if (di_fd >= 0) { + IOS_Close(di_fd); + di_fd = -1; + } + + return 0; +} + +s32 WDVD_GetHandle(void) +{ + /* Return di handle */ + return di_fd; +} + +s32 WDVD_Reset(void) +{ + s32 ret; + + memset(inbuf, 0, sizeof(inbuf)); + + /* Reset drive */ + inbuf[0] = IOCTL_DI_RESET << 24; + inbuf[1] = 1; + + ret = IOS_Ioctl(di_fd, IOCTL_DI_RESET, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf)); + if (ret < 0) + return ret; + + return (ret == 1) ? 0 : -ret; +} + +s32 WDVD_ReadDiskId(void *id) +{ + s32 ret; + + memset(inbuf, 0, sizeof(inbuf)); + + /* Read disc ID */ + inbuf[0] = IOCTL_DI_READID << 24; + + ret = IOS_Ioctl(di_fd, IOCTL_DI_READID, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf)); + if (ret < 0) + return ret; + + if (ret == 1) { + memcpy(id, outbuf, sizeof(dvddiskid)); + return 0; + } + + return -ret; +} + +s32 WDVD_Seek(u64 offset) +{ + s32 ret; + + memset(inbuf, 0, sizeof(inbuf)); + + /* Drive seek */ + inbuf[0] = IOCTL_DI_SEEK << 24; + inbuf[1] = (u32)(offset >> 2); + + ret = IOS_Ioctl(di_fd, IOCTL_DI_SEEK, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf)); + if (ret < 0) + return ret; + + return (ret == 1) ? 0 : -ret; + +} + +s32 WDVD_Offset(u64 offset) +{ + u32 *off = (u32 *)((void *)&offset); + s32 ret; + + memset(inbuf, 0, sizeof(inbuf)); + + /* Set offset */ + inbuf[0] = IOCTL_DI_OFFSET << 24; + inbuf[1] = (off[0]) ? 1: 0; + inbuf[2] = (off[1] >> 2); + + ret = IOS_Ioctl(di_fd, IOCTL_DI_OFFSET, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf)); + if (ret < 0) + return ret; + + return (ret == 1) ? 0 : -ret; +} + +s32 WDVD_StopLaser(void) +{ + s32 ret; + + memset(inbuf, 0, sizeof(inbuf)); + + /* Stop laser */ + inbuf[0] = IOCTL_DI_STOPLASER << 24; + + ret = IOS_Ioctl(di_fd, IOCTL_DI_STOPLASER, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf)); + if (ret < 0) + return ret; + + return (ret == 1) ? 0 : -ret; +} + +s32 WDVD_StopMotor(void) +{ + s32 ret; + + memset(inbuf, 0, sizeof(inbuf)); + + /* Stop motor */ + inbuf[0] = IOCTL_DI_STOPMOTOR << 24; + + ret = IOS_Ioctl(di_fd, IOCTL_DI_STOPMOTOR, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf)); + if (ret < 0) + return ret; + + return (ret == 1) ? 0 : -ret; +} + +s32 WDVD_OpenPartition(u64 offset) +{ + u8 *vector = NULL; + + u32 *buffer = NULL; + s32 ret; + + /* Allocate memory */ + buffer = (u32 *)memalign(32, 0x5000); + if (!buffer) + return -1; + + /* Set vector pointer */ + vector = (u8 *)buffer; + + memset(buffer, 0, 0x5000); + + /* Open partition */ + buffer[0] = (u32)(buffer + 0x10); + buffer[1] = 0x20; + buffer[3] = 0x024A; + buffer[6] = (u32)(buffer + 0x380); + buffer[7] = 0x49E4; + buffer[8] = (u32)(buffer + 0x360); + buffer[9] = 0x20; + + buffer[(0x40 >> 2)] = IOCTL_DI_OPENPART << 24; + buffer[(0x40 >> 2) + 1] = offset >> 2; + + ret = IOS_Ioctlv(di_fd, IOCTL_DI_OPENPART, 3, 2, (ioctlv *)vector); + + /* Free memory */ + free(buffer); + + if (ret < 0) + return ret; + + return (ret == 1) ? 0 : -ret; +} + +s32 WDVD_ClosePartition(void) +{ + s32 ret; + + memset(inbuf, 0, sizeof(inbuf)); + + /* Close partition */ + inbuf[0] = IOCTL_DI_CLOSEPART << 24; + + ret = IOS_Ioctl(di_fd, IOCTL_DI_CLOSEPART, inbuf, sizeof(inbuf), NULL, 0); + if (ret < 0) + return ret; + + return (ret == 1) ? 0 : -ret; +} + +s32 WDVD_UnencryptedRead(void *buf, u32 len, u64 offset) +{ + s32 ret; + + memset(inbuf, 0, sizeof(inbuf)); + + /* Unencrypted read */ + inbuf[0] = IOCTL_DI_UNENCREAD << 24; + inbuf[1] = len; + inbuf[2] = (u32)(offset >> 2); + + ret = IOS_Ioctl(di_fd, IOCTL_DI_UNENCREAD, inbuf, sizeof(inbuf), buf, len); + if (ret < 0) + return ret; + + return (ret == 1) ? 0 : -ret; +} + +s32 WDVD_Read(void *buf, u32 len, u64 offset) +{ + s32 ret; + + memset(inbuf, 0, sizeof(inbuf)); + + /* Disc read */ + inbuf[0] = IOCTL_DI_READ << 24; + inbuf[1] = len; + inbuf[2] = (u32)(offset >> 2); + + ret = IOS_Ioctl(di_fd, IOCTL_DI_READ, inbuf, sizeof(inbuf), buf, len); + if (ret < 0) + return ret; + + return (ret == 1) ? 0 : -ret; +} + +s32 WDVD_WaitForDisc(void) +{ + s32 ret; + + memset(inbuf, 0, sizeof(inbuf)); + + /* Wait for disc */ + inbuf[0] = IOCTL_DI_WAITCVRCLOSE << 24; + + ret = IOS_Ioctl(di_fd, IOCTL_DI_WAITCVRCLOSE, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf)); + if (ret < 0) + return ret; + + return (ret == 1) ? 0 : -ret; +} + +s32 WDVD_GetCoverStatus(u32 *status) +{ + s32 ret; + + memset(inbuf, 0, sizeof(inbuf)); + + /* Get cover status */ + inbuf[0] = IOCTL_DI_GETCOVER << 24; + + ret = IOS_Ioctl(di_fd, IOCTL_DI_GETCOVER, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf)); + if (ret < 0) + return ret; + + if (ret == 1) { + /* Copy cover status */ + memcpy(status, outbuf, sizeof(u32)); + + return 0; + } + + return -ret; +} + +s32 WDVD_DisableReset(u8 val) +{ + s32 ret; + + memset(inbuf, 0, sizeof(inbuf)); + + /* Disable/Enable reset */ + inbuf[0] = IOCTL_DI_DISABLERESET << 24; + inbuf[1] = val; + + ret = IOS_Ioctl(di_fd, IOCTL_DI_DISABLERESET, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf)); + if (ret < 0) + return ret; + + return (ret == 1) ? 0 : -ret; +} + +s32 WDVD_SetUSBMode(u8 *id, int ios222) +{ + s32 ret; + + memset(inbuf, 0, sizeof(inbuf)); + + /* Set USB mode */ + if(ios222 == 1) { + inbuf[0] = IOCTL_DI_SETWBFSMODE << 24; + } else { + inbuf[0] = IOCTL_DI_SETUSBMODE << 24; + } + inbuf[1] = (id) ? 1 : 0; + + /* Copy ID */ + if (id) + memcpy(&inbuf[2], id, 6); + + ret = IOS_Ioctl(di_fd, IOCTL_DI_SETUSBMODE, inbuf, sizeof(inbuf), outbuf, sizeof(outbuf)); + if (ret < 0) + return ret; + + return (ret == 1) ? 0 : -ret; +} diff --git a/source/wdvd.h b/source/wdvd.h new file mode 100644 index 00000000..3626d59f --- /dev/null +++ b/source/wdvd.h @@ -0,0 +1,35 @@ +#ifndef _WDVD_H_ +#define _WDVD_H_ + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Prototypes */ +s32 WDVD_Init(void); +s32 WDVD_Close(void); +s32 WDVD_GetHandle(void); +s32 WDVD_Reset(void); +s32 WDVD_ReadDiskId(void *); +s32 WDVD_Seek(u64); +s32 WDVD_Offset(u64); +s32 WDVD_StopLaser(void); +s32 WDVD_StopMotor(void); +s32 WDVD_OpenPartition(u64); +s32 WDVD_ClosePartition(void); +s32 WDVD_UnencryptedRead(void *, u32, u64); +s32 WDVD_Read(void *, u32, u64); +s32 WDVD_WaitForDisc(void); +s32 WDVD_GetCoverStatus(u32 *); +s32 WDVD_DisableReset(u8); +s32 WDVD_SetUSBMode(u8 *, int ios222); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/source/wpad.c b/source/wpad.c new file mode 100644 index 00000000..2f8df22b --- /dev/null +++ b/source/wpad.c @@ -0,0 +1,71 @@ +#include +#include + +#include "sys.h" +#include "wpad.h" + +/* Constants */ +#define MAX_WIIMOTES 4 + +extern u8 shutdown; + +void __Wpad_PowerCallback(s32 chan) +{ + /* Poweroff console */ + shutdown = 1; +} + + +s32 Wpad_Init(void) +{ + s32 ret; + + /* Initialize Wiimote subsystem */ + ret = WPAD_Init(); + if (ret < 0) + return ret; + + /* Set POWER button callback */ + WPAD_SetPowerButtonCallback(__Wpad_PowerCallback); + + return ret; +} + +void Wpad_Disconnect(void) +{ + u32 cnt; + + /* Disconnect Wiimotes */ + for (cnt = 0; cnt < MAX_WIIMOTES; cnt++) + WPAD_Disconnect(cnt); + + /* Shutdown Wiimote subsystem */ + WPAD_Shutdown(); +} + +u32 Wpad_GetButtons(void) +{ + u32 buttons = 0, cnt; + + /* Scan pads */ + WPAD_ScanPads(); + + /* Get pressed buttons */ + for (cnt = 0; cnt < MAX_WIIMOTES; cnt++) + buttons |= WPAD_ButtonsDown(cnt); + + return buttons; +} + +u32 Wpad_WaitButtons(void) +{ + u32 buttons = 0; + + /* Wait for button pressing */ + while (!buttons) { + buttons = Wpad_GetButtons(); + VIDEO_WaitVSync(); + } + + return buttons; +} diff --git a/source/wpad.h b/source/wpad.h new file mode 100644 index 00000000..2679c8a9 --- /dev/null +++ b/source/wpad.h @@ -0,0 +1,23 @@ +#ifndef _WPAD_H_ +#define _WPAD_H_ + +#include + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Prototypes */ +s32 Wpad_Init(void); +void Wpad_Disconnect(void); +u32 Wpad_GetButtons(void); +u32 Wpad_WaitButtons(void); + +#ifdef __cplusplus +} +#endif + +#endif