/* This file is part of jpcsp. Jpcsp 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, either version 3 of the License, or (at your option) any later version. Jpcsp 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 for more details. You should have received a copy of the GNU General Public License along with Jpcsp. If not, see <http://www.gnu.org/licenses/>. */ package jpcsp.HLE.modules; import jpcsp.HLE.CanBeNull; import jpcsp.HLE.HLEFunction; import jpcsp.HLE.HLELogging; import jpcsp.HLE.HLEModule; import jpcsp.HLE.HLEUnimplemented; import jpcsp.HLE.TPointer; import jpcsp.HLE.TPointer32; import java.security.MessageDigest; import java.util.Calendar; import java.util.HashMap; import java.util.Random; import jpcsp.Clock; import jpcsp.Emulator; import jpcsp.State; import jpcsp.Allegrex.compiler.RuntimeContext; import jpcsp.HLE.Modules; import jpcsp.HLE.kernel.managers.SystemTimeManager; import jpcsp.util.Utilities; import org.apache.log4j.Logger; @HLELogging public class UtilsForUser extends HLEModule { public static Logger log = Modules.getLogger("UtilsForUser"); private HashMap<Integer, SceKernelUtilsMt19937Context> Mt19937List; private SceKernelUtilsMd5Context md5Ctx; private SceKernelUtilsSha1Context sha1Ctx; private static class SceKernelUtilsMt19937Context { private Random r; public SceKernelUtilsMt19937Context(TPointer ctxAddr, int seed) { r = new Random(seed); // Overwrite the context memory (628 bytes) ctxAddr.memset((byte) 0xCD, 628); } public int getInt(TPointer ctxAddr) { return r.nextInt(); } } private static class SceKernelUtilsContext { private final String algorithm; private final int hashLength; // Context vars. private int part1; private int part2; private int part3; private int part4; private int part5; private short tmpBytesRemaining; private short tmpBytesCalculated; private long fullDataSize; private byte[] buf; // Internal vars. private byte[] input; protected SceKernelUtilsContext(String algorithm, int hashLength) { this.algorithm = algorithm; this.hashLength = hashLength; part1 = 0; part2 = 0; part3 = 0; part4 = 0; part5 = 0; tmpBytesRemaining = 0; tmpBytesCalculated = 0; fullDataSize = 0; buf = new byte[64]; } public int init(TPointer ctxAddr) { ctxAddr.setValue32(0, part1); ctxAddr.setValue32(4, part2); ctxAddr.setValue32(8, part3); ctxAddr.setValue32(12, part4); ctxAddr.setValue32(16, part5); ctxAddr.setValue16(20, tmpBytesRemaining); ctxAddr.setValue16(22, tmpBytesCalculated); ctxAddr.setValue64(24, fullDataSize); ctxAddr.setArray(32, buf, 64); return 0; } public int update(TPointer ctxAddr, TPointer dataAddr, int dataSize) { input = dataAddr.getArray8(dataSize); return 0; } public int result(TPointer ctxAddr, TPointer resultAddr) { byte[] hash = null; if (input != null) { try { MessageDigest md = MessageDigest.getInstance(algorithm); hash = md.digest(input); } catch (Exception e) { // Ignore... log.warn(String.format("SceKernelUtilsContext(%s).result", algorithm), e); } } if (hash != null) { resultAddr.setArray(hash, hashLength); } return 0; } protected static int digest(TPointer inAddr, int inSize, TPointer outAddr, String algorithm, int hashLength) { byte[] input = inAddr.getArray8(inSize); byte[] hash = null; try { MessageDigest md = MessageDigest.getInstance(algorithm); hash = md.digest(input); } catch (Exception e) { // Ignore... log.warn(String.format("SceKernelUtilsContext(%s).digest", algorithm), e); } if (hash != null) { outAddr.setArray(hash, hashLength); } return 0; } } private static class SceKernelUtilsMd5Context extends SceKernelUtilsContext { private static final String algorithm = "MD5"; public SceKernelUtilsMd5Context() { super(algorithm, 16); } public static int digest(TPointer inAddr, int inSize, TPointer outAddr) { return digest(inAddr, inSize, outAddr, algorithm, 16); } } private static class SceKernelUtilsSha1Context extends SceKernelUtilsContext { private static final String algorithm = "SHA-1"; public SceKernelUtilsSha1Context() { super(algorithm, 20); } public static int digest(TPointer inAddr, int inSize, TPointer outAddr) { return digest(inAddr, inSize, outAddr, algorithm, 20); } } @Override public void start() { Mt19937List = new HashMap<Integer, SceKernelUtilsMt19937Context>(); super.start(); } protected static final int PSP_KERNEL_ICACHE_PROBE_MISS = 0; protected static final int PSP_KERNEL_ICACHE_PROBE_HIT = 1; protected static final int PSP_KERNEL_DCACHE_PROBE_MISS = 0; protected static final int PSP_KERNEL_DCACHE_PROBE_HIT = 1; protected static final int PSP_KERNEL_DCACHE_PROBE_HIT_DIRTY = 2; @HLELogging(level="trace") @HLEFunction(nid = 0xBFA98062, version = 150) public int sceKernelDcacheInvalidateRange(TPointer addr, int size) { return 0; } @HLELogging(level="info") @HLEFunction(nid = 0xC2DF770E, version = 150) public int sceKernelIcacheInvalidateRange(TPointer addr, int size) { if (log.isInfoEnabled()) { log.info(String.format("sceKernelIcacheInvalidateRange addr=%s, size=%d", addr, size)); } RuntimeContext.invalidateRange(addr.getAddress(), size); return 0; } @HLELogging(level="info") @HLEFunction(nid = 0xC8186A58, version = 150) public int sceKernelUtilsMd5Digest(TPointer inAddr, int inSize, TPointer outAddr) { int result = SceKernelUtilsMd5Context.digest(inAddr, inSize, outAddr); if (log.isDebugEnabled()) { log.debug(String.format("sceKernelUtilsMd5Digest input:%s, output:%s", Utilities.getMemoryDump(inAddr.getAddress(), inSize), Utilities.getMemoryDump(outAddr.getAddress(), 16))); } return result; } @HLELogging(level="info") @HLEFunction(nid = 0x9E5C5086, version = 150) public int sceKernelUtilsMd5BlockInit(TPointer md5CtxAddr) { md5Ctx = new SceKernelUtilsMd5Context(); return md5Ctx.init(md5CtxAddr); } @HLELogging(level="info") @HLEFunction(nid = 0x61E1E525, version = 150) public int sceKernelUtilsMd5BlockUpdate(TPointer md5CtxAddr, TPointer inAddr, int inSize) { return md5Ctx.update(md5CtxAddr, inAddr, inSize); } @HLELogging(level="info") @HLEFunction(nid = 0xB8D24E78, version = 150) public int sceKernelUtilsMd5BlockResult(TPointer md5CtxAddr, TPointer outAddr) { return md5Ctx.result(md5CtxAddr, outAddr); } @HLELogging(level="info") @HLEFunction(nid = 0x840259F1, version = 150) public int sceKernelUtilsSha1Digest(TPointer inAddr, int inSize, TPointer outAddr) { return SceKernelUtilsSha1Context.digest(inAddr, inSize, outAddr); } @HLELogging(level="info") @HLEFunction(nid = 0xF8FCD5BA, version = 150) public int sceKernelUtilsSha1BlockInit(TPointer sha1CtxAddr) { sha1Ctx = new SceKernelUtilsSha1Context(); return sha1Ctx.init(sha1CtxAddr); } @HLELogging(level="info") @HLEFunction(nid = 0x346F6DA8, version = 150) public int sceKernelUtilsSha1BlockUpdate(TPointer sha1CtxAddr, TPointer inAddr, int inSize) { return sha1Ctx.update(sha1CtxAddr, inAddr, inSize); } @HLELogging(level="info") @HLEFunction(nid = 0x585F1C09, version = 150) public int sceKernelUtilsSha1BlockResult(TPointer sha1CtxAddr, TPointer outAddr) { return sha1Ctx.result(sha1CtxAddr, outAddr); } @HLEFunction(nid = 0xE860E75E, version = 150) public int sceKernelUtilsMt19937Init(TPointer ctxAddr, int seed) { // We'll use the address of the ctx as a key Mt19937List.remove(ctxAddr.getAddress()); // Remove records of any already existing context at a0 Mt19937List.put(ctxAddr.getAddress(), new SceKernelUtilsMt19937Context(ctxAddr, seed)); return 0; } @HLEFunction(nid = 0x06FB8A63, version = 150) public int sceKernelUtilsMt19937UInt(TPointer ctxAddr) { SceKernelUtilsMt19937Context ctx = Mt19937List.get(ctxAddr.getAddress()); if (ctx == null) { log.warn(String.format("sceKernelUtilsMt19937UInt uninitialised context %s", ctxAddr)); return 0; } return ctx.getInt(ctxAddr); } @HLEFunction(nid = 0x37FB5C42, version = 150) public int sceKernelGetGPI() { int gpi; if (State.debugger != null) { gpi = State.debugger.GetGPI(); if (log.isDebugEnabled()) { log.debug(String.format("sceKernelGetGPI returning 0x%02X", gpi)); } } else { gpi = 0; if (log.isDebugEnabled()) { log.debug("sceKernelGetGPI debugger not enabled"); } } return gpi; } @HLEFunction(nid = 0x6AD345D7, version = 150) public int sceKernelSetGPO(int value) { if (State.debugger != null) { State.debugger.SetGPO(value); } else { if (log.isDebugEnabled()) { log.debug("sceKernelSetGPO debugger not enabled"); } } return 0; } @HLEFunction(nid = 0x91E4F6A7, version = 150) public int sceKernelLibcClock() { return (int) SystemTimeManager.getSystemTime(); } @HLEFunction(nid = 0x27CC57F0, version = 150) public int sceKernelLibcTime(@CanBeNull TPointer32 time_t_addr) { int seconds = (int)(Calendar.getInstance().getTimeInMillis() / 1000); time_t_addr.setValue(seconds); return seconds; } @HLEFunction(nid = 0x71EC4271, version = 150) public int sceKernelLibcGettimeofday(@CanBeNull TPointer32 tp, @CanBeNull TPointer32 tzp) { Clock.TimeNanos currentTimeNano = Emulator.getClock().currentTimeNanos(); int tv_sec = currentTimeNano.seconds; int tv_usec = currentTimeNano.millis * 1000 + currentTimeNano.micros; tp.setValue(0, tv_sec); tp.setValue(4, tv_usec); // PSP always returning 0 for these 2 values: int tz_minuteswest = 0; int tz_dsttime = 0; tzp.setValue(0, tz_minuteswest); tzp.setValue(4, tz_dsttime); return 0; } @HLELogging(level="trace") @HLEFunction(nid = 0x79D1C3FA, version = 150) public void sceKernelDcacheWritebackAll() { } @HLELogging(level="trace") @HLEFunction(nid = 0xB435DEC5, version = 150) public void sceKernelDcacheWritebackInvalidateAll() { } @HLELogging(level="trace") @HLEFunction(nid = 0x3EE30821, version = 150) public int sceKernelDcacheWritebackRange(TPointer addr, int size) { return 0; } @HLELogging(level="trace") @HLEFunction(nid = 0x34B9FA9E, version = 150) public void sceKernelDcacheWritebackInvalidateRange(TPointer addr, int size) { } @HLELogging(level="trace") @HLEFunction(nid = 0x80001C4C, version = 150) public int sceKernelDcacheProbe(TPointer addr) { return PSP_KERNEL_DCACHE_PROBE_HIT; // Dummy } @HLEUnimplemented @HLEFunction(nid = 0x16641D70, version = 150) public int sceKernelDcacheReadTag() { return 0; } @HLELogging(level="info") @HLEFunction(nid = 0x920F104A, version = 150) public void sceKernelIcacheInvalidateAll() { // Some games attempt to change compiled code at runtime // by calling this function. // Use the RuntimeContext to regenerate a compiling context // and restart from there. // This method only works for compiled code being called by // JR $rs // or // JALR $rs, $rd // but not for compiled code being called by // JAL xxxx RuntimeContext.invalidateAll(); } @HLELogging(level="trace") @HLEFunction(nid = 0x4FD31C9D, version = 150) public int sceKernelIcacheProbe(TPointer addr) { return PSP_KERNEL_ICACHE_PROBE_HIT; // Dummy } @HLEUnimplemented @HLEFunction(nid = 0xFB05FAD0, version = 150) public void sceKernelIcacheReadTag() { } }