// Copyright (c) 2012- PPSSPP Project.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
// GNU General Public License 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <map>
#include <vector>
#include "Common/ChunkFile.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/FunctionWrappers.h"
#include "Core/CoreTiming.h"
#include "Core/MemMap.h"
#include "Core/Reporting.h"
#include "Core/Config.h"

#include "Core/HLE/scePower.h"
#include "Core/HLE/sceKernelThread.h"
#include "Core/HLE/sceKernelInterrupt.h"

struct VolatileWaitingThread {
	SceUID threadID;
	u32 addrPtr;
	u32 sizePtr;
};

const int PSP_POWER_ERROR_TAKEN_SLOT = 0x80000020;
const int PSP_POWER_ERROR_SLOTS_FULL = 0x80000022;
const int PSP_POWER_ERROR_EMPTY_SLOT = 0x80000025;
const int PSP_POWER_ERROR_INVALID_CB = 0x80000100;
const int PSP_POWER_ERROR_INVALID_SLOT = 0x80000102;

const int PSP_POWER_CB_AC_POWER = 0x00001000;
const int PSP_POWER_CB_BATTERY_EXIST = 0x00000080;
const int PSP_POWER_CB_BATTERY_FULL = 0x00000064;

const int POWER_CB_AUTO = -1;

// These are the callback slots for user mode applications.
const int numberOfCBPowerSlots = 16;

// These are the callback slots for kernel mode applications.
const int numberOfCBPowerSlotsPrivate = 32;

static bool volatileMemLocked;
static int powerCbSlots[numberOfCBPowerSlots];
static std::vector<VolatileWaitingThread> volatileWaitingThreads;

// Should this belong here, or in CoreTiming?
static int RealpllFreq = 222000000;
static int RealbusFreq = 111000000;
static int pllFreq = 222000000;
static int busFreq = 111000000;

// The CPU mhz can only be a multiple of the PLL divided by 511.
int PowerCpuMhzToHz(int desired, int pllHz) {
	double maxfreq = desired * 1000000.0;

	double step = (double)pllHz / 511.0;
	// These values seem to be locked.
	if (pllHz >= 333000000 && desired == 333) {
		return 333000000;
	}  else if (pllHz >= 222000000 && desired == 222) {
		return 222000000;
	}

	double freq = 0;
	while (freq + step < maxfreq) {
		freq += step;
	}

	// We match the PSP's HLE funcs better when we have the same float error, it seems.
	return (float)(freq / 1000000.0f) * 1000000;
}

int PowerPllMhzToHz(int mhz) {
	// These seem to be the only steps it has.
	if (mhz <= 190)
		return 190285721;
	if (mhz <= 222)
		return 222000000;
	if (mhz <= 266)
		return 266399994;
	if (mhz <= 333)
		return 333000000;
	return mhz * 1000000;
}

int PowerBusMhzToHz(int mhz) {
	// These seem to be the only steps it has.
	if (mhz <= 95)
		return 95142860;
	if (mhz <= 111)
		return 111000000;
	if (mhz <= 133)
		return 133199997;
	if (mhz <= 166)
		return 166500000;
	return mhz * 1000000;
}

void __PowerInit() {
	memset(powerCbSlots, 0, sizeof(powerCbSlots));
	volatileMemLocked = false;
	volatileWaitingThreads.clear();

	if (g_Config.iLockedCPUSpeed > 0) {
		pllFreq = PowerPllMhzToHz(g_Config.iLockedCPUSpeed);
		busFreq = PowerBusMhzToHz(pllFreq / 2000000);
		CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(g_Config.iLockedCPUSpeed, pllFreq));
	} else {
		pllFreq = PowerPllMhzToHz(222);
		busFreq = PowerBusMhzToHz(111);
	}
	RealpllFreq = PowerPllMhzToHz(222);
	RealbusFreq = PowerBusMhzToHz(111);
}

