Implement format

This commit is contained in:
Phillip Stephens 2016-03-26 11:34:03 -07:00
parent a2a7386a46
commit 4c2d5e7214
12 changed files with 959 additions and 48 deletions

View File

@ -1,9 +1,12 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.0)
project(Card) project(kabufuda)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wno-multichar -fno-exceptions") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wno-multichar -fno-exceptions")
include_directories(include) include_directories(include)
add_library(card STATIC add_library(kabufuda STATIC
include/Card.hpp src/Card.cpp) 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) add_subdirectory(test)

View File

@ -3,13 +3,16 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <stdint.h>
#include <memory.h> #include <memory.h>
namespace card #include "Util.hpp"
namespace kabufuda
{ {
uint32_t constexpr BlockSize = 0x2000; uint32_t constexpr BlockSize = 0x2000;
uint32_t constexpr MaxFiles = 127; uint32_t constexpr MaxFiles = 127;
uint32_t constexpr FSTBlocks = 5;
uint32_t constexpr MbitToBlocks = 0x10;
/** /**
* @brief The EPermissions enum * @brief The EPermissions enum
@ -52,12 +55,13 @@ enum class ECardSize : uint16_t
*/ */
enum class EEncoding : uint16_t enum class EEncoding : uint16_t
{ {
ASCII, /**< Standard ASCII Encoding */ ASCII, /**< Standard ASCII Encoding */
SJIS /**< SJIS Encoding for japanese */ SJIS /**< SJIS Encoding for japanese */
}; };
class File class File
{ {
#pragma pack(push, 4)
union union
{ {
struct struct
@ -76,42 +80,47 @@ class File
}; };
uint8_t __raw[0x40]; uint8_t __raw[0x40];
}; };
#pragma pop()
public: public:
File() {} File() {}
File(char data[0x40]) File(char data[0x40]);
{ File(const char* filename);
memcpy(__raw, data, 0x40);
}
File(const char* filename)
{
memset(m_filename, 0, 0x20);
memcpy(m_filename, filename, 0x20);
}
~File() {} ~File() {}
}; };
class BlockAllocationTable class BlockAllocationTable
{ {
friend class Card;
#pragma pack(push, 4)
union union
{ {
struct struct
{ {
uint16_t m_checksum; uint16_t m_checksum;
uint16_t m_checksumInv; uint16_t m_checksumInv;
uint16_t m_updateCounter;
uint16_t m_freeBlocks; uint16_t m_freeBlocks;
uint16_t m_lastAllocated; uint16_t m_lastAllocated;
uint16_t m_map[0xFFB]; uint16_t m_map[0xFFB];
}; };
uint8_t __raw[BlockSize]; uint8_t __raw[BlockSize];
}; };
#pragma pop()
public: public:
BlockAllocationTable() {} explicit BlockAllocationTable(uint32_t blockCount = (uint32_t(ECardSize::Card2043Mb) * MbitToBlocks));
BlockAllocationTable(uint8_t data[BlockSize]); BlockAllocationTable(uint8_t data[BlockSize]);
~BlockAllocationTable() {} ~BlockAllocationTable() {}
uint16_t getNextBlock() const;
uint16_t nextFreeBlock(uint16_t maxBlocks, uint16_t startingBlock = FSTBlocks);
}; };
class Directory class Directory
{ {
friend class Card;
#pragma pack(push, 4)
union union
{ {
struct struct
@ -124,17 +133,17 @@ class Directory
}; };
uint8_t __raw[BlockSize]; uint8_t __raw[BlockSize];
}; };
#pragma pop()
public: public:
Directory() {} Directory();
Directory(uint8_t data[BlockSize])
{ Directory(uint8_t data[BlockSize]);
memcpy(__raw, data, BlockSize);
}
~Directory() {} ~Directory() {}
}; };
class Card class Card
{ {
#pragma pack(push, 4)
union union
{ {
struct struct
@ -154,7 +163,8 @@ class Card
}; };
uint8_t __raw[BlockSize]; uint8_t __raw[BlockSize];
}; };
std::string m_filename; #pragma pop()
SystemString m_filename;
Directory m_dir; Directory m_dir;
Directory m_dirBackup; Directory m_dirBackup;
Directory* m_dirInUse = nullptr; Directory* m_dirInUse = nullptr;
@ -166,13 +176,14 @@ class Card
char m_maker[3] = {'\0'}; char m_maker[3] = {'\0'};
void setChecksum(uint16_t checksum) void setChecksum(uint16_t checksum)
{ {
m_checksum = (checksum); m_checksum = SBig(checksum);
m_checksumInv = ~checksum; m_checksumInv = SBig(checksum ^ 0xFFFF);
} }
FILE* m_fileHandle;
public: public:
Card(); 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(); ~Card();
/** /**
@ -216,21 +227,26 @@ public:
void getChecksum(uint16_t* checksum, uint16_t* inverse); void getChecksum(uint16_t* checksum, uint16_t* inverse);
/** /**
* @brief Formats the memory card and assigns a new serial * @brief Formats the memory card and assigns a new serial
* @param size The desired size of the file * @param size The desired size of the file @sa ECardSize
* @param size The desired encoding * @param encoding The desired encoding @sa EEncoding
* @sa ECardSize
* @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 * @brief calculateChecksum
* @param data * @param data
* @param len * @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__ #endif // __CARD_HPP__

76
include/SRAM.hpp Normal file
View File

@ -0,0 +1,76 @@
#ifndef SRAM_HPP
#define SRAM_HPP
#include <stdint.h>
// 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

310
include/Util.hpp Normal file
View File

@ -0,0 +1,310 @@
#ifndef __UTIL_HPP__
#define __UTIL_HPP__
#ifndef _WIN32
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/statvfs.h>
#else
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <Windows.h>
#include <wchar.h>
#include "winsupport.hpp"
#endif
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <algorithm>
#include <string>
#include <string.h>
#undef bswap16
#undef bswap32
#undef bswap64
namespace kabufuda
{
/* Type-sensitive byte swappers */
template <typename T>
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 <typename T>
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 <typename T>
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__

