2016-02-18 21:16:05 -08:00

165 lines
5.2 KiB
C

#include <sys/syslimits.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <3ds.h>
#include <errno.h>
#include "../../list.h"
#include "../../error.h"
#include "../../../util.h"
#include "task.h"
typedef struct {
install_cia_result* result;
FS_MediaType dest;
u64 size;
void* data;
Result (*read)(void* data, u32* bytesRead, void* buffer, u32 size);
Handle cancelEvent;
} install_cia_data;
#define bswap_64(x) \
({ \
uint64_t __x = (x); \
((uint64_t)( \
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \
(uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \
})
static u32 align(u32 offset, u32 alignment) {
return (offset + (alignment - 1)) & ~(alignment - 1);
}
static void task_install_cia_thread(void* arg) {
install_cia_data* data = (install_cia_data*) arg;
memset(data->result, 0, sizeof(install_cia_result));
u32 bufferSize = 1024 * 256;
u8* buffer = (u8*) calloc(1, bufferSize);
if(buffer != NULL) {
bool firstBlock = true;
u64 titleId = 0;
Handle ciaHandle = 0;
u32 bytesRead = 0;
u32 bytesWritten = 0;
u64 offset = 0;
while(offset < data->size) {
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
data->result->cancelled = true;
break;
}
u32 readSize = bufferSize;
if(data->size - offset < readSize) {
readSize = (u32) (data->size - offset);
}
Result readRes = 0;
if(R_FAILED(readRes = data->read(data->data, &bytesRead, buffer, readSize))) {
if(readRes == -1) {
data->result->ioerr = true;
data->result->ioerrno = errno;
} else {
data->result->result = readRes;
}
break;
}
if(firstBlock) {
firstBlock = false;
u32 headerSize = *(u32*) &buffer[0x00];
u32 certSize = *(u32*) &buffer[0x08];
titleId = bswap_64(*(u64*) &buffer[align(headerSize, 64) + align(certSize, 64) + 0x1DC]);
if((titleId >> 32) & 0x8000) {
data->dest = MEDIATYPE_NAND;
}
u8 n3ds = false;
if(R_SUCCEEDED(APT_CheckNew3DS(&n3ds)) && !n3ds && ((titleId >> 28) & 0xF) == 2) {
data->result->wrongSystem = true;
break;
}
if(R_FAILED(data->result->result = AM_StartCiaInstall(data->dest, &ciaHandle))) {
break;
}
}
if(R_FAILED(data->result->result = FSFILE_Write(ciaHandle, &bytesWritten, offset, buffer, bytesRead, 0))) {
break;
}
offset += bytesRead;
}
free(buffer);
if(ciaHandle != 0) {
if(R_FAILED(data->result->result)) {
AM_CancelCIAInstall(ciaHandle);
} else if(R_SUCCEEDED(data->result->result = AM_FinishCiaInstall(ciaHandle))) {
if(titleId == 0x0004013800000002LL || titleId == 0x0004013820000002LL) {
data->result->result = AM_InstallFirm(titleId);
}
}
}
} else {
data->result->result = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY);
}
if(R_FAILED(data->result->result) || data->result->cancelled || data->result->ioerr || data->result->wrongSystem) {
data->result->failed = true;
}
data->result->finished = true;
svcCloseHandle(data->cancelEvent);
free(data);
}
Handle task_install_cia(install_cia_result* result, FS_MediaType dest, u64 size, void* data, Result (*read)(void* data, u32* bytesRead, void* buffer, u32 size)) {
if(result == NULL || size == 0 || read == NULL) {
return 0;
}
install_cia_data* installData = (install_cia_data*) calloc(1, sizeof(install_cia_data));
installData->result = result;
installData->dest = dest;
installData->size = size;
installData->data = data;
installData->read = read;
Result eventRes = svcCreateEvent(&installData->cancelEvent, 1);
if(R_FAILED(eventRes)) {
error_display_res(NULL, NULL, eventRes, "Failed to create CIA installation cancel event.");
free(installData);
return 0;
}
if(threadCreate(task_install_cia_thread, installData, 0x4000, 0x18, 1, true) == NULL) {
error_display(NULL, NULL, "Failed to create CIA installation thread.");
svcCloseHandle(installData->cancelEvent);
free(installData);
return 0;
}
return installData->cancelEvent;
}