mirror of
https://github.com/modmii/SysCheck-ModMii-Edition.git
synced 2024-11-22 08:09:19 +01:00
-Fixed all the stuff I messed up with the last commit
-Added missing libraries -Removed the need to reload the runtime IOS -Fixed incorrect runtime IOS type -Added some extra delay when scrolling -SysCheck directory is created if it doesn't exist -Added skipped IOS's to list -Fixed vWii cIOS's sometimes showing as regular cIOS's -Fixed crash when the language is set to French TODO: -Finish working on fat mounting. It's probably broken now. -Integrate vIOS check by version number
This commit is contained in:
parent
1e4ec18746
commit
af1b05fa5f
@ -75,6 +75,7 @@ const char* BUT_ConfirmUpload;
|
|||||||
const char* TXT_Upload;
|
const char* TXT_Upload;
|
||||||
const char* BUT_OK;
|
const char* BUT_OK;
|
||||||
const char* TXT_OriginalRegion;
|
const char* TXT_OriginalRegion;
|
||||||
|
const char* TXT_IOSSkipped;
|
||||||
char TXT_ReportDate[100];
|
char TXT_ReportDate[100];
|
||||||
char MSG_Buffer[1024];
|
char MSG_Buffer[1024];
|
||||||
char MSG_Buffer2[1024];
|
char MSG_Buffer2[1024];
|
||||||
|
@ -9,10 +9,8 @@
|
|||||||
#define MEM_PROT (MEM_REG_BASE + 0x20a)
|
#define MEM_PROT (MEM_REG_BASE + 0x20a)
|
||||||
#define HOLLYWOOD_VERSION (*(vu32*)0x80003138)
|
#define HOLLYWOOD_VERSION (*(vu32*)0x80003138)
|
||||||
#define LOADER_STUB (vu32*)0x80001800
|
#define LOADER_STUB (vu32*)0x80001800
|
||||||
#define IOS_START (*((vu32*)0x80003130))
|
#define IOS_TOP (*((vu32*)0x80003130))
|
||||||
#define IOS_END (*((vu32*)0x80003134))
|
#define IS_WII_U ((*(vu32*)(0xCd8005A0) >> 16 ) == 0xCAFE)
|
||||||
//#define IS_WII_U ((*(vu32*)(0xCd8005A0) >> 16 ) == 0xCAFE)
|
|
||||||
#define IS_WII_U ((*(vu16*)(0xCd8005A2)) == 0xCAFE)
|
|
||||||
#define MAX_ELEMENTS(x) ((sizeof((x))) / (sizeof((x)[0])))
|
#define MAX_ELEMENTS(x) ((sizeof((x))) / (sizeof((x)[0])))
|
||||||
|
|
||||||
// Turn upper and lower into a full title ID
|
// Turn upper and lower into a full title ID
|
||||||
@ -122,8 +120,14 @@ typedef struct _U8Entry
|
|||||||
};
|
};
|
||||||
} __attribute__( ( packed ) ) U8Entry;
|
} __attribute__( ( packed ) ) U8Entry;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const u32 titleID;
|
||||||
|
const s32 revision;
|
||||||
|
} vIOSdb_t;
|
||||||
|
|
||||||
extern const char *Regions[];
|
extern const char *Regions[];
|
||||||
extern u8 sysMenuInfoContent;
|
extern u8 sysMenuInfoContent;
|
||||||
|
extern const vIOSdb_t vIOSdb[];
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C"
|
||||||
|
@ -21,6 +21,7 @@ typedef struct {
|
|||||||
bool debug;
|
bool debug;
|
||||||
int skipIOSlist[512];
|
int skipIOSlist[512];
|
||||||
int skipIOScnt;
|
int skipIOScnt;
|
||||||
|
bool USB;
|
||||||
} arguments_t;
|
} arguments_t;
|
||||||
|
|
||||||
extern arguments_t arguments;
|
extern arguments_t arguments;
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
#define _UPDATE_H_
|
#define _UPDATE_H_
|
||||||
|
|
||||||
/* Constants */
|
/* Constants */
|
||||||
|
//#define REVISION 0 // For testing updateApp function
|
||||||
#define REVISION 41
|
#define REVISION 41
|
||||||
#define PATH "sd:/apps/SysCheckHDE/"
|
|
||||||
|
|
||||||
s32 updateApp(void);
|
s32 updateApp(void);
|
||||||
|
|
||||||
|
BIN
libs/lib/libCheckRegion.a
Normal file
BIN
libs/lib/libCheckRegion.a
Normal file
Binary file not shown.
BIN
libs/lib/libfreetype.a
Normal file
BIN
libs/lib/libfreetype.a
Normal file
Binary file not shown.
BIN
libs/lib/libgrrlib.a
Normal file
BIN
libs/lib/libgrrlib.a
Normal file
Binary file not shown.
BIN
libs/lib/libjpeg.a
Normal file
BIN
libs/lib/libjpeg.a
Normal file
Binary file not shown.
BIN
libs/lib/libpng.a
Normal file
BIN
libs/lib/libpng.a
Normal file
Binary file not shown.
BIN
libs/lib/libpngu.a
Normal file
BIN
libs/lib/libpngu.a
Normal file
Binary file not shown.
BIN
libs/lib/libruntimeiospatch.a
Normal file
BIN
libs/lib/libruntimeiospatch.a
Normal file
Binary file not shown.
BIN
libs/lib/libz.a
Normal file
BIN
libs/lib/libz.a
Normal file
Binary file not shown.
@ -142,7 +142,6 @@ float GetSysMenuNintendoVersion(u32 sysVersion)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char GetSysMenuRegion(u32 sysVersion) {
|
char GetSysMenuRegion(u32 sysVersion) {
|
||||||
char SysMenuRegion = 'X';
|
|
||||||
switch(sysVersion)
|
switch(sysVersion)
|
||||||
{
|
{
|
||||||
case 1: //Pre-launch
|
case 1: //Pre-launch
|
||||||
@ -160,7 +159,7 @@ char GetSysMenuRegion(u32 sysVersion) {
|
|||||||
case 513: //4.3U
|
case 513: //4.3U
|
||||||
case 545:
|
case 545:
|
||||||
case 609:
|
case 609:
|
||||||
SysMenuRegion = 'U';
|
return 'U';
|
||||||
break;
|
break;
|
||||||
case 130: //2.0E
|
case 130: //2.0E
|
||||||
case 162: //2.1E
|
case 162: //2.1E
|
||||||
@ -177,7 +176,7 @@ char GetSysMenuRegion(u32 sysVersion) {
|
|||||||
case 514: //4.3E
|
case 514: //4.3E
|
||||||
case 546:
|
case 546:
|
||||||
case 610:
|
case 610:
|
||||||
SysMenuRegion = 'E';
|
return 'E';
|
||||||
break;
|
break;
|
||||||
case 128: //2.0J
|
case 128: //2.0J
|
||||||
case 192: //2.2J
|
case 192: //2.2J
|
||||||
@ -193,7 +192,7 @@ char GetSysMenuRegion(u32 sysVersion) {
|
|||||||
case 512: //4.3J
|
case 512: //4.3J
|
||||||
case 544:
|
case 544:
|
||||||
case 608:
|
case 608:
|
||||||
SysMenuRegion = 'J';
|
return 'J';
|
||||||
break;
|
break;
|
||||||
case 326: //3.3K
|
case 326: //3.3K
|
||||||
case 390: //3.5K
|
case 390: //3.5K
|
||||||
@ -201,10 +200,10 @@ char GetSysMenuRegion(u32 sysVersion) {
|
|||||||
case 54454: // mauifrog 4.1K
|
case 54454: // mauifrog 4.1K
|
||||||
case 486: //4.2K
|
case 486: //4.2K
|
||||||
case 518: //4.3K
|
case 518: //4.3K
|
||||||
SysMenuRegion = 'K';
|
return 'K';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return SysMenuRegion;
|
return 'X';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the system menu version from TMD
|
// Get the system menu version from TMD
|
||||||
|
@ -69,6 +69,8 @@ enum BPB
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const char FAT_SIG[3] = {'F', 'A', 'T'};
|
static const char FAT_SIG[3] = {'F', 'A', 'T'};
|
||||||
|
static bool sd_mounted = false;
|
||||||
|
static bool usb_mounted = false;
|
||||||
|
|
||||||
static bool _FAT_partition_isFAT(const DISC_INTERFACE* disc, sec_t startSector)
|
static bool _FAT_partition_isFAT(const DISC_INTERFACE* disc, sec_t startSector)
|
||||||
{
|
{
|
||||||
@ -150,20 +152,22 @@ int MountSD(void)
|
|||||||
fatUnmount("SD:/");
|
fatUnmount("SD:/");
|
||||||
|
|
||||||
// Mount first FAT partition
|
// Mount first FAT partition
|
||||||
if (fatMount("SD", &__io_wiisd, GetFATPartition(&__io_wiisd), CACHE, SECTORS)) return 1;
|
if (fatMount("SD", &__io_wiisd, GetFATPartition(&__io_wiisd), CACHE, SECTORS)) {
|
||||||
|
sd_mounted = true;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnmountSD(void)
|
void UnmountSD(void)
|
||||||
{
|
{
|
||||||
|
if (!sd_mounted) return;
|
||||||
// Close all open files write back the cache and then shutdown them
|
// Close all open files write back the cache and then shutdown them
|
||||||
fatUnmount("SD:/");
|
fatUnmount("SD:/");
|
||||||
}
|
}
|
||||||
|
|
||||||
int MountUSB(void)
|
int MountUSB(void)
|
||||||
{
|
{
|
||||||
char dirpath[256];
|
|
||||||
s32 ret;
|
s32 ret;
|
||||||
|
|
||||||
/* Initialize interface */
|
/* Initialize interface */
|
||||||
@ -177,8 +181,8 @@ int MountUSB(void)
|
|||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
/* Set root directory */
|
/* Set root directory */
|
||||||
sprintf(dirpath, "usb:/");
|
chdir("usb:/");
|
||||||
chdir(dirpath);
|
usb_mounted = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -186,6 +190,7 @@ int MountUSB(void)
|
|||||||
void UnmountUSB(void)
|
void UnmountUSB(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if(!usb_mounted) return;
|
||||||
/* Unmount device */
|
/* Unmount device */
|
||||||
fatUnmount("usb");
|
fatUnmount("usb");
|
||||||
|
|
||||||
|
@ -4,12 +4,11 @@
|
|||||||
#include "fatMounter.h"
|
#include "fatMounter.h"
|
||||||
#include "languages.h"
|
#include "languages.h"
|
||||||
|
|
||||||
|
|
||||||
// CONF_LANG_ENGLISH
|
|
||||||
// CONF_LANG_GERMAN
|
// CONF_LANG_GERMAN
|
||||||
// CONF_LANG_FRENCH
|
// CONF_LANG_FRENCH
|
||||||
// CONF_LANG_SPANISH
|
|
||||||
// CONF_LANG_ITALIAN
|
// CONF_LANG_ITALIAN
|
||||||
|
// CONF_LANG_SPANISH
|
||||||
|
// CONF_LANG_ENGLISH
|
||||||
|
|
||||||
// !!! Doesn't support special chars... !!!
|
// !!! Doesn't support special chars... !!!
|
||||||
// !!! Don't change the order of the parameters !!!
|
// !!! Don't change the order of the parameters !!!
|
||||||
@ -96,6 +95,7 @@ int initLanguages(struct tm today)
|
|||||||
TXT_Upload = "Bericht wird hochgeladen...";
|
TXT_Upload = "Bericht wird hochgeladen...";
|
||||||
BUT_OK = "OK";
|
BUT_OK = "OK";
|
||||||
TXT_OriginalRegion = " (urspruengliche Region: ";
|
TXT_OriginalRegion = " (urspruengliche Region: ";
|
||||||
|
TXT_IOSSkipped = "Ubersprungenen";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONF_LANG_FRENCH:
|
case CONF_LANG_FRENCH:
|
||||||
@ -123,7 +123,7 @@ int initLanguages(struct tm today)
|
|||||||
TXT_SysMenu = "Menu Systeme %1.1f%c (v%d)";
|
TXT_SysMenu = "Menu Systeme %1.1f%c (v%d)";
|
||||||
TXT_SysMenu2 = "Menu Systeme %s%s (v%d)";
|
TXT_SysMenu2 = "Menu Systeme %s%s (v%d)";
|
||||||
TXT_SysMenu3 = "Menu Systeme %1.1f%c (v%d, Info: v%d %s)";
|
TXT_SysMenu3 = "Menu Systeme %1.1f%c (v%d, Info: v%d %s)";
|
||||||
TXT_NO_HBC = "Chaine Homebrew n'est pas installé";
|
TXT_NO_HBC = "Chaine Homebrew n'est pas installe'";
|
||||||
TXT_HBC = "Chaine Homebrew 1.0.%d utilise IOS%d";
|
TXT_HBC = "Chaine Homebrew 1.0.%d utilise IOS%d";
|
||||||
TXT_HBC_NEW = "Chaine Homebrew 1.1.0 utilise IOS%d";
|
TXT_HBC_NEW = "Chaine Homebrew 1.1.0 utilise IOS%d";
|
||||||
TXT_HBC_112 = "Chaine Channel 1.1.%d utilise IOS%d";
|
TXT_HBC_112 = "Chaine Channel 1.1.%d utilise IOS%d";
|
||||||
@ -131,7 +131,7 @@ int initLanguages(struct tm today)
|
|||||||
TXT_Hollywood = "Hollywood v0x%x";
|
TXT_Hollywood = "Hollywood v0x%x";
|
||||||
TXT_ConsoleID = "Identifiant de la console: %d";
|
TXT_ConsoleID = "Identifiant de la console: %d";
|
||||||
TXT_ConsoleType = "Type de Console: %s";
|
TXT_ConsoleType = "Type de Console: %s";
|
||||||
TXT_ShopCountry ="Pays de la chaîne boutique: %s (%u)";
|
TXT_ShopCountry ="Pays de la chaine boutique: %s (%u)";
|
||||||
TXT_vBoot2 = "Boot2 v%u";
|
TXT_vBoot2 = "Boot2 v%u";
|
||||||
TXT_NrOfTitles = "%d titres trouves.";
|
TXT_NrOfTitles = "%d titres trouves.";
|
||||||
TXT_NrOfIOS = "%d IOS trouves sur cette console. %d sont des stubs.";
|
TXT_NrOfIOS = "%d IOS trouves sur cette console. %d sont des stubs.";
|
||||||
@ -172,6 +172,7 @@ int initLanguages(struct tm today)
|
|||||||
TXT_Upload = "Telechargement du rapport...";
|
TXT_Upload = "Telechargement du rapport...";
|
||||||
BUT_OK = "OK";
|
BUT_OK = "OK";
|
||||||
TXT_OriginalRegion = " (region d'origine: ";
|
TXT_OriginalRegion = " (region d'origine: ";
|
||||||
|
TXT_IOSSkipped = "Ignore'";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONF_LANG_ITALIAN:
|
case CONF_LANG_ITALIAN:
|
||||||
@ -210,7 +211,7 @@ int initLanguages(struct tm today)
|
|||||||
TXT_ShopCountry ="Paese canale Shop: %s (%u)";
|
TXT_ShopCountry ="Paese canale Shop: %s (%u)";
|
||||||
TXT_vBoot2 = "Boot2 v%u";
|
TXT_vBoot2 = "Boot2 v%u";
|
||||||
TXT_NrOfTitles = "Trovati %d giochi.";
|
TXT_NrOfTitles = "Trovati %d giochi.";
|
||||||
TXT_NrOfIOS = "Trovati %d IOS in questa console. %d di questi sono stub.";
|
TXT_NrOfIOS = "Trovati %d IOS in questa console. %d di questi sono stubs.";
|
||||||
TXT_AppTitle = "SysCheck HDE %s by JoostinOnline, Double_A, R2-D2199, and Nano";
|
TXT_AppTitle = "SysCheck HDE %s by JoostinOnline, Double_A, R2-D2199, and Nano";
|
||||||
TXT_AppIOS = "...appoggiato all'%sIOS%d (v. %d).";
|
TXT_AppIOS = "...appoggiato all'%sIOS%d (v. %d).";
|
||||||
ERR_AllocateMemory = "Impossibile allocare la memoria per %d giochi.";
|
ERR_AllocateMemory = "Impossibile allocare la memoria per %d giochi.";
|
||||||
@ -248,6 +249,7 @@ int initLanguages(struct tm today)
|
|||||||
TXT_Upload = "Invio rapporto...";
|
TXT_Upload = "Invio rapporto...";
|
||||||
BUT_OK = "OK";
|
BUT_OK = "OK";
|
||||||
TXT_OriginalRegion = " (regione originale: ";
|
TXT_OriginalRegion = " (regione originale: ";
|
||||||
|
TXT_IOSSkipped = "Ignorato";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONF_LANG_SPANISH:
|
case CONF_LANG_SPANISH:
|
||||||
@ -324,6 +326,7 @@ int initLanguages(struct tm today)
|
|||||||
TXT_Upload = "Cargar el informe...";
|
TXT_Upload = "Cargar el informe...";
|
||||||
BUT_OK = "OK";
|
BUT_OK = "OK";
|
||||||
TXT_OriginalRegion = " (region de origen: ";
|
TXT_OriginalRegion = " (region de origen: ";
|
||||||
|
TXT_IOSSkipped = "Omitido";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -362,7 +365,7 @@ int initLanguages(struct tm today)
|
|||||||
TXT_ShopCountry ="Shop Channel Country: %s (%u)";
|
TXT_ShopCountry ="Shop Channel Country: %s (%u)";
|
||||||
TXT_vBoot2 = "Boot2 v%u";
|
TXT_vBoot2 = "Boot2 v%u";
|
||||||
TXT_NrOfTitles = "Found %d titles.";
|
TXT_NrOfTitles = "Found %d titles.";
|
||||||
TXT_NrOfIOS = "Found %d IOS on this console. %d of them are stub.";
|
TXT_NrOfIOS = "Found %d IOS on this console. %d of them are stubs.";
|
||||||
TXT_AppTitle = "SysCheck HDE %s by JoostinOnline, Double_A, R2-D2199, and Nano";
|
TXT_AppTitle = "SysCheck HDE %s by JoostinOnline, Double_A, R2-D2199, and Nano";
|
||||||
TXT_AppIOS = "...runs on %sIOS%d (rev %d).";
|
TXT_AppIOS = "...runs on %sIOS%d (rev %d).";
|
||||||
ERR_AllocateMemory = "Unable to allocate the memory for %d titles.";
|
ERR_AllocateMemory = "Unable to allocate the memory for %d titles.";
|
||||||
@ -400,6 +403,7 @@ int initLanguages(struct tm today)
|
|||||||
TXT_Upload = "Uploading report...";
|
TXT_Upload = "Uploading report...";
|
||||||
BUT_OK = "OK";
|
BUT_OK = "OK";
|
||||||
TXT_OriginalRegion = " (original region: ";
|
TXT_OriginalRegion = " (original region: ";
|
||||||
|
TXT_IOSSkipped = "Skipped";
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
46
source/sys.c
46
source/sys.c
@ -326,15 +326,12 @@ inline s32 RemoveBogusTMD(void)
|
|||||||
|
|
||||||
|
|
||||||
inline bool CheckIOSType(void) {
|
inline bool CheckIOSType(void) {
|
||||||
if (AHB_ACCESS == false) return false;
|
//if (AHB_ACCESS == false) return false;
|
||||||
//u32 start_address = IOS_START;
|
u32 start_address = IOS_TOP;
|
||||||
//u32 end_address = IOS_END;
|
const char WL_String[] = {0x57, 0x4C, 0x3A, 0x20, 0x30, 0x32, 0x2F, 0x30, 0x32, 0x2F, 0x31, 0x32}; // "WL: 02/02/12"
|
||||||
const u32 start_address = 0x90000000;
|
|
||||||
const u32 end_address = 0x94000000;
|
|
||||||
const u8 WL_String[] = {0x57, 0x4C, 0x3A, 0x20, 0x30, 0x32, 0x2F, 0x30, 0x32, 0x2F, 0x31, 0x32}; // "WL: 02/02/12"
|
|
||||||
u32 i;
|
u32 i;
|
||||||
for(i = start_address; i < end_address - sizeof(WL_String); i++) {
|
for(i = start_address; i < 0x94000000 - sizeof(WL_String); i++) {
|
||||||
if (memcmp((u8*)i, WL_String, sizeof(WL_String)) == 0) return true;
|
if (memcmp((char*)i, WL_String, sizeof(WL_String)) == 0) return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -727,3 +724,36 @@ s32 get_miosinfo(char *str)
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Minimum vIOS versions
|
||||||
|
const vIOSdb_t vIOSdb[] = {
|
||||||
|
{9, 1290},
|
||||||
|
{12, 782},
|
||||||
|
{13, 1288},
|
||||||
|
{14, 1288},
|
||||||
|
{15, 1288},
|
||||||
|
{17, 1288},
|
||||||
|
{21, 1295},
|
||||||
|
{22, 1550},
|
||||||
|
{28, 2063},
|
||||||
|
{31, 3864},
|
||||||
|
{33, 3864},
|
||||||
|
{34, 3864},
|
||||||
|
{35, 3864},
|
||||||
|
{36, 3864},
|
||||||
|
{37, 5919},
|
||||||
|
{38, 4380},
|
||||||
|
{41, 3863},
|
||||||
|
{43, 3863},
|
||||||
|
{45, 3863},
|
||||||
|
{46, 3863},
|
||||||
|
{48, 4380},
|
||||||
|
{53, 5919},
|
||||||
|
{55, 5919},
|
||||||
|
{56, 5918},
|
||||||
|
{57, 6175},
|
||||||
|
{58, 6432},
|
||||||
|
{59, 7201},
|
||||||
|
{62, 6430},
|
||||||
|
{80, 6430}
|
||||||
|
};
|
||||||
|
@ -31,8 +31,8 @@
|
|||||||
#include "wiibasics.h"
|
#include "wiibasics.h"
|
||||||
|
|
||||||
// Filename
|
// Filename
|
||||||
#define REPORT "sd:/sysCheck.csv"
|
#define REPORT "/sysCheck.csv"
|
||||||
#define HASHLOG "sd:/IOSsyscheck.log"
|
#define HASHLOG "/IOSsyscheck.log"
|
||||||
#define VERSION_1_1_0 65536
|
#define VERSION_1_1_0 65536
|
||||||
|
|
||||||
|
|
||||||
@ -47,6 +47,7 @@ int main(int argc, char **argv)
|
|||||||
memset(arguments.skipIOSlist, 0, sizeof(arguments.skipIOSlist));
|
memset(arguments.skipIOSlist, 0, sizeof(arguments.skipIOSlist));
|
||||||
arguments.skipIOScnt = 0;
|
arguments.skipIOScnt = 0;
|
||||||
arguments.debug = false;
|
arguments.debug = false;
|
||||||
|
arguments.USB = strlen(argv[0]) && (argv[0][0] == 'U' || argv[0][0] == 'u');
|
||||||
|
|
||||||
InitGecko();
|
InitGecko();
|
||||||
if(argc>=1){
|
if(argc>=1){
|
||||||
@ -273,17 +274,18 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Not the most efficient way to remove argument-skipped IOS's, but it works.
|
// Not the most efficient way to remove argument-skipped IOS's, but it works.
|
||||||
while (arguments.skipIOScnt > 0) {
|
int tempSkipIOScnt = arguments.skipIOScnt;
|
||||||
|
while (tempSkipIOScnt > 0) {
|
||||||
for (i = nbTitles; i--;) {
|
for (i = nbTitles; i--;) {
|
||||||
titleID = titles[i] & 0xFFFFFFFF;
|
titleID = titles[i] & 0xFFFFFFFF;
|
||||||
if(arguments.skipIOSlist[arguments.skipIOScnt - 1] > 0 && titleID == arguments.skipIOSlist[arguments.skipIOScnt - 1]) {
|
if(arguments.skipIOSlist[tempSkipIOScnt - 1] > 0 && titleID == arguments.skipIOSlist[tempSkipIOScnt - 1]) {
|
||||||
logfile("Skipped IOS %i, titles[%i] = %i\r\n", arguments.skipIOSlist[arguments.skipIOScnt - 1], i, titles[i]);
|
logfile("Skipped IOS %i, titles[%i] = %i\r\n", arguments.skipIOSlist[tempSkipIOScnt - 1], i, titles[i]);
|
||||||
titles[i] = 0;
|
titles[i] = 0;
|
||||||
SystemInfo.countIOS--;
|
SystemInfo.countIOS--;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
arguments.skipIOScnt--;
|
tempSkipIOScnt--;
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckTime();
|
CheckTime();
|
||||||
@ -311,7 +313,7 @@ int main(int argc, char **argv)
|
|||||||
ios[i].titleID = 0;
|
ios[i].titleID = 0;
|
||||||
ios[i].mloadVersion = 0;
|
ios[i].mloadVersion = 0;
|
||||||
ios[i].baseIOS = -1;
|
ios[i].baseIOS = -1;
|
||||||
sprintf(ios[i].info, "NULL");
|
strcpy(ios[i].info, "NULL");
|
||||||
ios[i].isStub = false;
|
ios[i].isStub = false;
|
||||||
ios[i].revision = 0;
|
ios[i].revision = 0;
|
||||||
ios[i].infoFakeSignature = false;
|
ios[i].infoFakeSignature = false;
|
||||||
@ -391,6 +393,7 @@ int main(int argc, char **argv)
|
|||||||
if (ios[i].isStub) {
|
if (ios[i].isStub) {
|
||||||
gprintf("is stub\n");
|
gprintf("is stub\n");
|
||||||
logfile("is stub\r\n");
|
logfile("is stub\r\n");
|
||||||
|
usleep(100000); // A little delay so you can see what stubs were scanned
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -894,19 +897,32 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
// Display IOS vulnerabilities
|
// Display IOS vulnerabilities
|
||||||
int lineOffset = 0;
|
int lineOffset = 0;
|
||||||
|
int skippedOffset = 0;
|
||||||
|
int lastIOS = 0;
|
||||||
for (i = 0; i < nbTitles; i++)
|
for (i = 0; i < nbTitles; i++)
|
||||||
{
|
{
|
||||||
lineOffset = i + LAST;
|
lineOffset = i + LAST;
|
||||||
|
// TODO: Fix hiding the next IOS
|
||||||
|
if (arguments.skipIOScnt > 0) {
|
||||||
|
for(j = 0; j < arguments.skipIOScnt; j++) {
|
||||||
|
if (arguments.skipIOSlist[j] > lastIOS && arguments.skipIOSlist[j] < ios[i].titleID) {
|
||||||
|
snprintf(ReportBuffer[skippedOffset + lineOffset], MAX_ELEMENTS(ReportBuffer[0]), "%sIOS%d: %s", (SystemInfo.deviceType == CONSOLE_WII_U) ? "v" : "", arguments.skipIOSlist[j], TXT_IOSSkipped);
|
||||||
|
skippedOffset++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (selectedIOS > -1) i = selectedIOS; //If specific IOS is selected
|
if (selectedIOS > -1) i = selectedIOS; //If specific IOS is selected
|
||||||
|
|
||||||
if (ios[i].titleID == TID_BC) {
|
if (ios[i].titleID == TID_BC) {
|
||||||
sprintf(ReportBuffer[lineOffset], "BC v%d", ios[i].revision);
|
sprintf(ReportBuffer[skippedOffset + lineOffset], "BC v%d", ios[i].revision);
|
||||||
} else if (ios[i].titleID == TID_MIOS) {
|
} else if (ios[i].titleID == TID_MIOS) {
|
||||||
sprintf(ReportBuffer[lineOffset], "MIOS v%d%s", ios[i].revision, SystemInfo.miosInfo);
|
sprintf(ReportBuffer[skippedOffset + lineOffset], "MIOS v%d%s", ios[i].revision, SystemInfo.miosInfo);
|
||||||
} else if (ios[i].baseIOS == 75 && (ios[i].titleID==222 || ios[i].titleID==224 || ios[i].titleID==223 || ios[i].titleID==202 || ios[i].titleID==225)) {
|
} else if (ios[i].baseIOS == 75 && (ios[i].titleID==222 || ios[i].titleID==224 || ios[i].titleID==223 || ios[i].titleID==202 || ios[i].titleID==225)) {
|
||||||
sprintf(ReportBuffer[lineOffset], "%sIOS%d[38+37] (rev %d, Info: %s):", ios[i].infovIOS ? "v" : "", ios[i].titleID, ios[i].revision, ios[i].info);
|
sprintf(ReportBuffer[skippedOffset + lineOffset], "%sIOS%d[38+37] (rev %d, Info: %s):", ios[i].infovIOS ? "v" : "", ios[i].titleID, ios[i].revision, ios[i].info);
|
||||||
} else if (ios[i].baseIOS == 98 && (ios[i].titleID==222 || ios[i].titleID==224 || ios[i].titleID==223 || ios[i].titleID==202 || ios[i].titleID==225)) {
|
} else if (ios[i].baseIOS == 98 && (ios[i].titleID==222 || ios[i].titleID==224 || ios[i].titleID==223 || ios[i].titleID==202 || ios[i].titleID==225)) {
|
||||||
sprintf(ReportBuffer[lineOffset], "%sIOS%d[38+60] (rev %d, Info: %s):", ios[i].infovIOS ? "v" : "", ios[i].titleID, ios[i].revision, ios[i].info);
|
sprintf(ReportBuffer[skippedOffset + lineOffset], "%sIOS%d[38+60] (rev %d, Info: %s):", ios[i].infovIOS ? "v" : "", ios[i].titleID, ios[i].revision, ios[i].info);
|
||||||
} else {
|
} else {
|
||||||
if(ios[i].mloadVersion > 0 && ios[i].baseIOS > 0) {
|
if(ios[i].mloadVersion > 0 && ios[i].baseIOS > 0) {
|
||||||
int v, s;
|
int v, s;
|
||||||
@ -917,54 +933,59 @@ int main(int argc, char **argv)
|
|||||||
v = 4;
|
v = 4;
|
||||||
s = 0;
|
s = 0;
|
||||||
}
|
}
|
||||||
sprintf(ReportBuffer[lineOffset], "%sIOS%d[%d] (rev %d, Info: hermes-v%d.%d):", ios[i].infovIOS ? "v" : "", ios[i].titleID, ios[i].baseIOS, ios[i].revision, v, s);
|
sprintf(ReportBuffer[skippedOffset + lineOffset], "%sIOS%d[%d] (rev %d, Info: hermes-v%d.%d):", ios[i].infovIOS ? "v" : "", ios[i].titleID, ios[i].baseIOS, ios[i].revision, v, s);
|
||||||
} else if(ios[i].baseIOS > 0) {
|
} else if(ios[i].baseIOS > 0) {
|
||||||
snprintf(ReportBuffer[lineOffset], MAX_ELEMENTS(ReportBuffer[0]), "%sIOS%d[%d] (rev %d, Info: %s):", ios[i].infovIOS ? "v" : "", ios[i].titleID, ios[i].baseIOS, ios[i].revision, ios[i].info);
|
snprintf(ReportBuffer[skippedOffset + lineOffset], MAX_ELEMENTS(ReportBuffer[0]), "%sIOS%d[%d] (rev %d, Info: %s):", ios[i].infovIOS ? "v" : "", ios[i].titleID, ios[i].baseIOS, ios[i].revision, ios[i].info);
|
||||||
} else if (strcmp(ios[i].info, "NULL") != 0 && !ios[i].isStub) {
|
} else if (strcmp(ios[i].info, "NULL") != 0 && !ios[i].isStub) {
|
||||||
snprintf(ReportBuffer[lineOffset], MAX_ELEMENTS(ReportBuffer[0]), "%sIOS%d (rev %d, Info: %s):", ios[i].infovIOS ? "v" : "", ios[i].titleID, ios[i].revision, ios[i].info);
|
snprintf(ReportBuffer[skippedOffset + lineOffset], MAX_ELEMENTS(ReportBuffer[0]), "%sIOS%d (rev %d, Info: %s):", ios[i].infovIOS ? "v" : "", ios[i].titleID, ios[i].revision, ios[i].info);
|
||||||
} else if (ios[i].titleID == 249 && ios[i].revision > 11 && ios[i].revision < 18) {
|
} else if (ios[i].titleID == 249 && ios[i].revision > 11 && ios[i].revision < 18) {
|
||||||
sprintf(ReportBuffer[lineOffset], "%sIOS%d[38] (rev %d):", ios[i].infovIOS ? "v" : "", ios[i].titleID, ios[i].revision);
|
sprintf(ReportBuffer[skippedOffset + lineOffset], "%sIOS%d[38] (rev %d):", ios[i].infovIOS ? "v" : "", ios[i].titleID, ios[i].revision);
|
||||||
} else {
|
} else {
|
||||||
snprintf(ReportBuffer[lineOffset], MAX_ELEMENTS(ReportBuffer[0]), "%sIOS%d (rev %d):", ios[i].infovIOS ? "v" : "", ios[i].titleID, ios[i].revision);
|
snprintf(ReportBuffer[skippedOffset + lineOffset], MAX_ELEMENTS(ReportBuffer[0]), "%sIOS%d (rev %d):", ios[i].infovIOS ? "v" : "", ios[i].titleID, ios[i].revision);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check BootMii As IOS (BootMii As IOS is installed on IOS254 rev 31338)
|
// Check BootMii As IOS (BootMii As IOS is installed on IOS254 rev 31338)
|
||||||
if (ios[i].titleID == TID_BOOTMII && (ios[i].revision == 31338 || ios[i].revision == 65281))
|
if (ios[i].titleID == TID_BOOTMII && (ios[i].revision == 31338 || ios[i].revision == 65281))
|
||||||
strcat (ReportBuffer[lineOffset]," BootMii");
|
strcat (ReportBuffer[skippedOffset + lineOffset]," BootMii");
|
||||||
else if (ios[i].titleID == TID_NANDEMU && ios[i].revision == 65535)
|
else if (ios[i].titleID == TID_NANDEMU && ios[i].revision == 65535)
|
||||||
strcat (ReportBuffer[lineOffset]," NANDEmu");
|
strcat (ReportBuffer[skippedOffset + lineOffset]," NANDEmu");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (ios[i].isStub && strcmp(ios[i].info, "NULL") == 0) {
|
if (ios[i].isStub && strcmp(ios[i].info, "NULL") == 0) {
|
||||||
gprintf("1. titleID: %d %s\n", ios[i].titleID, ios[i].info);
|
gprintf("1. titleID: %d %s\n", ios[i].titleID, ios[i].info);
|
||||||
strcat (ReportBuffer[lineOffset], TXT_Stub);
|
strcat (ReportBuffer[skippedOffset + lineOffset], TXT_Stub);
|
||||||
} else if (ios[i].isStub && strcmp(ios[i].info, "NULL") != 0) {
|
} else if (ios[i].isStub && strcmp(ios[i].info, "NULL") != 0) {
|
||||||
gprintf("2. titleID: %d %s\n", ios[i].titleID, ios[i].info);
|
gprintf("2. titleID: %d %s\n", ios[i].titleID, ios[i].info);
|
||||||
strcat (ReportBuffer[lineOffset], ios[i].info);
|
strcat (ReportBuffer[skippedOffset + lineOffset], ios[i].info);
|
||||||
} else if(ios[i].titleID != TID_BC && ios[i].titleID != TID_MIOS) {
|
} else if(ios[i].titleID != TID_BC && ios[i].titleID != TID_MIOS) {
|
||||||
if(ios[i].infoFakeSignature) strcat(ReportBuffer[lineOffset], TXT_Trucha);
|
if(ios[i].infoFakeSignature) strcat(ReportBuffer[skippedOffset + lineOffset], TXT_Trucha);
|
||||||
if(ios[i].infoESIdentify) strcat(ReportBuffer[lineOffset], TXT_ES);
|
if(ios[i].infoESIdentify) strcat(ReportBuffer[skippedOffset + lineOffset], TXT_ES);
|
||||||
if(ios[i].infoFlashAccess) strcat(ReportBuffer[lineOffset], TXT_Flash);
|
if(ios[i].infoFlashAccess) strcat(ReportBuffer[skippedOffset + lineOffset], TXT_Flash);
|
||||||
if(ios[i].infoNANDAccess) strcat(ReportBuffer[lineOffset], TXT_NAND);
|
if(ios[i].infoNANDAccess) strcat(ReportBuffer[skippedOffset + lineOffset], TXT_NAND);
|
||||||
if(ios[i].infoVersionPatch) strcat(ReportBuffer[lineOffset], TXT_VersionP);
|
if(ios[i].infoVersionPatch) strcat(ReportBuffer[skippedOffset + lineOffset], TXT_VersionP);
|
||||||
if(ios[i].infoBoot2Access) strcat(ReportBuffer[lineOffset], TXT_Boot2);
|
if(ios[i].infoBoot2Access) strcat(ReportBuffer[skippedOffset + lineOffset], TXT_Boot2);
|
||||||
if(ios[i].infoUSB2) strcat(ReportBuffer[lineOffset], TXT_USB);
|
if(ios[i].infoUSB2) strcat(ReportBuffer[skippedOffset + lineOffset], TXT_USB);
|
||||||
if(!ios[i].infoFakeSignature && !ios[i].infoESIdentify && !ios[i].infoFlashAccess && !ios[i].infoNANDAccess && !ios[i].infoUSB2 && !ios[i].infoVersionPatch) strcat(ReportBuffer[lineOffset], TXT_NoPatch);
|
if(!ios[i].infoFakeSignature && !ios[i].infoESIdentify && !ios[i].infoFlashAccess && !ios[i].infoNANDAccess && !ios[i].infoUSB2 && !ios[i].infoVersionPatch) strcat(ReportBuffer[skippedOffset + lineOffset], TXT_NoPatch);
|
||||||
|
|
||||||
ReportBuffer[lineOffset][strlen(ReportBuffer[lineOffset])-1]='\0';
|
ReportBuffer[skippedOffset + lineOffset][strlen(ReportBuffer[skippedOffset + lineOffset])-1]='\0';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
lastIOS = ios[i].titleID;
|
||||||
}
|
}
|
||||||
|
|
||||||
int NumLines = lineOffset+1;
|
int NumLines = lineOffset + skippedOffset + 1;
|
||||||
sprintf(ReportBuffer[NumLines], TXT_ReportDate);
|
sprintf(ReportBuffer[NumLines], TXT_ReportDate);
|
||||||
CheckTime();
|
CheckTime();
|
||||||
|
|
||||||
// Mount the SD Card
|
// Mount the SD Card
|
||||||
UpdateTime();
|
UpdateTime();
|
||||||
printLoading(MSG_MountSD);
|
printLoading(MSG_MountSD);
|
||||||
MountSD();
|
//if(arguments.USB)
|
||||||
|
// MountUSB();
|
||||||
|
//else
|
||||||
|
// MountSD();
|
||||||
|
fatInitDefault();
|
||||||
CheckTime();
|
CheckTime();
|
||||||
|
|
||||||
// Initialise the FAT file system
|
// Initialise the FAT file system
|
||||||
@ -1032,7 +1053,8 @@ int main(int argc, char **argv)
|
|||||||
// Return to the loader
|
// Return to the loader
|
||||||
if (wpressed & WPAD_BUTTON_HOME) {
|
if (wpressed & WPAD_BUTTON_HOME) {
|
||||||
// Unmount the SD Card
|
// Unmount the SD Card
|
||||||
UnmountSD();
|
//UnmountSD();
|
||||||
|
//UnmountUSB();
|
||||||
deinitGUI();
|
deinitGUI();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
@ -1040,7 +1062,8 @@ int main(int argc, char **argv)
|
|||||||
// Return to System Menu
|
// Return to System Menu
|
||||||
if (wpressed & WPAD_BUTTON_PLUS) {
|
if (wpressed & WPAD_BUTTON_PLUS) {
|
||||||
// Unmount the SD Card
|
// Unmount the SD Card
|
||||||
UnmountSD();
|
//UnmountSD();
|
||||||
|
//UnmountUSB();
|
||||||
deinitGUI();
|
deinitGUI();
|
||||||
SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0);
|
SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0);
|
||||||
}
|
}
|
||||||
@ -1048,7 +1071,8 @@ int main(int argc, char **argv)
|
|||||||
// Shutdown Wii
|
// Shutdown Wii
|
||||||
if (wpressed & WPAD_BUTTON_MINUS) {
|
if (wpressed & WPAD_BUTTON_MINUS) {
|
||||||
// Unmount the SD Card
|
// Unmount the SD Card
|
||||||
UnmountSD();
|
//UnmountSD();
|
||||||
|
//UnmountUSB();
|
||||||
deinitGUI();
|
deinitGUI();
|
||||||
SYS_ResetSystem(SYS_POWEROFF, 0, 0);
|
SYS_ResetSystem(SYS_POWEROFF, 0, 0);
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,22 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <network.h>
|
#include <network.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "update.h"
|
#include "update.h"
|
||||||
#include "gecko.h"
|
#include "gecko.h"
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
|
#include "tools.h"
|
||||||
|
|
||||||
s32 downloadSyscheckFile(const char* fileName) {
|
s32 downloadSyscheckFile(const char* update_dir, const char* fileName) {
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
char buf[128] = {0};
|
char buf[128] = {0};
|
||||||
u32 http_status;
|
u32 http_status;
|
||||||
u8* outbuf;
|
u8* outbuf;
|
||||||
u32 length;
|
u32 length;
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "http://syscheck-hd.googlecode.com/svn/trunk/SysCheckHDE/%s", fileName);
|
snprintf(buf, sizeof(buf), "http://sourceforge.net/p/syscheck-hde/code/HEAD/tree/trunk/SysCheckHDE/%s?format=raw", fileName);
|
||||||
|
|
||||||
ret = http_request(buf, 1 << 31);
|
ret = http_request(buf, 1 << 31);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
@ -39,7 +42,7 @@ s32 downloadSyscheckFile(const char* fileName) {
|
|||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
sprintf(buf, "%s%s", PATH, fileName);
|
sprintf(buf, "%s%s", update_dir, fileName);
|
||||||
|
|
||||||
FILE *file = fopen(buf, "w");
|
FILE *file = fopen(buf, "w");
|
||||||
|
|
||||||
@ -56,16 +59,22 @@ s32 downloadSyscheckFile(const char* fileName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s32 updateApp(void) {
|
s32 updateApp(void) {
|
||||||
int ret = 0;
|
int ret = net_init();
|
||||||
|
|
||||||
ret = net_init();
|
char update_dir[21];
|
||||||
if (ret < 0)
|
sprintf(update_dir, "%s:/apps/SysCheckHDE", arguments.USB ? "usb" : "sd");
|
||||||
goto out;
|
mkdir(update_dir,S_IWRITE|S_IREAD); // attempt to make dir
|
||||||
|
chdir(update_dir);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
net_deinit();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
u32 http_status;
|
u32 http_status;
|
||||||
u8* outbuf;
|
u8* outbuf;
|
||||||
u32 length;
|
u32 length;
|
||||||
|
|
||||||
ret = http_request("http://syscheck-hd.googlecode.com/svn/trunk/Version.txt", 1 << 31);
|
ret = http_request("http://sourceforge.net/p/syscheck-hde/code/HEAD/tree/trunk/Version.txt?format=raw", 1 << 31);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
{
|
{
|
||||||
gprintf("Error making http request\n");
|
gprintf("Error making http request\n");
|
||||||
@ -74,39 +83,36 @@ s32 updateApp(void) {
|
|||||||
|
|
||||||
ret = http_get_result(&http_status, &outbuf, &length);
|
ret = http_get_result(&http_status, &outbuf, &length);
|
||||||
|
|
||||||
if (((int)*outbuf & 0xF0000000) == 0xF0000000)
|
|
||||||
{
|
|
||||||
ret = -2;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strncmp((char*)outbuf, "Version=", sizeof("Version=")))
|
if (!strncmp((char*)outbuf, "Version=", sizeof("Version=")))
|
||||||
{
|
{
|
||||||
int version = atoi((char*)(outbuf + sizeof("Version=")));
|
int version = atoi((char*)(outbuf + sizeof("Version=")));
|
||||||
gprintf("INT: %i\n", version);
|
gprintf("INT: %i\n", version);
|
||||||
|
|
||||||
if (version > REVISION) {
|
if (version > REVISION) {
|
||||||
ret = downloadSyscheckFile("boot.dol");
|
ret = downloadSyscheckFile(update_dir, "boot.dol");
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
goto out;
|
net_deinit();
|
||||||
ret = downloadSyscheckFile("meta.xml");
|
return ret;
|
||||||
if (ret < 0)
|
}
|
||||||
goto out;
|
ret = downloadSyscheckFile(update_dir, "meta.xml");
|
||||||
ret = downloadSyscheckFile("icon.png");
|
if (ret < 0) {
|
||||||
if (ret < 0)
|
net_deinit();
|
||||||
goto out;
|
return ret;
|
||||||
|
}
|
||||||
|
ret = downloadSyscheckFile(update_dir, "icon.png");
|
||||||
|
if (ret < 0) {
|
||||||
|
net_deinit();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = 2;
|
net_deinit();
|
||||||
goto out;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
ret = -3;
|
net_deinit();
|
||||||
goto out;
|
return -3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
out:
|
|
||||||
net_deinit();
|
net_deinit();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user