/* 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 java.net.URI; import java.net.URISyntaxException; import org.apache.log4j.Logger; import jpcsp.HLE.BufferInfo; import jpcsp.HLE.BufferInfo.LengthInfo; import jpcsp.HLE.BufferInfo.Usage; import jpcsp.HLE.CanBeNull; import jpcsp.HLE.HLEFunction; import jpcsp.HLE.HLEModule; import jpcsp.HLE.HLEUnimplemented; import jpcsp.HLE.Modules; import jpcsp.HLE.PspString; import jpcsp.HLE.TPointer; import jpcsp.HLE.TPointer32; import jpcsp.HLE.kernel.types.pspParsedUri; import jpcsp.memory.IMemoryReader; import jpcsp.memory.IMemoryWriter; import jpcsp.memory.MemoryReader; import jpcsp.memory.MemoryWriter; import jpcsp.util.Utilities; public class sceParseUri extends HLEModule { public static Logger log = Modules.getLogger("sceParseUri"); private static final boolean[] escapeCharTable = new boolean[] { true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, false, true, true, false, false, true, false, false, false, false, false, false, false, false, false, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, true, true, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true }; private static final int[] hexTable = new int[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; protected int getHexValue(int hexChar) { if (hexChar >= '0' && hexChar <= '9') { return hexChar - '0'; } if (hexChar >= 'A' && hexChar <= 'F') { return hexChar - 'A' + 10; } if (hexChar >= 'a' && hexChar <= 'f') { return hexChar - 'a' + 10; } return 0; } private int addString(TPointer workArea, int workAreaSize, int offset, String s) { if (s == null) { s = ""; } int length = s.length() + 1; if (offset + length > workAreaSize) { length = workAreaSize - offset; if (length <= 0) { return offset; } } workArea.setStringNZ(offset, length, s); return offset + length; } private String getUriComponent(int componentAddr, int flags, int flag) { if ((flags & flag) == 0 || componentAddr == 0) { return null; } return Utilities.readStringZ(componentAddr); } @HLEFunction(nid = 0x568518C9, version = 150) public int sceUriParse(@CanBeNull @BufferInfo(lengthInfo=LengthInfo.fixedLength, length=44, usage=Usage.out) TPointer parsedUriArea, PspString url, @CanBeNull @BufferInfo(lengthInfo=LengthInfo.nextNextParameter, usage=Usage.out) TPointer workArea, @CanBeNull @BufferInfo(usage=Usage.out) TPointer32 workAreaSizeAddr, int workAreaSize) { if (parsedUriArea.isNull() || workArea.isNull()) { // The required workArea size if maximum the size if the URL + 7 times the null-byte // for string termination. workAreaSizeAddr.setValue(url.getString().length() + 7); return 0; } String urlString = sceHttp.patchUrl(url.getString()); if (!urlString.equals(url.getString())) { log.info(String.format("sceUriParse patched URL '%s' into '%s'", url.getString(), urlString)); } // Parse the URL into URI components URI uri; try { uri = new URI(urlString); } catch (URISyntaxException e) { log.error("parsedUriArea", e); return -1; } // Parsing of the userInfo in the format "<userName>:<password>" String userInfo = uri.getUserInfo(); String userInfoUserName = userInfo; String userInfoPassword = ""; if (userInfo != null) { int userInfoColon = userInfo.indexOf(":"); if (userInfoColon >= 0) { userInfoUserName = userInfo.substring(0, userInfoColon); userInfoPassword = userInfo.substring(userInfoColon + 1); } } String query = uri.getQuery(); if (query != null && query.length() > 0) { query = "?" + query; } pspParsedUri parsedUri = new pspParsedUri(); int offset = 0; if (uri.getSchemeSpecificPart() != null && uri.getSchemeSpecificPart().startsWith("//")) { parsedUri.noSlash = 0; } else { parsedUri.noSlash = 1; } // Store the URI components in sequence into workArea // and store the respective addresses into the parsedUri structure. parsedUri.schemeAddr = workArea.getAddress() + offset; offset = addString(workArea, workAreaSize, offset, uri.getScheme()); parsedUri.userInfoUserNameAddr = workArea.getAddress() + offset; offset = addString(workArea, workAreaSize, offset, userInfoUserName); parsedUri.userInfoPasswordAddr = workArea.getAddress() + offset; offset = addString(workArea, workAreaSize, offset, userInfoPassword); parsedUri.hostAddr = workArea.getAddress() + offset; offset = addString(workArea, workAreaSize, offset, uri.getHost()); parsedUri.pathAddr = workArea.getAddress() + offset; offset = addString(workArea, workAreaSize, offset, uri.getPath()); parsedUri.queryAddr = workArea.getAddress() + offset; offset = addString(workArea, workAreaSize, offset, query); parsedUri.fragmentAddr = workArea.getAddress() + offset; offset = addString(workArea, workAreaSize, offset, uri.getFragment()); if (uri.getPort() < 0) { if ("http".equals(uri.getScheme())) { parsedUri.port = 80; } else if ("https".equals(uri.getScheme())) { parsedUri.port = 443; } else { parsedUri.port = 0; } } else { parsedUri.port = uri.getPort(); } workAreaSizeAddr.setValue(offset); parsedUri.write(parsedUriArea); return 0; } @HLEFunction(nid = 0x7EE318AF, version = 150) public int sceUriBuild(@CanBeNull @BufferInfo(lengthInfo=LengthInfo.nextNextParameter, usage=Usage.out) TPointer workArea, @CanBeNull @BufferInfo(usage=Usage.out) TPointer32 workAreaSizeAddr, int workAreaSize, @BufferInfo(lengthInfo=LengthInfo.fixedLength, length=44, usage=Usage.in) TPointer parsedUriAddr, int flags) { pspParsedUri parsedUri = new pspParsedUri(); parsedUri.read(parsedUriAddr); // Extract the URI components from the parseUri structure String scheme = getUriComponent(parsedUri.schemeAddr, flags, 0x1); String userInfoUserName = getUriComponent(parsedUri.userInfoUserNameAddr, flags, 0x10); String userInfoPassword = getUriComponent(parsedUri.userInfoPasswordAddr, flags, 0x20); String host = getUriComponent(parsedUri.hostAddr, flags, 0x2); String path = getUriComponent(parsedUri.pathAddr, flags, 0x8); String query = getUriComponent(parsedUri.queryAddr, flags, 0x40); String fragment = getUriComponent(parsedUri.fragmentAddr, flags, 0x80); int port = (flags & 0x4) != 0 ? parsedUri.port : -1; // Build the complete URI String uri = ""; if (scheme != null && scheme.length() > 0) { uri += scheme + ":"; } if (parsedUri.noSlash == 0) { uri += "//"; } if (userInfoUserName != null) { uri += userInfoUserName; } if (userInfoPassword != null && userInfoPassword.length() > 0) { uri += ":" + userInfoPassword; } if (host != null) { uri += host; } if (port > 0) { int defaultPort = -1; if (parsedUri.schemeAddr != 0) { String protocol = Utilities.readStringZ(parsedUri.schemeAddr); defaultPort = Utilities.getDefaultPortForProtocol(protocol); } if (port > 0 && port != defaultPort) { uri += ":" + port; } } if (path != null) { uri += path; } if (query != null) { uri += query; } if (fragment != null) { uri += fragment; } // Return the URI and its size if (workArea.isNotNull()) { workArea.setStringNZ(workAreaSize, uri); if (log.isDebugEnabled()) { log.debug(String.format("sceUriBuild returning '%s'", uri)); } } workAreaSizeAddr.setValue(uri.length() + 1); return 0; } @HLEFunction(nid = 0x49E950EC, version = 150) public int sceUriEscape(@CanBeNull TPointer escapedAddr, @CanBeNull TPointer32 escapedLengthAddr, int escapedBufferLength, TPointer source) { IMemoryReader memoryReader = MemoryReader.getMemoryReader(source.getAddress(), 1); IMemoryWriter memoryWriter = null; if (escapedAddr.isNotNull()) { memoryWriter = MemoryWriter.getMemoryWriter(escapedAddr.getAddress(), escapedBufferLength, 1); } int escapedLength = 0; while (true) { int c = memoryReader.readNext(); if (c == 0) { if (escapedAddr.isNotNull()) { if (escapedLength < escapedBufferLength) { memoryWriter.writeNext(c); } } escapedLength++; break; } if (escapeCharTable[c]) { if (escapedAddr.isNotNull()) { if (escapedLength + 3 > escapedBufferLength) { break; } memoryWriter.writeNext('%'); memoryWriter.writeNext(hexTable[c >> 4]); memoryWriter.writeNext(hexTable[c & 0x0F]); } escapedLength += 3; } else { if (escapedAddr.isNotNull()) { if (escapedLength + 1 > escapedBufferLength) { break; } memoryWriter.writeNext(c); } escapedLength++; } } if (memoryWriter != null) { memoryWriter.flush(); } escapedLengthAddr.setValue(escapedLength); return 0; } @HLEFunction(nid = 0x062BB07E, version = 150) public int sceUriUnescape(@CanBeNull TPointer unescapedAddr, @CanBeNull TPointer32 unescapedLengthAddr, int unescapedBufferLength, TPointer source) { IMemoryReader memoryReader = MemoryReader.getMemoryReader(source.getAddress(), 1); IMemoryWriter memoryWriter = null; if (unescapedAddr.isNotNull()) { memoryWriter = MemoryWriter.getMemoryWriter(unescapedAddr.getAddress(), unescapedBufferLength, 1); } int unescapedLength = 0; while (true) { int c = memoryReader.readNext(); if (c == 0) { if (unescapedAddr.isNotNull()) { if (unescapedLength < unescapedBufferLength) { memoryWriter.writeNext(c); } } unescapedLength++; break; } if (unescapedAddr.isNotNull()) { if (unescapedLength + 1 > unescapedBufferLength) { break; } if (c == '%') { int hex1 = memoryReader.readNext(); int hex2 = memoryReader.readNext(); c = (getHexValue(hex1) << 4) + getHexValue(hex2); } // Remark: '+' sign is not unescaped to ' ' by this function memoryWriter.writeNext(c); } unescapedLength++; } if (memoryWriter != null) { memoryWriter.flush(); } unescapedLengthAddr.setValue(unescapedLength); return 0; } @HLEUnimplemented @HLEFunction(nid = 0x8885A782, version = 150) public int sceUriSweepPath(@BufferInfo(lengthInfo=LengthInfo.nextNextParameter, usage=Usage.out) TPointer outputAddr, @BufferInfo(lengthInfo=LengthInfo.nextParameter, usage=Usage.in) TPointer inputAddr, int length) { // TODO Implemented URI path sweeping... outputAddr.memcpy(inputAddr.getAddress(), length); return 0; } }