void __PowerDoState(PointerWrap &p) {
	auto s = p.Section("scePower",1,2);
	if (!s)
		return;

	if (s >= 2) {
		p.Do(RealpllFreq);
		p.Do(RealbusFreq);

		if (RealpllFreq < 1000000)
			RealpllFreq = PowerPllMhzToHz(RealpllFreq);
		if (RealbusFreq < 1000000)
			RealbusFreq = PowerBusMhzToHz(RealbusFreq);
	} else {
		RealpllFreq = PowerPllMhzToHz(222);
		RealbusFreq = PowerBusMhzToHz(111);
	}
	if (g_Config.iLockedCPUSpeed > 0) {
		pllFreq = PowerPllMhzToHz(g_Config.iLockedCPUSpeed);
		busFreq = PowerBusMhzToHz(pllFreq / 2000000);
		CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(g_Config.iLockedCPUSpeed, pllFreq));
	} else {
		pllFreq = RealpllFreq;
		busFreq = RealbusFreq;
	}
	p.DoArray(powerCbSlots, ARRAY_SIZE(powerCbSlots));
	p.Do(volatileMemLocked);
	p.Do(volatileWaitingThreads);
}

static int scePowerGetBatteryLifePercent() {
	DEBUG_LOG(HLE, "100=scePowerGetBatteryLifePercent");
	return 100;
}

static int scePowerGetBatteryLifeTime() {
	DEBUG_LOG(HLE, "0=scePowerGetBatteryLifeTime()");
	// 0 means we're on AC power.
	return 0;
}

static int scePowerGetBatteryTemp() {
	DEBUG_LOG(HLE, "0=scePowerGetBatteryTemp()");
	// 0 means celsius temperature of the battery
	return 0;
}

static int scePowerIsPowerOnline() {
	DEBUG_LOG(HLE, "1=scePowerIsPowerOnline");
	return 1;
}

static int scePowerIsBatteryExist() {
	DEBUG_LOG(HLE, "1=scePowerIsBatteryExist");
	return 1;
}

static int scePowerIsBatteryCharging() {
	DEBUG_LOG(HLE, "0=scePowerIsBatteryCharging");
	return 0;
}

static int scePowerGetBatteryChargingStatus() {
	DEBUG_LOG(HLE, "0=scePowerGetBatteryChargingStatus");
	return 0;
}

static int scePowerIsLowBattery() {
	DEBUG_LOG(HLE, "0=scePowerIsLowBattery");
	return 0;
}

static int scePowerRegisterCallback(int slot, int cbId) {
	DEBUG_LOG(HLE, "0=scePowerRegisterCallback(%i, %i)", slot, cbId);

	if (slot < -1 || slot >= numberOfCBPowerSlotsPrivate) {
		return PSP_POWER_ERROR_INVALID_SLOT;
	}
	if (slot >= numberOfCBPowerSlots) {
		return SCE_KERNEL_ERROR_PRIV_REQUIRED;
	}
	// TODO: If cbId is invalid return PSP_POWER_ERROR_INVALID_CB.
	if (cbId == 0) {
		return PSP_POWER_ERROR_INVALID_CB;
	}

	int retval = -1;

	if (slot == POWER_CB_AUTO) { // -1 signifies auto select of bank
		for (int i=0; i < numberOfCBPowerSlots; i++) {
			if (powerCbSlots[i] == 0 && retval == -1) { // found an empty slot
				powerCbSlots[i] = cbId;
				retval = i;
			}
		}
		if (retval == -1) {
			return PSP_POWER_ERROR_SLOTS_FULL;
		}
	} else {
		if (powerCbSlots[slot] == 0) {
			powerCbSlots[slot] = cbId;
			retval = 0;
		} else {
			return PSP_POWER_ERROR_TAKEN_SLOT;
		}
	}
	if (retval >= 0) {
		int arg = PSP_POWER_CB_AC_POWER | PSP_POWER_CB_BATTERY_EXIST | PSP_POWER_CB_BATTERY_FULL;
		__KernelNotifyCallback(cbId, arg);
	}
	return retval;
}

static int scePowerUnregisterCallback(int slotId) {
	DEBUG_LOG(HLE, "0=scePowerUnregisterCallback(%i)", slotId);

	if (slotId < 0 || slotId >= numberOfCBPowerSlotsPrivate) {
		return PSP_POWER_ERROR_INVALID_SLOT;
	}
	if (slotId >= numberOfCBPowerSlots) {
		return SCE_KERNEL_ERROR_PRIV_REQUIRED;
	}

	if (powerCbSlots[slotId] != 0) {
		int cbId = powerCbSlots[slotId];
		DEBUG_LOG(HLE, "0=scePowerUnregisterCallback(%i) (cbid = %i)", slotId, cbId);
		powerCbSlots[slotId] = 0;
	} else {
		return PSP_POWER_ERROR_EMPTY_SLOT;
	}

	return 0;
}

