diff --git a/apps/photo_upgrader/meta.xml b/apps/photo_upgrader/meta.xml
new file mode 100644
index 0000000..c894b7d
--- /dev/null
+++ b/apps/photo_upgrader/meta.xml
@@ -0,0 +1,11 @@
+
+
+ photo_upgrader
+ 0.9.0
+
+ thepikachugamer
+
+
+
+
+
diff --git a/include/tools.h b/include/tools.h
index 902f054..7a8f064 100644
--- a/include/tools.h
+++ b/include/tools.h
@@ -24,6 +24,9 @@ void init_video(int row, int col);
uint16_t input_scan(uint16_t value);
int quit(int ret);
+int net_init_retry(unsigned int retries);
+int get_title_rev(const uint64_t tid);
+unsigned int check_title(const uint64_t tid);
bool check_dolphin(void);
bool check_vwii(void);
diff --git a/photo_upgrader.zip b/photo_upgrader.zip
index feda510..f15c305 100644
Binary files a/photo_upgrader.zip and b/photo_upgrader.zip differ
diff --git a/source/main.c b/source/main.c
index 914b917..8e90087 100644
--- a/source/main.c
+++ b/source/main.c
@@ -24,6 +24,12 @@ const char header[] = "Photo Channel 1.1 installer v" VERSION ", by thepikachuga
bool offline_mode = true;
static fstats file_stats ATTRIBUTE_ALIGN(32);
+
+const uint64_t
+ tid = 0x0001000248415941LL,
+ tid_new = 0x0001000248414141LL,
+ tid_stub = 0x0001000048415A41LL;
+
const aeskey wii_ckey = {0xEB, 0xE4, 0x2A, 0x22, 0x5E, 0x85, 0x93, 0xE4, 0x48, 0xD9, 0xC5, 0x45, 0x73, 0x81, 0xAA, 0xF7};
void* NUS_Download(const uint64_t tid, const char* obj, unsigned int* size, int* ec) {
@@ -53,16 +59,15 @@ void* NUS_Download(const uint64_t tid, const char* obj, unsigned int* size, int*
}
void* FS_Read(const char* filepath, unsigned int* filesize, int* ret) {
- *filesize = 0;
*ret = ISFS_Open(filepath, ISFS_OPEN_READ);
if(*ret < 0) return NULL;
int fd = *ret;
-
- *ret = ISFS_GetFileStats(fd, &file_stats);
- if(*ret < 0) return NULL;
- *filesize = file_stats.file_length;
-
+ if(! *filesize) {
+ *ret = ISFS_GetFileStats(fd, &file_stats);
+ if(*ret < 0) return NULL;
+ *filesize = file_stats.file_length;
+ }
unsigned char *buffer = memalign(0x20, *filesize);
if(!buffer) {
*ret = -ENOMEM;
@@ -85,296 +90,213 @@ signed_blob* fetch_tmd(const uint64_t tid, unsigned int* size, int* ret) {
signed_blob* s_tmd = NULL;
if(offline_mode) {
- printf("> Reading %016llx title metadata... ", tid);
*ret = ES_GetStoredTMDSize(tid, size);
- if(*ret < 0) {
- printf("failed! (%d)\n", *ret);
- return NULL;
- }
+ if(*ret < 0) return NULL;
+
s_tmd = memalign(0x20, *size);
if(!s_tmd) {
- printf("failed! (No memory? [%u bytes])\n", *size);
*ret = -ENOMEM;
return NULL;
}
+
*ret = ES_GetStoredTMD(tid, s_tmd, *size);
- if(! *ret) {
- printf("OK! (size = %u)\n", *size);
- return s_tmd;
- }
- else {
+ if(*ret < 0) {
free(s_tmd);
- printf("failed! (%d)\n", *ret);
return NULL;
}
+
+ return s_tmd;
}
else {
- printf("> Downloading %016llx title metadata... ", tid);
unsigned char* _tmd = NUS_Download(tid, "tmd", size, ret);
- if(*ret < 0) {
- printf("failed! (%d)\n", (*ret < -117000) ? abs(*ret + 117000) : *ret);
- return NULL;
- }
+ if(*ret < 0) return NULL;
s_tmd = memalign(0x20, *size);
- if(!s_tmd) {
- printf("failed! (No memory? [%u bytes])\n", *size);
- *ret = -ENOMEM;
- }
- else {
+ if(s_tmd)
memcpy(s_tmd, _tmd, *size);
- printf("OK! (size = %u)\n", *size);
- }
+ else
+ *ret = -ENOMEM;
+
free(_tmd);
+ return s_tmd;
}
-
- return s_tmd;
}
signed_blob* fetch_tik(const uint64_t tid, unsigned int* size, int* ret) {
signed_blob* s_tik;
if(offline_mode) {
- printf("> Reading %016llx ticket... ", tid);
char filepath[30];
sprintf(filepath, "/ticket/%08x/%08x.tik", tid_hi(tid), tid_lo(tid));
- s_tik = FS_Read(filepath, size, ret);
- if(*ret < 0)
- printf("failed! (%d)\n", *ret);
- else
- printf("OK! (size = %u)\n", *size);
-
- return s_tik;
+ return FS_Read(filepath, size, ret);
}
else {
- printf("> Downloading %016llx ticket... ", tid);
unsigned char* _tik = NUS_Download(tid, "cetk", size, ret);
- if(*ret < 0) {
- printf("failed! (%d)\n", (*ret + 117000) > -1000 ? abs(*ret + 117000) : *ret);
- return NULL;
- }
+ if(*ret < 0) return NULL;
s_tik = memalign(0x20, *size);
- if(!s_tik) {
- printf("failed! (No memory? [%u bytes])\n", *size);
- *ret = -ENOMEM;
- }
- else {
+ if (s_tik)
memcpy(s_tik, _tik, *size);
- printf("OK! (size = %u)\n", *size);
- }
+ else
+ *ret = -ENOMEM;
free(_tik);
return s_tik;
}
}
+void get_titlekey(tik* ticket, aeskey out) {
+ struct AES_ctx aes = {};
+ aeskey iv = {};
+ memcpy(out, ticket->cipher_title_key, sizeof(aeskey));
+ *(uint64_t*) iv = ticket->titleid;
-int main() {
- const uint64_t
- tid = 0x0001000248415941LL,
- tid_new = 0x0001000248414141LL,
- tid_stub = 0x0001000048415A41LL;
+ AES_init_ctx_iv(&aes, wii_ckey, iv);
+ AES_CBC_decrypt_buffer(&aes, out, sizeof(aeskey));
+}
- int ret = 0;
+void change_tid(tmd* tmd, tik* ticket, const uint64_t tid_new, aeskey title_key) {
+ struct AES_ctx aes = {};
+ aeskey iv = {};
- unsigned int
- certs_size = 0,
- tmd_size = 0,
- tik_size = 0,
- cnt = 0,
- _cnt = 0;
+ ticket->titleid = tid_new;
+ *(uint64_t*) iv = ticket->titleid;
- signed_blob
- *s_certs = NULL,
- *s_tmd = NULL,
- *s_tik = NULL;
+ AES_init_ctx_iv(&aes, wii_ckey, iv);
+ AES_CBC_encrypt_buffer(&aes, title_key, sizeof(aeskey));
+ memcpy(ticket->cipher_title_key, title_key, sizeof(aeskey));
- struct AES_ctx AESctx_titlekey = {}, AESctx_title = {};
+ tmd->title_id = tid_new;
+}
- init_video(2, 0);
- printf(header);
+int purge_title(const uint64_t tid) {
+ unsigned int viewcnt = 0;
+ int ret;
- printf("Applying IOS patches... ");
- ret = IosPatch_RUNTIME(true, false, true, false);
- if (ret < 0) {
- printf("Failed! (%d)" "\n"
- "Is your Homebrew Channel updated?" "\n"
- "Is in meta.xml?" "\n\n"
+ ret = ES_GetNumTicketViews(tid, &viewcnt);
+ if (!viewcnt) return ret ? ret : ENOENT;
- "Exiting in 5s...", ret);
- sleep(5);
- return 0x0D800064;
+ tikview
+ view ATTRIBUTE_ALIGN(0x20) = {},
+ views[viewcnt] ATTRIBUTE_ALIGN(0x20) = {};
+
+ ret = ES_GetTicketViews(tid, views, viewcnt);
+ if (ret < 0) return ret;
+ for(unsigned int i = 0; i < viewcnt; i++) {
+ memcpy(&view, views + i, sizeof(tikview));
+ ret = ES_DeleteTicket(&view);
+ if (ret < 0) return ret;
}
- printf("OK! (patch count = %d)\n\n", ret);
- WPAD_Init();
- PAD_Init();
- ISFS_Initialize();
+ ES_DeleteTitleContent(tid);
+ return ES_DeleteTitle(tid); // not fatal enough to matter tbh
+}
+
+int install() {
+ int ret = 0;
+ unsigned int certs_size = 0, tmd_size = 0, tik_size = 0;
- ES_GetNumTicketViews(tid_stub, &_cnt);
- if(_cnt) {
- printf("This console owns the Photo Channel 1.1 stub.\n" "Retrieve it at the Wii Shop Channel.\n");
- return quit(0);
+ signed_blob *s_certs = NULL, *s_tmd = NULL, *s_tik = NULL;
+
+ struct AES_ctx AESctx_title = {};
+
+ ret = get_title_rev(tid_new);
+ if(ret > 2) {
+ printf(
+ "You already did this?" "\n"
+ "(Photo channel 1.0 title version %u is > 2)" "\n", ret);
+ return 0;
+ }
+
+ ret = check_title(tid_stub);
+ if (ret) {
+ printf( ret >= 2 ?
+ "Photo channel 1.1 stub is already installed, do you need something..?\n" :
+ "You own Photo Channel 1.1 in the Wii Shop Channel, maybe you want to get it?\n");
+ return 0;
}
- ES_GetTitleContentsCount(tid, &cnt);
- if (!cnt) {
+ if (check_title(tid) >= 2)
+ printf("HAYA is present, using offline mode.\n\n");
+
+ else {
printf("HAYA is not present, using online mode.\n");
printf("> Initializing network... ");
-
- for (int s = 0; s < 5; s++) {
- ret = net_init();
- if(!ret || ret != -EAGAIN) break;
- }
+ ret = net_init_retry(5);
if (ret < 0) {
printf(
- "failed! (%d)\n\n"
- "Try grab a Photo Channel 1.1 WAD from NUS Downloader and install it.\n"
- "This will work around the internet requirement.\n", ret);
- return quit(ret);
+ "failed! (%d)" "\n\n"
+
+ "Try grab a Photo Channel 1.1 WAD from NUS Downloader and install it." "\n"
+ "This will work around the internet requirement." "\n", ret);
+ return ret;
}
+
uint32_t hostip = net_gethostip();
printf("OK! Wii IP address: %hhu.%hhu.%hhu.%hhu\n\n",
hostip >>24, hostip >>16, hostip >>8, hostip >>0);
-
offline_mode = false;
}
- else
- printf("HAYA is present, using offline mode.\n\n");
if(check_dolphin())
printf("\x1b[41m This is Dolphin Emulator, expect -2011. \x1b[40m\n");
printf(
- "This application will install the hidden Photo Channel 1.1" "\n"
+ "This will install the hidden Photo Channel 1.1" "\n"
"title directly over Photo Channel 1.0." "\n\n"
- "Is this OK?" "\n"
- );
+ "Is this OK?" "\n");
sleep(3);
- if(!confirmation()) return quit(2976579765);
+ if(!confirmation()) return 2976579765;
printf("\n> Reading certs... ");
s_certs = FS_Read("/sys/cert.sys", &certs_size, &ret);
if (ret < 0) {
printf("failed! (%d)\n", ret);
- return quit(ret);
+ return ret;
}
-
printf("OK! (size = %u)\n", certs_size);
+ printf("> Reading TMD... ");
s_tmd = fetch_tmd(tid, &tmd_size, &ret);
- if(ret < 0) return quit(ret);
-
+ if (ret < 0) {
+ printf("failed! (%d)\n", ret);
+ return ret;
+ }
+ printf("OK! (size = %u)\n", tmd_size);
tmd* p_tmd = SIGNATURE_PAYLOAD(s_tmd);
+ printf("> Reading ticket... ");
s_tik = fetch_tik(tid, &tik_size, &ret);
- if(ret < 0) return quit(ret);
-
+ if (ret < 0) {
+ printf("failed! (%d)\n", ret);
+ return ret;
+ }
+ printf("OK! (size = %u)\n", tik_size);
tik* p_tik = SIGNATURE_PAYLOAD(s_tik);
printf("> Changing Title ID... ");
-
- aeskey cipher_tkey = {}, title_key = {}, iv = {};
- memcpy(title_key, p_tik->cipher_title_key, sizeof(aeskey));
- *(uint64_t*) iv = p_tik->titleid;
-
- AES_init_ctx_iv(&AESctx_titlekey, wii_ckey, iv);
- AES_CBC_decrypt_buffer(&AESctx_titlekey, title_key, sizeof(aeskey));
-
+ aeskey title_key = {};
+ get_titlekey(p_tik, title_key);
AES_init_ctx(&AESctx_title, title_key);
- memcpy(cipher_tkey, title_key, sizeof(aeskey));
-
- p_tik->titleid = tid_new;
- *(uint64_t*) iv = p_tik->titleid;
- AES_ctx_set_iv(&AESctx_titlekey, iv);
- AES_CBC_encrypt_buffer(&AESctx_titlekey, cipher_tkey, sizeof(aeskey));
-
- memcpy(p_tik->cipher_title_key, cipher_tkey, sizeof(aeskey));
-
- p_tmd->title_id = tid_new;
+ change_tid(p_tmd, p_tik, tid_new, title_key);
printf("OK!\n");
- if(check_vwii()) {
- printf("* This seems to be a vWii, setting IOS to 58.\n");
- p_tmd->sys_version = 0x000000010000003ALL;
- }
-
-/* printf("> Faking signatures... ");
+ if(check_vwii()) p_tmd->sys_version = 1LL << 32 | 58;
- memset(SIGNATURE_SIG(s_tik), 0, SIGNATURE_SIZE(s_tik) - 4);
- memset(SIGNATURE_SIG(s_tmd), 0, SIGNATURE_SIZE(s_tmd) - 4);
-
- sha1 tmdhash, tikhash;
- short int filler = 0;
- for(; filler < ((1 << (sizeof(filler) * 8) ) - 1); filler++) {
- if(tmdhash[0]) {
- p_tmd->fill3 = filler;
- SHA1((unsigned char*)p_tmd, TMD_SIZE(p_tmd), tmdhash);
- }
- if(tikhash[0]) {
- p_tik->padding = filler;
- SHA1((unsigned char*)p_tik, sizeof(tik), tikhash);
- }
- if(!tmdhash[0] && !tikhash[0]) break;
- }
- if(tmdhash[0] || tikhash[0]) {
- printf("failed! What?\n");
- return quit(~1);
- }
- printf("OK!\n");
-*/
printf("> Removing Photo Channel 1.0... ");
- tikview
- *views = NULL,
- view ATTRIBUTE_ALIGN(0x20) = {};
- unsigned int viewcnt = 0;
-
- ret = ES_GetNumTicketViews(tid_new, &viewcnt);
- if (ret < 0) {
- printf("failed! (get view count, %d)\n", ret);
- return quit(ret);
- }
-
- if(viewcnt) {
- views = memalign(0x20, sizeof(tikview) * viewcnt);
- if (!views) {
- printf("No memory?\n");
- return -ENOMEM;
- }
-
- ret = ES_GetTicketViews(tid_new, views, viewcnt);
- if (ret < 0) {
- printf("failed! (get views, %d)\n", ret);
- return quit(ret);
- }
-
- for(unsigned int i = 0; i < viewcnt; i++) {
- memcpy(&view, views + i, sizeof(tikview));
- ret = ES_DeleteTicket(&view);
- if (ret < 0) {
- printf("failed! (delete view #%d -> %d)", i, ret);
- return quit(ret);
- }
- }
- free(views);
-
- ES_DeleteTitleContent(tid_new);
- ES_DeleteTitle(tid_new); // not fatal enough to matter tbh
- } else
- printf("not present.. ");
-
- printf("OK!\n");
+ ret = purge_title(tid_new);
+ if (ret < 0) printf("failed..? (%d)\n", ret);
+ else printf("OK!\n");
printf("> Installing ticket... ");
ret = ES_AddTicket(s_tik, STD_SIGNED_TIK_SIZE, s_certs, certs_size, NULL, 0);
if (ret < 0) {
printf("failed! (%d)\n", ret);
- return quit(ret);
+ return ret;
}
printf("OK!\n");
@@ -383,7 +305,7 @@ int main() {
if (ret < 0) {
ES_AddTitleCancel();
printf("failed! (%d)\n", ret);
- return quit(ret);
+ return ret;
}
printf("OK!\n");
@@ -402,7 +324,7 @@ int main() {
if(ret < 0) {
printf("failed! (start, %d)\n", ret);
ES_AddTitleCancel();
- return quit(ret);
+ return ret;
}
cfd = ret;
@@ -412,7 +334,7 @@ int main() {
if(ret < 0) {
printf("failed! (read, %d)\n", ret);
ES_AddTitleCancel();
- return quit(ret);
+ return ret;
}
aeskey encrypt_iv = {};
@@ -437,7 +359,7 @@ int main() {
printf("error! (%d)\n", ret);
ES_AddContentFinish(cfd);
ES_AddTitleCancel();
- return quit(ret);
+ return ret;
}
}
} else {
@@ -449,7 +371,7 @@ int main() {
if(ret < 0) {
printf("failed! (%d)\n", ret);
ES_AddTitleFinish();
- return quit(ret);
+ return ret;
}
printf("OK! (size = %u)\n", _csize);
@@ -458,7 +380,7 @@ int main() {
if(ret < 0) {
printf("failed! (start, %d)\n", ret);
ES_AddTitleCancel();
- return quit(ret);
+ return ret;
}
cfd = ret;
@@ -467,7 +389,7 @@ int main() {
printf("failed! (%d)\n", ret);
ES_AddContentFinish(cfd);
ES_AddTitleCancel();
- return quit(ret);
+ return ret;
}
}
@@ -476,7 +398,7 @@ int main() {
if (ret < 0) {
printf("failed! (%d)\n", ret);
ES_AddTitleCancel();
- return quit(ret);
+ return ret;
}
printf("OK!\n\n");
@@ -486,10 +408,187 @@ int main() {
if (ret < 0) {
printf("failed! (%d)\n", ret);
ES_AddTitleCancel();
- return quit(ret);
+ return ret;
}
printf("OK!\n");
printf("\x1b[42m All done! \x1b[40m\n");
- return quit(0);
+ return 0;
+}
+
+int restore() {
+ int ret = 0;
+ unsigned int certs_size = 0, tik_size = 0, tmd_size = 0;
+ signed_blob *s_certs, *s_tmd, *s_tik;
+
+ int HAAA_rev = get_title_rev(tid_new), HAZA = check_title(tid_stub);
+ OSReport("HAAA rev is %u, HAZA existence level is %d\n", HAAA_rev, HAZA);
+ if(HAAA_rev <= 2 && HAZA >= 2) {
+ printf("Check Data management.\n");
+ return 0;
+ }
+
+ printf("> Initializing network... ");
+ ret = net_init_retry(5);
+ if (ret < 0) {
+ printf(
+ "failed! (%d)" "\n\n"
+
+ "May as well just grab the original Photo Channel 1.0 WAD" "\n\n", ret
+ );
+ return ret;
+ }
+ uint32_t hostip = net_gethostip();
+ printf("OK! Wii IP address: %hhu.%hhu.%hhu.%hhu\n\n",
+ hostip >>24, hostip >>16, hostip >>8, hostip >>0);
+ offline_mode = false;
+
+ printf("> Removing Photo Channel 1.0*... ");
+ ret = purge_title(tid_new);
+ if (ret < 0) printf("failed? (%d)...\n", ret);
+ else printf("OK!\n");
+
+ printf("> Reading certs... ");
+ s_certs = FS_Read("/sys/cert.sys", &certs_size, &ret);
+ if (ret < 0) {
+ printf("failed! (%d)\n", ret);
+ return ret;
+ }
+ printf("OK! (size = %u)\n", certs_size);
+
+ printf("> Downloading TMD... ");
+ s_tmd = fetch_tmd(tid_new, &tmd_size, &ret);
+ if (ret < 0) {
+ printf("failed! (%d)\n", ret);
+ return ret;
+ }
+ printf("OK! (size = %u)\n", tmd_size);
+ tmd* p_tmd = SIGNATURE_PAYLOAD(s_tmd);
+
+ printf("> Downloading ticket... ");
+ s_tik = fetch_tik(tid_new, &tik_size, &ret);
+ if (ret < 0) {
+ printf("failed! (%d)\n", ret);
+ return ret;
+ }
+ printf("OK! (size = %u)\n", tik_size);
+
+ printf("> Installing ticket... ");
+ ret = ES_AddTicket(s_tik, STD_SIGNED_TIK_SIZE, s_certs, certs_size, NULL, 0);
+ if (ret < 0) {
+ printf("failed! (%d)\n", ret);
+ return ret;
+ }
+ printf("OK!\n");
+
+ printf("> Starting title installation... ");
+ ret = ES_AddTitleStart(s_tmd, SIGNED_TMD_SIZE(s_tmd), s_certs, certs_size, NULL, 0);
+ if (ret < 0) {
+ ES_AddTitleCancel();
+ printf("failed! (%d)\n", ret);
+ return ret;
+ }
+ printf("OK!\n");
+
+ for (unsigned int i = 0; i < p_tmd->num_contents; ++i) {
+ tmd_content* content = p_tmd->contents + i;
+ unsigned int cid = content->cid, csize = content->size, align_csize = ALIGN(csize, 0x20), _csize = 0;
+ unsigned char* buffer = NULL;
+ int cfd = 0;
+
+ printf(">> Downloading content %08x... ", cid);
+
+ ret = ES_AddContentStart(p_tmd->title_id, cid);
+ if (ret < 0) {
+ ES_AddTitleCancel();
+ printf("failed! (start, %d)\n", ret);
+ return ret;
+ }
+ cfd = ret;
+
+ char cidstr[9];
+ sprintf(cidstr, "%08x", cid);
+
+ buffer = NUS_Download(tid_new, cidstr, &_csize, &ret);
+ if (ret < 0) {
+ ES_AddContentFinish(cfd);
+ ES_AddTitleCancel();
+ printf("failed! (%d)\n", ret);
+ return ret;
+ }
+ printf("OK! (size = %u)\n", _csize);
+
+ printf(">> Installing... ");
+ ret = ES_AddContentData(cfd, buffer, align_csize);
+ if (ret < 0) {
+ ES_AddContentFinish(cfd);
+ ES_AddTitleCancel();
+ printf("failed! (%d)\n", ret);
+ return ret;
+ }
+
+ ret = ES_AddContentFinish(cfd);
+ if (ret < 0) {
+ ES_AddTitleCancel();
+ printf("failed! (%d)\n", ret);
+ return ret;
+ }
+ printf("OK!\n");
+ }
+ ret = ES_AddTitleFinish();
+ if (ret < 0) {
+ printf("failed! (%d)\n", ret);
+ return ret;
+ }
+
+ printf("OK!!\n");
+ return 0;
+}
+
+int main() {
+ int ret = 0;
+
+ init_video(2, 0);
+ printf(header);
+
+ if(!check_dolphin()) {
+ printf("Applying IOS patches... ");
+ ret = IosPatch_RUNTIME(true, false, true, false);
+ if (ret < 0) {
+ printf("Failed! (%d)" "\n"
+ "Is your Homebrew Channel updated?" "\n"
+ "Is in meta.xml?" "\n\n"
+
+ "Exiting in 5s...", ret);
+ sleep(5);
+ return 0x0D800064;
+ }
+ printf("OK! (patch count = %d)\n\n", ret);
+ }
+ WPAD_Init();
+ PAD_Init();
+ ISFS_Initialize();
+
+ printf(
+ "Super basic menu" "\n\n"
+
+ "Press A to install Photo Channel 1.1." "\n"
+ "Press B to restore Photo Channel 1.0." "\n"
+ "Press HOME/START to return to loader." "\n"
+ );
+
+ while(true) {
+ unsigned int input = input_scan(0);
+ if(input & input_a) {
+ ret = install();
+ break;
+ }
+ else if(input & input_b) {
+ ret = restore();
+ break;
+ }
+ else if(input & input_home) return 0;
+ }
+ return quit(ret);
+
}
diff --git a/source/tools.c b/source/tools.c
index 2e743e2..20fcd3e 100644
--- a/source/tools.c
+++ b/source/tools.c
@@ -1,6 +1,7 @@
#include "tools.h"
#include
+#include
#include
#include
#include
@@ -25,7 +26,23 @@ void init_video(int row, int col) {
VIDEO_Flush();
VIDEO_WaitVSync();
if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
- printf("\x1b[%d;%dH", row, col);
+ //printf("\x1b[%d;%dH", row, col);
+}
+
+unsigned int check_title(const uint64_t tid) {
+ OSReport("check_title(%016llx)\n", tid);
+ unsigned int lv = 0, viewcnt = 0, cnt = 0;
+
+ ES_GetNumTicketViews(tid, &viewcnt);
+ OSReport("* view count: %u\n", viewcnt);
+ if (viewcnt) {
+ lv++;
+ ES_GetTitleContentsCount(tid, &cnt);
+ OSReport("* contents count: %u\n", cnt);
+ if (cnt) lv++;
+ }
+ OSReport("total existence level is %u\n", lv);
+ return lv;
}
bool check_dolphin(void) {
@@ -38,14 +55,33 @@ bool check_dolphin(void) {
}
bool check_vwii(void) {
- if(!~vwii) {
- unsigned int cnt = 0;
- ES_GetTitleContentsCount(0x0000000100000200LL, &cnt);
- vwii = cnt > 0;
- }
- return vwii;
+ if(!~vwii) vwii = check_title( 1LL << 32 | 0x200 ) >= 2;
+ return vwii;
+}
+
+int get_title_rev(const uint64_t tid) {
+ unsigned int viewsize = 0;
+
+ ES_GetTMDViewSize(tid, &viewsize);
+ if(!viewsize) return -ENOENT;
+
+ unsigned char _view[viewsize] ATTRIBUTE_ALIGN(0x20);
+ tmd_view* view = (void*)_view;
+
+ int ret = ES_GetTMDView(tid, _view, viewsize);
+ if(ret < 0) return ret;
+ else return view->title_version;
}
+int net_init_retry(unsigned int retries) {
+ int ret = 0;
+ for (int s = 0; s < 5; s++) {
+ ret = net_init();
+ if(!ret || ret != -EAGAIN) break;
+ }
+
+ return ret;
+}
uint16_t input_scan(uint16_t value) {
uint16_t input = 0x00;