Files
Nintendont/loader/source/global.c
FIX94 afcfa4b653 -added deadzones to wii vc gamepad reads and heavily changed memory to make space for that, hopefully it didnt break anything
-completely restructured how internal wii vc titles get read in, again hopefully it didnt break anything
2017-09-20 06:14:16 +02:00

661 lines
15 KiB
C

/*
Nintendont (Loader) - Playing Gamecubes in Wii mode on a Wii U
Copyright (C) 2013 crediar
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 version 2.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <gccore.h>
#include <ogc/audio.h>
#include <ogc/consol.h>
#include <ogc/dsp.h>
#include <ogc/es.h>
#include <ogc/gx_struct.h>
#include <ogc/lwp_threads.h>
#include <ogc/system.h>
#include <ogc/video.h>
#include <ogc/wiilaunch.h>
#include <stdio.h>
#include <unistd.h>
#include "ff_utf8.h"
#include "diskio.h"
#include "Config.h"
#include "exi.h"
#include "global.h"
#include "font_zip.h"
#include "dip.h"
#include "unzip/unzip.h"
// Background image.
#include "background_png.h"
GRRLIB_ttfFont *myFont;
GRRLIB_texImg *background;
GRRLIB_texImg *screen_buffer;
static bool bg_isWidescreen = false;
static float bg_xScale = 1.0f;
static int bg_xPos = 0;
u32 POffset;
NIN_CFG* ncfg = (NIN_CFG*)0x93004000;
bool UseSD;
const char* const GetRootDevice()
{
static const char* const SdStr = "sd";
static const char* const UsbStr = "usb";
if (UseSD)
return SdStr;
else
return UsbStr;
}
void RAMInit(void)
{
__asm("lis 3, 0x8390\n\
mtspr 0x3F3, 3");
__asm("mfspr 3, 1008");
__asm("ori 3, 3, 0x200");
__asm("mtspr 1008, 3");
__asm("mtfsb1 4*cr7+gt");
memset( (void*)0x80000000, 0, 0x100 );
memset( (void*)0x80003000, 0, 0x100 );
//memset( (void*)0x80003F00, 0, 0x11FC100 );
memset( (void*)0x81340000, 0, 0x3C );
__asm(" isync\n\
li 4, 0 \n\
mtspr 541, 4\n\
mtspr 540, 4 \n\
mtspr 543, 4\n\
mtspr 542, 4\n\
mtspr 531, 4\n\
mtspr 530, 4\n\
mtspr 533, 4\n\
mtspr 532, 4\n\
mtspr 535, 4\n\
mtspr 534, 4\n\
isync");
*(vu32*)0x80000028 = 0x01800000;
*(vu32*)0x8000002C = 0;
*(vu32*)0x8000002C = *(vu32*)0xCC00302C >> 28; // console type
*(vu32*)0x80000038 = 0x01800000;
*(vu32*)0x800000F0 = 0x01800000;
*(vu32*)0x800000EC = 0x81800000;
*(vu32*)0x80003100 = 0x01800000; //Physical MEM1 size
*(vu32*)0x80003104 = 0x01800000; //Simulated MEM1 size
*(vu32*)0x80003108 = 0x01800000;
*(vu32*)0x8000310C = 0;
*(vu32*)0x80003110 = 0;
*(vu32*)0x80003114 = 0;
*(vu32*)0x80003118 = 0; //Physical MEM2 size
*(vu32*)0x8000311C = 0; //Simulated MEM2 size
*(vu32*)0x80003120 = 0;
*(vu32*)0x80003124 = 0x0000FFFF;
*(vu32*)0x80003128 = 0;
*(vu32*)0x80003130 = 0x0000FFFF; //IOS Heap Range
*(vu32*)0x80003134 = 0;
*(vu32*)0x80003138 = 0x11; //Hollywood Version
*(vu32*)0x8000313C = 0;
*(vu32*)0x800030CC = 0;
*(vu32*)0x800030C8 = 0;
*(vu32*)0x800030D0 = 0;
*(vu32*)0x800030C4 = 0;
*(vu32*)0x800030C8 = 0;
*(vu32*)0x800030D8 = 0;
*(vu32*)0x8000315C = 0x81;
// __asm("lis 3, 0x8000\n");
*(vu16*)0xCC00501A = 156;
//*(vu32*)0xCC006400 = 0x340;
*(vu32*)0x800030CC = 0;
*(vu32*)0x800030C8 = 0;
*(vu32*)0x800030D0 = 0;
// memset( (void*)0x80003040, 0, 0x80 );
*(vu32*)0x800030C4 = 0;
*(vu32*)0x800030C8 = 0;
//*(vu32*)0xCC003004 = 0xF0;
//*(vu32*)0xCD000034 = 0x4000;
*(vu32*)0x800030D8 = 0;
*(vu32*)0x8000315C = 0x81;
}
void unzip_data(const void *input, const unsigned int input_size,
void **output, unsigned int *output_size)
{
char filepath[20]; //statically linked zip file
snprintf(filepath,20,"%x+%x",(unsigned int)input,input_size);
unzFile uf = unzOpen(filepath); //opens zip in memory
unzOpenCurrentFile(uf); //current file is the only file
unz_file_info file_info; //get file info for uncompressed size
unzGetCurrentFileInfo(uf,&file_info,NULL,0,NULL,0,NULL,0);
*output_size = file_info.uncompressed_size; //set output size
*output = malloc(*output_size); //allocate the required size
unzReadCurrentFile(uf,*output,*output_size); //read it all
unzCloseCurrentFile(uf); //done reading out the only file
unzClose(uf); //close our static archive, all done!
}
static void *font_ttf = NULL;
static unsigned int font_ttf_size = 0;
/**
* Initialize the loader.
* This also loads the background image.
* @param autoboot Set if autobooting. (This disables the fade-in.)
*/
void Initialise(bool autoboot)
{
int i;
AUDIO_Init(NULL);
DSP_Init();
AUDIO_StopDMA();
AUDIO_RegisterDMACallback(NULL);
CheckForGecko();
gprintf("GRRLIB_Init = %i\r\n", GRRLIB_Init());
unzip_data(font_zip, font_zip_size, &font_ttf, &font_ttf_size);
gprintf("Decompressed font.ttf with %i bytes\r\n", font_ttf_size);
myFont = GRRLIB_LoadTTF(font_ttf, font_ttf_size);
background = GRRLIB_LoadTexturePNG(background_png);
screen_buffer = GRRLIB_CreateEmptyTexture(rmode->fbWidth, rmode->efbHeight);
// Calculate the background image scale.
bg_isWidescreen = (CONF_GetAspectRatio() == CONF_ASPECT_16_9);
if (bg_isWidescreen)
{
// Widescreen. 0.75x scaling, 80px offset.
bg_xScale = 0.75f;
bg_xPos = 80;
}
else
{
// Standard screen. 1.0x scaling, 0px offset.
bg_xScale = 1.0f;
bg_xPos = 0;
}
if(autoboot == false)
{
for (i=0; i<255; i +=5) // Fade background image in from black screen
{
if (bg_isWidescreen)
{
// Clear the sides.
GRRLIB_Rectangle(0, 0, 80, 480, RGBA(222, 223, 224, i), true);
GRRLIB_Rectangle(80+480, 0, 80, 480, RGBA(222, 223, 224, i), true);
}
GRRLIB_DrawImg(bg_xPos, 0, background, 0, bg_xScale, 1, RGBA(255, 255, 255, i)); // Opacity increases as i does
GRRLIB_Render();
}
ClearScreen();
}
gprintf("Initialize Finished\r\n");
}
static void (*stub)() = (void*)0x80001800;
inline void DrawBuffer(void) {
GRRLIB_DrawImg(0, 0, screen_buffer, 0, 1, 1, 0xFFFFFFFF);
}
inline void UpdateScreen(void) {
GRRLIB_Screen2Texture(0, 0, screen_buffer, GX_FALSE);
GRRLIB_Render();
DrawBuffer();
}
raw_irq_handler_t BeforeIOSReload()
{
__STM_Close();
write32(0x80003140, 0);
__MaskIrq(IRQ_PI_ACR);
return IRQ_Free(IRQ_PI_ACR);
}
extern void udelay(u32 us);
void AfterIOSReload(raw_irq_handler_t handle, u32 rev)
{
while((read32(0x80003140)) != rev)
udelay(1000);
u32 counter;
for (counter = 0; !(read32(0x0d000004) & 2); counter++)
{
udelay(1000);
if (counter >= 40000)
break;
}
IRQ_Request(IRQ_PI_ACR, handle, NULL);
__UnmaskIrq(IRQ_PI_ACR);
__IPC_Reinitialize();
__STM_Init();
}
/**
* Exit Nintendont and return to the loader.
* @param ret Exit code.
*/
#define SYSTEM_MENU 0x0000000100000002ULL
void ExitToLoader(int ret)
{
extern vu32 KernelLoaded;
UpdateScreen();
UpdateScreen(); // Triple render to ensure it gets seen
GRRLIB_Render();
gprintf("Exiting Nintendont...\r\n");
sleep(3);
GRRLIB_FreeTexture(background);
GRRLIB_FreeTexture(screen_buffer);
GRRLIB_FreeTTF(myFont);
GRRLIB_Exit();
CloseDevices();
if(KernelLoaded)
{
*(vu32*)0xD3003420 = 0x1DEA; //Kernel Reset
while(*(vu32*)0xD3003420 != 0) usleep(20000);
}
gprintf("Loader Exit\n");
VIDEO_SetBlack(TRUE);
VIDEO_Flush();
VIDEO_WaitVSync();
//This whole exit routine is basically equal to the game exit stub
if(*(vu32*)0x80001804 == 0x53545542 && *(vu32*)0x80001808 == 0x48415858) //stubhaxx
__lwp_thread_stopmultitasking(stub);
__ES_Init(); //make sure this is back open
u32 numviews;
STACK_ALIGN(tikview,views,4,32);
ES_GetNumTicketViews(SYSTEM_MENU, &numviews);
ES_GetTicketViews(SYSTEM_MENU, views, numviews);
ES_LaunchTitle(SYSTEM_MENU, &views[0]);
while(1) usleep(20000);
}
/**
* Load the configuration file from the root device.
* @return True if loaded successfully; false if not.
*/
bool LoadNinCFG(void)
{
bool ConfigLoaded = true;
FIL cfg;
if (f_open_char(&cfg, "/nincfg.bin", FA_READ|FA_OPEN_EXISTING) != FR_OK)
return false;
// Read the configuration file into memory.
UINT BytesRead;
f_read(&cfg, ncfg, sizeof(NIN_CFG), &BytesRead);
f_close(&cfg);
switch( ncfg->Version ) {
case 2:
if (BytesRead != 540)
ConfigLoaded = false;
break;
default:
if (BytesRead != sizeof(NIN_CFG))
ConfigLoaded = false;
break;
}
if (ncfg->Magicbytes != 0x01070CF6)
ConfigLoaded = false;
UpdateNinCFG();
if (ncfg->Version != NIN_CFG_VERSION)
ConfigLoaded = false;
if (ncfg->MaxPads > NIN_CFG_MAXPAD)
ConfigLoaded = false;
if (ncfg->MaxPads < 0)
ConfigLoaded = false;
return ConfigLoaded;
}
inline void ClearScreen()
{
if (bg_isWidescreen)
{
// Clear the sides.
GRRLIB_Rectangle(0, 0, 80, 480, RGBA(222, 223, 224, 255), true);
GRRLIB_Rectangle(80+480, 0, 80, 480, RGBA(222, 223, 224, 255), true);
}
GRRLIB_DrawImg(bg_xPos, 0, background, 0, bg_xScale, 1, RGBA(255, 255, 255, 255));
}
static inline char ascii(char s)
{
if (s < 0x20) return '.';
else if (s > 0x7E) return '.';
return s;
}
void hexdump(void *d, int len)
{
if( d == (void*)NULL )
return;
u8 *data;
int i, off;
data = (u8*)d;
for (off=0; off<len; off += 16)
{
gprintf("%08x ",off);
for(i=0; i<16; i++)
if((i+off)>=len)
gprintf(" ");
else
gprintf("%02x ",data[off+i]);
gprintf(" ");
for(i=0; i<16; i++)
if((i+off)>=len)
gprintf(" ");
else
gprintf("%c",ascii(data[off+i]));
gprintf("\r\n");
}
}
bool IsGCGame(u8 *Buffer)
{
u32 AMB1 = *(vu32*)(Buffer+0x4);
u32 GCMagic = *(vu32*)(Buffer+0x1C);
return (AMB1 == 0x414D4231 || GCMagic == 0xC2339F3D);
}
void UpdateNinCFG()
{
if (ncfg->Version == 2)
{ //251 blocks, used to be there
ncfg->Unused = 0x2;
ncfg->Version = 3;
}
if (ncfg->Version == 3)
{ //new memcard setting space
ncfg->MemCardBlocks = ncfg->Unused;
ncfg->VideoScale = 0;
ncfg->VideoOffset = 0;
ncfg->Version = 4;
}
if (ncfg->Version == 4)
{ //Option got changed so not confuse it
ncfg->Config &= ~NIN_CFG_HID;
ncfg->Version = 5;
}
if (ncfg->Version == 5)
{ //New Video Mode option
ncfg->VideoMode &= ~NIN_VID_PATCH_PAL50;
ncfg->Version = 6;
}
if (ncfg->Version == 6)
{ //New flag, disabled by default
ncfg->Config &= ~NIN_CFG_ARCADE_MODE;
ncfg->Version = 7;
}
if (ncfg->Version == 7)
{ // Wiimote CC Rumble, disabled by default;
// don't skip IPL by default.
ncfg->Config &= ~NIN_CFG_CC_RUMBLE;
ncfg->Config &= ~NIN_CFG_SKIP_IPL;
ncfg->Version = 8;
}
}
int CreateNewFile(const char *Path, unsigned int size)
{
FIL f;
// Check if the file already exists.
if (f_open_char(&f, Path, FA_READ|FA_OPEN_EXISTING) == FR_OK)
{ //create ONLY new files
f_close(&f);
return -1;
}
// Allocate a temporary buffer.
void *buf = calloc(size, 1);
if(buf == NULL)
{
gprintf("Failed to allocate %u bytes!\r\n", size);
return -2;
}
// Create the file.
if (f_open_char(&f, Path, FA_WRITE|FA_CREATE_NEW) != FR_OK)
{
gprintf("Failed to create %s!\r\n", Path);
free(buf);
return -3;
}
// Reserve space in the file.
f_expand(&f, size, 1);
// Write the temporary buffer to disk.
UINT wrote;
f_write(&f, buf, size, &wrote);
f_close(&f);
FlushDevices();
free(buf);
gprintf("Created %s with %u bytes!\r\n", Path, wrote);
return 0;
}
/** Device mount/unmount. **/
// 0 == SD, 1 == USB
FATFS *devices[2];
// Device initialization data.
typedef struct _devInitInfo_t
{
const WCHAR devNameFF[8];
const char devNameDisplay[4];
// Maximum init timeout, in seconds.
// (0 = only try once)
int timeout;
} devInitInfo_t;
static const devInitInfo_t devInitInfo[2] =
{
{{'s', 'd', ':', 0}, "SD", 0},
{{'u', 's', 'b', ':', 0}, "USB", 10}
};
/**
* Initialize and mount a device.
* @param pdrv Device number.
* @return Mount point (WCHAR), or NULL on error.
*/
const WCHAR *MountDevice(BYTE pdrv)
{
if (pdrv < DEV_SD || pdrv > DEV_USB)
return NULL;
// Attempt to initialize this device
// TODO: Do initialization asynchronously.
if (devInitInfo[pdrv].timeout > 0)
{
// Attempt multiple inits within a timeout period.
time_t timeout = time(NULL);
while (time(NULL) - timeout < devInitInfo[pdrv].timeout)
{
if (disk_initialize(pdrv) == 0)
break;
usleep(50000);
}
}
else
{
// Only attempt a single init.
disk_initialize(pdrv);
}
if (disk_status(pdrv) == 0)
{
// Device initialized.
devices[pdrv] = (FATFS*)memalign(32, sizeof(FATFS));
if (f_mount(devices[pdrv], devInitInfo[pdrv].devNameFF, 1) == FR_OK)
{
gprintf("Mounted %s!\n", devInitInfo[pdrv].devNameDisplay);
}
else
{
// Could not mount the filesystem.
free(devices[pdrv]);
devices[pdrv] = NULL;
}
}
return (devices[pdrv] ? devInitInfo[pdrv].devNameFF : NULL);
}
/**
* Unmount and shut down a device.
* @param pdrv Device number.
* @return 0 on success or if the drive was already shut down; non-zero on error.
*/
int UnmountDevice(BYTE pdrv)
{
if (pdrv < DEV_SD || pdrv > DEV_USB)
return -1;
// FIXME: Close the log file if it's on this device?
// Check if the device is mounted.
if (devices[pdrv] != NULL)
{
// Unmount the device.
f_mount(NULL, devInitInfo[pdrv].devNameFF, 1);
// Free the FatFS object.
free(devices[pdrv]);
devices[pdrv] = 0;
}
// Shut down the device driver.
disk_shutdown(pdrv);
return 0;
}
/**
* Shut down all devices.
* This also closes the log file.
*/
void CloseDevices(void)
{
closeLog();
UnmountDevice(DEV_SD);
UnmountDevice(DEV_USB);
}
/**
* Flush all device caches.
*/
void FlushDevices(void)
{
disk_flush(DEV_SD);
disk_flush(DEV_USB);
}
/**
* Does a filename have a supported file extension?
* @return True if it does; false if it doesn't.
*/
bool IsSupportedFileExt(const char *filename)
{
size_t len = strlen(filename);
if (len >= 5 && filename[len-4] == '.')
{
const int extpos = len-3;
if (!strcasecmp(&filename[extpos], "gcm") ||
!strcasecmp(&filename[extpos], "iso") ||
!strcasecmp(&filename[extpos], "cso"))
{
// File extension is supported.
return true;
}
}
else if (len >= 6 && filename[len-5] == '.')
{
const int extpos = len-4;
if (!strcasecmp(&filename[extpos], "ciso"))
{
// File extension is supported.
return true;
}
}
// File extension is NOT supported.
return false;
}
/**
* Check if an ID6 is a known multi-game disc.
* @param id6 ID6. (must be 6 bytes)
* @return True if this is a known multi-game disc; false if not.
*/
bool IsMultiGameDisc(const char *id6)
{
// Reference: https://gbatemp.net/threads/wit-wiimms-iso-tools-gamecube-disc-support.251630/#post-3088119
// NOTE: GCOx52 is "Call of Duty: Finest Hour".
if (!memcmp(id6, "GCO", 3) && id6[4]=='D' && id6[5]=='V')
{
// GCOxDV(D5) or GCOxDV(D9).
return true;
}
// Check for older IDs.
static const char multi_game_ids[3][8] = {"COBRAM", "GGCOSD", "RGCOSD"};
u32 i;
for (i = 0; i < 3; i++)
{
if (!memcmp(id6, multi_game_ids[i], 6))
{
// Found a multi-game disc.
return true;
}
}
// Not a multi-game disc.
return false;
}