static int sceKernelPowerLock(int lockType) {
	DEBUG_LOG(HLE, "0=sceKernelPowerLock(%i)", lockType);
	if (lockType == 0) {
		return 0;
	} else {
		return SCE_KERNEL_ERROR_INVALID_MODE;
	}
}

static int sceKernelPowerUnlock(int lockType) {
	DEBUG_LOG(HLE, "0=sceKernelPowerUnlock(%i)", lockType);
	if (lockType == 0) {
		return 0;
	} else {
		return SCE_KERNEL_ERROR_INVALID_MODE;
	}
}

static int sceKernelPowerTick(int flag) {
	DEBUG_LOG(HLE, "UNIMPL 0=sceKernelPowerTick(%i)", flag);
	return 0;
}

static int __KernelVolatileMemLock(int type, u32 paddr, u32 psize) {
	if (type != 0) {
		return SCE_KERNEL_ERROR_INVALID_MODE;
	}
	if (volatileMemLocked) {
		return SCE_KERNEL_ERROR_POWER_VMEM_IN_USE;
	}

	// Volatile RAM is always at 0x08400000 and is of size 0x00400000.
	// It's always available in the emu.
	// TODO: Should really reserve this properly!
	if (Memory::IsValidAddress(paddr)) {
		Memory::Write_U32(0x08400000, paddr);
	}
	if (Memory::IsValidAddress(psize)) {
		Memory::Write_U32(0x00400000, psize);
	}
	volatileMemLocked = true;

	return 0;
}

static int sceKernelVolatileMemTryLock(int type, u32 paddr, u32 psize) {
	u32 error = __KernelVolatileMemLock(type, paddr, psize);

	switch (error) {
	case 0:
		// HACK: This fixes Crash Tag Team Racing.
		// Should only wait 1200 cycles though according to Unknown's testing,
		// and with that it's still broken. So it's not this, unfortunately.
		// Leaving it in for the 0.9.8 release anyway.
		hleEatCycles(500000);
		DEBUG_LOG(HLE, "sceKernelVolatileMemTryLock(%i, %08x, %08x) - success", type, paddr, psize);
		break;

	case SCE_KERNEL_ERROR_POWER_VMEM_IN_USE:
		ERROR_LOG(HLE, "sceKernelVolatileMemTryLock(%i, %08x, %08x) - already locked!", type, paddr, psize);
		break;

	default:
		ERROR_LOG_REPORT(HLE, "%08x=sceKernelVolatileMemTryLock(%i, %08x, %08x) - error", type, paddr, psize, error);
		break;
	}

	return error;
}

static int sceKernelVolatileMemUnlock(int type) {
	if (type != 0) {
		ERROR_LOG_REPORT(HLE, "sceKernelVolatileMemUnlock(%i) - invalid mode", type);
		return SCE_KERNEL_ERROR_INVALID_MODE;
	}
	if (volatileMemLocked) {
		volatileMemLocked = false;

		// Wake someone, always fifo.
		bool wokeThreads = false;
		u32 error;
		while (!volatileWaitingThreads.empty() && !volatileMemLocked) {
			VolatileWaitingThread waitInfo = volatileWaitingThreads.front();
			volatileWaitingThreads.erase(volatileWaitingThreads.begin());

			int waitID = __KernelGetWaitID(waitInfo.threadID, WAITTYPE_VMEM, error);
			// If they were force-released, just skip.
			if (waitID == 1 && __KernelVolatileMemLock(0, waitInfo.addrPtr, waitInfo.sizePtr) == 0) {
				__KernelResumeThreadFromWait(waitInfo.threadID, 0);
				wokeThreads = true;
			}
		}

		if (wokeThreads) {
			INFO_LOG(HLE, "sceKernelVolatileMemUnlock(%i) handed over to another thread", type);
			hleReSchedule("volatile mem unlocked");
		} else {
			DEBUG_LOG(HLE, "sceKernelVolatileMemUnlock(%i)", type);
		}
	} else {
		ERROR_LOG_REPORT(HLE, "sceKernelVolatileMemUnlock(%i) FAILED - not locked", type);
		// I guess it must use a sema.
		return SCE_KERNEL_ERROR_SEMA_OVF;
	}
	return 0;
}

