Compare commits

...

24 Commits

Author SHA1 Message Date
Naim2000 d9ba968f90 wkb: bind 2 2024-05-03 18:14:16 -05:00
Naim2000 e49d9a8850 nevermind, checking the other way around too 2024-05-03 18:07:30 -05:00
Naim2000 7cc66cc0a1 1.0.2 => 1.1 2024-05-03 17:19:17 -05:00
Naim2000 7e86d9ce5f disallow uninstalling vWii WADs on Wii 2024-05-03 17:18:50 -05:00
Naim2000 4875edf350 add later HBC title ids 2024-05-03 10:11:15 -05:00
Naim2000 66a8fb1440 Update meta.xml 2024-05-02 21:38:37 -05:00
Naim2000 7b47e2800e make menu pagination much nicer 2024-05-02 21:16:44 -05:00
Naim2000 60c42a760c make LEFT/RIGHT on wad list menu more natural 2024-05-02 20:37:31 -05:00
Naim2000 d18498361e slight mishap in VersionLookupTable 2024-05-02 20:29:31 -05:00
Naim2000 34aae016b9 typod IS_WIIU (2) 2024-05-02 20:28:53 -05:00
Naim2000 d5a817b680 swap LEFT and RIGHT on wad list menu
Closes #12
2024-05-02 09:26:24 -05:00
thepikachugamer 1a825dcfce
Korean Key detection (#11)
* check TMD for vwii_title flag for IOS WADs
2024-04-27 12:17:23 -05:00
thepikachugamer 608d954afe
Merge pull request #10 from MikeIsAStar/handle-all-known-product-areas
Handle all known product areas
2024-03-22 22:31:15 -05:00
MikeIsAStar 13dd003f3d Handle all known product areas 2024-03-16 17:41:48 -04:00
Naim2000 bc99919c45 TWN region code !? 2024-03-09 22:46:44 -05:00
Naim2000 53072c83fe viewData wasn't being freed 2024-03-01 18:11:11 -05:00
Naim2000 289a1cee79 Do not delete personalized tickets 2024-03-01 10:13:36 -05:00
thepikachugamer 18f2e3f07f
Fix ES_EALIGN when deleting ticket(s?) (#1) 2024-03-01 10:08:02 -05:00
Naim2000 5283493c4e Skip installing already present shared contents, verify WAD header a bit 2024-03-01 09:31:12 -05:00
Naim2000 1d0f7d6f67 Bump buffer size to 16KB 2024-03-01 07:23:30 -05:00
thepikachugamer cb2351b877
Fix tickets using the vWii common key (#9)
* had to download libogc's aes driver, hoping this works
2024-03-01 07:18:50 -05:00
thepikachugamer abd5f48be9
USB Keyboard support (#8) 2024-02-24 15:41:19 -05:00
thepikachugamer 8c0269a8bc
(Hopefully) fix unsuspecting crashes when retaining Priiloader (#7)
* "memalign is busted apparently"

* accidentally used ES_GetTMDView

* "static u32 cid" - hero of time
2024-02-12 21:19:57 -05:00
XFlak b520bc7f88
Add files via upload 2024-02-07 13:48:03 +02:00
21 changed files with 1631 additions and 549 deletions

View File

@ -21,7 +21,7 @@ include $(DEVKITPPC)/wii_rules
TARGET := boot
BUILD := build
SOURCES := source include source/libtinysmb source/libpng source/libpng/pngu
DATA := data
DATA := data
INCLUDES :=
#---------------------------------------------------------------------------------
@ -36,7 +36,7 @@ LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS := -lmxml -ltinysmb -lpng -lfat -lwiidrc -lwiiuse -lbte -logc -lm -lz -lwiilight
LIBS := -lmxml -ltinysmb -lpng -lfat -lwiikeyboard -lwiidrc -lwiiuse -lbte -logc -lm -lz -lwiilight
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing

125
source/aes.c Normal file
View File

@ -0,0 +1,125 @@
/*-------------------------------------------------------------
aes.c -- AES Engine
Copyright (C) 2022
GaryOderNichts
Joris 'DacoTaco' Vermeylen info@dacotaco.com
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.
-------------------------------------------------------------*/
#if defined(HW_RVL)
#include <string.h>
#include <gctypes.h>
#include <gcutil.h>
#include <ogc/ipc.h>
#include "aes.h"
#define AES_HEAPSIZE 0x200
#define AES_IOCTLV_ENCRYPT 2
#define AES_IOCTLV_DECRYPT 3
static s32 __aes_fd = -1;
static s32 __aes_hid = -1;
static s32 AES_ExecuteCommand(s32 command, const void* key, u32 key_size, void* iv, u32 iv_size, const void* in_data, void* out_data, u32 data_size)
{
if ((((u32)in_data | (u32)out_data) & 0xF) != 0)
return -4;
if (key_size != 16 || iv_size != 16 || (data_size & 15) != 0)
return -4;
ioctlv* params = (ioctlv*)iosAlloc(__aes_hid, sizeof(ioctlv) * 4);
if (!params)
return -1;
s32 ret = -1;
for (u32 i = 0; i < data_size; i += AES_BLOCK_SIZE) {
u32 size = i+AES_BLOCK_SIZE >= data_size
? data_size - i
: AES_BLOCK_SIZE;
params[0].data = (void*)((u32)in_data + i);
params[0].len = size;
params[1].data = (void*) key;
params[1].len = key_size;
params[2].data = (void*)((u32)out_data + i);
params[2].len = size;
params[3].data = iv;
params[3].len = iv_size;
ret = IOS_Ioctlv(__aes_fd, command, 2, 2, params);
if (ret < 0)
break;
}
iosFree(__aes_hid, params);
return ret;
}
s32 AES_Init(void)
{
if (__aes_fd >= 0)
return -1;
__aes_fd = IOS_Open("/dev/aes", 0);
if (__aes_fd < 0)
return __aes_fd;
//only create heap if it wasn't created yet.
//its never disposed, so only create once.
if(__aes_hid < 0)
__aes_hid = iosCreateHeap(AES_HEAPSIZE);
if (__aes_hid < 0) {
AES_Close();
return __aes_hid;
}
return 0;
}
s32 AES_Close(void)
{
if (__aes_fd < 0)
return -1;
IOS_Close(__aes_fd);
__aes_fd = -1;
return 0;
}
s32 AES_Encrypt(const void* key, u32 key_size, void* iv, u32 iv_size, const void* in_data, void* out_data, u32 data_size)
{
return AES_ExecuteCommand(AES_IOCTLV_ENCRYPT, key, key_size, iv, iv_size, in_data, out_data, data_size);
}
s32 AES_Decrypt(const void* key, u32 key_size, void* iv, u32 iv_size, const void* in_data, void* out_data, u32 data_size)
{
return AES_ExecuteCommand(AES_IOCTLV_DECRYPT, key, key_size, iv, iv_size, in_data, out_data, data_size);
}
#endif

53
source/aes.h Normal file
View File

@ -0,0 +1,53 @@
/*-------------------------------------------------------------
aes.h -- AES Engine
Copyright (C) 2022
GaryOderNichts
Joris 'DacoTaco' Vermeylen info@dacotaco.com
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
-------------------------------------------------------------*/
#ifndef __AES_H___
#define __AES_H___
#if defined(HW_RVL)
#include <gctypes.h>
#include <gcutil.h>
#define AES_BLOCK_SIZE 0x10000
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
s32 AES_Init(void);
s32 AES_Close(void);
s32 AES_Decrypt(const void* key, u32 key_size, void* iv, u32 iv_size, const void* in_data, void* out_data, u32 data_size);
s32 AES_Encrypt(const void* key, u32 key_size, void* iv, u32 iv_size, const void* in_data, void* out_data, u32 data_size);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
#endif

View File

@ -1,56 +1,32 @@
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include "fileops.h"
#include "malloc.h"
static struct stat st;
bool FSOPFileExists(const char* file)
{
FILE* f;
f = fopen(file, "rb");
if (f)
{
fclose(f);
return true;
}
return false;
return !stat(file, &st) && !S_ISDIR(st.st_mode);
}
bool FSOPFolderExists(const char* path)
{
DIR* dir;
dir = opendir(path);
if (dir)
{
closedir(dir);
return true;
}
return false;
return !stat(path, &st) && S_ISDIR(st.st_mode);
}
size_t FSOPGetFileSizeBytes(const char* path)
{
FILE* f;
size_t size = 0;
if (stat(path, &st) < 0) return 0;
f = fopen(path, "rb");
if (!f)
return 0;
fseek(f, 0, SEEK_END);
size = ftell(f);
fclose(f);
return size;
return st.st_size;
}
void FSOPDeleteFile(const char* file)
{
if (!FSOPFileExists(file))
return;
remove(file);
if (FSOPFileExists(file))
remove(file);
}
void FSOPMakeFolder(const char* path)
@ -85,13 +61,16 @@ s32 FSOPReadOpenFile(FILE* fp, void* buffer, u32 offset, u32 length)
s32 FSOPReadOpenFileA(FILE* fp, void** buffer, u32 offset, u32 length)
{
*buffer = memalign(32, length);
*buffer = memalign32(length);
if (!*buffer)
return -1;
s32 ret = FSOPReadOpenFile(fp, *buffer, offset, length);
if (ret < 0)
if (ret <= 0)
{
free(*buffer);
*buffer = NULL;
}
return ret;
}

View File

@ -1,6 +1,7 @@
#ifndef __FILEOPS_H__
#define __FILEOPS_H__
#include <sys/stat.h>
#include <dirent.h>
#include <ogcsys.h>
@ -14,4 +15,4 @@ void FSOPMakeFolder(const char* path);
s32 FSOPReadOpenFile(FILE* fp, void* buffer, u32 offset, u32 length);
s32 FSOPReadOpenFileA(FILE* fp, void** buffer, u32 offset, u32 length);
#endif
#endif

11
source/malloc.h Normal file
View File

@ -0,0 +1,11 @@
#include <stdlib.h>
static inline void *memalign32(size_t size)
{
return aligned_alloc(0x20, (size + 0x1F) & ~0x1F);
}
static inline void *memalign64(size_t size)
{
return aligned_alloc(0x40, (size + 0x3F) & ~0x3F);
}

View File

@ -3,6 +3,7 @@
#include <string.h>
#include <malloc.h>
#include <ogcsys.h>
#include <ogc/pad.h>
#include <wiilight.h>
#include <wiidrc/wiidrc.h>
#include <unistd.h>
@ -16,7 +17,7 @@
#include "video.h"
#include "wad.h"
#include "wpad.h"
#include <ogc/pad.h>
#include "wkb.h"
#include "globals.h"
#include "iospatch.h"
#include "appboot.h"
@ -646,42 +647,29 @@ int Menu_BatchProcessWads(fatFile *files, int fileCount, char *inFilePath, int i
if (thisFile->installstate < 0)
{
char str[41];
strncpy(str, thisFile->filename, 40); //Only 40 chars to fit the screen
str[40]=0;
printf(" %.40s: ", thisFile->filename);
i++;
switch (thisFile->installstate)
{
case -106:
{
printf(" %s Not installed?\n", str);
} break;
case -996:
{
printf(" %s Read error\n", str);
} break;
case -998:
{
printf(" %s Skipped\n", str);
} break;
case -999:
{
printf(" %s BRICK BLOCKED\n", str);
} break;
case -1036:
{
printf(" %s Needed IOS missing\n", str);
} break;
case -4100:
{
printf(" %s No trucha bug?\n", str);
} break;
default:
{
printf(" %s error %d\n", str, thisFile->installstate);
} break;
case -106: puts("Not installed?"); break;
case -996: puts("Read error"); break;
case -998: puts("Skipped"); break;
case -999: puts("BRICK BLOCKED"); break;
case -1010: puts("Wii System memory full!");
case -1022: puts("Content hash mismatch"); break;
case -1035: puts("Newer version already installed"); break;
case -1036: puts("Needed IOS missing!"); break;
case -2011: puts("No trucha bug?"); break;
/*
* from libogc.
* This rarely happens unless the WAD had an invalid ticket/tmd size
* (certs were not stripped after downloading from NUS maybe?)
*/
case ES_EINVAL: puts("Invalid WAD?"); break;
default: printf("error %d\n", thisFile->installstate); break;
}
if(i == 17)
@ -1084,24 +1072,34 @@ getList:
if (--selected < 0)
selected = (fileCnt - 1);
}
else if (buttons & WPAD_BUTTON_LEFT)
else if (buttons & WPAD_BUTTON_RIGHT)
{
selected += ENTRIES_PER_PAGE;
if (selected >= fileCnt)
selected = 0;
if (fileCnt - start > ENTRIES_PER_PAGE) {
start += ENTRIES_PER_PAGE;
selected += ENTRIES_PER_PAGE;
if (selected >= fileCnt)
selected = fileCnt - 1;
}
else {
selected = fileCnt - 1;
}
}
else if (buttons & WPAD_BUTTON_DOWN)
{
if (++selected >= fileCnt)
selected = 0;
}
else if (buttons & WPAD_BUTTON_RIGHT)
else if (buttons & WPAD_BUTTON_LEFT)
{
selected -= ENTRIES_PER_PAGE;
if (selected < 0)
selected = (fileCnt - 1);
if (start >= ENTRIES_PER_PAGE) {
start -= ENTRIES_PER_PAGE;
selected -= ENTRIES_PER_PAGE;
if (selected < 0)
selected = 0;
}
else {
selected = start = 0;
}
}
else if (buttons & WPAD_BUTTON_HOME)
{
@ -1496,9 +1494,10 @@ u32 WaitButtons(void)
u32 buttons = 0;
u32 buttonsGC = 0;
u32 buttonsDRC = 0;
u16 buttonsWKB = 0;
/* Wait for button pressing */
while (!buttons && !buttonsGC && !buttonsDRC)
while (!(buttons | buttonsGC | buttonsDRC | buttonsWKB))
{
// Wii buttons
buttons = Wpad_GetButtons();
@ -1509,6 +1508,9 @@ u32 WaitButtons(void)
// DRC buttons
buttonsDRC = WiiDRC_GetButtons();
// USB Keyboard buttons
buttonsWKB = WKB_GetButtons();
VIDEO_WaitVSync();
}
@ -1581,6 +1583,9 @@ u32 WaitButtons(void)
buttons |= WPAD_BUTTON_1;
}
if (buttonsWKB)
buttons |= buttonsWKB;
return buttons;
} // WaitButtons

267
source/mini_seeprom.c Normal file
View File

@ -0,0 +1,267 @@
/*
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
SEEPROM support
Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
Copyright (C) 2008, 2009 Haxx Enterprises <bushing@gmail.com>
Copyright (C) 2008, 2009 Hector Martin "marcan" <marcan@marcansoft.com>
Copyright (C) 2008, 2009 John Kelley <wiidev@kelley.ca>
Copyright (C) 2020 Pablo Curiel "DarkMatterCore" <pabloacurielz@gmail.com>
# This code is licensed to you under the terms of the GNU GPL, version 2;
# see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
*/
#include <ogc/machine/processor.h>
#include <unistd.h>
#include <string.h>
#include "mini_seeprom.h"
#define HW_REG_BASE 0xd800000
#define HW_GPIO1OUT (HW_REG_BASE + 0x0e0)
#define HW_GPIO1IN (HW_REG_BASE + 0x0e8)
#define HW_SEEPROM_BLK_SIZE 2
#define HW_SEEPROM_BLK_CNT (SEEPROM_SIZE / HW_SEEPROM_BLK_SIZE)
#define eeprom_delay() usleep(5)
enum {
GP_EEP_CS = 0x000400,
GP_EEP_CLK = 0x000800,
GP_EEP_MOSI = 0x001000,
GP_EEP_MISO = 0x002000
};
static void seeprom_send_bits(u16 value, u8 bits)
{
if (!bits || bits > 16) return;
while(bits--)
{
if (value & (1 << bits))
{
mask32(HW_GPIO1OUT, 0, GP_EEP_MOSI);
} else {
mask32(HW_GPIO1OUT, GP_EEP_MOSI, 0);
}
eeprom_delay();
mask32(HW_GPIO1OUT, 0, GP_EEP_CLK);
eeprom_delay();
mask32(HW_GPIO1OUT, GP_EEP_CLK, 0);
eeprom_delay();
}
}
static u16 seeprom_recv_bits(u8 bits)
{
if (!bits || bits > 16) return 0;
int res = 0;
while(bits--)
{
res <<= 1;
mask32(HW_GPIO1OUT, 0, GP_EEP_CLK);
eeprom_delay();
mask32(HW_GPIO1OUT, GP_EEP_CLK, 0);
eeprom_delay();
res |= !!(read32(HW_GPIO1IN) & GP_EEP_MISO);
}
return (u16)res;
}
u16 seeprom_read(void *dst, u16 offset, u16 size)
{
/*
* WiiUBrew told me that you interact with the SEEPROM the exact same way you do on Wii.
* However the contents are way different. Like there's absolutely no vWii stuff here.
*/
if (read16(0xCD8005A0) == 0xCAFE) return 0;
if (!dst || offset >= SEEPROM_SIZE || !size || (offset + size) > SEEPROM_SIZE) return 0;
u16 cur_offset = 0;
u8 *ptr = (u8*)dst;
u8 val[HW_SEEPROM_BLK_SIZE] = {0};
// Calculate block offsets and sizes
u8 start_addr = (u8)(offset / HW_SEEPROM_BLK_SIZE);
u8 start_addr_offset = (u8)(offset % HW_SEEPROM_BLK_SIZE);
u8 end_addr = (u8)((offset + size) / HW_SEEPROM_BLK_SIZE);
u8 end_addr_size = (u8)((offset + size) % HW_SEEPROM_BLK_SIZE);
if (!end_addr_size)
{
end_addr--;
end_addr_size = HW_SEEPROM_BLK_SIZE;
}
if (end_addr == start_addr) end_addr_size -= start_addr_offset;
mask32(HW_GPIO1OUT, GP_EEP_CLK, 0);
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
eeprom_delay();
for(u16 i = start_addr; i <= end_addr; i++)
{
if (cur_offset >= size) break;
// Start command cycle
mask32(HW_GPIO1OUT, 0, GP_EEP_CS);
// Send read command + address
seeprom_send_bits(0x600 | i, 11);
// Receive data
*((u16*)val) = seeprom_recv_bits(16);
// End of command cycle
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
eeprom_delay();
// Copy read data to destination buffer
if (i == start_addr && start_addr_offset != 0)
{
// Handle unaligned read at start address
memcpy(ptr + cur_offset, val + start_addr_offset, HW_SEEPROM_BLK_SIZE - start_addr_offset);
cur_offset += (HW_SEEPROM_BLK_SIZE - start_addr_offset);
} else
if (i == end_addr && end_addr_size != HW_SEEPROM_BLK_SIZE)
{
// Handle unaligned read at end address
memcpy(ptr + cur_offset, val, end_addr_size);
cur_offset += end_addr_size;
} else {
// Normal read
memcpy(ptr + cur_offset, val, HW_SEEPROM_BLK_SIZE);
cur_offset += HW_SEEPROM_BLK_SIZE;
}
}
return cur_offset;
}
#if 0
u16 seeprom_write(const void *src, u16 offset, u16 size)
{
if (!src || offset >= SEEPROM_SIZE || !size || (offset + size) > SEEPROM_SIZE) return 0;
u32 level = 0;
u16 cur_offset = 0;
const u8 *ptr = (const u8*)src;
u8 val[HW_SEEPROM_BLK_SIZE] = {0};
// Calculate block offsets and sizes
u8 start_addr = (u8)(offset / HW_SEEPROM_BLK_SIZE);
u8 start_addr_offset = (u8)(offset % HW_SEEPROM_BLK_SIZE);
u8 end_addr = (u8)((offset + size) / HW_SEEPROM_BLK_SIZE);
u8 end_addr_size = (u8)((offset + size) % HW_SEEPROM_BLK_SIZE);
if (!end_addr_size)
{
end_addr--;
end_addr_size = HW_SEEPROM_BLK_SIZE;
}
if (end_addr == start_addr) end_addr_size -= start_addr_offset;
// Disable CPU interruptions
_CPU_ISR_Disable(level);
mask32(HW_GPIO1OUT, GP_EEP_CLK, 0);
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
eeprom_delay();
// EWEN - Enable programming commands
mask32(HW_GPIO1OUT, 0, GP_EEP_CS);
seeprom_send_bits(0x4FF, 11);
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
eeprom_delay();
for(u16 i = start_addr; i <= end_addr; i++)
{
if (cur_offset >= size) break;
// Copy data to write from source buffer
if ((i == start_addr && start_addr_offset != 0) || (i == end_addr && end_addr_size != HW_SEEPROM_BLK_SIZE))
{
// Read data from SEEPROM to handle unaligned writes
// Start command cycle
mask32(HW_GPIO1OUT, 0, GP_EEP_CS);
// Send read command + address
seeprom_send_bits(0x600 | i, 11);
// Receive data
*((u16*)val) = seeprom_recv_bits(16);
// End of command cycle
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
eeprom_delay();
if (i == start_addr && start_addr_offset != 0)
{
// Handle unaligned write at start address
memcpy(val + start_addr_offset, ptr + cur_offset, HW_SEEPROM_BLK_SIZE - start_addr_offset);
cur_offset += (HW_SEEPROM_BLK_SIZE - start_addr_offset);
} else {
// Handle unaligned write at end address
memcpy(val, ptr + cur_offset, end_addr_size);
cur_offset += end_addr_size;
}
} else {
// Normal write
memcpy(val, ptr + cur_offset, HW_SEEPROM_BLK_SIZE);
cur_offset += HW_SEEPROM_BLK_SIZE;
}
// Start command cycle
mask32(HW_GPIO1OUT, 0, GP_EEP_CS);
// Send write command + address
seeprom_send_bits(0x500 | i, 11);
// Send data
seeprom_send_bits(*((u16*)val), 16);
// End of command cycle
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
eeprom_delay();
// Wait until SEEPROM is ready (write cycle is self-timed so no clocking needed)
mask32(HW_GPIO1OUT, 0, GP_EEP_CS);
do {
eeprom_delay();
} while(!(read32(HW_GPIO1IN) & GP_EEP_MISO));
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
eeprom_delay();
}
// EWDS - Disable programming commands
mask32(HW_GPIO1OUT, 0, GP_EEP_CS);
seeprom_send_bits(0x400, 11);
mask32(HW_GPIO1OUT, GP_EEP_CS, 0);
eeprom_delay();
// Enable CPU interruptions
_CPU_ISR_Restore(level);
return cur_offset;
}
#endif

59
source/mini_seeprom.h Normal file
View File

@ -0,0 +1,59 @@
/*
mini - a Free Software replacement for the Nintendo/BroadOn IOS.
SEEPROM support
Copyright (C) 2008, 2009 Sven Peter <svenpeter@gmail.com>
# This code is licensed to you under the terms of the GNU GPL, version 2;
# see http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
*/
#ifndef __MINI_SEEPROM_H__
#define __MINI_SEEPROM_H__
#define SEEPROM_SIZE 0x100
typedef struct
{
union {
struct {
u8 boot2version;
u8 unknown1;
u8 unknown2;
u8 pad;
u32 update_tag;
};
u8 data[8];
};
u16 checksum; // sum of data[] elements?
} __attribute__((packed)) eep_boot2_ctr_t;
typedef struct
{
union {
u32 nand_gen; // matches offset 0x8 in nand SFFS blocks
u8 data[4];
};
u16 checksum; // sum of data[] elements?
} __attribute__((packed)) eep_nand_ctr_t;
struct SEEPROM
{
u32 ms_id; // 0x00000002
u32 ca_id; // 0x00000001
u32 ng_key_id;
u8 ng_sig[60];
eep_boot2_ctr_t boot2_counters[2];
eep_nand_ctr_t nand_counters[3]; // current slot rotates on each write
u8 pad0[6];
u8 korean_key[16];
u8 pad1[116];
u16 prng_seed[2]; // u32 with lo word stored first, incremented every time IOS starts. Used with the PRNG key to setup IOS's PRNG (syscalls 73/74 etc.)
u8 pad2[4];
};
_Static_assert(sizeof(struct SEEPROM) == SEEPROM_SIZE, "SEEPROM struct size incorrect!");
u16 seeprom_read(void *dst, u16 offset, u16 size);
// u16 seeprom_write(const void *src, u16 offset, u16 size);
#endif /* __MINI_SEEPROM_H__ */

View File

@ -1,10 +1,10 @@
#include <stdio.h>
#include <stdarg.h>
#include <ogcsys.h>
#include <malloc.h>
#include <string.h>
#include "nand.h"
#include "malloc.h"
#include "fileops.h"
/* Buffer */
@ -118,7 +118,7 @@ u8* NANDReadFromFile(const char* path, u32 offset, u32 length, u32* size)
if (!length)
length = IOS_Seek(fd, 0, SEEK_END);
u8* data = (u8*)memalign(0x40, length);
u8* data = memalign64(length);
if (!data)
{
*size = 0;
@ -155,9 +155,10 @@ u8* NANDLoadFile(const char* path, u32* size)
s32 NANDWriteFileSafe(const char* path, u8* data, u32 size)
{
char tmpPath[ISFS_MAXPATH] ATTRIBUTE_ALIGN(64);
NANDInitialize();
char* tmpPath = (char*)memalign(0x40, ISFS_MAXPATH);
u32 i;
for (i = strlen(path); i > 0; --i)
@ -175,43 +176,29 @@ s32 NANDWriteFileSafe(const char* path, u8* data, u32 size)
ret = ISFS_CreateFile(tmpPath, 0, 3, 3, 3);
if (ret < 0)
{
free(tmpPath);
return ret;
}
}
else
else if (ret < 0)
{
if (ret < 0)
{
free(tmpPath);
return ret;
}
return ret;
}
s32 fd = IOS_Open(tmpPath, 2);
if (fd < 0)
{
free(tmpPath);
return fd;
}
ret = IOS_Write(fd, data, size);
IOS_Close(fd);
if (ret != size)
{
free(tmpPath);
return ret - 3;
}
if (strcmp(tmpPath, path))
ret = ISFS_Rename(tmpPath, path);
else
ret = 0;
free(tmpPath);
return ret;
}

67
source/otp.c Normal file
View File

@ -0,0 +1,67 @@
#include <ogc/machine/processor.h>
#include <unistd.h>
#include <string.h>
#include "otp.h"
#define HW_OTP_COMMAND (*(vu32*)0xCD8001EC)
#define HW_OTP_DATA (*(vu32*)0xCD8001F0)
#define HW_OTP_BLK_SIZE 4
#define HW_OTP_BLK_CNT (OTP_SIZE / HW_OTP_BLK_SIZE)
u8 otp_read(void *dst, u8 offset, u8 size)
{
if (!dst || offset >= OTP_SIZE || !size || (offset + size) > OTP_SIZE) return 0;
u8 cur_offset = 0;
u8 *ptr = (u8*)dst;
u8 val[HW_OTP_BLK_SIZE] = {0};
// Calculate block offsets and sizes
u8 start_addr = (offset / HW_OTP_BLK_SIZE);
u8 start_addr_offset = (offset % HW_OTP_BLK_SIZE);
u8 end_addr = ((offset + size) / HW_OTP_BLK_SIZE);
u8 end_addr_size = ((offset + size) % HW_OTP_BLK_SIZE);
if (!end_addr_size)
{
end_addr--;
end_addr_size = HW_OTP_BLK_SIZE;
}
if (end_addr == start_addr) end_addr_size -= start_addr_offset;
for(u8 i = start_addr; i <= end_addr; i++)
{
if (cur_offset >= size) break;
// Send command + address
HW_OTP_COMMAND = (0x80000000 | i);
// Receive data
*((u32*)val) = HW_OTP_DATA;
// Copy read data to destination buffer
if (i == start_addr && start_addr_offset != 0)
{
// Handle unaligned read at start address
memcpy(ptr + cur_offset, val + start_addr_offset, HW_OTP_BLK_SIZE - start_addr_offset);
cur_offset += (HW_OTP_BLK_SIZE - start_addr_offset);
} else
if (i == end_addr && end_addr_size != HW_OTP_BLK_SIZE)
{
// Handle unaligned read at end address
memcpy(ptr + cur_offset, val, end_addr_size);
cur_offset += end_addr_size;
} else {
// Normal read
memcpy(ptr + cur_offset, val, HW_OTP_BLK_SIZE);
cur_offset += HW_OTP_BLK_SIZE;
}
}
return cur_offset;
}

29
source/otp.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef __OTP_H__
#define __OTP_H__
#define OTP_SIZE 0x80
typedef struct
{
u8 boot1_hash[20];
u8 common_key[16];
u8 ng_id[4];
union { // first two bytes of nand_hmac overlap last two bytes of ng_priv
struct {
u8 ng_priv[30];
u8 _wtf1[18];
};
struct {
u8 _wtf2[28];
u8 nand_hmac[20];
};
};
u8 nand_key[16];
u8 rng_key[16];
u32 unk1;
u32 unk2; // 0x00000007
} otp_t;
u8 otp_read(void *dst, u8 offset, u8 size);
#endif /* __OTP_H__ */

View File

@ -1,14 +1,18 @@
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <ogcsys.h>
#include <ogc/es.h>
#include "sys.h"
#include "aes.h"
#include "nand.h"
#include "mini_seeprom.h"
#include "malloc.h"
#include "mload.h"
#include "ehcmodule_elf.h"
/* Constants */
#define CERTS_LEN 0x280
#define CERTS_LEN 0x280
/* Variables */
static const char certs_fs[] ATTRIBUTE_ALIGN(32) = "/sys/cert.sys";
@ -29,75 +33,104 @@ void __Sys_PowerCallback(void)
Sys_Shutdown();
}
bool isIOSstub(u8 ios_number)
{
u32 tmd_size;
tmd_view *ios_tmd;
if((boot2version >= 5) && ( ios_number == 202 || ios_number == 222 || ios_number == 223 || ios_number == 224)) return true;
ES_GetTMDViewSize(0x0000000100000000ULL | ios_number, &tmd_size);
if (!tmd_size)
{
//getting size failed. invalid or fake tmd for sure!
//gprintf("failed to get tmd for ios %d\n",ios_number);
return true;
}
ios_tmd = (tmd_view *)memalign( 32, (tmd_size+31)&(~31) );
if(!ios_tmd)
{
//gprintf("failed to mem align the TMD struct!\n");
return true;
}
memset(ios_tmd , 0, tmd_size);
ES_GetTMDView(0x0000000100000000ULL | ios_number, (u8*)ios_tmd , tmd_size);
//gprintf("IOS %d is rev %d(0x%x) with tmd size of %u and %u contents\n",ios_number,ios_tmd->title_version,ios_tmd->title_version,tmd_size,ios_tmd->num_contents);
/*Stubs have a few things in common:
- title version : it is mostly 65280 , or even better : in hex the last 2 digits are 0.
example : IOS 60 rev 6400 = 0x1900 = 00 = stub
- exception for IOS21 which is active, the tmd size is 592 bytes (or 140 with the views)
- the stub ios' have 1 app of their own (type 0x1) and 2 shared apps (type 0x8001).
eventho the 00 check seems to work fine , we'll only use other knowledge as well cause some
people/applications install an ios with a stub rev >_> ...*/
u8 Version = ios_tmd->title_version;
if((boot2version >= 5) && (ios_number == 249 || ios_number == 250) && (Version < 18)) return true;
if(( ios_number == 202 || ios_number == 222 || ios_number == 223 || ios_number == 224) && (Version < 4)) return true;
//version now contains the last 2 bytes. as said above, if this is 00, its a stub
if ( Version == 0 )
{
if ( ( ios_tmd->num_contents == 3) && (ios_tmd->contents[0].type == 1 && ios_tmd->contents[1].type == 0x8001 && ios_tmd->contents[2].type == 0x8001) )
{
//gprintf("IOS %d is a stub\n",ios_number);
free(ios_tmd);
return true;
}
else
{
//gprintf("IOS %d is active\n",ios_number);
free(ios_tmd);
return false;
}
}
//gprintf("IOS %d is active\n",ios_number);
free(ios_tmd);
return false;
}
bool tmdIsStubIOS(tmd* p_tmd)
{
return
p_tmd->sys_version >> 32 == 0
&& p_tmd->num_contents == 3
&& p_tmd->contents[0].type == 0x0001
&& p_tmd->contents[1].type == 0x8001
&& p_tmd->contents[2].type == 0x8001;
}
bool ES_CheckHasKoreanKey(void)
{
aeskey korean_key;
unsigned char iv[16] = {};
__attribute__ ((__aligned__(0x10)))
unsigned char data[16] = {0x56, 0x52, 0x6f, 0x63, 0xa1, 0x2c, 0xd1, 0x32, 0x07, 0x99, 0x82, 0x3b, 0x1b, 0x08, 0x17, 0xd0};
if (seeprom_read(korean_key, offsetof(struct SEEPROM, korean_key), sizeof(korean_key)) != sizeof(korean_key))
return false;
AES_Decrypt(korean_key, 0x10, iv, 0x10, data, data, sizeof(data));
// return (!strcmp((char*) data, "thepikachugamer")) Just remembered that this is how the Trucha bug came to be
return (!memcmp(data, "thepikachugamer", sizeof(data)));
}
bool isIOSstub(u8 ios_number)
{
u32 tmd_size = 0;
tmd_view *ios_tmd;
if ((boot2version >= 5) && (ios_number == 202 || ios_number == 222 || ios_number == 223 || ios_number == 224))
return true;
ES_GetTMDViewSize(0x0000000100000000ULL | ios_number, &tmd_size);
if (!tmd_size)
{
// getting size failed. invalid or fake tmd for sure!
// gprintf("failed to get tmd for ios %d\n",ios_number);
return true;
}
ios_tmd = memalign32(tmd_size);
if (!ios_tmd)
{
// gprintf("failed to mem align the TMD struct!\n");
return true;
}
memset(ios_tmd, 0, tmd_size);
ES_GetTMDView(0x0000000100000000ULL | ios_number, (u8 *)ios_tmd, tmd_size);
// gprintf("IOS %d is rev %d(0x%x) with tmd size of %u and %u contents\n",ios_number,ios_tmd->title_version,ios_tmd->title_version,tmd_size,ios_tmd->num_contents);
/*Stubs have a few things in common:
- title version : it is mostly 65280 , or even better : in hex the last 2 digits are 0.
example : IOS 60 rev 6400 = 0x1900 = 00 = stub
- exception for IOS21 which is active, the tmd size is 592 bytes (or 140 with the views)
- the stub ios' have 1 app of their own (type 0x1) and 2 shared apps (type 0x8001).
eventho the 00 check seems to work fine , we'll only use other knowledge as well cause some
people/applications install an ios with a stub rev >_> ...*/
u8 Version = ios_tmd->title_version;
if ((boot2version >= 5) && (ios_number == 249 || ios_number == 250) && (Version < 18))
return true;
if ((ios_number == 202 || ios_number == 222 || ios_number == 223 || ios_number == 224) && (Version < 4))
return true;
// version now contains the last 2 bytes. as said above, if this is 00, its a stub
if (Version == 0)
{
if ((ios_tmd->num_contents == 3) && (ios_tmd->contents[0].type == 1 && ios_tmd->contents[1].type == 0x8001 && ios_tmd->contents[2].type == 0x8001))
{
// gprintf("IOS %d is a stub\n",ios_number);
free(ios_tmd);
return true;
}
else
{
// gprintf("IOS %d is active\n",ios_number);
free(ios_tmd);
return false;
}
}
// gprintf("IOS %d is active\n",ios_number);
free(ios_tmd);
return false;
}
bool loadIOS(int ios)
{
if(isIOSstub(ios)) return false;
if (isIOSstub(ios))
return false;
mload_close();
if(IOS_ReloadIOS(ios)>=0)
if (IOS_ReloadIOS(ios) >= 0)
{
if (IOS_GetVersion() != 249 && IOS_GetVersion() != 250)
{
if (mload_init() >= 0)
{
data_elf my_data_elf;
mload_elf((void *) ehcmodule_elf, &my_data_elf);
mload_elf((void *)ehcmodule_elf, &my_data_elf);
mload_run_thread(my_data_elf.start, my_data_elf.stack, my_data_elf.size_stack, 0x47);
}
}
@ -125,17 +158,20 @@ void Sys_Reboot(void)
void Sys_Shutdown(void)
{
/* Poweroff console */
if(CONF_GetShutdownMode() == CONF_SHUTDOWN_IDLE) {
if (CONF_GetShutdownMode() == CONF_SHUTDOWN_IDLE)
{
s32 ret;
/* Set LED mode */
ret = CONF_GetIdleLedMode();
if(ret >= 0 && ret <= 2)
if (ret >= 0 && ret <= 2)
STM_SetLedMode(ret);
/* Shutdown to idle */
STM_ShutdownToIdle();
} else {
}
else
{
/* Shutdown to standby */
STM_ShutdownToStandby();
}
@ -143,26 +179,33 @@ void Sys_Shutdown(void)
void Sys_LoadMenu(void)
{
/* SYSCALL(exit) does all of this already */
exit(0);
/* Also the code gcc generated for this looks really painful */
#if 0
int HBC = 0;
char * sig = (char *)0x80001804;
if( sig[0] == 'S' &&
sig[1] == 'T' &&
sig[2] == 'U' &&
sig[3] == 'B' &&
sig[4] == 'H' &&
sig[5] == 'A' &&
sig[6] == 'X' &&
sig[7] == 'X')
char *sig = (char *)0x80001804;
if (sig[0] == 'S' &&
sig[1] == 'T' &&
sig[2] == 'U' &&
sig[3] == 'B' &&
sig[4] == 'H' &&
sig[5] == 'A' &&
sig[6] == 'X' &&
sig[7] == 'X')
{
HBC=1; // Exit to HBC
HBC = 1; // Exit to HBC
}
/* Homebrew Channel stub */
if (HBC == 1) {
if (HBC == 1)
{
exit(0);
}
/* Return to the Wii system menu */
SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0);
#endif
}
s32 Sys_GetCerts(signed_blob **certs, u32 *len)
@ -183,14 +226,101 @@ s32 Sys_GetCerts(signed_blob **certs, u32 *len)
IOS_Close(fd);
/* Set values */
if (ret > 0) {
if (ret > 0)
{
*certs = certificates;
*len = sizeof(certificates);
*len = sizeof(certificates);
}
return ret;
}
s32 Sys_GetSharedContents(SharedContent** out, u32* count)
{
if (!out || !count) return false;
int ret = 0;
u32 size;
SharedContent* buf = (SharedContent*)NANDLoadFile("/shared1/content.map", &size);
if (!buf)
return (s32)size;
else if (size % sizeof(SharedContent) != 0) {
free(buf);
return -996;
}
*out = buf;
*count = size / sizeof(SharedContent);
return 0;
}
bool Sys_SharedContentPresent(tmd_content* content, SharedContent shared[], u32 count)
{
if (!shared || !content || !count)
return false;
if (!(content->type & 0x8000))
return false;
for (SharedContent* s_content = shared; s_content < shared + count; s_content++)
{
if (memcmp(s_content->hash, content->hash, sizeof(sha1)) == 0)
return true;
}
return false;
}
bool Sys_GetcIOSInfo(int IOS, cIOSInfo* out)
{
int ret;
u64 titleID = 0x0000000100000000ULL | IOS;
ATTRIBUTE_ALIGN(0x20) char path[ISFS_MAXPATH];
u32 size;
cIOSInfo* buf = NULL;
u32 view_size = 0;
if (ES_GetTMDViewSize(titleID, &view_size) < 0)
return false;
tmd_view* view = memalign32(view_size);
if (!view)
return false;
if (ES_GetTMDView(titleID, (u8*)view, view_size) < 0)
goto fail;
tmd_view_content* content0 = NULL;
for (tmd_view_content* con = view->contents; con < view->contents + view->num_contents; con++)
{
if (con->index == 0)
content0 = con;
}
if (!content0)
goto fail;
sprintf(path, "/title/00000001/%08x/content/%08x.app", IOS, content0->cid);
buf = (cIOSInfo*)NANDLoadFile(path, &size);
if (!buf || size != 0x40 || buf->hdr_magic != CIOS_INFO_MAGIC || buf->hdr_version != CIOS_INFO_VERSION)
goto fail;
*out = *buf;
free(view);
free(buf);
return true;
fail:
free(view);
free(buf);
return false;
}
void SetPRButtons(bool enabled)
{
gDisablePRButtons = !enabled;

View File

@ -1,15 +1,51 @@
#ifndef _SYS_H_
#define _SYS_H_
/* /shared1/content.map entry */
typedef struct
{
char filename[8];
sha1 hash;
} ATTRIBUTE_PACKED SharedContent;
/* "cIOS build tag" */
enum
{
CIOS_INFO_MAGIC = 0x1EE7C105,
CIOS_INFO_VERSION = 1
};
typedef struct
{
u32 hdr_magic; // 0x1EE7C105
u32 hdr_version; // 1
u32 cios_version; // Eg. 11
u32 ios_base; // Eg. 60
char name[16];
char cios_version_str[16];
char _padding[16];
} cIOSInfo;
// _Static_assert(sizeof(cIOSInfo) == 0x40, "Incorrect cIOSInfo struct size, do i really need to pack this..?");
#define IS_WIIU (*(vu16*)0xCD8005A0 == 0xCAFE)
extern u32 boot2version;
/* Prototypes */
bool isIOSstub(u8 ios_number);
bool tmdIsStubIOS(tmd*);
bool loadIOS(int ios);
bool ES_CheckHasKoreanKey(void);
void Sys_Init(void);
void Sys_Reboot(void);
void Sys_Shutdown(void);
void Sys_LoadMenu(void);
s32 Sys_GetCerts(signed_blob **, u32 *);
bool Sys_GetcIOSInfo(int IOS, cIOSInfo*);
s32 Sys_GetSharedContents(SharedContent** out, u32* count);
bool Sys_SharedContentPresent(tmd_content* content, SharedContent shared[], u32 count);
void SetPRButtons(bool enabled);
#endif

View File

@ -2,12 +2,14 @@
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <malloc.h>
#include <ogcsys.h>
#include <ogc/es.h>
#include "sha1.h"
#include "aes.h"
#include "utils.h"
#include "otp.h"
#include "malloc.h"
s32 Title_ZeroSignature(signed_blob *p_sig)
{
@ -79,7 +81,7 @@ s32 Title_GetList(u64 **outbuf, u32 *outlen)
{
u64 *titles = NULL;
u32 len, nb_titles;
u32 nb_titles;
s32 ret;
/* Get number of titles */
@ -87,11 +89,9 @@ s32 Title_GetList(u64 **outbuf, u32 *outlen)
if (ret < 0)
return ret;
/* Calculate buffer lenght */
len = round_up(sizeof(u64) * nb_titles, 32);
/* Allocate memory */
titles = memalign(32, len);
titles = memalign32(nb_titles * sizeof(u64));
if (!titles)
return -1;
@ -126,7 +126,7 @@ s32 Title_GetTicketViews(u64 tid, tikview **outbuf, u32 *outlen)
return ret;
/* Allocate memory */
views = (tikview *)memalign(32, sizeof(tikview) * nb_views);
views = memalign32(sizeof(tikview) * nb_views);
if (!views)
return -1;
@ -161,7 +161,7 @@ s32 Title_GetTMD(u64 tid, signed_blob **outbuf, u32 *outlen)
return ret;
/* Allocate memory */
p_tmd = memalign(32, round_up(len, 32));
p_tmd = memalign32(len);
if (!p_tmd)
return -1;
@ -290,7 +290,7 @@ s32 Title_GetIOSVersions(u8 **outbuf, u32 *outlen)
}
/* Allocate memory */
buffer = (u8 *)memalign(32, cnt);
buffer = memalign32(cnt);
if (!buffer) {
ret = -1;
goto out;
@ -318,3 +318,26 @@ out:
return ret;
}
__attribute__((aligned(0x10)))
aeskey WiiCommonKey, vWiiCommonKey;
void Title_SetupCommonKeys(void)
{
static bool keys_ok = false;
if (keys_ok)
return;
// Grab the Wii common key...
otp_read(WiiCommonKey, offsetof(otp_t, common_key), sizeof(aeskey));
// ...and decrypt the vWii common key with it.
static const unsigned char vwii_key_enc_bin[0x10] = { 0x6e, 0x18, 0xdb, 0x23, 0x84, 0x7c, 0xba, 0x6c, 0x19, 0x31, 0xa4, 0x17, 0x9b, 0xaf, 0x8e, 0x09 };
unsigned char iv[0x10] = {};
memcpy(vWiiCommonKey, vwii_key_enc_bin, sizeof(vwii_key_enc_bin));
AES_Decrypt(WiiCommonKey, sizeof(aeskey), iv, sizeof(iv), vWiiCommonKey, vWiiCommonKey, sizeof(aeskey));
keys_ok = true;
return;
};

View File

@ -1,8 +1,13 @@
#ifndef _TITLE_H_
#define _TITLE_H_
#include <ogc/es.h>
/* Constants */
#define BLOCK_SIZE 1024
#define BLOCK_SIZE 0x4000
/* Variables */
extern aeskey WiiCommonKey, vWiiCommonKey;
/* Prototypes */
s32 Title_ZeroSignature(signed_blob *);
@ -15,5 +20,6 @@ s32 Title_GetVersion(u64, u16 *);
s32 Title_GetSysVersion(u64, u64 *);
s32 Title_GetSize(u64, u32 *);
s32 Title_GetIOSVersions(u8 **, u32 *);
void Title_SetupCommonKeys(void);
#endif

View File

@ -8,12 +8,15 @@
#include <wiidrc/wiidrc.h>
#include "sys.h"
#include "title.h"
#include "aes.h"
#include "gui.h"
#include "menu.h"
#include "restart.h"
#include "sys.h"
#include "video.h"
#include "wpad.h"
#include "wkb.h"
#include "fat.h"
#include "nand.h"
#include "globals.h"
@ -179,8 +182,12 @@ int main(int argc, char **argv)
Wpad_Init();
PAD_Init();
WiiDRC_Init();
WKB_Initialize();
WIILIGHT_Init();
AES_Init();
Title_SetupCommonKeys();
/* Print disclaimer */
//Disclaimer();

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +1,150 @@
/*
* Most of this code was adapted from Priiloader.
* https://github.com/DacoTaco/priiloader/blob/master/tools/DacosLove/source/Input.cpp
*/
#include <stdlib.h>
#include <unistd.h>
#include <ogc/lwp.h>
#include <wiiuse/wpad.h>
#include "wkb.h"
/*
s32 USBKeyboard_Open(const eventcallback cb);
void USBKeyboard_Close(void);
bool USBKeyboard_IsConnected(void);
s32 USBKeyboard_Scan(void);
s32 USBKeyboard_SetLed(const USBKeyboard_led led, bool on);
s32 USBKeyboard_ToggleLed(const USBKeyboard_led led);
*/
s32 WkbInit(void)
enum
{
s32 retval = 0;
/* Configuration i guess */
WKB_THREAD_PRIORITY = 0x7F,
WKB_THREAD_STACK = 0x4000,
WKB_THREAD_UDELAY = 400,
WKB_ANIMATION_UDELAY= 250000,
retval = USBKeyboard_Initialize();
/* The keys themselves */
WKB_ARROW_UP = 0x52,
WKB_ARROW_DOWN = 0x51,
WKB_ARROW_LEFT = 0x50,
WKB_ARROW_RIGHT = 0x4F,
return (retval);
WKB_SPACEBAR = 0x2C,
WKB_ENTER = 0x28,
WKB_NUMPAD_ENTER = 0x58,
WKB_BACKSPACE = 0x2A,
WKB_DELETE = 0x4C,
WKB_ESCAPE = 0x29,
WKB_HOME = 0x4A,
} // WkbInit
WKB_KEY_PLUS = 0x2E,
WKB_KEY_MINUS = 0x2D,
s32 WkbDeInit(void)
WKB_KEY_X = 0x1B,
WKB_KEY_Y = 0x1C,
WKB_KEY_1 = 0x1E,
WKB_KEY_2 = 0x1F,
};
static lwp_t WKBThreadHandle = LWP_THREAD_NULL;
static volatile bool WKBThreadActive;
static u16 WKBButtonsPressed;
static void WKBEventHandler(USBKeyboard_event evt)
{
s32 retval = 0;
// OSReport("Keyboard event: {%i, 0x%02hhx}", evt.type, evt.keyCode);
retval = USBKeyboard_Deinitialize();
if (!(evt.type == USBKEYBOARD_PRESSED || evt.type == USBKEYBOARD_RELEASED))
return;
return (retval);
u16 button;
switch (evt.keyCode)
{
/*
* Maybe I should create an array with a map of keycodes to the corresponding WPAD buttons.
* Like there's just this u16 WKBKeyMap[0x100] somewhere here and I can just say button = WKBKeyMap[evt.keyCode];
*/
case WKB_ENTER:
case WKB_NUMPAD_ENTER: button = WPAD_BUTTON_A; break;
case WKB_BACKSPACE: button = WPAD_BUTTON_B; break;
} // WkbDeInit
case WKB_ARROW_UP: button = WPAD_BUTTON_UP; break;
case WKB_ARROW_DOWN: button = WPAD_BUTTON_DOWN; break;
case WKB_ARROW_LEFT: button = WPAD_BUTTON_LEFT; break;
case WKB_ARROW_RIGHT: button = WPAD_BUTTON_RIGHT; break;
u32 WkbWaitKey (void)
case WKB_ESCAPE:
case WKB_HOME: button = WPAD_BUTTON_HOME; break;
case WKB_KEY_PLUS: button = WPAD_BUTTON_PLUS; break;
case WKB_KEY_MINUS: button = WPAD_BUTTON_MINUS; break;
case WKB_KEY_1: button = WPAD_BUTTON_1; break;
case WKB_KEY_2: button = WPAD_BUTTON_2; break;
default: return;
}
if (evt.type == USBKEYBOARD_PRESSED)
WKBButtonsPressed |= button;
else
WKBButtonsPressed &= ~button;
}
static void* WKBThread(__attribute__((unused)) void* arg)
{
u32 retval = 0;
while (WKBThreadActive)
{
/*
* Despite having a return type of s32, USBKeyboard_Open() returns 1 if it was successful, rather than 0.
* So this statement right here will check if a USB keyboard was detected, and if not, try open it again.
*/
if (!USBKeyboard_IsConnected() && USBKeyboard_Open(WKBEventHandler) == true)
{
/*
* And once it does open it successfully:
*
* "wake up the keyboard by sending it a command.
* im looking at you, bastard LINQ keyboard."
* - https://github.com/DacoTaco/priiloader/blob/master/tools/DacosLove/source/Input.cpp#L93-L94
*
* Just thought i would make this a fun lil animation :)
*/
for (int led = 0; led < 3; led++) { USBKeyboard_SetLed(led, true); usleep(WKB_ANIMATION_UDELAY); }
}
// Stub
return (retval);
USBKeyboard_Scan();
usleep(WKB_THREAD_UDELAY);
}
} // WkbWaitKey
// for (int led = 3; led; led--) { USBKeyboard_SetLed(led, false); usleep(WKB_ANIMATION_UDELAY); }
return NULL;
}
void WKB_Initialize(void)
{
USB_Initialize();
USBKeyboard_Initialize();
WKBThreadActive = true;
LWP_CreateThread(&WKBThreadHandle, WKBThread, NULL, NULL, WKB_THREAD_STACK, WKB_THREAD_PRIORITY);
atexit(WKB_Deinitialize);
}
void WKB_Deinitialize(void)
{
WKBThreadActive = false;
usleep(WKB_THREAD_UDELAY);
USBKeyboard_Close();
USBKeyboard_Deinitialize();
if (WKBThreadHandle != LWP_THREAD_NULL)
LWP_JoinThread(WKBThreadHandle, NULL);
WKBThreadHandle = LWP_THREAD_NULL;
}
u16 WKB_GetButtons(void)
{
u16 buttons = WKBButtonsPressed;
WKBButtonsPressed = 0;
return buttons;
}
//void Wpad_Disconnect(void);
//u32 Wpad_GetButtons(void);

View File

@ -5,15 +5,14 @@
//#include <stdlib.h>
//#include <string.h>
//#include <malloc.h>
#include <ogcsys.h> // u8, u16, etc...
#include <gctypes.h> // u8, u16, etc...
#include <wiikeyboard/keyboard.h>
#include <wiikeyboard/usbkeyboard.h>
/* Prototypes */
s32 WkbInit(void);
u32 WkbWaitKey (void);
//void Wpad_Disconnect(void);
//u32 Wpad_GetButtons(void);
void WKB_Initialize(void);
void WKB_Deinitialize(void);
u16 WKB_GetButtons(void);
#endif

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<app version="1">
<name>YAWM ModMii Edition</name>
<version>1.0</version>
<version>1.1</version>
<coder>Various</coder>
<short_description>Manage WADs and Launch Apps</short_description>
<long_description>Yet Another Wad Manager ModMii Edition (yawmME)