Nn hai.rpl

From WiiUBrew
Jump to navigation Jump to search

nn_hai.rpl is used by Wii VC titles to handle getting into vWii mode.

Reverse Engineering

typedef struct _CMPTMode3CMDBuffer {
	char enableDisableFlag;
	unsigned int apdParam;
} CMPTMode3CMDBuffer;

/*	workBuffer must be at least 0xC0 in size, and aligned so that
	a clrlwi 26 on it results in 0 */
void nn::hai::launch::PrepareLaunch(unsigned int* out, void* workBuffer, size_t workBufferSz) {
	nn::hai::error::Error errorInstance; //stack
	nn::hai::error::Init(&errorInstance);
	if (errorInstance) goto quit; //will skip StopIfRequired call

	if (!workBuffer) {
		//much nn::hai::error::Error handling ensues; "Invalid buffer"
		goto quit;
	}

	int slotno = 0; //register
	unsigned int nn_act_status; //also on stack
	nn_act_status = nn::act::Initialize();

	if (nn_act_status & /*who knows? clrrwi. RA, RS, 31... 31?*/) {
		slotno = nn::act::GetSlotNo();
	}

	nn::act::Finalize();

	if (!(nn_act_status & /*same again*/)) {
		//Error handling; "failed to initialize nn::act"
		goto quit;
	}

	unsigned int nn_pdm_status; //register
	nn_pdm_status = nn::pdm::Initialize();
	if (!(nn_pdm_status & /*again*/)) {
		//Error handling; "failed to initialize nn::pdm"
		goto quit;
	}

	nn::pdm::NotifyEnterHaiModeEvent();
	nn::pdm::Finalize();

	unsigned int screenState = CMPTCheckScreenState(); //register
	if (!screenState) {
		//Error handling
		if (screenState == -9) {
			//"HDMI seems not beeing linked to TV\n" [sic]
			//Unlike the other error handlers, this does *not* set a fatal error
			goto quit;
		} else {
			//"failed to check screen state"
			goto quit;
		}
	}

	if (!CMPTAcctSetEulaBySlotNo(slotno)) {
		//Error handling: "failed to set EULA for current account"
		goto quit;
	}

	CMPTMode3CMDBuffer cmd_buffer = {0, 0};
	if (getSysConfig(&cmd_buffer.enableDisableFlag) != 1) {
		//Error handling: "failed to get enable/disable flag of US"
		goto quit;
	}

	unsigned int apd_param = 0; //stack
	if (IM_GetNVParameterWithoutHandleAndItb(3, &apd_param)) {
		//Error handling: "failed to get APD params"
		goto quit;
	}

	unsigned int apd_param2 = 0; //stack
	if (apd_param) {
		if (IM_GetNVParameterWithoutHandleAndItb(4, &apd_param2)) {
			//Error handling; as above
			goto quit;
		}
	}

	cmd_buffer.apdParam = apd_param2;

	if (CMPTExSetWorkBuffer(workBuffer, workBufferSz)) {
		//Error handling: "failed to set work workBuffer for prepare l" [sic]
		goto quit;
	}

	if (CMPTExPrepareLaunch(3, &cmd_buffer, 5)) {
		//Error handling: "failed to prepare compat mode launch"
		//not fatal if return code was -0xA
		goto quit;
	}

quit:
	nn::hai::error::StopIfRequired(&errorInstance);

	unsigned int* mystery_stack_ptr; //completely random place on stack, see for yourself
	for (int i = 5; i != 0; i--) { //praise bdnz
		*out = *mystery_stack_ptr;
		out += 4; //add 4 bytes, not 4 ints
		mystery_stack_ptr += 4;
	}
}

void nn::hai::launch::Launch() {
	nn::hai::error::Error errorInstance; //stack
	nn::hai::error::Init(&errorInstance);
	if (errorInstance != 0 /* wtf C++ */) {
		//Error handling; SetFatalError etc.
	}

	int ret = CMPTExLaunch();
	if (ret) {
		COSWarn(1, "[launch.cpp:%d] Err:", 237);
		COSWarn(1, "failed to launch compat mode: %d\n", ret);
		//Error handling
	}

	nn::hai::error::StopIfRequired(&errorInstance);
}