static int sceKernelVolatileMemLock(int type, u32 paddr, u32 psize) {
	u32 error = 0;

	// If dispatch is disabled or in an interrupt, don't check, just return an error.
	// But still write the addr and size (some games require this to work, and it's testably true.)
	if (!__KernelIsDispatchEnabled()) {
		error = SCE_KERNEL_ERROR_CAN_NOT_WAIT;
	} else if (__IsInInterrupt()) {
		error = SCE_KERNEL_ERROR_ILLEGAL_CONTEXT;
	} else {
		error = __KernelVolatileMemLock(type, paddr, psize);
	}

	switch (error) {
	case 0:
		// Should only wait 1200 cycles though according to Unknown's testing,
		hleEatCycles(1200);
		DEBUG_LOG(HLE, "sceKernelVolatileMemLock(%i, %08x, %08x) - success", type, paddr, psize);
		break;

	case SCE_KERNEL_ERROR_POWER_VMEM_IN_USE:
		{
			WARN_LOG(HLE, "sceKernelVolatileMemLock(%i, %08x, %08x) - already locked, waiting", type, paddr, psize);
			const VolatileWaitingThread waitInfo = { __KernelGetCurThread(), paddr, psize };
			volatileWaitingThreads.push_back(waitInfo);
			__KernelWaitCurThread(WAITTYPE_VMEM, 1, 0, 0, false, "volatile mem waited");
		}
		break;

	case SCE_KERNEL_ERROR_CAN_NOT_WAIT:
		{
			WARN_LOG(HLE, "sceKernelVolatileMemLock(%i, %08x, %08x): dispatch disabled", type, paddr, psize);
			Memory::Write_U32(0x08400000, paddr);
			Memory::Write_U32(0x00400000, psize);
		}
		break;

	case SCE_KERNEL_ERROR_ILLEGAL_CONTEXT:
		{
			WARN_LOG(HLE, "sceKernelVolatileMemLock(%i, %08x, %08x): in interrupt", type, paddr, psize);
			Memory::Write_U32(0x08400000, paddr);
			Memory::Write_U32(0x00400000, psize);
		}
		break;

	default:
		ERROR_LOG_REPORT(HLE, "%08x=sceKernelVolatileMemLock(%i, %08x, %08x) - error", type, paddr, psize, error);
		break;
	}

	return error;
}


static u32 scePowerSetClockFrequency(u32 pllfreq, u32 cpufreq, u32 busfreq) {
	// 190 might (probably) be a typo for 19, but it's what the actual PSP validates against.
	if (pllfreq < 19 || pllfreq < cpufreq || pllfreq > 333) {
		return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid pll frequency");
	}
	if (cpufreq == 0 || cpufreq > 333) {
		return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid cpu frequency");
	}
	if (busfreq == 0 || busfreq > 166) {
		return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid bus frequency");
	}
	// TODO: More restrictions.
	if (g_Config.iLockedCPUSpeed > 0) {
		INFO_LOG(HLE, "scePowerSetClockFrequency(%i,%i,%i): locked by user config at %i, %i, %i", pllfreq, cpufreq, busfreq, g_Config.iLockedCPUSpeed, g_Config.iLockedCPUSpeed, busFreq);
	} else {
		INFO_LOG(HLE, "scePowerSetClockFrequency(%i,%i,%i)", pllfreq, cpufreq, busfreq);
	}
	// Only reschedules when the stepped PLL frequency changes.
	// It seems like the busfreq parameter has no effect (but can cause errors.)
	if (RealpllFreq != PowerPllMhzToHz(pllfreq)) {
		int oldPll = RealpllFreq / 1000000;

		RealpllFreq = PowerPllMhzToHz(pllfreq);
		RealbusFreq = PowerBusMhzToHz(RealpllFreq / 2000000);
		if (g_Config.iLockedCPUSpeed <= 0) {
			pllFreq = RealpllFreq;
			busFreq = RealbusFreq;
			CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq));
		}

		// The delay depends on the source and destination frequency, most are 150ms.
		int newPll = RealpllFreq / 1000000;
		int usec = 150000;
		if ((newPll == 190 && oldPll == 222) || (newPll == 222 && oldPll == 190))
			usec = 15700;
		else if ((newPll == 266 && oldPll == 333) || (newPll == 333 && oldPll == 266))
			usec = 16600;

		return hleDelayResult(0, "scepower set clockFrequency", usec);
	}
	if (g_Config.iLockedCPUSpeed <= 0)
		CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq));
	return 0;
}

