From 4c2d5e72147dc54621d5c1bb01032948a9170c53 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Sat, 26 Mar 2016 11:34:03 -0700 Subject: [PATCH] Implement format --- CMakeLists.txt | 9 +- include/Card.hpp | 80 +++++---- include/SRAM.hpp | 76 +++++++++ include/Util.hpp | 310 ++++++++++++++++++++++++++++++++++ include/WideStringConvert.hpp | 7 + include/utf8proc.h | 293 ++++++++++++++++++++++++++++++++ src/Card.cpp | 125 ++++++++++++-- src/SRAM.cpp | 32 ++++ src/Util.cpp | 26 +++ src/WideStringConvert.cpp | 43 +++++ test/CMakeLists.txt | 2 +- test/main.cpp | 4 +- 12 files changed, 959 insertions(+), 48 deletions(-) create mode 100644 include/SRAM.hpp create mode 100644 include/Util.hpp create mode 100644 include/WideStringConvert.hpp create mode 100644 include/utf8proc.h create mode 100644 src/SRAM.cpp create mode 100644 src/Util.cpp create mode 100644 src/WideStringConvert.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ec3be8a..9647299 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,12 @@ cmake_minimum_required(VERSION 3.0) -project(Card) +project(kabufuda) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wno-multichar -fno-exceptions") include_directories(include) -add_library(card STATIC - include/Card.hpp src/Card.cpp) +add_library(kabufuda STATIC + include/Card.hpp src/Card.cpp + include/Util.hpp src/Util.cpp + include/SRAM.hpp src/SRAM.cpp + include/WideStringConvert.hpp src/WideStringConvert.cpp) add_subdirectory(test) diff --git a/include/Card.hpp b/include/Card.hpp index 75fdda0..c87a349 100644 --- a/include/Card.hpp +++ b/include/Card.hpp @@ -3,13 +3,16 @@ #include #include -#include #include -namespace card +#include "Util.hpp" + +namespace kabufuda { -uint32_t constexpr BlockSize = 0x2000; -uint32_t constexpr MaxFiles = 127; +uint32_t constexpr BlockSize = 0x2000; +uint32_t constexpr MaxFiles = 127; +uint32_t constexpr FSTBlocks = 5; +uint32_t constexpr MbitToBlocks = 0x10; /** * @brief The EPermissions enum @@ -52,12 +55,13 @@ enum class ECardSize : uint16_t */ enum class EEncoding : uint16_t { - ASCII, /**< Standard ASCII Encoding */ - SJIS /**< SJIS Encoding for japanese */ + ASCII, /**< Standard ASCII Encoding */ + SJIS /**< SJIS Encoding for japanese */ }; class File { +#pragma pack(push, 4) union { struct @@ -76,42 +80,47 @@ class File }; uint8_t __raw[0x40]; }; +#pragma pop() + public: File() {} - File(char data[0x40]) - { - memcpy(__raw, data, 0x40); - } - File(const char* filename) - { - memset(m_filename, 0, 0x20); - memcpy(m_filename, filename, 0x20); - } + File(char data[0x40]); + File(const char* filename); ~File() {} }; class BlockAllocationTable { + friend class Card; +#pragma pack(push, 4) union { struct { uint16_t m_checksum; uint16_t m_checksumInv; + uint16_t m_updateCounter; uint16_t m_freeBlocks; uint16_t m_lastAllocated; uint16_t m_map[0xFFB]; }; uint8_t __raw[BlockSize]; }; +#pragma pop() + public: - BlockAllocationTable() {} + explicit BlockAllocationTable(uint32_t blockCount = (uint32_t(ECardSize::Card2043Mb) * MbitToBlocks)); BlockAllocationTable(uint8_t data[BlockSize]); ~BlockAllocationTable() {} + + uint16_t getNextBlock() const; + uint16_t nextFreeBlock(uint16_t maxBlocks, uint16_t startingBlock = FSTBlocks); }; class Directory { + friend class Card; +#pragma pack(push, 4) union { struct @@ -124,17 +133,17 @@ class Directory }; uint8_t __raw[BlockSize]; }; +#pragma pop() public: - Directory() {} - Directory(uint8_t data[BlockSize]) - { - memcpy(__raw, data, BlockSize); - } + Directory(); + + Directory(uint8_t data[BlockSize]); ~Directory() {} }; class Card { +#pragma pack(push, 4) union { struct @@ -154,7 +163,8 @@ class Card }; uint8_t __raw[BlockSize]; }; - std::string m_filename; +#pragma pop() + SystemString m_filename; Directory m_dir; Directory m_dirBackup; Directory* m_dirInUse = nullptr; @@ -166,13 +176,14 @@ class Card char m_maker[3] = {'\0'}; void setChecksum(uint16_t checksum) { - m_checksum = (checksum); - m_checksumInv = ~checksum; + m_checksum = SBig(checksum); + m_checksumInv = SBig(checksum ^ 0xFFFF); } + FILE* m_fileHandle; public: Card(); - Card(const std::string& filepath, const char* game = nullptr, const char* maker=nullptr); + Card(const SystemString& filepath, const char* game = nullptr, const char* maker=nullptr); ~Card(); /** @@ -216,21 +227,26 @@ public: void getChecksum(uint16_t* checksum, uint16_t* inverse); /** * @brief Formats the memory card and assigns a new serial - * @param size The desired size of the file - * @param size The desired encoding - * @sa ECardSize - * @sa EEncoding + * @param size The desired size of the file @sa ECardSize + * @param encoding The desired encoding @sa EEncoding */ - void format(ECardSize size = ECardSize::Card59Mb, EEncoding encoding = EEncoding::ASCII); + void format(EDeviceId deviceId, ECardSize size = ECardSize::Card2043Mb, EEncoding encoding = EEncoding::ASCII); + + /** + * @brief getSizeMbit + * @return + */ + static uint32_t getSizeMbitFromFile(const SystemString& filename); }; /** * @brief calculateChecksum * @param data * @param len - * @return + * @param checksum + * @param checksum */ -uint16_t calculateChecksum(void* data, size_t len); +void calculateChecksum(uint16_t* data, size_t len, uint16_t* checksum, uint16_t* checksumInv); } #endif // __CARD_HPP__ diff --git a/include/SRAM.hpp b/include/SRAM.hpp new file mode 100644 index 0000000..e6c4566 --- /dev/null +++ b/include/SRAM.hpp @@ -0,0 +1,76 @@ +#ifndef SRAM_HPP +#define SRAM_HPP + +#include + +// Modified code taken from libogc + +/*------------------------------------------------------------- +system.h -- OS functions and initialization +Copyright (C) 2004 +Michael Wiedenbauer (shagkur) +Dave Murphy (WinterMute) +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: +1. The origin of this software must not be misrepresented; you +must not claim that you wrote the original software. If you use +this software in a product, an acknowledgment in the product +documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source +distribution. +-------------------------------------------------------------*/ + +namespace kabufuda +{ +#pragma pack(push,1) +union SRAMFlags +{ + uint8_t Hex; + struct + { + uint8_t : 2; + uint8_t sound : 1; // Audio settings; 0 = Mono, 1 = Stereo + uint8_t initialized : 1; // if 0, displays prompt to set language on boot and asks user to set options and time/date + uint8_t : 2; + uint8_t boot_menu : 1; // if 1, skips logo animation and boots into the system menu regardless of if there is a disc inserted + uint8_t progressive : 1; // if 1, automatically displays Progressive Scan prompt in games that support it + }; +}; + +union SRAM +{ + uint8_t p_SRAM[64]; + struct // Stored configuration value from the system SRAM area + { + uint16_t checksum; // Holds the block checksum. + uint16_t checksum_inv; // Holds the inverse block checksum + uint32_t ead0; // Unknown attribute + uint32_t ead1; // Unknown attribute + uint32_t counter_bias; // Bias value for the realtime clock + int8_t display_offsetH; // Pixel offset for the VI + uint8_t ntd; // Unknown attribute + uint8_t lang; // Language of system + SRAMFlags flags; // Device and operations flag + + // Stored configuration value from the extended SRAM area + uint8_t flash_id[2][12]; // flash_id[2][12] 96bit memorycard unlock flash ID + uint32_t wirelessKbd_id; // Device ID of last connected wireless keyboard + uint16_t wirelessPad_id[4]; // 16-bit device ID of last connected pad. + uint8_t dvderr_code; // last non-recoverable error from DVD interface + uint8_t __padding0; // reserved + uint8_t flashID_chksum[2]; // 8-bit checksum of unlock flash ID + uint32_t __padding1; // padding + }; +}; +#pragma pack(pop) + +extern const SRAM g_SRAM; +} + +#endif // SRAM_HPP diff --git a/include/Util.hpp b/include/Util.hpp new file mode 100644 index 0000000..2de5740 --- /dev/null +++ b/include/Util.hpp @@ -0,0 +1,310 @@ +#ifndef __UTIL_HPP__ +#define __UTIL_HPP__ + +#ifndef _WIN32 +#include +#include +#include +#include +#include +#include +#include +#include +#else +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include "winsupport.hpp" +#endif + +#include +#include +#include +#include +#include +#include + +#undef bswap16 +#undef bswap32 +#undef bswap64 + +namespace kabufuda +{ + +/* Type-sensitive byte swappers */ +template +static inline T bswap16(T val) +{ +#if __GNUC__ + return __builtin_bswap16(val); +#elif _WIN32 + return _byteswap_ushort(val); +#else + return (val = (val << 8) | ((val >> 8) & 0xFF)); +#endif +} + +template +static inline T bswap32(T val) +{ +#if __GNUC__ + return __builtin_bswap32(val); +#elif _WIN32 + return _byteswap_ulong(val); +#else + val = (val & 0x0000FFFF) << 16 | (val & 0xFFFF0000) >> 16; + val = (val & 0x00FF00FF) << 8 | (val & 0xFF00FF00) >> 8; + return val; +#endif +} + +template +static inline T bswap64(T val) +{ +#if __GNUC__ + return __builtin_bswap64(val); +#elif _WIN32 + return _byteswap_uint64(val); +#else + return ((val & 0xFF00000000000000ULL) >> 56) | + ((val & 0x00FF000000000000ULL) >> 40) | + ((val & 0x0000FF0000000000ULL) >> 24) | + ((val & 0x000000FF00000000ULL) >> 8) | + ((val & 0x00000000FF000000ULL) << 8) | + ((val & 0x0000000000FF0000ULL) << 24) | + ((val & 0x000000000000FF00ULL) << 40) | + ((val & 0x00000000000000FFULL) << 56); +#endif +} + + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +static inline int16_t SBig(int16_t val) {return bswap16(val);} +static inline uint16_t SBig(uint16_t val) {return bswap16(val);} +static inline int32_t SBig(int32_t val) {return bswap32(val);} +static inline uint32_t SBig(uint32_t val) {return bswap32(val);} +static inline int64_t SBig(int64_t val) {return bswap64(val);} +static inline uint64_t SBig(uint64_t val) {return bswap64(val);} +static inline float SBig(float val) +{ + int32_t ival = bswap32(*((int32_t*)(&val))); + return *((float*)(&ival)); +} +static inline double SBig(double val) +{ + int64_t ival = bswap64(*((int64_t*)(&val))); + return *((double*)(&ival)); +} +#define SBIG(q) ( ( (q) & 0x000000FF ) << 24 | ( (q) & 0x0000FF00 ) << 8 \ + | ( (q) & 0x00FF0000 ) >> 8 | ( (q) & 0xFF000000 ) >> 24 ) + +static inline int16_t SLittle(int16_t val) {return val;} +static inline uint16_t SLittle(uint16_t val) {return val;} +static inline int32_t SLittle(int32_t val) {return val;} +static inline uint32_t SLittle(uint32_t val) {return val;} +static inline int64_t SLittle(int64_t val) {return val;} +static inline uint64_t SLittle(uint64_t val) {return val;} +static inline float SLittle(float val) {return val;} +static inline double SLittle(double val) {return val;} +#define SLITTLE(q) (q) +#else +static inline int16_t SLittle(int16_t val) {return bswap16(val);} +static inline uint16_t SLittle(uint16_t val) {return bswap16(val);} +static inline int32_t SLittle(int32_t val) {return bswap32(val);} +static inline uint32_t SLittle(uint32_t val) {return bswap32(val);} +static inline int64_t SLittle(int64_t val) {return bswap64(val);} +static inline uint64_t SLittle(uint64_t val) {return bswap64(val);} +static inline float SLittle(float val) +{ + int32_t ival = bswap32(*((int32_t*)(&val))); + return *((float*)(&ival)); +} +static inline double SLittle(double val) +{ + int64_t ival = bswap64(*((int64_t*)(&val))); + return *((double*)(&ival)); +} +#define SLITTLE(q) ( ( (q) & 0x000000FF ) << 24 | ( (q) & 0x0000FF00 ) << 8 \ + | ( (q) & 0x00FF0000 ) >> 8 | ( (q) & 0xFF000000 ) >> 24 ) + +static inline int16_t SBig(int16_t val) {return val;} +static inline uint16_t SBig(uint16_t val) {return val;} +static inline int32_t SBig(int32_t val) {return val;} +static inline uint32_t SBig(uint32_t val) {return val;} +static inline int64_t SBig(int64_t val) {return val;} +static inline uint64_t SBig(uint64_t val) {return val;} +static inline float SBig(float val) {return val;} +static inline double SBig(double val) {return val;} +#define SBIG(q) (q) +#endif + +#if CARD_UCS2 +typedef wchar_t SystemChar; +static inline size_t StrLen(const SystemChar* str) {return wcslen(str);} +typedef std::wstring SystemString; +static inline void ToLower(SystemString& str) +{std::transform(str.begin(), str.end(), str.begin(), towlower);} +static inline void ToUpper(SystemString& str) +{std::transform(str.begin(), str.end(), str.begin(), towupper);} +class SystemUTF8View +{ + std::string m_utf8; +public: + explicit SystemUTF8View(const SystemString& str) + : m_utf8(WideToUTF8(str)) {} + operator const std::string&() const {return m_utf8;} + const std::string& str() const {return m_utf8;} + const char* c_str() const {return m_utf8.c_str();} + std::string operator+(const std::string& other) const {return m_utf8 + other;} + std::string operator+(const char* other) const {return m_utf8 + other;} +}; +inline std::string operator+(const std::string& lhs, const SystemUTF8View& rhs) {return lhs + std::string(rhs);} +inline std::string operator+(const char* lhs, const SystemUTF8View& rhs) {return lhs + std::string(rhs);} +class SystemStringView +{ + std::wstring m_sys; +public: + explicit SystemStringView(const std::string& str) + : m_sys(UTF8ToWide(str)) {} + operator const std::wstring&() const {return m_sys;} + const std::wstring& sys_str() const {return m_sys;} + const SystemChar* c_str() const {return m_sys.c_str();} + std::wstring operator+(const std::wstring& other) const {return m_sys + other;} + std::wstring operator+(const wchar_t* other) const {return m_sys + other;} +}; +inline std::wstring operator+(const std::wstring& lhs, const SystemStringView& rhs) {return lhs + std::wstring(rhs);} +inline std::wstring operator+(const wchar_t* lhs, const SystemStringView& rhs) {return lhs + std::wstring(rhs);} +#ifndef _S +#define _S(val) L ## val +#endif +typedef struct _stat Sstat; +#else +typedef char SystemChar; +static inline size_t StrLen(const SystemChar* str) {return strlen(str);} +typedef std::string SystemString; +static inline void ToLower(SystemString& str) +{std::transform(str.begin(), str.end(), str.begin(), tolower);} +static inline void ToUpper(SystemString& str) +{std::transform(str.begin(), str.end(), str.begin(), toupper);} +class SystemUTF8View +{ + const std::string& m_utf8; +public: + explicit SystemUTF8View(const SystemString& str) + : m_utf8(str) {} + operator const std::string&() const {return m_utf8;} + const std::string& str() const {return m_utf8;} + const char* c_str() const {return m_utf8.c_str();} + std::string operator+(const std::string& other) const {return std::string(m_utf8) + other;} + std::string operator+(const char* other) const {return std::string(m_utf8) + other;} +}; +inline std::string operator+(const std::string& lhs, const SystemUTF8View& rhs) {return lhs + std::string(rhs);} +inline std::string operator+(const char* lhs, const SystemUTF8View& rhs) {return lhs + std::string(rhs);} +class SystemStringView +{ + const std::string& m_sys; +public: + explicit SystemStringView(const std::string& str) + : m_sys(str) {} + operator const std::string&() const {return m_sys;} + const std::string& sys_str() const {return m_sys;} + const SystemChar* c_str() const {return m_sys.c_str();} + std::string operator+(const std::string& other) const {return m_sys + other;} + std::string operator+(const char* other) const {return m_sys + other;} +}; +inline std::string operator+(const std::string& lhs, const SystemStringView& rhs) {return lhs + std::string(rhs);} +inline std::string operator+(const char* lhs, const SystemStringView& rhs) {return lhs + std::string(rhs);} +#ifndef _S +#define _S(val) val +#endif +typedef struct stat Sstat; +#endif + +uint64_t getGCTime(); + +enum class FileLockType +{ + None = 0, + Read, + Write +}; +static inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLockType lock=FileLockType::None) +{ +#if CARD_UCS2 + FILE* fp = _wfopen(path, mode); + if (!fp) + return nullptr; +#else + FILE* fp = fopen(path, mode); + if (!fp) + return nullptr; +#endif + + if (lock != FileLockType::None) + { +#if _WIN32 + OVERLAPPED ov = {}; + LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1, &ov); +#else + if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB)) + fprintf(stderr, "flock %s: %s", path, strerror(errno)); +#endif + } + + return fp; +} + +static inline int FSeek(FILE* fp, int64_t offset, int whence) +{ +#if _WIN32 + return _fseeki64(fp, offset, whence); +#elif __APPLE__ || __FreeBSD__ + return fseeko(fp, offset, whence); +#else + return fseeko64(fp, offset, whence); +#endif +} + +static inline int64_t FTell(FILE* fp) +{ +#if _WIN32 + return _ftelli64(fp); +#elif __APPLE__ || __FreeBSD__ + return ftello(fp); +#else + return ftello64(fp); +#endif +} + +static inline int Rename(const SystemChar* oldpath, const SystemChar* newpath) +{ +#if CARD_UCS2 + return _wrename(oldpath, newpath); +#else + return rename(oldpath, newpath); +#endif +} + +static inline int Stat(const SystemChar* path, Sstat* statOut) +{ +#if CARD_UCS2 + size_t pos; + for (pos=0 ; pos<3 && path[pos] != L'\0' ; ++pos) {} + if (pos == 2 && path[1] == L':') + { + SystemChar fixPath[4] = {path[0], L':', L'/', L'\0'}; + return _wstat(fixPath, statOut); + } + return _wstat(path, statOut); +#else + return stat(path, statOut); +#endif +} +} + +#endif // __UTIL_HPP__ diff --git a/include/WideStringConvert.hpp b/include/WideStringConvert.hpp new file mode 100644 index 0000000..80d0703 --- /dev/null +++ b/include/WideStringConvert.hpp @@ -0,0 +1,7 @@ +#include + +namespace kabufuda +{ +std::string WideToUTF8(const std::wstring& src); +std::wstring UTF8ToWide(const std::string& src); +} diff --git a/include/utf8proc.h b/include/utf8proc.h new file mode 100644 index 0000000..1e90264 --- /dev/null +++ b/include/utf8proc.h @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2009 Public Software Group e. V., Berlin, Germany + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + + +/** + * @mainpage + * + * utf8proc is a free/open-source (MIT/expat licensed) C library + * providing Unicode normalization, case-folding, and other operations + * for strings in the UTF-8 encoding, supporting Unicode version + * 7.0.0. See the utf8proc home page (http://julialang.org/utf8proc/) + * for downloads and other information, or the source code on github + * (https://github.com/JuliaLang/utf8proc). + * + * For the utf8proc API documentation, see: @ref utf8proc.h + * + * The features of utf8proc include: + * + * - Transformation of strings (@ref utf8proc_map) to: + * - decompose (@ref UTF8PROC_DECOMPOSE) or compose (@ref UTF8PROC_COMPOSE) Unicode combining characters (http://en.wikipedia.org/wiki/Combining_character) + * - canonicalize Unicode compatibility characters (@ref UTF8PROC_COMPAT) + * - strip "ignorable" (@ref UTF8PROC_IGNORE) characters, control characters (@ref UTF8PROC_STRIPCC), or combining characters such as accents (@ref UTF8PROC_STRIPMARK) + * - case-folding (@ref UTF8PROC_CASEFOLD) + * - Unicode normalization: @ref utf8proc_NFD, @ref utf8proc_NFC, @ref utf8proc_NFKD, @ref utf8proc_NFKC + * - Detecting grapheme boundaries (@ref utf8proc_grapheme_break and @ref UTF8PROC_CHARBOUND) + * - Character-width computation: @ref utf8proc_charwidth + * - Classification of characters by Unicode category: @ref utf8proc_category and @ref utf8proc_category_string + * - Encode (@ref utf8proc_encode_char) and decode (@ref utf8proc_iterate) Unicode codepoints to/from UTF-8. + */ + +/** @file */ + +#ifndef UTF8PROC_H +#define UTF8PROC_H + +/** @name API version + * + * The utf8proc API version MAJOR.MINOR.PATCH, following + * semantic-versioning rules (http://semver.org) based on API + * compatibility. + * + * This is also returned at runtime by @ref utf8proc_version; however, the + * runtime version may append a string like "-dev" to the version number + * for prerelease versions. + * + * @note The shared-library version number in the Makefile may be different, + * being based on ABI compatibility rather than API compatibility. + */ +/** @{ */ +/** The MAJOR version number (increased when backwards API compatibility is broken). */ +#define UTF8PROC_VERSION_MAJOR 1 +/** The MINOR version number (increased when new functionality is added in a backwards-compatible manner). */ +#define UTF8PROC_VERSION_MINOR 3 +/** The PATCH version (increased for fixes that do not change the API). */ +#define UTF8PROC_VERSION_PATCH 0 +/** @} */ + +#include +#include +#ifdef _MSC_VER +typedef signed char utf8proc_int8_t; +typedef unsigned char utf8proc_uint8_t; +typedef short utf8proc_int16_t; +typedef unsigned short utf8proc_uint16_t; +typedef int utf8proc_int32_t; +typedef unsigned int utf8proc_uint32_t; +# ifdef _WIN64 +typedef __int64 utf8proc_ssize_t; +typedef unsigned __int64 utf8proc_size_t; +# else +typedef int utf8proc_ssize_t; +typedef unsigned int utf8proc_size_t; +# endif +# ifndef __cplusplus +typedef unsigned char utf8proc_bool; +enum {false, true}; +# else +typedef bool utf8proc_bool; +# endif +#else +# include +# include +typedef int8_t utf8proc_int8_t; +typedef uint8_t utf8proc_uint8_t; +typedef int16_t utf8proc_int16_t; +typedef uint16_t utf8proc_uint16_t; +typedef int32_t utf8proc_int32_t; +typedef uint32_t utf8proc_uint32_t; +typedef size_t utf8proc_size_t; +typedef ssize_t utf8proc_ssize_t; +typedef bool utf8proc_bool; +#endif +#include + +/** @name Error codes + * Error codes being returned by almost all functions. + */ +/** @{ */ +/** Memory could not be allocated. */ +#define UTF8PROC_ERROR_NOMEM -1 +/** The given string is too long to be processed. */ +#define UTF8PROC_ERROR_OVERFLOW -2 +/** The given string is not a legal UTF-8 string. */ +#define UTF8PROC_ERROR_INVALIDUTF8 -3 +/** The @ref UTF8PROC_REJECTNA flag was set and an unassigned codepoint was found. */ +#define UTF8PROC_ERROR_NOTASSIGNED -4 +/** Invalid options have been used. */ +#define UTF8PROC_ERROR_INVALIDOPTS -5 +/** @} */ + +#define UTF8PROC_cont(ch) (((ch) & 0xc0) == 0x80) + +/** + * Reads a single codepoint from the UTF-8 sequence being pointed to by `str`. + * The maximum number of bytes read is `strlen`, unless `strlen` is + * negative (in which case up to 4 bytes are read). + * + * If a valid codepoint could be read, it is stored in the variable + * pointed to by `codepoint_ref`, otherwise that variable will be set to -1. + * In case of success, the number of bytes read is returned; otherwise, a + * negative error code is returned. + */ +static inline utf8proc_ssize_t utf8proc_iterate( + const utf8proc_uint8_t *str, utf8proc_ssize_t strlen, utf8proc_int32_t *dst +) { + utf8proc_uint32_t uc; + const utf8proc_uint8_t *end; + + *dst = -1; + if (!strlen) return 0; + end = str + ((strlen < 0) ? 4 : strlen); + uc = *str++; + if (uc < 0x80) { + *dst = uc; + return 1; + } + // Must be between 0xc2 and 0xf4 inclusive to be valid + if ((uc - 0xc2) > (0xf4-0xc2)) return UTF8PROC_ERROR_INVALIDUTF8; + if (uc < 0xe0) { // 2-byte sequence + // Must have valid continuation character + if (!UTF8PROC_cont(*str)) return UTF8PROC_ERROR_INVALIDUTF8; + *dst = ((uc & 0x1f)<<6) | (*str & 0x3f); + return 2; + } + if (uc < 0xf0) { // 3-byte sequence + if ((str + 1 >= end) || !UTF8PROC_cont(*str) || !UTF8PROC_cont(str[1])) + return UTF8PROC_ERROR_INVALIDUTF8; + // Check for surrogate chars + if (uc == 0xed && *str > 0x9f) + return UTF8PROC_ERROR_INVALIDUTF8; + uc = ((uc & 0xf)<<12) | ((*str & 0x3f)<<6) | (str[1] & 0x3f); + if (uc < 0x800) + return UTF8PROC_ERROR_INVALIDUTF8; + *dst = uc; + return 3; + } + // 4-byte sequence + // Must have 3 valid continuation characters + if ((str + 2 >= end) || !UTF8PROC_cont(*str) || !UTF8PROC_cont(str[1]) || !UTF8PROC_cont(str[2])) + return UTF8PROC_ERROR_INVALIDUTF8; + // Make sure in correct range (0x10000 - 0x10ffff) + if (uc == 0xf0) { + if (*str < 0x90) return UTF8PROC_ERROR_INVALIDUTF8; + } else if (uc == 0xf4) { + if (*str > 0x8f) return UTF8PROC_ERROR_INVALIDUTF8; + } + *dst = ((uc & 7)<<18) | ((*str & 0x3f)<<12) | ((str[1] & 0x3f)<<6) | (str[2] & 0x3f); + return 4; +} + +/** + * Encodes the codepoint as an UTF-8 string in the byte array pointed + * to by `dst`. This array must be at least 4 bytes long. + * + * In case of success the number of bytes written is returned, and + * otherwise 0 is returned. + * + * This function does not check whether `codepoint` is valid Unicode. + */ +static inline utf8proc_ssize_t utf8proc_encode_char(utf8proc_int32_t uc, utf8proc_uint8_t *dst) { + if (uc < 0x00) { + return 0; + } else if (uc < 0x80) { + dst[0] = uc; + return 1; + } else if (uc < 0x800) { + dst[0] = 0xC0 + (uc >> 6); + dst[1] = 0x80 + (uc & 0x3F); + return 2; + // Note: we allow encoding 0xd800-0xdfff here, so as not to change + // the API, however, these are actually invalid in UTF-8 + } else if (uc < 0x10000) { + dst[0] = 0xE0 + (uc >> 12); + dst[1] = 0x80 + ((uc >> 6) & 0x3F); + dst[2] = 0x80 + (uc & 0x3F); + return 3; + } else if (uc < 0x110000) { + dst[0] = 0xF0 + (uc >> 18); + dst[1] = 0x80 + ((uc >> 12) & 0x3F); + dst[2] = 0x80 + ((uc >> 6) & 0x3F); + dst[3] = 0x80 + (uc & 0x3F); + return 4; + } else return 0; +} + +#ifdef __cplusplus +#include +#include + +class UTF8Iterator : public std::iterator +{ + std::string::const_iterator m_it; +public: + UTF8Iterator(const std::string::const_iterator& it) : m_it(it) {} + UTF8Iterator& operator+=(size_t v) + { + for (size_t i=0 ; i(&*m_it), -1, &dummy); +#ifndef NDEBUG + if (*m_it == '\0') + { + fprintf(stderr, "ERROR! UTF8-iterator null-term fail\n"); + abort(); + } + else if (sz > 0) + m_it += sz; + else + { + fprintf(stderr, "ERROR! UTF8Iterator character fail"); + abort(); + } +#else + if (sz > 0) + m_it += sz; +#endif + } + return *this; + } + UTF8Iterator& operator++() + { + return this->operator+=(1); + } + UTF8Iterator operator+(size_t v) const + { + UTF8Iterator ret(m_it); + ret += v; + return ret; + } + uint32_t operator*() const + { + utf8proc_int32_t ret; + utf8proc_iterate(reinterpret_cast(&*m_it), -1, &ret); + return ret; + } + std::string::const_iterator iter() const {return m_it;} + size_t countTo(std::string::const_iterator end) const + { + UTF8Iterator it(m_it); + size_t ret = 0; + while (it.iter() < end) + { + ++ret; + ++it; + } + return ret; + } +}; + +#endif + +#endif + diff --git a/src/Card.cpp b/src/Card.cpp index 0f153c7..fbc57d6 100644 --- a/src/Card.cpp +++ b/src/Card.cpp @@ -1,22 +1,43 @@ #include "Card.hpp" +#include "SRAM.hpp" #include #include +#include -namespace card +namespace kabufuda { Card::Card() { memset(__raw, 0xFF, BlockSize); } -Card::Card(const std::string &filepath, const char* game, const char* maker) - : m_filepath(filepath) +Card::Card(const SystemString& filename, const char* game, const char* maker) + : m_filename(filename) { memset(__raw, 0xFF, BlockSize); if (game && strlen(game) == 4) memcpy(m_game, game, 4); if (maker && strlen(maker) == 2) memcpy(m_maker, maker, 2); + + m_fileHandle = Fopen(m_filename.c_str(), _S("rb")); + if (m_fileHandle) + { + fread(__raw, 1, BlockSize, m_fileHandle); + fread(m_dir.__raw, 1, BlockSize, m_fileHandle); + fread(m_dirBackup.__raw, 1, BlockSize, m_fileHandle); + fread(m_bat.__raw, 1, BlockSize, m_fileHandle); + fread(m_batBackup.__raw, 1, BlockSize, m_fileHandle); + if (m_dir.m_updateCounter < m_dirBackup.m_updateCounter) + m_dirInUse = &m_dirBackup; + else + m_dirInUse = &m_dir; + + if (m_bat.m_updateCounter < m_batBackup.m_updateCounter) + m_batInUse = &m_batBackup; + else + m_batInUse = &m_bat; + } } Card::~Card() @@ -86,23 +107,105 @@ void Card::getChecksum(uint16_t* checksum, uint16_t* inverse) *inverse = m_checksumInv; } -void Card::format(ECardSize size) +void Card::format(EDeviceId id, ECardSize size, EEncoding encoding) { + memset(__raw, 0xFF, BlockSize); + uint64_t rand = uint64_t(getGCTime()); + m_formatTime = SBig(rand); + for (int i = 0; i < 12; i++) + { + rand = (((rand * (uint64_t)0x41c64e6d) + (uint64_t)0x3039) >> 16); + m_serial[i] = (uint8_t)(g_SRAM.flash_id[uint32_t(id)][i] + (uint32_t)rand); + rand = (((rand * (uint64_t)0x41c64e6d) + (uint64_t)0x3039) >> 16); + rand &= (uint64_t)0x7fffULL; + } + m_sramBias = g_SRAM.counter_bias; + m_sramLanguage = SBig(g_SRAM.lang); + m_unknown = 0; /* 1 works for slot A, 0 both */ + m_deviceId = 0; + m_sizeMb = SBig(uint16_t(size)); + m_encoding = SBig(uint16_t(encoding)); + calculateChecksum((uint16_t*)__raw, 0xFE, &m_checksum, &m_checksumInv); + m_dir = Directory(); + m_dirBackup = m_dir; + m_dirInUse = &m_dir; + m_bat = BlockAllocationTable(uint32_t(size) * MbitToBlocks); + m_batBackup = m_bat; + m_batInUse = &m_bat; + m_sizeMb = SBig(uint16_t(size)); + m_encoding = SBig(uint16_t(encoding)); + + FILE* f = Fopen(m_filename.c_str(), _S("wb")); + if (f) + { + fwrite(__raw, 1, BlockSize, f); + fwrite(m_dir.__raw, 1, BlockSize, f); + fwrite(m_dirBackup.__raw, 1, BlockSize, f); + fwrite(m_bat.__raw, 1, BlockSize, f); + fwrite(m_batBackup.__raw, 1, BlockSize, f); + uint32_t dataLen = ((uint32_t(size) * MbitToBlocks) - 5) * BlockSize; + std::unique_ptr data(new char[dataLen]); + memset(data.get(), 0xFF, dataLen); + fwrite(data.get(), 1, dataLen, f); + fclose(f); + } } -void Card::format() +uint32_t Card::getSizeMbitFromFile(const SystemString& filename) { - FILE* file = fopen(m_filename.c_str(), "wb"); + Sstat stat; + Stat(filename.c_str(), &stat); + return (stat.st_size / BlockSize) / MbitToBlocks; } -uint16_t calculateChecksum(void* data, size_t len) +File::File(char data[]) { - uint16_t ret = 0; - for (size_t i = 0; i < len; ++i) - ret += *(uint8_t*)(reinterpret_cast(data) + i); + memcpy(__raw, data, 0x40); +} - return ret; +File::File(const char* filename) +{ + memset(m_filename, 0, 0x20); + memcpy(m_filename, filename, 32 - strlen(filename)); +} + +BlockAllocationTable::BlockAllocationTable(uint32_t blockCount) +{ + memset(__raw, 0, BlockSize); + m_freeBlocks = SBig(uint16_t(blockCount - 5)); + m_lastAllocated = SBig(uint16_t(4)); + calculateChecksum((uint16_t*)(__raw + 4), 0xFFE, &m_checksum, &m_checksumInv); +} + +Directory::Directory() +{ + memset(__raw, 0xFF, BlockSize); + m_updateCounter = 0; + calculateChecksum((uint16_t*)__raw, 0xFFE, &m_checksum, &m_checksumInv); +} + +Directory::Directory(uint8_t data[]) +{ + memcpy((uint16_t*)__raw, data, BlockSize); +} + + +void calculateChecksum(uint16_t* data, size_t len, uint16_t* checksum, uint16_t* checksumInv) +{ + *checksum = 0; + *checksumInv = 0; + for (size_t i = 0; i < len; i++) + { + *checksum += SBig(data[i]); + *checksumInv += SBig(uint16_t(data[i] ^ 0xFFFF)); + } + *checksum = SBig(uint16_t(*checksum)); + *checksumInv = SBig(uint16_t(*checksumInv)); + if (*checksum == 0xFFFF) + *checksum = 0; + if (*checksumInv == 0xFFFF) + *checksumInv = 0; } } diff --git a/src/SRAM.cpp b/src/SRAM.cpp new file mode 100644 index 0000000..01552ad --- /dev/null +++ b/src/SRAM.cpp @@ -0,0 +1,32 @@ +#include "SRAM.hpp" + +namespace kabufuda +{ +const SRAM g_SRAM = +{{ + 0xFF, 0x6B, + 0x00, 0x91, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0x40, + 0x00, + 0x00, + 0x00, + 0x2C, + 0x41, 0x58, 0x49, 0x4F, 0x44, 0x4C, 0x5F, 0x53, 0x4C, 0x4F, 0x54, 0x41, + 0x41, 0x58, 0x49, 0x4F, 0x44, 0x4C, 0x5F, 0x53, 0x4C, 0x4F, 0x54, 0x42, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, + 0x00, + 0x6E, 0x6D, + 0x00, 0x00, + 0x00, 0x00 +}}; + + +} + diff --git a/src/Util.cpp b/src/Util.cpp new file mode 100644 index 0000000..218a78c --- /dev/null +++ b/src/Util.cpp @@ -0,0 +1,26 @@ +#include "Util.hpp" +#include + +namespace kabufuda +{ +uint64_t getGCTime() +{ + time_t sysTime, tzDiff, tzDST; + struct tm * gmTime; + + time(&sysTime); + + // Account for DST where needed + gmTime = localtime(&sysTime); + if (gmTime->tm_isdst == 1) + tzDST = 3600; + else + tzDST = 0; + + // Lazy way to get local time in sec + gmTime = gmtime(&sysTime); + tzDiff = sysTime - mktime(gmTime); + + return (uint64_t)(sysTime + tzDiff + tzDST) - 0x386D4380; +} +} diff --git a/src/WideStringConvert.cpp b/src/WideStringConvert.cpp new file mode 100644 index 0000000..d0aabc8 --- /dev/null +++ b/src/WideStringConvert.cpp @@ -0,0 +1,43 @@ +#include "WideStringConvert.hpp" +#include "utf8proc.h" + +namespace kabufuda +{ +std::string WideToUTF8(const std::wstring& src) +{ + std::string retval; + retval.reserve(src.length()); + for (wchar_t ch : src) + { + utf8proc_uint8_t mb[4]; + utf8proc_ssize_t c = utf8proc_encode_char(utf8proc_int32_t(ch), mb); + if (c < 0) + { + fprintf(stderr, "invalid UTF-8 character while encoding"); + return retval; + } + retval.append(reinterpret_cast(mb), c); + } + return retval; +} + +std::wstring UTF8ToWide(const std::string& src) +{ + std::wstring retval; + retval.reserve(src.length()); + const utf8proc_uint8_t* buf = reinterpret_cast(src.c_str()); + while (*buf) + { + utf8proc_int32_t wc; + utf8proc_ssize_t len = utf8proc_iterate(buf, -1, &wc); + if (len < 0) + { + fprintf(stderr, "invalid UTF-8 character while decoding"); + return retval; + } + buf += len; + retval += wchar_t(wc); + } + return retval; +} +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9c8ab31..1058a70 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,4 +3,4 @@ add_executable(cardtest main.cpp) target_link_libraries(cardtest - card) + kabufuda) diff --git a/test/main.cpp b/test/main.cpp index e270cab..43e2f86 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -3,6 +3,8 @@ int main() { - card::Card card{"test.mc"}; + kabufuda::Card mc{_S("test.USA.raw")}; + mc.format(kabufuda::EDeviceId::SlotA, kabufuda::ECardSize::Card123Mb); + printf("File Mbit %x\n", kabufuda::Card::getSizeMbitFromFile(_S("test.USA.raw"))); return 0; }