diff --git a/CMakeLists.txt b/CMakeLists.txt index 6fbdd2a..ae7ea6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,7 +87,7 @@ target_compile_definitions( ) link_libraries(common) -function(addExecutable name address stackTop) +function(addPS1Executable name address stackTop) add_executable(${name} ${ARGN}) target_link_options(${name} PRIVATE -Ttext=0x${address}) @@ -110,13 +110,18 @@ endfunction() # IMPORTANT: these addresses assume the boot executable's size (including code, # heap and stack allocations as well as the resource archive) is <576 KB # (0x90000 bytes). -addExecutable( +addPS1Executable( main 800a0000 801dfff0 src/common/file/fat.cpp src/common/file/file.cpp src/common/file/iso9660.cpp src/common/file/misc.cpp src/common/file/zip.cpp + src/common/util/hash.cpp + src/common/util/log.cpp + src/common/util/misc.cpp + src/common/util/string.cpp + src/common/util/tween.cpp src/common/args.cpp src/common/gpu.cpp src/common/gpufont.cpp @@ -126,7 +131,6 @@ addExecutable( src/common/rom.cpp src/common/romdrivers.cpp src/common/spu.cpp - src/common/util.cpp src/libc/crt0.c src/main/app/app.cpp src/main/app/cartactions.cpp @@ -188,16 +192,18 @@ target_compile_definitions( $<$: #ENABLE_ARGV=1 #ENABLE_LOGGING=1 + NDEBUG=1 > ) function(addLauncher address stackTop) - addExecutable( +addPS1Executable( launcher${address} ${address} ${stackTop} + src/common/util/hash.cpp + src/common/util/misc.cpp src/common/args.cpp src/common/ide.cpp src/common/io.cpp - src/common/util.cpp src/launcher/main.cpp src/libc/crt0.c src/vendor/printf.c @@ -236,12 +242,13 @@ function(addBootStub name resourceName) VERBATIM ) - addExecutable( + addPS1Executable( ${name} 80010000 0 src/boot/crt0.s src/boot/main.cpp + src/common/util/misc.cpp + src/common/util/string.cpp src/common/io.cpp - src/common/util.cpp ) addBinaryFile( ${name} _resourceArchive _resourceArchiveLength diff --git a/cmake/executable.ld b/cmake/executable.ld index be23fa2..f9c3e14 100644 --- a/cmake/executable.ld +++ b/cmake/executable.ld @@ -17,8 +17,7 @@ ENTRY(_start) MEMORY { - KERNEL_RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 0x010000 - APP_RAM (rwx) : ORIGIN = 0x80010000, LENGTH = 0x7f0000 + APP_RAM (rwx) : ORIGIN = 0x80010000, LENGTH = 0x3f0000 } SECTIONS { @@ -108,7 +107,7 @@ SECTIONS { } > APP_RAM /DISCARD/ : { - *(.note.GNU-stack .gnu_debuglink .gnu.lto_*) + *(.note.* .gnu_debuglink .gnu.lto_*) *(.MIPS.abiflags) } } diff --git a/src/boot/main.cpp b/src/boot/main.cpp index 1167500..35e4841 100644 --- a/src/boot/main.cpp +++ b/src/boot/main.cpp @@ -16,8 +16,10 @@ #include #include +#include "common/util/misc.hpp" +#include "common/util/string.hpp" +#include "common/util/templates.hpp" #include "common/io.hpp" -#include "common/util.hpp" #include "ps1/system.h" extern "C" const uint8_t _resourceArchive[]; diff --git a/src/common/args.cpp b/src/common/args.cpp index ac20b9c..42dd075 100644 --- a/src/common/args.cpp +++ b/src/common/args.cpp @@ -16,8 +16,9 @@ #include #include +#include "common/util/hash.hpp" +#include "common/util/templates.hpp" #include "common/args.hpp" -#include "common/util.hpp" namespace args { diff --git a/src/common/args.hpp b/src/common/args.hpp index 2fae9be..6189f8d 100644 --- a/src/common/args.hpp +++ b/src/common/args.hpp @@ -18,7 +18,7 @@ #include #include "common/file/file.hpp" -#include "common/util.hpp" +#include "common/util/misc.hpp" namespace args { diff --git a/src/common/file/fat.cpp b/src/common/file/fat.cpp index 0cf66bf..d015b8b 100644 --- a/src/common/file/fat.cpp +++ b/src/common/file/fat.cpp @@ -18,9 +18,10 @@ #include #include "common/file/fat.hpp" #include "common/file/file.hpp" +#include "common/util/log.hpp" +#include "common/util/misc.hpp" #include "common/ide.hpp" #include "common/io.hpp" -#include "common/util.hpp" #include "ps1/system.h" #include "vendor/diskio.h" #include "vendor/ff.h" diff --git a/src/common/file/file.cpp b/src/common/file/file.cpp index aac0943..a095297 100644 --- a/src/common/file/file.cpp +++ b/src/common/file/file.cpp @@ -19,8 +19,9 @@ #include #include #include "common/file/file.hpp" +#include "common/util/hash.hpp" +#include "common/util/templates.hpp" #include "common/gpu.hpp" -#include "common/util.hpp" namespace file { diff --git a/src/common/file/file.hpp b/src/common/file/file.hpp index 657163d..a027a2e 100644 --- a/src/common/file/file.hpp +++ b/src/common/file/file.hpp @@ -18,9 +18,10 @@ #include #include +#include "common/util/hash.hpp" +#include "common/util/templates.hpp" #include "common/gpu.hpp" #include "common/spu.hpp" -#include "common/util.hpp" namespace file { diff --git a/src/common/file/iso9660.cpp b/src/common/file/iso9660.cpp index ed56729..07013bb 100644 --- a/src/common/file/iso9660.cpp +++ b/src/common/file/iso9660.cpp @@ -18,8 +18,10 @@ #include #include "common/file/file.hpp" #include "common/file/iso9660.hpp" +#include "common/util/hash.hpp" +#include "common/util/log.hpp" +#include "common/util/templates.hpp" #include "common/ide.hpp" -#include "common/util.hpp" namespace file { diff --git a/src/common/file/iso9660.hpp b/src/common/file/iso9660.hpp index ceab721..137217f 100644 --- a/src/common/file/iso9660.hpp +++ b/src/common/file/iso9660.hpp @@ -19,8 +19,8 @@ #include #include #include "common/file/file.hpp" +#include "common/util/templates.hpp" #include "common/ide.hpp" -#include "common/util.hpp" namespace file { diff --git a/src/common/file/misc.cpp b/src/common/file/misc.cpp index b1222b0..466cc2a 100644 --- a/src/common/file/misc.cpp +++ b/src/common/file/misc.cpp @@ -18,7 +18,9 @@ #include #include "common/file/file.hpp" #include "common/file/misc.hpp" -#include "common/util.hpp" +#include "common/util/hash.hpp" +#include "common/util/log.hpp" +#include "common/util/templates.hpp" #include "ps1/pcdrv.h" namespace file { diff --git a/src/common/file/misc.hpp b/src/common/file/misc.hpp index 44b694f..87f7315 100644 --- a/src/common/file/misc.hpp +++ b/src/common/file/misc.hpp @@ -19,7 +19,8 @@ #include #include #include "common/file/file.hpp" -#include "common/util.hpp" +#include "common/util/hash.hpp" +#include "common/util/templates.hpp" #include "ps1/pcdrv.h" namespace file { diff --git a/src/common/file/zip.cpp b/src/common/file/zip.cpp index fdd4098..bea0c4a 100644 --- a/src/common/file/zip.cpp +++ b/src/common/file/zip.cpp @@ -18,7 +18,8 @@ #include #include "common/file/file.hpp" #include "common/file/zip.hpp" -#include "common/util.hpp" +#include "common/util/log.hpp" +#include "common/util/templates.hpp" #include "vendor/miniz.h" namespace file { diff --git a/src/common/gpu.cpp b/src/common/gpu.cpp index 069d063..abdc673 100644 --- a/src/common/gpu.cpp +++ b/src/common/gpu.cpp @@ -17,8 +17,9 @@ #include #include #include +#include "common/util/misc.hpp" +#include "common/util/templates.hpp" #include "common/gpu.hpp" -#include "common/util.hpp" #include "ps1/gpucmd.h" #include "ps1/registers.h" #include "ps1/system.h" diff --git a/src/common/ide.cpp b/src/common/ide.cpp index 7ae853c..cc59eb6 100644 --- a/src/common/ide.cpp +++ b/src/common/ide.cpp @@ -16,10 +16,11 @@ #include #include +#include "common/util/log.hpp" +#include "common/util/templates.hpp" #include "common/ide.hpp" #include "common/idedefs.hpp" #include "common/io.hpp" -#include "common/util.hpp" #include "ps1/registers573.h" #include "ps1/system.h" diff --git a/src/common/ide.hpp b/src/common/ide.hpp index 89c1018..6d7bdb0 100644 --- a/src/common/ide.hpp +++ b/src/common/ide.hpp @@ -18,8 +18,8 @@ #include #include +#include "common/util/templates.hpp" #include "common/idedefs.hpp" -#include "common/util.hpp" #include "ps1/registers573.h" namespace ide { diff --git a/src/common/io.cpp b/src/common/io.cpp index 3c56cb1..ff27d62 100644 --- a/src/common/io.cpp +++ b/src/common/io.cpp @@ -16,8 +16,8 @@ #include #include +#include "common/util/misc.hpp" #include "common/io.hpp" -#include "common/util.hpp" #include "ps1/registers.h" #include "ps1/registers573.h" #include "ps1/system.h" diff --git a/src/common/io.hpp b/src/common/io.hpp index 33b7d1f..ff4cd31 100644 --- a/src/common/io.hpp +++ b/src/common/io.hpp @@ -19,7 +19,7 @@ #include #include #include -#include "common/util.hpp" +#include "common/util/misc.hpp" #include "ps1/registers.h" #include "ps1/registers573.h" #include "ps1/system.h" diff --git a/src/common/rom.cpp b/src/common/rom.cpp index 6c865d3..f7de4d2 100644 --- a/src/common/rom.cpp +++ b/src/common/rom.cpp @@ -16,10 +16,12 @@ #include #include +#include "common/util/hash.hpp" +#include "common/util/log.hpp" +#include "common/util/templates.hpp" #include "common/io.hpp" #include "common/rom.hpp" #include "common/romdrivers.hpp" -#include "common/util.hpp" #include "ps1/registers.h" #include "ps1/registers573.h" diff --git a/src/common/rom.hpp b/src/common/rom.hpp index 0746c20..eb1c4ae 100644 --- a/src/common/rom.hpp +++ b/src/common/rom.hpp @@ -18,7 +18,9 @@ #include #include -#include "common/util.hpp" +#include "common/util/hash.hpp" +#include "common/util/misc.hpp" +#include "common/util/string.hpp" #include "ps1/registers.h" namespace rom { diff --git a/src/common/romdrivers.cpp b/src/common/romdrivers.cpp index dca9700..f86001b 100644 --- a/src/common/romdrivers.cpp +++ b/src/common/romdrivers.cpp @@ -16,6 +16,7 @@ #include #include +#include "common/util/log.hpp" #include "common/rom.hpp" #include "common/romdrivers.hpp" diff --git a/src/common/spu.cpp b/src/common/spu.cpp index 960bf41..7eed88d 100644 --- a/src/common/spu.cpp +++ b/src/common/spu.cpp @@ -16,8 +16,9 @@ #include #include +#include "common/util/misc.hpp" +#include "common/util/templates.hpp" #include "common/spu.hpp" -#include "common/util.hpp" #include "ps1/registers.h" #include "ps1/system.h" diff --git a/src/common/util.cpp b/src/common/util.cpp deleted file mode 100644 index 013c9d7..0000000 --- a/src/common/util.cpp +++ /dev/null @@ -1,701 +0,0 @@ -/* - * 573in1 - Copyright (C) 2022-2024 spicyjpeg - * - * 573in1 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. - * - * 573in1 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 - * 573in1. If not, see . - */ - -#include -#include -#include -#include -#include -#include "common/util.hpp" -#include "ps1/registers.h" -#include "ps1/system.h" - -namespace util { - -/* String hashing */ - -Hash hash(const char *str, char terminator) { - auto _str = reinterpret_cast(str); - Hash value = 0; - - while (*_str && (*_str != terminator)) - value = Hash(*(_str++)) + (value << 6) + (value << 16) - value; - - return value; -} - -Hash hash(const uint8_t *data, size_t length) { - Hash value = 0; - - for (; length; length--) - value = Hash(*(data++)) + (value << 6) + (value << 16) - value; - - return value; -} - -/* Date and time class */ - -bool Date::isValid(void) const { - if ((hour > 23) || (minute > 59) || (second > 59)) - return false; - if ((month < 1) || (month > 12)) - return false; - if ((day < 1) || (day > getMonthDayCount())) - return false; - - return true; -} - -bool Date::isLeapYear(void) const { - if (year % 4) - return false; - if (!(year % 100) && (year % 400)) - return false; - - return true; -} - -int Date::getDayOfWeek(void) const { - // See https://datatracker.ietf.org/doc/html/rfc3339#appendix-B - int _year = year, _month = month - 2; - - if (_month <= 0) { - _month += 12; - _year--; - } - - int century = _year / 100; - _year %= 100; - - int weekday = 0 - + day - + (_month * 26 - 2) / 10 - + _year - + _year / 4 - + century / 4 - + century * 5; - - return weekday % 7; -} - -int Date::getMonthDayCount(void) const { - switch (month) { - case 2: - return isLeapYear() ? 29 : 28; - - case 4: - case 6: - case 9: - case 11: - return 30; - - default: - return 31; - } -} - -uint32_t Date::toDOSTime(void) const { - int _year = year - 1980; - - if (!isValid()) - return 0; - if ((_year < 0) || (_year > 127)) - return 0; - - return 0 - | (_year << 25) - | (month << 21) - | (day << 16) - | (hour << 11) - | (minute << 5) - | (second >> 1); -} - -size_t Date::toString(char *output) const { - if (!isValid()) { - *output = 0; - return 0; - } - - return sprintf( - output, "%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, - second - ); -} - -/* Tween/animation classes */ - -template void Tween::setValue( - int time, T start, T target, int duration -) { - //assert(duration <= 0x800); - - _base = start; - _delta = target - start; - - _endTime = time + duration; - _timeScale = TWEEN_UNIT / duration; -} - -template void Tween::setValue(T target) { - _base = target; - _delta = static_cast(0); - - _endTime = 0; -} - -template T Tween::getValue(int time) const { - int remaining = time - _endTime; - - if (remaining >= 0) - return _base + _delta; - - return _base + ( - _delta * E::apply(remaining * _timeScale + TWEEN_UNIT) - ) / TWEEN_UNIT; -} - -template class Tween; -template class Tween; -template class Tween; -template class Tween; -template class Tween; -template class Tween; -template class Tween; -template class Tween; -template class Tween; - -/* Logging framework */ - -// Global state, I know, but it's a necessary evil. -Logger logger; - -void LogBuffer::clear(void) { - for (auto line : _lines) - line[0] = 0; -} - -char *LogBuffer::allocateLine(void) { - size_t tail = _tail; - _tail = (tail + 1) % MAX_LOG_LINES; - - return _lines[tail]; -} - -void Logger::setLogBuffer(LogBuffer *buffer) { - util::CriticalSection sec; - - _buffer = buffer; -} - -void Logger::setupSyslog(int baudRate) { - util::CriticalSection sec; - - if (baudRate) { - initSerialIO(baudRate); - _enableSyslog = true; - } else { - _enableSyslog = false; - } -} - -void Logger::log(const char *format, ...) { - util::CriticalSection sec; - - va_list ap; - - if (_buffer) { - auto line = _buffer->allocateLine(); - - va_start(ap, format); - vsnprintf(line, MAX_LOG_LINE_LENGTH, format, ap); - va_end(ap); - - if (_enableSyslog) - puts(line); - } else if (_enableSyslog) { - va_start(ap, format); - vprintf(format, ap); - va_end(ap); - - putchar('\n'); - } -} - -/* MD5 hash */ - -static const uint32_t _MD5_SEED[]{ - 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 -}; - -static const uint32_t _MD5_ADD[][16]{ - { - 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, - 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, - 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, - 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 - }, { - 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, - 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, - 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, - 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a - }, { - 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, - 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, - 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, - 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 - }, { - 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, - 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, - 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, - 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 - } -}; - -static const uint8_t _MD5_SHIFT[][4]{ - { 7, 12, 17, 22 }, - { 5, 9, 14, 20 }, - { 4, 11, 16, 23 }, - { 6, 10, 15, 21 } -}; - -MD5::MD5(void) -: _blockCount(0), _bufferLength(0) { - __builtin_memcpy(_state, _MD5_SEED, sizeof(_state)); -} - -void MD5::_flushBlock(const void *data) { - auto input = reinterpret_cast(data); - - assertAligned(data); - - auto a = _state[0], b = _state[1], c = _state[2], d = _state[3]; - - for (int i = 0; i < 16; i++) { - auto _d = d; - auto _e = a + _addF(b, c, d) + input[_indexF(i)] + _MD5_ADD[0][i]; - - d = c; - c = b; - b += rotateLeft(_e, _MD5_SHIFT[0][i % 4]); - a = _d; - } - for (int i = 0; i < 16; i++) { - auto _d = d; - auto _e = a + _addG(b, c, d) + input[_indexG(i)] + _MD5_ADD[1][i]; - - d = c; - c = b; - b += rotateLeft(_e, _MD5_SHIFT[1][i % 4]); - a = _d; - } - for (int i = 0; i < 16; i++) { - auto _d = d; - auto _e = a + _addH(b, c, d) + input[_indexH(i)] + _MD5_ADD[2][i]; - - d = c; - c = b; - b += rotateLeft(_e, _MD5_SHIFT[2][i % 4]); - a = _d; - } - for (int i = 0; i < 16; i++) { - auto _d = d; - auto _e = a + _addI(b, c, d) + input[_indexI(i)] + _MD5_ADD[3][i]; - - d = c; - c = b; - b += rotateLeft(_e, _MD5_SHIFT[3][i % 4]); - a = _d; - } - - _state[0] += a; - _state[1] += b; - _state[2] += c; - _state[3] += d; - _blockCount++; -} - -void MD5::update(const uint8_t *data, size_t length) { - if (_bufferLength > 0) { - auto ptr = &_blockBuffer[_bufferLength]; - auto freeSpace = sizeof(_blockBuffer) - _bufferLength; - - if (length >= freeSpace) { - __builtin_memcpy(ptr, data, freeSpace); - _flushBlock(_blockBuffer); - - data += freeSpace; - length -= freeSpace; - _bufferLength = 0; - } else { - __builtin_memcpy(ptr, data, length); - - _bufferLength += length; - return; - } - } - - // Avoid copying data to the intermediate block buffer whenever possible. - for (; - length >= sizeof(_blockBuffer); - length -= sizeof(_blockBuffer), data += sizeof(_blockBuffer) - ) - _flushBlock(data); - - if (length > 0) { - __builtin_memcpy(_blockBuffer, data, length); - _bufferLength = length; - } -} - -void MD5::digest(uint8_t *output) { - uint64_t length = ( - uint64_t(_blockCount) * sizeof(_blockBuffer) + uint64_t(_bufferLength) - ) * 8; - - _blockBuffer[_bufferLength++] = 1 << 7; - - while (_bufferLength != (sizeof(_blockBuffer) - 8)) { - if (_bufferLength == sizeof(_blockBuffer)) { - _flushBlock(_blockBuffer); - _bufferLength = 0; - } - - _blockBuffer[_bufferLength++] = 0; - } - - for (int i = 8; i > 0; i--, length >>= 8) - _blockBuffer[_bufferLength++] = uint8_t(length & 0xff); - - _flushBlock(_blockBuffer); - __builtin_memcpy(output, _state, sizeof(_state)); -} - -/* LZ4 decompressor */ - -void decompressLZ4( - uint8_t *output, const uint8_t *input, size_t maxOutputLength, - size_t inputLength -) { - auto outputEnd = &output[maxOutputLength]; - auto inputEnd = &input[inputLength]; - - while (input < inputEnd) { - uint8_t token = *(input++); - - // Copy literals from the input stream. - int literalLength = token >> 4; - - if (literalLength == 0xf) { - uint8_t addend; - - do { - addend = *(input++); - literalLength += addend; - } while (addend == 0xff); - } - - for (; literalLength && (output < outputEnd); literalLength--) - *(output++) = *(input++); - if (input >= inputEnd) - break; - - int offset = input[0] | (input[1] << 8); - input += 2; - - // Copy from previously decompressed data. Note that this *must* be done - // one byte at a time, as the compressor relies on out-of-bounds copies - // repeating the last byte. - int copyLength = token & 0xf; - - if (copyLength == 0xf) { - uint8_t addend; - - do { - addend = *(input++); - copyLength += addend; - } while (addend == 0xff); - } - - auto copySource = output - offset; - copyLength += 4; - - for (; copyLength && (output < outputEnd); copyLength--) - *(output++) = *(copySource++); - } -} - -/* CRC calculation */ - -static constexpr uint8_t _CRC8_POLY = 0x8c; -static constexpr uint16_t _CRC16_POLY = 0x1021; -static constexpr uint32_t _CRC32_POLY = 0xedb88320; - -uint8_t dsCRC8(const uint8_t *data, size_t length) { - uint8_t crc = 0; - - for (; length; length--) { - uint8_t value = *(data++); - - for (int bit = 8; bit; bit--) { - uint8_t temp = crc ^ value; - - value >>= 1; - crc >>= 1; - if (temp & 1) - crc ^= _CRC8_POLY; - } - } - - return crc & 0xff; -} - -uint16_t zsCRC16(const uint8_t *data, size_t length) { - uint16_t crc = 0xffff; - - for (; length; length--) { - crc ^= *(data++) << 8; - - for (int bit = 8; bit; bit--) { - uint16_t temp = crc; - - crc <<= 1; - if (temp & (1 << 15)) - crc ^= _CRC16_POLY; - } - } - - return (crc ^ 0xffff) & 0xffff; -} - -uint32_t zipCRC32(const uint8_t *data, size_t length, uint32_t crc) { - // This CRC32 implementation uses a lookup table cached in the scratchpad - // area in order to improve performance. - auto table = reinterpret_cast(CACHE_BASE); - crc = ~crc; - - for (; length; length--) - crc = (crc >> 8) ^ table[(crc ^ *(data++)) & 0xff]; - - return ~crc; -} - -void initZipCRC32(void) { - auto table = reinterpret_cast(CACHE_BASE); - - for (int i = 0; i < 256; i++) { - uint32_t crc = i; - - for (int bit = 8; bit; bit--) { - uint32_t temp = crc; - - crc >>= 1; - if (temp & 1) - crc ^= _CRC32_POLY; - } - - *(table++) = crc; - } -} - -extern "C" uint32_t mz_crc32(uint32_t crc, const uint8_t *data, size_t length) { - return zipCRC32(data, length, crc); -} - -/* String manipulation */ - -const char HEX_CHARSET[]{ "0123456789ABCDEF" }; -const char BASE41_CHARSET[]{ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./:" }; - -size_t hexValueToString(char *output, uint32_t value, size_t numDigits) { - output += numDigits; - *output = 0; - - for (size_t i = numDigits; i; i--, value >>= 4) - *(--output) = HEX_CHARSET[value & 0xf]; - - return numDigits; -} - -size_t hexToString( - char *output, const uint8_t *input, size_t length, char separator -) { - size_t outLength = 0; - - for (; length; length--) { - uint8_t value = *(input++); - - *(output++) = HEX_CHARSET[value >> 4]; - *(output++) = HEX_CHARSET[value & 0xf]; - - if (separator && (length > 1)) { - *(output++) = separator; - outLength += 3; - } else { - outLength += 2; - } - } - - *output = 0; - return outLength; -} - -size_t serialNumberToString(char *output, const uint8_t *input) { - uint32_t value = - input[0] | (input[1] << 8) | (input[2] << 16) | (input[3] << 24); - - return sprintf(output, "%04d-%04d", (value / 10000) % 10000, value % 10000); -} - -// This format is used by Konami's tools to display trace IDs in the TID_81 -// format. -static const char _TRACE_ID_CHECKSUM_CHARSET[]{ "0X987654321" }; - -size_t traceIDToString(char *output, const uint8_t *input) { - uint16_t high = (input[0] << 8) | input[1]; - uint32_t low = - (input[2] << 24) | (input[3] << 16) | (input[4] << 8) | input[5]; - - size_t length = sprintf(&output[1], "%02d-%04d", high % 100, low % 10000); - - // The checksum is calculated in a very weird way: - // code = AB-CDEF - // checksum = (A*7 + B*6 + C*5 + D*4 + E*3 + F*2) % 11 - int checksum = 0, multiplier = 7; - - for (const char *ptr = &output[1]; *ptr; ptr++) { - if (*ptr != '-') - checksum += (*ptr - '0') * (multiplier--); - } - - output[0] = _TRACE_ID_CHECKSUM_CHARSET[checksum % 11]; - return length + 1; -} - -// This encoding is similar to standard base45, but with some problematic -// characters (' ', '$', '%', '*') excluded. -size_t encodeBase41(char *output, const uint8_t *input, size_t length) { - size_t outLength = 0; - - for (int i = length + 1; i > 0; i -= 2) { - int value = *(input++) << 8; - value |= *(input++); - - *(output++) = BASE41_CHARSET[value % 41]; - *(output++) = BASE41_CHARSET[(value / 41) % 41]; - *(output++) = BASE41_CHARSET[value / 1681]; - outLength += 3; - } - - *output = 0; - return outLength; -} - -/* PS1 executable loader */ - -bool ExecutableHeader::validateMagic(void) const { -#if 0 - return ( - hash(magic, sizeof(magic)) == - hash("PS-X EXE\0\0\0\0\0\0\0\0", sizeof(magic), 0) - ); -#else - return true - && (magic[0] == concatenate('P', 'S', '-', 'X')) - && (magic[1] == concatenate(' ', 'E', 'X', 'E')) - && !magic[2] - && !magic[3] - && !(entryPoint % 4) - && !(textOffset % 4) - && !(textLength % 2048) - && !dataLength - && !bssLength; -#endif -} - -ExecutableLoader::ExecutableLoader( - void *entryPoint, void *initialGP, void *stackTop -) : _entryPoint(entryPoint), _initialGP(initialGP), _numArgs(0) { - _argListPtr = reinterpret_cast(uintptr_t(stackTop) & ~7) - - MAX_EXECUTABLE_ARGS; - _currentStackPtr = reinterpret_cast(_argListPtr); -} - -bool ExecutableLoader::addArgument(const char *arg) { - if (_numArgs >= MAX_EXECUTABLE_ARGS) - return false; - - _argListPtr[_numArgs++] = arg; - return true; -} - -bool ExecutableLoader::copyArgument(const char *arg, size_t length) { - if (_numArgs >= MAX_EXECUTABLE_ARGS) - return false; - - // Command-line arguments must be copied to the top of the new stack in - // order to ensure the executable is going to be able to access them at any - // time. - *(--_currentStackPtr) = 0; - _currentStackPtr -= length; - __builtin_memcpy(_currentStackPtr, arg, length); - - _argListPtr[_numArgs++] = _currentStackPtr; - return true; -} - -bool ExecutableLoader::formatArgument(const char *format, ...) { - char buffer[64]; - va_list ap; - - va_start(ap, format); - int length = vsnprintf(buffer, sizeof(buffer), format, ap); - va_end(ap); - - return copyArgument(buffer, length + 1); -} - -[[noreturn]] void ExecutableLoader::run( - int rawArgc, const char *const *rawArgv -) { -#if 0 - disableInterrupts(); - flushCache(); -#endif - - register int a0 __asm__("a0") = rawArgc; - register const char *const *a1 __asm__("a1") = rawArgv; - register void *gp __asm__("gp") = _initialGP; - - auto stackTop = uintptr_t(_currentStackPtr) & ~7; - - // Changing the stack pointer and return address is not something that - // should be done in a C++ function, but hopefully it's fine here since - // we're jumping out right after setting it. - __asm__ volatile( - ".set push\n" - ".set noreorder\n" - "li $ra, %0\n" - "jr %1\n" - "addiu $sp, %2, -8\n" - ".set pop\n" - :: "i"(DEV2_BASE), "r"(_entryPoint), "r"(stackTop), - "r"(a0), "r"(a1), "r"(gp) - ); - __builtin_unreachable(); -} - -} diff --git a/src/common/util.hpp b/src/common/util.hpp deleted file mode 100644 index e495a31..0000000 --- a/src/common/util.hpp +++ /dev/null @@ -1,518 +0,0 @@ -/* - * 573in1 - Copyright (C) 2022-2024 spicyjpeg - * - * 573in1 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. - * - * 573in1 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 - * 573in1. If not, see . - */ - -#pragma once - -#include -#include -#include -#include -#include "ps1/system.h" - -namespace util { - -/* Misc. template utilities */ - -template static inline uint32_t sum(const T *data, size_t length) { - uint32_t value = 0; - - for (; length; length--) - value += uint32_t(*(data++)); - - return value; -} - -template static inline T min(T a, T b) { - return (a < b) ? a : b; -} - -template static inline T max(T a, T b) { - return (a > b) ? a : b; -} - -template static inline T clamp(T value, T minValue, T maxValue) { - return (value < minValue) ? minValue : - ((value > maxValue) ? maxValue : value); -} - -template static inline T rotateLeft(T value, int amount) { - return T((value << amount) | (value >> (sizeof(T) * 8 - amount))); -} - -template static inline T rotateRight(T value, int amount) { - return T((value >> amount) | (value << (sizeof(T) * 8 - amount))); -} - -// These shall only be used with unsigned types. -template static inline T truncateToMultiple(T value, T length) { - return value - (value % length); -} - -template static inline T roundUpToMultiple(T value, T length) { - T diff = value % length; - return diff ? (value - diff + length) : value; -} - -template static inline void assertAligned(X *ptr) { - assert(!(reinterpret_cast(ptr) % alignof(T))); -} - -template static inline void clear(T &obj, uint8_t value = 0) { - __builtin_memset(&obj, value, sizeof(obj)); -} - -template static constexpr inline size_t countOf(T &array) { - return sizeof(array) / sizeof(array[0]); -} - -template static inline T forcedCast(X item) { - return reinterpret_cast(reinterpret_cast(item)); -} - -static constexpr inline uint16_t concatenate(uint8_t a, uint8_t b) { - return a | (b << 8); -} - -static constexpr inline uint32_t concatenate( - uint8_t a, uint8_t b, uint8_t c, uint8_t d -) { - return a | (b << 8) | (c << 16) | (d << 24); -} - -/* String hashing (http://www.cse.yorku.ca/~oz/hash.html) */ - -using Hash = uint32_t; - -template static constexpr inline Hash hash( - const T *const data, size_t length = -1, Hash value = 0 -) { - if (*data && length) - return hash( - &data[1], length - 1, - Hash(*data) + (value << 6) + (value << 16) - value - ); - - return value; -} - -Hash hash(const char *str, char terminator = 0); -Hash hash(const uint8_t *data, size_t length); - -/* Critical section helper */ - -class CriticalSection { -private: - bool _enable; - -public: - inline CriticalSection(void) { - _enable = disableInterrupts(); - } - inline ~CriticalSection(void) { - if (_enable) - enableInterrupts(); - } -}; - -class ThreadCriticalSection { -public: - inline ThreadCriticalSection(void) { - bool enable = disableInterrupts(); - - assert(enable); - } - inline ~ThreadCriticalSection(void) { - enableInterrupts(); - } -}; - -/* Simple "smart" pointer */ - -class Data { -public: - void *ptr; - size_t length; - - inline Data(void) - : ptr(nullptr), length(0) {} - inline ~Data(void) { - destroy(); - } - - template inline T *as(void) { - return reinterpret_cast(ptr); - } - template inline const T *as(void) const { - return reinterpret_cast(ptr); - } - template inline T *allocate(size_t count = 1) { - return reinterpret_cast(allocate(sizeof(T) * count)); - } - - inline void *allocate(size_t _length) { - if (ptr) - delete[] as(); - - ptr = _length ? (new uint8_t[_length]) : nullptr; - length = _length; - - return ptr; - } - inline void destroy(void) { - if (ptr) { - delete[] as(); - ptr = nullptr; - } - } -}; - -/* Simple ring buffer */ - -template class RingBuffer { -private: - T _items[N]; - size_t _head, _tail; - -public: - size_t length; - - inline RingBuffer(void) - : _head(0), _tail(0), length(0) {} - - inline T *pushItem(void) volatile { - if (length >= N) - return nullptr; - - size_t i = _tail; - _tail = (i + 1) % N; - length++; - - return &_items[i]; - } - inline T *popItem(void) volatile { - if (!length) - return nullptr; - - size_t i = _head; - _head = (i + 1) % N; - length--; - - return &_items[i]; - } - inline T *peekItem(void) const { - if (!length) - return nullptr; - - return &_items[_head]; - } -}; - -/* Date and time class */ - -class Date { -public: - uint16_t year; - uint8_t month, day, hour, minute, second; - - inline void reset(void) { - year = 2024; - month = 1; - day = 1; - hour = 0; - minute = 0; - second = 0; - } - - bool isValid(void) const; - bool isLeapYear(void) const; - int getDayOfWeek(void) const; - int getMonthDayCount(void) const; - uint32_t toDOSTime(void) const; - size_t toString(char *output) const; -}; - -/* Tween/animation classes */ - -static constexpr int TWEEN_UNIT = 1 << 12; - -class LinearEasing { -public: - template static inline T apply(T value) { - return value; - } -}; - -class QuadInEasing { -public: - template static inline T apply(T value) { - return (value * value) / TWEEN_UNIT; - } -}; - -class QuadOutEasing { -public: - template static inline T apply(T value) { - return (value * 2) - ((value * value) / TWEEN_UNIT); - } -}; - -template class Tween { -private: - T _base, _delta; - int _endTime, _timeScale; - -public: - inline Tween(void) { - setValue(static_cast(0)); - } - inline Tween(T start) { - setValue(start); - } - - inline T getTargetValue(void) const { - return _base + _delta; - } - inline bool isDone(int time) const { - return time >= _endTime; - } - inline void setValue(int time, T target, int duration) { - setValue(time, getValue(time), target, duration); - } - - void setValue(int time, T start, T target, int duration); - void setValue(T target); - T getValue(int time) const; -}; - -/* Logging framework */ - -static constexpr int MAX_LOG_LINE_LENGTH = 128; -static constexpr int MAX_LOG_LINES = 64; - -class LogBuffer { -private: - char _lines[MAX_LOG_LINES][MAX_LOG_LINE_LENGTH]; - int _tail; - -public: - inline LogBuffer(void) - : _tail(0) { - clear(); - } - - // 0 = last line, 1 = second to last, etc. - inline const char *getLine(int line) const { - return _lines[size_t(_tail - (line + 1)) % MAX_LOG_LINES]; - } - - void clear(void); - char *allocateLine(void); -}; - -class Logger { -private: - LogBuffer *_buffer; - bool _enableSyslog; - -public: - inline Logger(void) - : _buffer(nullptr), _enableSyslog(false) {} - - void setLogBuffer(LogBuffer *buffer); - void setupSyslog(int baudRate); - void log(const char *format, ...); -}; - -extern Logger logger; - -/* PS1 executable loader */ - -static constexpr size_t EXECUTABLE_BODY_OFFSET = 2048; -static constexpr size_t MAX_EXECUTABLE_ARGS = 32; - -class ExecutableHeader { -public: - uint32_t magic[4]; - - uint32_t entryPoint, initialGP; - uint32_t textOffset, textLength; - uint32_t dataOffset, dataLength; - uint32_t bssOffset, bssLength; - uint32_t stackOffset, stackLength; - uint32_t _reserved[5]; - - inline void *getEntryPoint(void) const { - return reinterpret_cast(entryPoint); - } - inline void *getInitialGP(void) const { - return reinterpret_cast(initialGP); - } - inline void *getTextPtr(void) const { - return reinterpret_cast(textOffset); - } - inline void *getStackPtr(void) const { - return reinterpret_cast(stackOffset + stackLength); - } - inline const char *getRegionString(void) const { - return reinterpret_cast(this + 1); - } - inline void relocateText(const void *source) const { - __builtin_memcpy(getTextPtr(), source, textLength); - } - - bool validateMagic(void) const; -}; - -class ExecutableLoader { -private: - void *_entryPoint, *_initialGP; - - size_t _numArgs; - const char **_argListPtr; - char *_currentStackPtr; - -public: - inline bool copyArgument(const char *arg) { - return copyArgument(arg, __builtin_strlen(arg)); - } - [[noreturn]] inline void run(void) { - run(_numArgs, _argListPtr); - } - - ExecutableLoader(void *entryPoint, void *initialGP, void *stackTop); - bool addArgument(const char *arg); - bool copyArgument(const char *arg, size_t length); - bool formatArgument(const char *format, ...); - [[noreturn]] void run(int rawArgc, const char *const *rawArgv); -}; - -/* MD5 hash */ - -class MD5 { -private: - uint32_t _state[4]; - uint8_t _blockBuffer[64]; - size_t _blockCount, _bufferLength; - - static inline int _indexF(int index) { - return index; - } - static inline uint32_t _addF(uint32_t x, uint32_t y, uint32_t z) { - return z ^ (x & (y ^ z)); // (x & y) | ((~x) & z) - } - static inline int _indexG(int index) { - return ((index * 5) + 1) % 16; - } - static inline uint32_t _addG(uint32_t x, uint32_t y, uint32_t z) { - return y ^ (z & (x ^ y)); // (x & z) | (y & (~z)) - } - static inline int _indexH(int index) { - return ((index * 3) + 5) % 16; - } - static inline uint32_t _addH(uint32_t x, uint32_t y, uint32_t z) { - return x ^ y ^ z; - } - static inline int _indexI(int index) { - return (index * 7) % 16; - } - static inline uint32_t _addI(uint32_t x, uint32_t y, uint32_t z) { - return (y ^ (x | (~z))); - } - - void _flushBlock(const void *data); - -public: - MD5(void); - void update(const uint8_t *data, size_t length); - void digest(uint8_t *output); -}; - -/* Other APIs */ - -static inline size_t getLZ4InPlaceMargin(size_t inputLength) { - return (inputLength >> 8) + 32; -} - -void decompressLZ4( - uint8_t *output, const uint8_t *input, size_t maxOutputLength, - size_t inputLength -); - -uint8_t dsCRC8(const uint8_t *data, size_t length); -uint16_t zsCRC16(const uint8_t *data, size_t length); -uint32_t zipCRC32(const uint8_t *data, size_t length, uint32_t crc = 0); -void initZipCRC32(void); - -extern const char HEX_CHARSET[], BASE41_CHARSET[]; - -size_t hexValueToString(char *output, uint32_t value, size_t numDigits = 8); -size_t hexToString( - char *output, const uint8_t *input, size_t length, char separator = 0 -); -size_t serialNumberToString(char *output, const uint8_t *input); -size_t traceIDToString(char *output, const uint8_t *input); -size_t encodeBase41(char *output, const uint8_t *input, size_t length); - -} - -static constexpr inline util::Hash operator""_h( - const char *const literal, size_t length -) { - return util::hash(literal, length); -} - -/* Logging macros */ - -#define LOG(type, fmt, ...) \ - util::logger.log( \ - type ",%s(%d): " fmt, __func__, __LINE__ __VA_OPT__(,) __VA_ARGS__ \ - ) - -#ifdef ENABLE_APP_LOGGING -#define LOG_APP(fmt, ...) LOG("app", fmt __VA_OPT__(,) __VA_ARGS__) -#else -#define LOG_APP(fmt, ...) -#endif - -#ifdef ENABLE_CART_IO_LOGGING -#define LOG_CART_IO(fmt, ...) LOG("cart", fmt __VA_OPT__(,) __VA_ARGS__) -#else -#define LOG_CART_IO(fmt, ...) -#endif - -#ifdef ENABLE_CART_DATA_LOGGING -#define LOG_CART_DATA(fmt, ...) LOG("data", fmt __VA_OPT__(,) __VA_ARGS__) -#else -#define LOG_CART_DATA(fmt, ...) -#endif - -#ifdef ENABLE_ROM_LOGGING -#define LOG_ROM(fmt, ...) LOG("rom", fmt __VA_OPT__(,) __VA_ARGS__) -#else -#define LOG_ROM(fmt, ...) -#endif - -#ifdef ENABLE_IDE_LOGGING -#define LOG_IDE(fmt, ...) LOG("ide", fmt __VA_OPT__(,) __VA_ARGS__) -#else -#define LOG_IDE(fmt, ...) -#endif - -#ifdef ENABLE_FS_LOGGING -#define LOG_FS(fmt, ...) LOG("fs", fmt __VA_OPT__(,) __VA_ARGS__) -#else -#define LOG_FS(fmt, ...) -#endif diff --git a/src/common/util/hash.cpp b/src/common/util/hash.cpp new file mode 100644 index 0000000..894d552 --- /dev/null +++ b/src/common/util/hash.cpp @@ -0,0 +1,272 @@ +/* + * 573in1 - Copyright (C) 2022-2024 spicyjpeg + * + * 573in1 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. + * + * 573in1 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 + * 573in1. If not, see . + */ + +#include +#include +#include "common/util/hash.hpp" +#include "common/util/templates.hpp" +#include "ps1/registers.h" + +namespace util { + +/* String hashing (http://www.cse.yorku.ca/~oz/hash.html) */ + +Hash hash(const char *str, char terminator) { + auto _str = reinterpret_cast(str); + Hash value = 0; + + while (*_str && (*_str != terminator)) + value = Hash(*(_str++)) + (value << 6) + (value << 16) - value; + + return value; +} + +Hash hash(const uint8_t *data, size_t length) { + Hash value = 0; + + for (; length; length--) + value = Hash(*(data++)) + (value << 6) + (value << 16) - value; + + return value; +} + +/* CRC calculation */ + +static constexpr uint8_t _CRC8_POLY = 0x8c; +static constexpr uint16_t _CRC16_POLY = 0x1021; +static constexpr uint32_t _CRC32_POLY = 0xedb88320; + +uint8_t dsCRC8(const uint8_t *data, size_t length) { + uint8_t crc = 0; + + for (; length; length--) { + uint8_t value = *(data++); + + for (int bit = 8; bit; bit--) { + uint8_t temp = crc ^ value; + + value >>= 1; + crc >>= 1; + if (temp & 1) + crc ^= _CRC8_POLY; + } + } + + return crc & 0xff; +} + +uint16_t zsCRC16(const uint8_t *data, size_t length) { + uint16_t crc = 0xffff; + + for (; length; length--) { + crc ^= *(data++) << 8; + + for (int bit = 8; bit; bit--) { + uint16_t temp = crc; + + crc <<= 1; + if (temp & (1 << 15)) + crc ^= _CRC16_POLY; + } + } + + return (crc ^ 0xffff) & 0xffff; +} + +uint32_t zipCRC32(const uint8_t *data, size_t length, uint32_t crc) { + // This CRC32 implementation uses a lookup table cached in the scratchpad + // area in order to improve performance. + auto table = reinterpret_cast(CACHE_BASE); + crc = ~crc; + + for (; length; length--) + crc = (crc >> 8) ^ table[(crc ^ *(data++)) & 0xff]; + + return ~crc; +} + +void initZipCRC32(void) { + auto table = reinterpret_cast(CACHE_BASE); + + for (int i = 0; i < 256; i++) { + uint32_t crc = i; + + for (int bit = 8; bit; bit--) { + uint32_t temp = crc; + + crc >>= 1; + if (temp & 1) + crc ^= _CRC32_POLY; + } + + *(table++) = crc; + } +} + +extern "C" uint32_t mz_crc32(uint32_t crc, const uint8_t *data, size_t length) { + return zipCRC32(data, length, crc); +} + +/* MD5 hash */ + +static const uint32_t _MD5_SEED[]{ + 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 +}; + +static const uint32_t _MD5_ADD[][16]{ + { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821 + }, { + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a + }, { + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665 + }, { + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 + } +}; + +static const uint8_t _MD5_SHIFT[][4]{ + { 7, 12, 17, 22 }, + { 5, 9, 14, 20 }, + { 4, 11, 16, 23 }, + { 6, 10, 15, 21 } +}; + +MD5::MD5(void) +: _blockCount(0), _bufferLength(0) { + __builtin_memcpy(_state, _MD5_SEED, sizeof(_state)); +} + +void MD5::_flushBlock(const void *data) { + auto input = reinterpret_cast(data); + + assertAligned(data); + + auto a = _state[0], b = _state[1], c = _state[2], d = _state[3]; + + for (int i = 0; i < 16; i++) { + auto _d = d; + auto _e = a + _addF(b, c, d) + input[_indexF(i)] + _MD5_ADD[0][i]; + + d = c; + c = b; + b += rotateLeft(_e, _MD5_SHIFT[0][i % 4]); + a = _d; + } + for (int i = 0; i < 16; i++) { + auto _d = d; + auto _e = a + _addG(b, c, d) + input[_indexG(i)] + _MD5_ADD[1][i]; + + d = c; + c = b; + b += rotateLeft(_e, _MD5_SHIFT[1][i % 4]); + a = _d; + } + for (int i = 0; i < 16; i++) { + auto _d = d; + auto _e = a + _addH(b, c, d) + input[_indexH(i)] + _MD5_ADD[2][i]; + + d = c; + c = b; + b += rotateLeft(_e, _MD5_SHIFT[2][i % 4]); + a = _d; + } + for (int i = 0; i < 16; i++) { + auto _d = d; + auto _e = a + _addI(b, c, d) + input[_indexI(i)] + _MD5_ADD[3][i]; + + d = c; + c = b; + b += rotateLeft(_e, _MD5_SHIFT[3][i % 4]); + a = _d; + } + + _state[0] += a; + _state[1] += b; + _state[2] += c; + _state[3] += d; + _blockCount++; +} + +void MD5::update(const uint8_t *data, size_t length) { + if (_bufferLength > 0) { + auto ptr = &_blockBuffer[_bufferLength]; + auto freeSpace = sizeof(_blockBuffer) - _bufferLength; + + if (length >= freeSpace) { + __builtin_memcpy(ptr, data, freeSpace); + _flushBlock(_blockBuffer); + + data += freeSpace; + length -= freeSpace; + _bufferLength = 0; + } else { + __builtin_memcpy(ptr, data, length); + + _bufferLength += length; + return; + } + } + + // Avoid copying data to the intermediate block buffer whenever possible. + for (; + length >= sizeof(_blockBuffer); + length -= sizeof(_blockBuffer), data += sizeof(_blockBuffer) + ) + _flushBlock(data); + + if (length > 0) { + __builtin_memcpy(_blockBuffer, data, length); + _bufferLength = length; + } +} + +void MD5::digest(uint8_t *output) { + uint64_t length = ( + uint64_t(_blockCount) * sizeof(_blockBuffer) + uint64_t(_bufferLength) + ) * 8; + + _blockBuffer[_bufferLength++] = 1 << 7; + + while (_bufferLength != (sizeof(_blockBuffer) - 8)) { + if (_bufferLength == sizeof(_blockBuffer)) { + _flushBlock(_blockBuffer); + _bufferLength = 0; + } + + _blockBuffer[_bufferLength++] = 0; + } + + for (int i = 8; i > 0; i--, length >>= 8) + _blockBuffer[_bufferLength++] = uint8_t(length & 0xff); + + _flushBlock(_blockBuffer); + __builtin_memcpy(output, _state, sizeof(_state)); +} + +} diff --git a/src/common/util/hash.hpp b/src/common/util/hash.hpp new file mode 100644 index 0000000..3c85ec6 --- /dev/null +++ b/src/common/util/hash.hpp @@ -0,0 +1,99 @@ +/* + * 573in1 - Copyright (C) 2022-2024 spicyjpeg + * + * 573in1 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. + * + * 573in1 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 + * 573in1. If not, see . + */ + +#pragma once + +#include +#include + +namespace util { + +/* String hashing (http://www.cse.yorku.ca/~oz/hash.html) */ + +using Hash = uint32_t; + +template static constexpr inline Hash hash( + const T *const data, size_t length = -1, Hash value = 0 +) { + if (*data && length) + return hash( + &data[1], length - 1, + Hash(*data) + (value << 6) + (value << 16) - value + ); + + return value; +} + +Hash hash(const char *str, char terminator = 0); +Hash hash(const uint8_t *data, size_t length); + +/* CRC calculation */ + +uint8_t dsCRC8(const uint8_t *data, size_t length); +uint16_t zsCRC16(const uint8_t *data, size_t length); +uint32_t zipCRC32(const uint8_t *data, size_t length, uint32_t crc = 0); +void initZipCRC32(void); + +/* MD5 hash */ + +class MD5 { +private: + uint32_t _state[4]; + uint8_t _blockBuffer[64]; + size_t _blockCount, _bufferLength; + + static inline int _indexF(int index) { + return index; + } + static inline uint32_t _addF(uint32_t x, uint32_t y, uint32_t z) { + return z ^ (x & (y ^ z)); // (x & y) | ((~x) & z) + } + static inline int _indexG(int index) { + return ((index * 5) + 1) % 16; + } + static inline uint32_t _addG(uint32_t x, uint32_t y, uint32_t z) { + return y ^ (z & (x ^ y)); // (x & z) | (y & (~z)) + } + static inline int _indexH(int index) { + return ((index * 3) + 5) % 16; + } + static inline uint32_t _addH(uint32_t x, uint32_t y, uint32_t z) { + return x ^ y ^ z; + } + static inline int _indexI(int index) { + return (index * 7) % 16; + } + static inline uint32_t _addI(uint32_t x, uint32_t y, uint32_t z) { + return (y ^ (x | (~z))); + } + + void _flushBlock(const void *data); + +public: + MD5(void); + void update(const uint8_t *data, size_t length); + void digest(uint8_t *output); +}; + +} + +/* String hashing operator */ + +static constexpr inline util::Hash operator""_h( + const char *const literal, size_t length +) { + return util::hash(literal, length); +} diff --git a/src/common/util/log.cpp b/src/common/util/log.cpp new file mode 100644 index 0000000..1f56b47 --- /dev/null +++ b/src/common/util/log.cpp @@ -0,0 +1,81 @@ +/* + * 573in1 - Copyright (C) 2022-2024 spicyjpeg + * + * 573in1 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. + * + * 573in1 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 + * 573in1. If not, see . + */ + +#include +#include +#include +#include "common/util/log.hpp" +#include "common/util/misc.hpp" + +namespace util { + +/* Logging framework */ + +Logger logger; + +void LogBuffer::clear(void) { + for (auto line : _lines) + line[0] = 0; +} + +char *LogBuffer::allocateLine(void) { + size_t tail = _tail; + _tail = (tail + 1) % MAX_LOG_LINES; + + return _lines[tail]; +} + +void Logger::setLogBuffer(LogBuffer *buffer) { + CriticalSection sec; + + _buffer = buffer; +} + +void Logger::setupSyslog(int baudRate) { + CriticalSection sec; + + if (baudRate) { + initSerialIO(baudRate); + _enableSyslog = true; + } else { + _enableSyslog = false; + } +} + +void Logger::log(const char *format, ...) { + CriticalSection sec; + + va_list ap; + + if (_buffer) { + auto line = _buffer->allocateLine(); + + va_start(ap, format); + vsnprintf(line, MAX_LOG_LINE_LENGTH, format, ap); + va_end(ap); + + if (_enableSyslog) + puts(line); + } else if (_enableSyslog) { + va_start(ap, format); + vprintf(format, ap); + va_end(ap); + + putchar('\n'); + } +} + +} diff --git a/src/common/util/log.hpp b/src/common/util/log.hpp new file mode 100644 index 0000000..8ab0bf1 --- /dev/null +++ b/src/common/util/log.hpp @@ -0,0 +1,107 @@ +/* + * 573in1 - Copyright (C) 2022-2024 spicyjpeg + * + * 573in1 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. + * + * 573in1 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 + * 573in1. If not, see . + */ + +#pragma once + +#include + +namespace util { + +/* Logging framework */ + +static constexpr int MAX_LOG_LINE_LENGTH = 128; +static constexpr int MAX_LOG_LINES = 64; + +class LogBuffer { +private: + char _lines[MAX_LOG_LINES][MAX_LOG_LINE_LENGTH]; + int _tail; + +public: + inline LogBuffer(void) + : _tail(0) { + clear(); + } + + // 0 = last line, 1 = second to last, etc. + inline const char *getLine(int line) const { + return _lines[size_t(_tail - (line + 1)) % MAX_LOG_LINES]; + } + + void clear(void); + char *allocateLine(void); +}; + +class Logger { +private: + LogBuffer *_buffer; + bool _enableSyslog; + +public: + inline Logger(void) + : _buffer(nullptr), _enableSyslog(false) {} + + void setLogBuffer(LogBuffer *buffer); + void setupSyslog(int baudRate); + void log(const char *format, ...); +}; + +extern Logger logger; + +} + +/* Logging macros */ + +#define LOG(type, fmt, ...) \ + util::logger.log( \ + type ",%s(%d): " fmt, __func__, __LINE__ __VA_OPT__(,) __VA_ARGS__ \ + ) + +#ifdef ENABLE_APP_LOGGING +#define LOG_APP(fmt, ...) LOG("app", fmt __VA_OPT__(,) __VA_ARGS__) +#else +#define LOG_APP(fmt, ...) +#endif + +#ifdef ENABLE_CART_IO_LOGGING +#define LOG_CART_IO(fmt, ...) LOG("cart", fmt __VA_OPT__(,) __VA_ARGS__) +#else +#define LOG_CART_IO(fmt, ...) +#endif + +#ifdef ENABLE_CART_DATA_LOGGING +#define LOG_CART_DATA(fmt, ...) LOG("data", fmt __VA_OPT__(,) __VA_ARGS__) +#else +#define LOG_CART_DATA(fmt, ...) +#endif + +#ifdef ENABLE_ROM_LOGGING +#define LOG_ROM(fmt, ...) LOG("rom", fmt __VA_OPT__(,) __VA_ARGS__) +#else +#define LOG_ROM(fmt, ...) +#endif + +#ifdef ENABLE_IDE_LOGGING +#define LOG_IDE(fmt, ...) LOG("ide", fmt __VA_OPT__(,) __VA_ARGS__) +#else +#define LOG_IDE(fmt, ...) +#endif + +#ifdef ENABLE_FS_LOGGING +#define LOG_FS(fmt, ...) LOG("fs", fmt __VA_OPT__(,) __VA_ARGS__) +#else +#define LOG_FS(fmt, ...) +#endif diff --git a/src/common/util/misc.cpp b/src/common/util/misc.cpp new file mode 100644 index 0000000..fd33b15 --- /dev/null +++ b/src/common/util/misc.cpp @@ -0,0 +1,211 @@ +/* + * 573in1 - Copyright (C) 2022-2024 spicyjpeg + * + * 573in1 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. + * + * 573in1 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 + * 573in1. If not, see . + */ + +#include +#include +#include +#include "common/util/hash.hpp" +#include "common/util/misc.hpp" +#include "common/util/templates.hpp" +#include "ps1/system.h" + +namespace util { + +/* Date and time class */ + +bool Date::isValid(void) const { + if ((hour > 23) || (minute > 59) || (second > 59)) + return false; + if ((month < 1) || (month > 12)) + return false; + if ((day < 1) || (day > getMonthDayCount())) + return false; + + return true; +} + +bool Date::isLeapYear(void) const { + if (year % 4) + return false; + if (!(year % 100) && (year % 400)) + return false; + + return true; +} + +int Date::getDayOfWeek(void) const { + // See https://datatracker.ietf.org/doc/html/rfc3339#appendix-B + int _year = year, _month = month - 2; + + if (_month <= 0) { + _month += 12; + _year--; + } + + int century = _year / 100; + _year %= 100; + + int weekday = 0 + + day + + (_month * 26 - 2) / 10 + + _year + + _year / 4 + + century / 4 + + century * 5; + + return weekday % 7; +} + +int Date::getMonthDayCount(void) const { + switch (month) { + case 2: + return isLeapYear() ? 29 : 28; + + case 4: + case 6: + case 9: + case 11: + return 30; + + default: + return 31; + } +} + +uint32_t Date::toDOSTime(void) const { + int _year = year - 1980; + + if (!isValid()) + return 0; + if ((_year < 0) || (_year > 127)) + return 0; + + return 0 + | (_year << 25) + | (month << 21) + | (day << 16) + | (hour << 11) + | (minute << 5) + | (second >> 1); +} + +size_t Date::toString(char *output) const { + if (!isValid()) { + *output = 0; + return 0; + } + + return sprintf( + output, "%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, + second + ); +} + +/* PS1 executable loader */ + +bool ExecutableHeader::validateMagic(void) const { +#if 0 + return ( + hash(magic, sizeof(magic)) == + hash("PS-X EXE\0\0\0\0\0\0\0\0", sizeof(magic), 0) + ); +#else + return true + && (magic[0] == concatenate('P', 'S', '-', 'X')) + && (magic[1] == concatenate(' ', 'E', 'X', 'E')) + && !magic[2] + && !magic[3] + && !(entryPoint % 4) + && !(textOffset % 4) + && !(textLength % 2048) + && !dataLength + && !bssLength; +#endif +} + +ExecutableLoader::ExecutableLoader( + void *entryPoint, void *initialGP, void *stackTop +) : _entryPoint(entryPoint), _initialGP(initialGP), _numArgs(0) { + _argListPtr = reinterpret_cast(uintptr_t(stackTop) & ~7) + - MAX_EXECUTABLE_ARGS; + _currentStackPtr = reinterpret_cast(_argListPtr); +} + +bool ExecutableLoader::addArgument(const char *arg) { + if (_numArgs >= MAX_EXECUTABLE_ARGS) + return false; + + _argListPtr[_numArgs++] = arg; + return true; +} + +bool ExecutableLoader::copyArgument(const char *arg, size_t length) { + if (_numArgs >= MAX_EXECUTABLE_ARGS) + return false; + + // Command-line arguments must be copied to the top of the new stack in + // order to ensure the executable is going to be able to access them at any + // time. + *(--_currentStackPtr) = 0; + _currentStackPtr -= length; + __builtin_memcpy(_currentStackPtr, arg, length); + + _argListPtr[_numArgs++] = _currentStackPtr; + return true; +} + +bool ExecutableLoader::formatArgument(const char *format, ...) { + char buffer[64]; + va_list ap; + + va_start(ap, format); + int length = vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + + return copyArgument(buffer, length + 1); +} + +[[noreturn]] void ExecutableLoader::run( + int rawArgc, const char *const *rawArgv +) { +#if 0 + disableInterrupts(); + flushCache(); +#endif + + register int a0 __asm__("a0") = rawArgc; + register const char *const *a1 __asm__("a1") = rawArgv; + register void *gp __asm__("gp") = _initialGP; + + auto stackTop = uintptr_t(_currentStackPtr) & ~7; + + // Changing the stack pointer and return address is not something that + // should be done in a C++ function, but hopefully it's fine here since + // we're jumping out right after setting it. + __asm__ volatile( + ".set push\n" + ".set noreorder\n" + "li $ra, %0\n" + "jr %1\n" + "addiu $sp, %2, -8\n" + ".set pop\n" + :: "i"(DEV2_BASE), "r"(_entryPoint), "r"(stackTop), + "r"(a0), "r"(a1), "r"(gp) + ); + __builtin_unreachable(); +} + +} diff --git a/src/common/util/misc.hpp b/src/common/util/misc.hpp new file mode 100644 index 0000000..e1b5e80 --- /dev/null +++ b/src/common/util/misc.hpp @@ -0,0 +1,139 @@ +/* + * 573in1 - Copyright (C) 2022-2024 spicyjpeg + * + * 573in1 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. + * + * 573in1 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 + * 573in1. If not, see . + */ + +#pragma once + +#include +#include +#include +#include "ps1/system.h" + +namespace util { + +/* Date and time class */ + +class Date { +public: + uint16_t year; + uint8_t month, day, hour, minute, second; + + inline void reset(void) { + year = 2024; + month = 1; + day = 1; + hour = 0; + minute = 0; + second = 0; + } + + bool isValid(void) const; + bool isLeapYear(void) const; + int getDayOfWeek(void) const; + int getMonthDayCount(void) const; + uint32_t toDOSTime(void) const; + size_t toString(char *output) const; +}; + +/* Critical section helper */ + +class CriticalSection { +private: + bool _enable; + +public: + inline CriticalSection(void) { + _enable = disableInterrupts(); + } + inline ~CriticalSection(void) { + if (_enable) + enableInterrupts(); + } +}; + +class ThreadCriticalSection { +public: + inline ThreadCriticalSection(void) { + bool enable = disableInterrupts(); + + //assert(enable); + } + inline ~ThreadCriticalSection(void) { + enableInterrupts(); + } +}; + +/* PS1 executable loader */ + +static constexpr size_t EXECUTABLE_BODY_OFFSET = 2048; +static constexpr size_t MAX_EXECUTABLE_ARGS = 32; + +class ExecutableHeader { +public: + uint32_t magic[4]; + + uint32_t entryPoint, initialGP; + uint32_t textOffset, textLength; + uint32_t dataOffset, dataLength; + uint32_t bssOffset, bssLength; + uint32_t stackOffset, stackLength; + uint32_t _reserved[5]; + + inline void *getEntryPoint(void) const { + return reinterpret_cast(entryPoint); + } + inline void *getInitialGP(void) const { + return reinterpret_cast(initialGP); + } + inline void *getTextPtr(void) const { + return reinterpret_cast(textOffset); + } + inline void *getStackPtr(void) const { + return reinterpret_cast(stackOffset + stackLength); + } + inline const char *getRegionString(void) const { + return reinterpret_cast(this + 1); + } + inline void relocateText(const void *source) const { + __builtin_memcpy(getTextPtr(), source, textLength); + } + + bool validateMagic(void) const; +}; + +class ExecutableLoader { +private: + void *_entryPoint, *_initialGP; + + size_t _numArgs; + const char **_argListPtr; + char *_currentStackPtr; + +public: + inline bool copyArgument(const char *arg) { + return copyArgument(arg, __builtin_strlen(arg)); + } + [[noreturn]] inline void run(void) { + run(_numArgs, _argListPtr); + } + + ExecutableLoader(void *entryPoint, void *initialGP, void *stackTop); + bool addArgument(const char *arg); + bool copyArgument(const char *arg, size_t length); + bool formatArgument(const char *format, ...); + [[noreturn]] void run(int rawArgc, const char *const *rawArgv); +}; + +} diff --git a/src/common/util/string.cpp b/src/common/util/string.cpp new file mode 100644 index 0000000..d064817 --- /dev/null +++ b/src/common/util/string.cpp @@ -0,0 +1,167 @@ +/* + * 573in1 - Copyright (C) 2022-2024 spicyjpeg + * + * 573in1 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. + * + * 573in1 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 + * 573in1. If not, see . + */ + +#include +#include +#include +#include "common/util/string.hpp" + +namespace util { + +/* String manipulation */ + +const char HEX_CHARSET[]{ "0123456789ABCDEF" }; +const char BASE41_CHARSET[]{ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+-./:" }; + +size_t hexValueToString(char *output, uint32_t value, size_t numDigits) { + output += numDigits; + *output = 0; + + for (size_t i = numDigits; i; i--, value >>= 4) + *(--output) = HEX_CHARSET[value & 0xf]; + + return numDigits; +} + +size_t hexToString( + char *output, const uint8_t *input, size_t length, char separator +) { + size_t outLength = 0; + + for (; length; length--) { + uint8_t value = *(input++); + + *(output++) = HEX_CHARSET[value >> 4]; + *(output++) = HEX_CHARSET[value & 0xf]; + + if (separator && (length > 1)) { + *(output++) = separator; + outLength += 3; + } else { + outLength += 2; + } + } + + *output = 0; + return outLength; +} + +size_t serialNumberToString(char *output, const uint8_t *input) { + uint32_t value = + input[0] | (input[1] << 8) | (input[2] << 16) | (input[3] << 24); + + return sprintf(output, "%04d-%04d", (value / 10000) % 10000, value % 10000); +} + +// This format is used by Konami's tools to display trace IDs in the TID_81 +// format. +static const char _TRACE_ID_CHECKSUM_CHARSET[]{ "0X987654321" }; + +size_t traceIDToString(char *output, const uint8_t *input) { + uint16_t high = (input[0] << 8) | input[1]; + uint32_t low = + (input[2] << 24) | (input[3] << 16) | (input[4] << 8) | input[5]; + + size_t length = sprintf(&output[1], "%02d-%04d", high % 100, low % 10000); + + // The checksum is calculated in a very weird way: + // code = AB-CDEF + // checksum = (A*7 + B*6 + C*5 + D*4 + E*3 + F*2) % 11 + int checksum = 0, multiplier = 7; + + for (const char *ptr = &output[1]; *ptr; ptr++) { + if (*ptr != '-') + checksum += (*ptr - '0') * (multiplier--); + } + + output[0] = _TRACE_ID_CHECKSUM_CHARSET[checksum % 11]; + return length + 1; +} + +// This encoding is similar to standard base45, but with some problematic +// characters (' ', '$', '%', '*') excluded. +size_t encodeBase41(char *output, const uint8_t *input, size_t length) { + size_t outLength = 0; + + for (int i = length + 1; i > 0; i -= 2) { + int value = *(input++) << 8; + value |= *(input++); + + *(output++) = BASE41_CHARSET[value % 41]; + *(output++) = BASE41_CHARSET[(value / 41) % 41]; + *(output++) = BASE41_CHARSET[value / 1681]; + outLength += 3; + } + + *output = 0; + return outLength; +} + +/* LZ4 decompressor */ + +void decompressLZ4( + uint8_t *output, const uint8_t *input, size_t maxOutputLength, + size_t inputLength +) { + auto outputEnd = &output[maxOutputLength]; + auto inputEnd = &input[inputLength]; + + while (input < inputEnd) { + uint8_t token = *(input++); + + // Copy literals from the input stream. + int literalLength = token >> 4; + + if (literalLength == 0xf) { + uint8_t addend; + + do { + addend = *(input++); + literalLength += addend; + } while (addend == 0xff); + } + + for (; literalLength && (output < outputEnd); literalLength--) + *(output++) = *(input++); + if (input >= inputEnd) + break; + + int offset = input[0] | (input[1] << 8); + input += 2; + + // Copy from previously decompressed data. Note that this *must* be done + // one byte at a time, as the compressor relies on out-of-bounds copies + // repeating the last byte. + int copyLength = token & 0xf; + + if (copyLength == 0xf) { + uint8_t addend; + + do { + addend = *(input++); + copyLength += addend; + } while (addend == 0xff); + } + + auto copySource = output - offset; + copyLength += 4; + + for (; copyLength && (output < outputEnd); copyLength--) + *(output++) = *(copySource++); + } +} + +} diff --git a/src/common/util/string.hpp b/src/common/util/string.hpp new file mode 100644 index 0000000..21efa71 --- /dev/null +++ b/src/common/util/string.hpp @@ -0,0 +1,47 @@ +/* + * 573in1 - Copyright (C) 2022-2024 spicyjpeg + * + * 573in1 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. + * + * 573in1 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 + * 573in1. If not, see . + */ + +#pragma once + +#include +#include + +namespace util { + +/* String manipulation */ + +extern const char HEX_CHARSET[], BASE41_CHARSET[]; + +size_t hexValueToString(char *output, uint32_t value, size_t numDigits = 8); +size_t hexToString( + char *output, const uint8_t *input, size_t length, char separator = 0 +); +size_t serialNumberToString(char *output, const uint8_t *input); +size_t traceIDToString(char *output, const uint8_t *input); +size_t encodeBase41(char *output, const uint8_t *input, size_t length); + +/* LZ4 decompressor */ + +static inline size_t getLZ4InPlaceMargin(size_t inputLength) { + return (inputLength >> 8) + 32; +} + +void decompressLZ4( + uint8_t *output, const uint8_t *input, size_t maxOutputLength, + size_t inputLength +); + +} diff --git a/src/common/util/templates.hpp b/src/common/util/templates.hpp new file mode 100644 index 0000000..684c7b1 --- /dev/null +++ b/src/common/util/templates.hpp @@ -0,0 +1,174 @@ +/* + * 573in1 - Copyright (C) 2022-2024 spicyjpeg + * + * 573in1 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. + * + * 573in1 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 + * 573in1. If not, see . + */ + +#pragma once + +#include +#include +#include + +namespace util { + +/* Misc. template utilities */ + +template static inline uint32_t sum(const T *data, size_t length) { + uint32_t value = 0; + + for (; length; length--) + value += uint32_t(*(data++)); + + return value; +} + +template static inline T min(T a, T b) { + return (a < b) ? a : b; +} + +template static inline T max(T a, T b) { + return (a > b) ? a : b; +} + +template static inline T clamp(T value, T minValue, T maxValue) { + return (value < minValue) ? minValue : + ((value > maxValue) ? maxValue : value); +} + +template static inline T rotateLeft(T value, int amount) { + return T((value << amount) | (value >> (sizeof(T) * 8 - amount))); +} + +template static inline T rotateRight(T value, int amount) { + return T((value >> amount) | (value << (sizeof(T) * 8 - amount))); +} + +// These shall only be used with unsigned types. +template static inline T truncateToMultiple(T value, T length) { + return value - (value % length); +} + +template static inline T roundUpToMultiple(T value, T length) { + T diff = value % length; + return diff ? (value - diff + length) : value; +} + +template static inline void assertAligned(X *ptr) { + //assert(!(reinterpret_cast(ptr) % alignof(T))); +} + +template static inline void clear(T &obj, uint8_t value = 0) { + __builtin_memset(&obj, value, sizeof(obj)); +} + +template static constexpr inline size_t countOf(T &array) { + return sizeof(array) / sizeof(array[0]); +} + +template static inline T forcedCast(X item) { + return reinterpret_cast(reinterpret_cast(item)); +} + +static constexpr inline uint16_t concatenate(uint8_t a, uint8_t b) { + return a | (b << 8); +} + +static constexpr inline uint32_t concatenate( + uint8_t a, uint8_t b, uint8_t c, uint8_t d +) { + return a | (b << 8) | (c << 16) | (d << 24); +} + +/* Simple "smart" pointer */ + +class Data { +public: + void *ptr; + size_t length; + + inline Data(void) + : ptr(nullptr), length(0) {} + inline ~Data(void) { + destroy(); + } + + template inline T *as(void) { + return reinterpret_cast(ptr); + } + template inline const T *as(void) const { + return reinterpret_cast(ptr); + } + template inline T *allocate(size_t count = 1) { + return reinterpret_cast(allocate(sizeof(T) * count)); + } + + inline void *allocate(size_t _length) { + if (ptr) + delete[] as(); + + ptr = _length ? (new uint8_t[_length]) : nullptr; + length = _length; + + return ptr; + } + inline void destroy(void) { + if (ptr) { + delete[] as(); + ptr = nullptr; + } + } +}; + +/* Simple ring buffer */ + +template class RingBuffer { +private: + T _items[N]; + size_t _head, _tail; + +public: + size_t length; + + inline RingBuffer(void) + : _head(0), _tail(0), length(0) {} + + inline T *pushItem(void) volatile { + if (length >= N) + return nullptr; + + size_t i = _tail; + _tail = (i + 1) % N; + length++; + + return &_items[i]; + } + inline T *popItem(void) volatile { + if (!length) + return nullptr; + + size_t i = _head; + _head = (i + 1) % N; + length--; + + return &_items[i]; + } + inline T *peekItem(void) const { + if (!length) + return nullptr; + + return &_items[_head]; + } +}; + +} diff --git a/src/common/util/tween.cpp b/src/common/util/tween.cpp new file mode 100644 index 0000000..ee23a6c --- /dev/null +++ b/src/common/util/tween.cpp @@ -0,0 +1,63 @@ +/* + * 573in1 - Copyright (C) 2022-2024 spicyjpeg + * + * 573in1 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. + * + * 573in1 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 + * 573in1. If not, see . + */ + +#include +#include +#include "common/util/tween.hpp" + +namespace util { + +template void Tween::setValue( + int time, T start, T target, int duration +) { + //assert(duration <= 0x800); + + _base = start; + _delta = target - start; + + _endTime = time + duration; + _timeScale = TWEEN_UNIT / duration; +} + +template void Tween::setValue(T target) { + _base = target; + _delta = static_cast(0); + + _endTime = 0; +} + +template T Tween::getValue(int time) const { + int remaining = time - _endTime; + + if (remaining >= 0) + return _base + _delta; + + return _base + ( + _delta * E::apply(remaining * _timeScale + TWEEN_UNIT) + ) / TWEEN_UNIT; +} + +template class Tween; +template class Tween; +template class Tween; +template class Tween; +template class Tween; +template class Tween; +template class Tween; +template class Tween; +template class Tween; + +} diff --git a/src/common/util/tween.hpp b/src/common/util/tween.hpp new file mode 100644 index 0000000..ee79567 --- /dev/null +++ b/src/common/util/tween.hpp @@ -0,0 +1,72 @@ +/* + * 573in1 - Copyright (C) 2022-2024 spicyjpeg + * + * 573in1 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. + * + * 573in1 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 + * 573in1. If not, see . + */ + +#pragma once + +namespace util { + +static constexpr int TWEEN_UNIT = 1 << 12; + +class LinearEasing { +public: + template static inline T apply(T value) { + return value; + } +}; + +class QuadInEasing { +public: + template static inline T apply(T value) { + return (value * value) / TWEEN_UNIT; + } +}; + +class QuadOutEasing { +public: + template static inline T apply(T value) { + return (value * 2) - ((value * value) / TWEEN_UNIT); + } +}; + +template class Tween { +private: + T _base, _delta; + int _endTime, _timeScale; + +public: + inline Tween(void) { + setValue(static_cast(0)); + } + inline Tween(T start) { + setValue(start); + } + + inline T getTargetValue(void) const { + return _base + _delta; + } + inline bool isDone(int time) const { + return time >= _endTime; + } + inline void setValue(int time, T target, int duration) { + setValue(time, getValue(time), target, duration); + } + + void setValue(int time, T start, T target, int duration); + void setValue(T target); + T getValue(int time) const; +}; + +} diff --git a/src/launcher/main.cpp b/src/launcher/main.cpp index ce68ada..a5c5dd3 100644 --- a/src/launcher/main.cpp +++ b/src/launcher/main.cpp @@ -15,10 +15,12 @@ */ #include +#include "common/util/log.hpp" +#include "common/util/misc.hpp" +#include "common/util/templates.hpp" #include "common/args.hpp" #include "common/ide.hpp" #include "common/io.hpp" -#include "common/util.hpp" #include "ps1/system.h" extern "C" uint8_t _textStart[]; diff --git a/src/main/app/app.cpp b/src/main/app/app.cpp index 3660713..e86a62f 100644 --- a/src/main/app/app.cpp +++ b/src/main/app/app.cpp @@ -21,12 +21,14 @@ #include "common/file/iso9660.hpp" #include "common/file/misc.hpp" #include "common/file/zip.hpp" +#include "common/util/log.hpp" +#include "common/util/misc.hpp" +#include "common/util/templates.hpp" #include "common/defs.hpp" #include "common/gpu.hpp" #include "common/ide.hpp" #include "common/io.hpp" #include "common/spu.hpp" -#include "common/util.hpp" #include "main/app/app.hpp" #include "main/cart/cart.hpp" #include "main/uibase.hpp" diff --git a/src/main/app/app.hpp b/src/main/app/app.hpp index af3b9b0..c5d5cbb 100644 --- a/src/main/app/app.hpp +++ b/src/main/app/app.hpp @@ -20,6 +20,8 @@ #include "common/file/file.hpp" #include "common/file/misc.hpp" #include "common/file/zip.hpp" +#include "common/util/log.hpp" +#include "common/util/templates.hpp" #include "common/ide.hpp" #include "main/app/cartactions.hpp" #include "main/app/cartunlock.hpp" diff --git a/src/main/app/cartactions.cpp b/src/main/app/cartactions.cpp index 7db6dcb..d419456 100644 --- a/src/main/app/cartactions.cpp +++ b/src/main/app/cartactions.cpp @@ -14,7 +14,10 @@ * 573in1. If not, see . */ -#include "common/util.hpp" +#include "common/util/hash.hpp" +#include "common/util/log.hpp" +#include "common/util/string.hpp" +#include "common/util/templates.hpp" #include "main/app/cartactions.hpp" #include "main/app/app.hpp" #include "main/uibase.hpp" diff --git a/src/main/app/cartunlock.cpp b/src/main/app/cartunlock.cpp index d4f853d..f05f700 100644 --- a/src/main/app/cartunlock.cpp +++ b/src/main/app/cartunlock.cpp @@ -15,7 +15,10 @@ */ #include -#include "common/util.hpp" +#include "common/util/hash.hpp" +#include "common/util/log.hpp" +#include "common/util/string.hpp" +#include "common/util/templates.hpp" #include "main/app/app.hpp" #include "main/app/cartunlock.hpp" #include "main/cart/cartdata.hpp" diff --git a/src/main/app/cartworkers.cpp b/src/main/app/cartworkers.cpp index 9c75b0e..651d1c3 100644 --- a/src/main/app/cartworkers.cpp +++ b/src/main/app/cartworkers.cpp @@ -18,9 +18,11 @@ #include #include #include "common/file/file.hpp" +#include "common/util/hash.hpp" +#include "common/util/log.hpp" +#include "common/util/templates.hpp" #include "common/defs.hpp" #include "common/io.hpp" -#include "common/util.hpp" #include "main/app/app.hpp" #include "main/cart/cart.hpp" #include "main/cart/cartdata.hpp" diff --git a/src/main/app/main.cpp b/src/main/app/main.cpp index 0527b11..db355c7 100644 --- a/src/main/app/main.cpp +++ b/src/main/app/main.cpp @@ -15,7 +15,8 @@ */ #include -#include "common/util.hpp" +#include "common/util/hash.hpp" +#include "common/util/templates.hpp" #include "main/app/app.hpp" #include "main/app/main.hpp" #include "main/uibase.hpp" diff --git a/src/main/app/misc.cpp b/src/main/app/misc.cpp index 0789131..6371146 100644 --- a/src/main/app/misc.cpp +++ b/src/main/app/misc.cpp @@ -18,10 +18,11 @@ #include #include #include "common/file/file.hpp" +#include "common/util/hash.hpp" +#include "common/util/templates.hpp" #include "common/ide.hpp" #include "common/io.hpp" #include "common/spu.hpp" -#include "common/util.hpp" #include "main/app/app.hpp" #include "main/app/misc.hpp" #include "main/uibase.hpp" diff --git a/src/main/app/misc.hpp b/src/main/app/misc.hpp index c9a9616..273194c 100644 --- a/src/main/app/misc.hpp +++ b/src/main/app/misc.hpp @@ -17,8 +17,8 @@ #pragma once #include +#include "common/util/templates.hpp" #include "common/spu.hpp" -#include "common/util.hpp" #include "main/uibase.hpp" #include "main/uicommon.hpp" #include "main/uimodals.hpp" diff --git a/src/main/app/miscworkers.cpp b/src/main/app/miscworkers.cpp index 314e3b2..bc7395d 100644 --- a/src/main/app/miscworkers.cpp +++ b/src/main/app/miscworkers.cpp @@ -17,12 +17,14 @@ #include #include #include "common/file/file.hpp" +#include "common/util/log.hpp" +#include "common/util/misc.hpp" +#include "common/util/templates.hpp" #include "common/defs.hpp" #include "common/ide.hpp" #include "common/idedefs.hpp" #include "common/io.hpp" #include "common/rom.hpp" -#include "common/util.hpp" #include "main/app/app.hpp" #include "ps1/system.h" diff --git a/src/main/app/modals.cpp b/src/main/app/modals.cpp index c170b4d..c10a45a 100644 --- a/src/main/app/modals.cpp +++ b/src/main/app/modals.cpp @@ -18,9 +18,11 @@ #include #include "common/file/file.hpp" #include "common/file/misc.hpp" +#include "common/util/hash.hpp" +#include "common/util/log.hpp" +#include "common/util/templates.hpp" #include "common/defs.hpp" #include "common/ide.hpp" -#include "common/util.hpp" #include "main/app/app.hpp" #include "main/app/modals.hpp" #include "main/uibase.hpp" diff --git a/src/main/app/modals.hpp b/src/main/app/modals.hpp index 91d93de..0aa78c2 100644 --- a/src/main/app/modals.hpp +++ b/src/main/app/modals.hpp @@ -17,8 +17,8 @@ #pragma once #include "common/file/file.hpp" +#include "common/util/templates.hpp" #include "common/ide.hpp" -#include "common/util.hpp" #include "main/uibase.hpp" #include "main/uicommon.hpp" #include "main/uimodals.hpp" diff --git a/src/main/app/romactions.cpp b/src/main/app/romactions.cpp index a8b22bd..077ca4c 100644 --- a/src/main/app/romactions.cpp +++ b/src/main/app/romactions.cpp @@ -15,9 +15,9 @@ */ #include +#include "common/util/log.hpp" #include "common/io.hpp" #include "common/rom.hpp" -#include "common/util.hpp" #include "main/app/app.hpp" #include "main/app/romactions.hpp" #include "main/uibase.hpp" diff --git a/src/main/app/romworkers.cpp b/src/main/app/romworkers.cpp index 4de27a9..5923f2a 100644 --- a/src/main/app/romworkers.cpp +++ b/src/main/app/romworkers.cpp @@ -18,10 +18,11 @@ #include #include #include "common/file/file.hpp" +#include "common/util/hash.hpp" +#include "common/util/templates.hpp" #include "common/defs.hpp" #include "common/rom.hpp" #include "common/romdrivers.hpp" -#include "common/util.hpp" #include "main/app/app.hpp" #include "main/app/romactions.hpp" diff --git a/src/main/app/tests.cpp b/src/main/app/tests.cpp index 77fddd9..ba9e5bc 100644 --- a/src/main/app/tests.cpp +++ b/src/main/app/tests.cpp @@ -14,10 +14,13 @@ * 573in1. If not, see . */ +#include "common/util/hash.hpp" +#include "common/util/log.hpp" +#include "common/util/string.hpp" +#include "common/util/templates.hpp" #include "common/gpu.hpp" #include "common/io.hpp" #include "common/spu.hpp" -#include "common/util.hpp" #include "main/app/app.hpp" #include "main/app/tests.hpp" #include "main/uibase.hpp" diff --git a/src/main/cart/cart.cpp b/src/main/cart/cart.cpp index e89666e..9f1d540 100644 --- a/src/main/cart/cart.cpp +++ b/src/main/cart/cart.cpp @@ -16,7 +16,10 @@ #include #include -#include "common/util.hpp" +#include "common/util/hash.hpp" +#include "common/util/log.hpp" +#include "common/util/string.hpp" +#include "common/util/templates.hpp" #include "main/cart/cart.hpp" #include "vendor/miniz.h" diff --git a/src/main/cart/cart.hpp b/src/main/cart/cart.hpp index a706e0d..2b44734 100644 --- a/src/main/cart/cart.hpp +++ b/src/main/cart/cart.hpp @@ -18,8 +18,9 @@ #include #include +#include "common/util/string.hpp" +#include "common/util/templates.hpp" #include "common/rom.hpp" -#include "common/util.hpp" namespace cart { diff --git a/src/main/cart/cartdata.cpp b/src/main/cart/cartdata.cpp index a119190..0f75e89 100644 --- a/src/main/cart/cartdata.cpp +++ b/src/main/cart/cartdata.cpp @@ -16,7 +16,9 @@ #include #include -#include "common/util.hpp" +#include "common/util/hash.hpp" +#include "common/util/log.hpp" +#include "common/util/templates.hpp" #include "main/cart/cart.hpp" #include "main/cart/cartdata.hpp" diff --git a/src/main/cart/cartdata.hpp b/src/main/cart/cartdata.hpp index b3c79cc..cb7f5cb 100644 --- a/src/main/cart/cartdata.hpp +++ b/src/main/cart/cartdata.hpp @@ -19,7 +19,7 @@ #include #include #include -#include "common/util.hpp" +#include "common/util/templates.hpp" #include "main/cart/cart.hpp" namespace cart { diff --git a/src/main/cart/cartio.cpp b/src/main/cart/cartio.cpp index a876bdd..75e5756 100644 --- a/src/main/cart/cartio.cpp +++ b/src/main/cart/cartio.cpp @@ -15,8 +15,11 @@ */ #include +#include "common/util/log.hpp" +#include "common/util/misc.hpp" +#include "common/util/string.hpp" +#include "common/util/templates.hpp" #include "common/io.hpp" -#include "common/util.hpp" #include "main/cart/cart.hpp" #include "main/cart/cartio.hpp" #include "main/cart/zs01.hpp" diff --git a/src/main/cart/zs01.cpp b/src/main/cart/zs01.cpp index f1f7304..3ad3948 100644 --- a/src/main/cart/zs01.cpp +++ b/src/main/cart/zs01.cpp @@ -16,7 +16,8 @@ #include #include -#include "common/util.hpp" +#include "common/util/hash.hpp" +#include "common/util/log.hpp" #include "main/cart/zs01.hpp" namespace cart { diff --git a/src/main/main.cpp b/src/main/main.cpp index a5c255d..be6136c 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -14,11 +14,12 @@ * 573in1. If not, see . */ +#include "common/util/hash.hpp" +#include "common/util/log.hpp" #include "common/args.hpp" #include "common/gpu.hpp" #include "common/io.hpp" #include "common/spu.hpp" -#include "common/util.hpp" #include "main/app/app.hpp" #include "main/uibase.hpp" #include "ps1/gpucmd.h" diff --git a/src/main/uibase.cpp b/src/main/uibase.cpp index f7a71d1..efa41c6 100644 --- a/src/main/uibase.cpp +++ b/src/main/uibase.cpp @@ -15,11 +15,13 @@ */ #include +#include "common/util/log.hpp" +#include "common/util/templates.hpp" +#include "common/util/tween.hpp" #include "common/gpu.hpp" #include "common/gpufont.hpp" #include "common/io.hpp" #include "common/pad.hpp" -#include "common/util.hpp" #include "main/uibase.hpp" #include "ps1/gpucmd.h" diff --git a/src/main/uibase.hpp b/src/main/uibase.hpp index 61c39da..943c914 100644 --- a/src/main/uibase.hpp +++ b/src/main/uibase.hpp @@ -17,10 +17,11 @@ #pragma once #include +#include "common/util/log.hpp" +#include "common/util/tween.hpp" #include "common/gpu.hpp" #include "common/gpufont.hpp" #include "common/spu.hpp" -#include "common/util.hpp" namespace ui { diff --git a/src/main/uicommon.cpp b/src/main/uicommon.cpp index d4431a4..c964c67 100644 --- a/src/main/uicommon.cpp +++ b/src/main/uicommon.cpp @@ -14,6 +14,7 @@ * 573in1. If not, see . */ +#include "common/util/templates.hpp" #include "common/defs.hpp" #include "common/gpu.hpp" #include "main/uibase.hpp" diff --git a/src/main/uicommon.hpp b/src/main/uicommon.hpp index 7f72727..f1f9d1c 100644 --- a/src/main/uicommon.hpp +++ b/src/main/uicommon.hpp @@ -17,8 +17,8 @@ #pragma once #include +#include "common/util/tween.hpp" #include "common/gpu.hpp" -#include "common/util.hpp" #include "main/uibase.hpp" namespace ui { diff --git a/src/main/uimodals.cpp b/src/main/uimodals.cpp index 1af8ead..e5cb96e 100644 --- a/src/main/uimodals.cpp +++ b/src/main/uimodals.cpp @@ -16,8 +16,11 @@ #include #include +#include "common/util/misc.hpp" +#include "common/util/string.hpp" +#include "common/util/templates.hpp" +#include "common/util/tween.hpp" #include "common/gpu.hpp" -#include "common/util.hpp" #include "main/uibase.hpp" #include "main/uimodals.hpp" diff --git a/src/main/uimodals.hpp b/src/main/uimodals.hpp index 591ebc5..61a69ce 100644 --- a/src/main/uimodals.hpp +++ b/src/main/uimodals.hpp @@ -17,7 +17,8 @@ #pragma once #include -#include "common/util.hpp" +#include "common/util/misc.hpp" +#include "common/util/tween.hpp" #include "main/uibase.hpp" namespace ui {