static u32 scePowerSetCpuClockFrequency(u32 cpufreq) {
	if (cpufreq == 0 || cpufreq > 333) {
		return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid frequency");
	}
	if (g_Config.iLockedCPUSpeed > 0) {
		return hleLogDebug(SCEMISC, 0, "locked by user config at %i", g_Config.iLockedCPUSpeed);
	}
	CoreTiming::SetClockFrequencyHz(PowerCpuMhzToHz(cpufreq, pllFreq));
	return hleLogSuccessI(SCEMISC, 0);
}

static u32 scePowerSetBusClockFrequency(u32 busfreq) {
	if (busfreq == 0 || busfreq > 111) {
		return hleLogWarning(SCEMISC, SCE_KERNEL_ERROR_INVALID_VALUE, "invalid frequency");
	}
	if (g_Config.iLockedCPUSpeed > 0) {
		return hleLogDebug(SCEMISC, 0, "locked by user config at %i", g_Config.iLockedCPUSpeed / 2);
	}

	// The value passed is validated, but then doesn't seem to matter for the result.
	// However, this sets a different hz than scePowerSetClockFrequency would have.

	if (pllFreq <= 190)
		busFreq = 94956673;
	else if (pllFreq <= 222)
		busFreq = 111000000;
	else if (pllFreq <= 266)
		busFreq = 132939331;
	else if (pllFreq <= 333)
		busFreq = 165848343;
	else
		busFreq = pllFreq / 2;

	return hleLogSuccessI(SCEMISC, 0);
}

static u32 scePowerGetCpuClockFrequencyInt() {
	int cpuFreq = CoreTiming::GetClockFrequencyHz() / 1000000;
	return hleLogSuccessI(SCEMISC, cpuFreq);
}

static u32 scePowerGetPllClockFrequencyInt() {
	return hleLogSuccessInfoI(SCEMISC, pllFreq / 1000000);
}

static u32 scePowerGetBusClockFrequencyInt() {
	return hleLogSuccessInfoI(SCEMISC, busFreq / 1000000);
}

static float scePowerGetCpuClockFrequencyFloat() {
	float cpuFreq = CoreTiming::GetClockFrequencyHz() / 1000000.0f;
	DEBUG_LOG(SCEMISC, "%f=scePowerGetCpuClockFrequencyFloat()", (float)cpuFreq);
	return cpuFreq;
}

static float scePowerGetPllClockFrequencyFloat() {
	INFO_LOG(SCEMISC, "%f=scePowerGetPllClockFrequencyFloat()", (float)pllFreq / 1000000.0f);
	return (float) pllFreq / 1000000.0f;
}

static float scePowerGetBusClockFrequencyFloat() {
	INFO_LOG(SCEMISC, "%f=scePowerGetBusClockFrequencyFloat()", (float)busFreq / 1000000.0f);
	return (float) busFreq / 1000000.0f;
}

static int scePowerTick() {
	DEBUG_LOG(SCEMISC, "scePowerTick()");
	// Don't think we need to do anything.
	return 0;
}


static u32 IsPSPNonFat() {
	DEBUG_LOG(SCEMISC, "%d=scePower_a85880d0_IsPSPNonFat()", g_Config.iPSPModel);

	return g_Config.iPSPModel;  
}