View File

@ -0,0 +1,7 @@
#include <string>
namespace kabufuda
{
std::string WideToUTF8(const std::wstring& src);
std::wstring UTF8ToWide(const std::string& src);
}

293
include/utf8proc.h Normal file
View File

@ -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 <stdlib.h>
#include <sys/types.h>
#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 <stdbool.h>
# include <inttypes.h>
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 <limits.h>
/** @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 <iterator>
#include <string>
class UTF8Iterator : public std::iterator<std::forward_iterator_tag, uint32_t>
{
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<v ; ++i)
{
utf8proc_int32_t dummy;
utf8proc_ssize_t sz = utf8proc_iterate(reinterpret_cast<const utf8proc_uint8_t*>(&*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<const utf8proc_uint8_t*>(&*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

View File

@ -1,22 +1,43 @@
#include "Card.hpp" #include "Card.hpp"
#include "SRAM.hpp"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <memory>
namespace card namespace kabufuda
{ {
Card::Card() Card::Card()
{ {
memset(__raw, 0xFF, BlockSize); memset(__raw, 0xFF, BlockSize);
} }
Card::Card(const std::string &filepath, const char* game, const char* maker) Card::Card(const SystemString& filename, const char* game, const char* maker)
: m_filepath(filepath) : m_filename(filename)
{ {
memset(__raw, 0xFF, BlockSize); memset(__raw, 0xFF, BlockSize);
if (game && strlen(game) == 4) if (game && strlen(game) == 4)
memcpy(m_game, game, 4); memcpy(m_game, game, 4);
if (maker && strlen(maker) == 2) if (maker && strlen(maker) == 2)
memcpy(m_maker, 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() Card::~Card()
@ -86,23 +107,105 @@ void Card::getChecksum(uint16_t* checksum, uint16_t* inverse)
*inverse = m_checksumInv; *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<char[]> 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; memcpy(__raw, data, 0x40);
for (size_t i = 0; i < len; ++i) }
ret += *(uint8_t*)(reinterpret_cast<uint8_t*>(data) + i);
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;
} }
} }

32
src/SRAM.cpp Normal file
View File

@ -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
}};
}

26
src/Util.cpp Normal file
View File

@ -0,0 +1,26 @@
#include "Util.hpp"
#include <time.h>
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;
}
}

43
src/WideStringConvert.cpp Normal file
View File

@ -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<char*>(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<const utf8proc_uint8_t*>(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;
}
}

View File

@ -3,4 +3,4 @@ add_executable(cardtest
main.cpp) main.cpp)
target_link_libraries(cardtest target_link_libraries(cardtest
card) kabufuda)

View File

@ -3,6 +3,8 @@
int main() 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; return 0;
} }