static const HLEFunction scePower[] = {
	{0X04B7766E, &WrapI_II<scePowerRegisterCallback>,         "scePowerRegisterCallback",          'i', "ii" },
	{0X2B51FE2F, nullptr,                                     "scePower_2B51FE2F",                 '?', ""   },
	{0X442BFBAC, nullptr,                                     "scePowerGetBacklightMaximum",       '?', ""   },
	{0XEFD3C963, &WrapI_V<scePowerTick>,                      "scePowerTick",                      'i', ""   },
	{0XEDC13FE5, nullptr,                                     "scePowerGetIdleTimer",              '?', ""   },
	{0X7F30B3B1, nullptr,                                     "scePowerIdleTimerEnable",           '?', ""   },
	{0X972CE941, nullptr,                                     "scePowerIdleTimerDisable",          '?', ""   },
	{0X27F3292C, nullptr,                                     "scePowerBatteryUpdateInfo",         '?', ""   },
	{0XE8E4E204, nullptr,                                     "scePower_E8E4E204",                 '?', ""   },
	{0XB999184C, nullptr,                                     "scePowerGetLowBatteryCapacity",     '?', ""   },
	{0X87440F5E, &WrapI_V<scePowerIsPowerOnline>,             "scePowerIsPowerOnline",             'i', ""   },
	{0X0AFD0D8B, &WrapI_V<scePowerIsBatteryExist>,            "scePowerIsBatteryExist",            'i', ""   },
	{0X1E490401, &WrapI_V<scePowerIsBatteryCharging>,         "scePowerIsBatteryCharging",         'i', ""   },
	{0XB4432BC8, &WrapI_V<scePowerGetBatteryChargingStatus>,  "scePowerGetBatteryChargingStatus",  'i', ""   },
	{0XD3075926, &WrapI_V<scePowerIsLowBattery>,              "scePowerIsLowBattery",              'i', ""   },
	{0X78A1A796, nullptr,                                     "scePowerIsSuspendRequired",         '?', ""   },
	{0X94F5A53F, nullptr,                                     "scePowerGetBatteryRemainCapacity",  '?', ""   },
	{0XFD18A0FF, nullptr,                                     "scePowerGetBatteryFullCapacity",    '?', ""   },
	{0X2085D15D, &WrapI_V<scePowerGetBatteryLifePercent>,     "scePowerGetBatteryLifePercent",     'i', ""   },
	{0X8EFB3FA2, &WrapI_V<scePowerGetBatteryLifeTime>,        "scePowerGetBatteryLifeTime",        'i', ""   },
	{0X28E12023, &WrapI_V<scePowerGetBatteryTemp>,            "scePowerGetBatteryTemp",            'i', ""   },
	{0X862AE1A6, nullptr,                                     "scePowerGetBatteryElec",            '?', ""   },
	{0X483CE86B, nullptr,                                     "scePowerGetBatteryVolt",            '?', ""   },
	{0XCB49F5CE, nullptr,                                     "scePowerGetBatteryChargeCycle",     '?', ""   },
	{0X23436A4A, nullptr,                                     "scePowerGetInnerTemp",              '?', ""   },
	{0X0CD21B1F, nullptr,                                     "scePowerSetPowerSwMode",            '?', ""   },
	{0X165CE085, nullptr,                                     "scePowerGetPowerSwMode",            '?', ""   },
	{0XD6D016EF, nullptr,                                     "scePowerLock",                      '?', ""   },
	{0XCA3D34C1, nullptr,                                     "scePowerUnlock",                    '?', ""   },
	{0XDB62C9CF, nullptr,                                     "scePowerCancelRequest",             '?', ""   },
	{0X7FA406DD, nullptr,                                     "scePowerIsRequest",                 '?', ""   },
	{0X2B7C7CF4, nullptr,                                     "scePowerRequestStandby",            '?', ""   },
	{0XAC32C9CC, nullptr,                                     "scePowerRequestSuspend",            '?', ""   },
	{0X2875994B, nullptr,                                     "scePower_2875994B",                 '?', ""   },
	{0X0074EF9B, nullptr,                                     "scePowerGetResumeCount",            '?', ""   },
	{0XDFA8BAF8, &WrapI_I<scePowerUnregisterCallback>,        "scePowerUnregisterCallback",        'i', "i"  },
	{0XDB9D28DD, &WrapI_I<scePowerUnregisterCallback>,        "scePowerUnregitserCallback",        'i', "i"  },
	{0X843FBF43, &WrapU_U<scePowerSetCpuClockFrequency>,      "scePowerSetCpuClockFrequency",      'x', "x"  },
	{0XB8D7B3FB, &WrapU_U<scePowerSetBusClockFrequency>,      "scePowerSetBusClockFrequency",      'x', "x"  },
	{0XFEE03A2F, &WrapU_V<scePowerGetCpuClockFrequencyInt>,   "scePowerGetCpuClockFrequency",      'x', ""   },
	{0X478FE6F5, &WrapU_V<scePowerGetBusClockFrequencyInt>,   "scePowerGetBusClockFrequency",      'x', ""   },
	{0XFDB5BFE9, &WrapU_V<scePowerGetCpuClockFrequencyInt>,   "scePowerGetCpuClockFrequencyInt",   'x', ""   },
	{0XBD681969, &WrapU_V<scePowerGetBusClockFrequencyInt>,   "scePowerGetBusClockFrequencyInt",   'x', ""   },
	{0XB1A52C83, &WrapF_V<scePowerGetCpuClockFrequencyFloat>, "scePowerGetCpuClockFrequencyFloat", 'f', ""   },
	{0X9BADB3EB, &WrapF_V<scePowerGetBusClockFrequencyFloat>, "scePowerGetBusClockFrequencyFloat", 'f', ""   },
	{0X737486F2, &WrapU_UUU<scePowerSetClockFrequency>,       "scePowerSetClockFrequency",         'x', "xxx"},
	{0X34F9C463, &WrapU_V<scePowerGetPllClockFrequencyInt>,   "scePowerGetPllClockFrequencyInt",   'x', ""   },
	{0XEA382A27, &WrapF_V<scePowerGetPllClockFrequencyFloat>, "scePowerGetPllClockFrequencyFloat", 'f', ""   },
	{0XEBD177D6, &WrapU_UUU<scePowerSetClockFrequency>,       "scePower_EBD177D6",                 'x', "xxx"}, // This is also the same as SetClockFrequency
	{0X469989AD, &WrapU_UUU<scePowerSetClockFrequency>,       "scePower_469989ad",                 'x', "xxx"}, // This is also the same as SetClockFrequency
	{0X545A7F3C, nullptr,                                     "scePower_545A7F3C",                 '?', ""   }, // TODO: Supposedly the same as SetClockFrequency also?
	{0XA4E93389, nullptr,                                     "scePower_A4E93389",                 '?', ""   }, // TODO: Supposedly the same as SetClockFrequency also?
	{0XA85880D0, &WrapU_V<IsPSPNonFat>,                       "scePower_a85880d0_IsPSPNonFat",     'x', ""   },
	{0X3951AF53, nullptr,                                     "scePowerWaitRequestCompletion",     '?', ""   },
	{0X0442D852, nullptr,                                     "scePowerRequestColdReset",          '?', ""   },
	{0XBAFA3DF0, nullptr,                                     "scePowerGetCallbackMode",           '?', ""   },
	{0XA9D22232, nullptr,                                     "scePowerSetCallbackMode",           '?', ""   },

	// These seem to be aliases.
	{0X23C31FFE, &WrapI_IUU<sceKernelVolatileMemLock>,        "scePowerVolatileMemLock",           'i', "ixx"},
	{0XFA97A599, &WrapI_IUU<sceKernelVolatileMemTryLock>,     "scePowerVolatileMemTryLock",        'i', "ixx"},
	{0XB3EDD801, &WrapI_I<sceKernelVolatileMemUnlock>,        "scePowerVolatileMemUnlock",         'i', "i"  },
};

//890129c in tyshooter looks bogus
const HLEFunction sceSuspendForUser[] = {
	{0XEADB1BD7, &WrapI_I<sceKernelPowerLock>,                "sceKernelPowerLock",                'i', "i"  }, //(int param) set param to 0
	{0X3AEE7261, &WrapI_I<sceKernelPowerUnlock>,              "sceKernelPowerUnlock",              'i', "i"  }, //(int param) set param to 0
	{0X090CCB3F, &WrapI_I<sceKernelPowerTick>,                "sceKernelPowerTick",                'i', "i"  },

	// There's an extra 4MB that can be allocated, which seems to be "volatile". These functions
	// let you grab it.
	{0XA14F40B2, &WrapI_IUU<sceKernelVolatileMemTryLock>,     "sceKernelVolatileMemTryLock",       'i', "ixx"},
	{0XA569E425, &WrapI_I<sceKernelVolatileMemUnlock>,        "sceKernelVolatileMemUnlock",        'i', "i"  },
	{0X3E0271D3, &WrapI_IUU<sceKernelVolatileMemLock>,        "sceKernelVolatileMemLock",          'i', "ixx"},
};


void Register_scePower() {
	RegisterModule("scePower",ARRAY_SIZE(scePower),scePower);
}

void Register_sceSuspendForUser() {
	RegisterModule("sceSuspendForUser", ARRAY_SIZE(sceSuspendForUser), sceSuspendForUser);
}
