Compare commits

...

22 Commits

Author SHA1 Message Date
4f3fbc0d0d Add EMSCRIPTEN checks 2022-08-03 18:11:08 -04:00
32090b4d4a Refactor to remove <Windows.h> from headers 2022-02-22 00:48:11 -05:00
2252e6c3c3 Fix typo 2021-06-30 16:21:37 -04:00
4cf61b7d5e Use UTF-8 exclusively internally 2021-06-28 18:58:22 -04:00
1111fb4839 Fix corruption issues while writing memory card to disk 2021-05-02 15:05:34 -07:00
106bb02fbf Fix -Wstringop-truncation issues 2021-04-05 13:22:56 -04:00
b585305009 Add AsyncIONX.cpp 2020-10-21 00:45:02 -04:00
6f5cb6f972 GCC nonstring fixes 2020-10-21 00:44:45 -04:00
4ee594d0fd Link rt for AIO on Linux 2020-10-07 20:11:05 -04:00
b63f7c6401 AsyncIOPosix: Add mode_t argument to open 2020-06-24 12:05:55 -04:00
4891a95688 Fix corrupted memory cards 2020-04-15 04:26:14 -07:00
Jack Andersen
6d8d389459 Merge commit '4648' 2020-04-10 19:01:39 -10:00
Jack Andersen
4648c93b8e Exclude CMake CXX standard from MSVC 2020-04-10 19:01:21 -10:00
Lioncash
1b813f0c70 Util: Make operator|= and &= constexpr for bitwise enums
Maintains parity with hecl's version of the macro, etc.
2020-04-05 10:00:12 -04:00
35e5c7c90f Merge pull request #12 from lioncash/const
General: Make member functions const where applicable
2019-09-03 00:37:32 -07:00
d48ba9523f Merge pull request #13 from lioncash/explicit
General: Use explicit where applicable
2019-09-03 00:37:23 -07:00
f74f1a3a6c Merge pull request #11 from lioncash/array
General: Use std::array for constructors where applicable
2019-09-03 00:36:32 -07:00
Lioncash
9d3a436e70 Card: Use UINT32_MAX for FileHandle's default initializer
Avoids a sign conversion
2019-09-02 08:45:07 -04:00
Lioncash
e96db1e032 General: Use explicit where applicable
Makes conversions explicit in order to prevent error-prone implicit
conversions.
2019-09-02 08:42:37 -04:00
Lioncash
ed2a6b3ce8 Card: Make querying functions const member functions where applicable
These don't modify member state, so they can be made const .
2019-09-02 08:26:43 -04:00
Lioncash
66ce5ed823 Directory: Make indexForFile() a const member function
This doesn't modify instance state, so it can be turned into a const
member function.
2019-09-02 08:20:50 -04:00
Lioncash
69d24d1027 General: Use std::array for constructors where applicable
Same behavior, but ensures input absolutely satisfies the total amount
of data that needs to be copied.
2019-09-02 08:14:44 -04:00
18 changed files with 315 additions and 629 deletions

View File

@@ -1,8 +1,10 @@
cmake_minimum_required(VERSION 3.10 FATAL_ERROR) # because of c++17
project(kabufuda)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if (NOT MSVC)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
add_library(kabufuda STATIC
include/kabufuda/Constants.hpp
@@ -13,37 +15,49 @@ add_library(kabufuda STATIC
include/kabufuda/File.hpp lib/kabufuda/File.cpp
include/kabufuda/Util.hpp lib/kabufuda/Util.cpp
include/kabufuda/SRAM.hpp lib/kabufuda/SRAM.cpp
include/kabufuda/WideStringConvert.hpp lib/kabufuda/WideStringConvert.cpp
)
if(WIN32)
if (MSVC)
target_compile_options(kabufuda PRIVATE
# Enforce various standards compliant behavior.
/permissive-
$<$<COMPILE_LANGUAGE:CXX>:/permissive->
# Enable standard volatile semantics.
/volatile:iso
$<$<COMPILE_LANGUAGE:CXX>:/volatile:iso>
# Reports the proper value for the __cplusplus preprocessor macro.
/Zc:__cplusplus
$<$<COMPILE_LANGUAGE:CXX>:/Zc:__cplusplus>
# Allow constexpr variables to have explicit external linkage.
/Zc:externConstexpr
# Assume that new throws exceptions, allowing better code generation.
/Zc:throwingNew
# Use latest C++ standard.
$<$<COMPILE_LANGUAGE:CXX>:/std:c++latest>
)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
# Flags for MSVC (not clang-cl)
target_compile_options(kabufuda PRIVATE
# Allow constexpr variables to have explicit external linkage.
$<$<COMPILE_LANGUAGE:CXX>:/Zc:externConstexpr>
# Assume that new throws exceptions, allowing better code generation.
$<$<COMPILE_LANGUAGE:CXX>:/Zc:throwingNew>
)
endif()
endif()
target_sources(kabufuda PRIVATE
include/kabufuda/winsupport.hpp
lib/kabufuda/AsyncIOWin32.cpp
)
elseif(NX OR EMSCRIPTEN)
target_sources(kabufuda PRIVATE
lib/kabufuda/AsyncIONX.cpp
)
else()
target_sources(kabufuda PRIVATE
lib/kabufuda/AsyncIOPosix.cpp
)
if(NOT APPLE)
target_link_libraries(kabufuda PUBLIC rt)
endif()
endif()
target_include_directories(kabufuda PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)

View File

@@ -1,11 +1,15 @@
#pragma once
#ifndef _WIN32
#include <aio.h>
#ifdef __SWITCH__
#include <sys/types.h>
using SizeReturn = ssize_t;
#else
#include <windows.h>
using SizeReturn = DWORD;
#include <aio.h>
using SizeReturn = ssize_t;
#endif
#else
using SizeReturn = unsigned long;
#endif
#include <cstddef>
@@ -14,36 +18,42 @@ using SizeReturn = DWORD;
#include "kabufuda/Util.hpp"
namespace kabufuda {
#if _WIN32
struct AsyncIOInner;
#endif
class AsyncIO {
#ifndef _WIN32
#if defined(__SWITCH__) || defined(EMSCRIPTEN)
FILE* m_fd;
#elif !defined(_WIN32)
int m_fd = -1;
std::vector<std::pair<struct aiocb, SizeReturn>> m_queue;
#else
HANDLE m_fh = INVALID_HANDLE_VALUE;
std::vector<std::pair<OVERLAPPED, SizeReturn>> m_queue;
AsyncIOInner* m_inner;
#endif
void _waitForOperation(size_t qIdx) const;
size_t m_maxBlock = 0;
mutable size_t m_maxBlock = 0;
public:
AsyncIO() = default;
AsyncIO(SystemStringView filename, bool truncate = false);
explicit AsyncIO(std::string_view filename, bool truncate = false);
~AsyncIO();
AsyncIO(AsyncIO&& other);
AsyncIO& operator=(AsyncIO&& other);
AsyncIO(const AsyncIO* other) = delete;
AsyncIO& operator=(const AsyncIO& other) = delete;
void resizeQueue(size_t queueSz) { m_queue.resize(queueSz); }
void resizeQueue(size_t queueSz);
bool asyncRead(size_t qIdx, void* buf, size_t length, off_t offset);
bool asyncWrite(size_t qIdx, const void* buf, size_t length, off_t offset);
ECardResult pollStatus(size_t qIdx, SizeReturn* szRet = nullptr) const;
ECardResult pollStatus() const;
void waitForCompletion() const;
#ifndef _WIN32
operator bool() const { return m_fd != -1; }
#if defined(__SWITCH__) || defined(EMSCRIPTEN)
explicit operator bool() const { return m_fd != nullptr; }
#elif !defined(_WIN32)
explicit operator bool() const { return m_fd != -1; }
#else
operator bool() const { return m_fh != INVALID_HANDLE_VALUE; }
explicit operator bool() const;
#endif
};

View File

@@ -17,14 +17,14 @@ namespace kabufuda {
class FileHandle {
friend class Card;
uint32_t idx = -1;
uint32_t idx = UINT32_MAX;
int32_t offset = 0;
FileHandle(uint32_t idx) : idx(idx) {}
explicit FileHandle(uint32_t idx) : idx(idx) {}
public:
FileHandle() = default;
uint32_t getFileNo() const { return idx; }
operator bool() const { return getFileNo() != UINT32_MAX; }
explicit operator bool() const { return getFileNo() != UINT32_MAX; }
};
struct ProbeResults {
@@ -97,11 +97,12 @@ class Card {
};
void _swapEndian();
};
CardHeader m_ch;
CardHeader m_tmpCh;
#pragma pack(pop)
SystemString m_filename;
CardHeader m_ch;
CardHeader m_tmpCh;
std::string m_filename;
AsyncIO m_fileHandle;
std::array<Directory, 2> m_dirs;
std::array<BlockAllocationTable, 2> m_bats;
@@ -136,7 +137,7 @@ public:
* @param game The game code.
* @param maker The maker code.
*/
Card(const char* game = nullptr, const char* maker = nullptr);
explicit Card(const char* game = nullptr, const char* maker = nullptr);
~Card();
/**
@@ -204,7 +205,7 @@ public:
*
* @return Gets the name of the given file.
*/
const char* getFilename(const FileHandle& fh);
const char* getFilename(const FileHandle& fh) const;
/**
* @brief deleteFile
@@ -269,7 +270,7 @@ public:
*
* @return The offset or -1 if an invalid handle is passed.
*/
int32_t tell(const FileHandle& fh);
int32_t tell(const FileHandle& fh) const;
/**
* @brief setPublic
@@ -425,7 +426,7 @@ public:
* @param checksum The checksum of the system header.
* @param inverse The inverse checksum of the system header.
*/
void getChecksum(uint16_t& checksum, uint16_t& inverse);
void getChecksum(uint16_t& checksum, uint16_t& inverse) const;
/**
* @brief Retrieves the available storage and directory space.
@@ -433,7 +434,7 @@ public:
* @param bytesNotUsed Number of free bytes out.
* @param filesNotUsed Number of free files out.
*/
void getFreeBlocks(int32_t& bytesNotUsed, int32_t& filesNotUsed);
void getFreeBlocks(int32_t& bytesNotUsed, int32_t& filesNotUsed) const;
/**
* @brief Formats the memory card and assigns a new serial.
@@ -449,7 +450,7 @@ public:
*
* @return ProbeResults structure.
*/
static ProbeResults probeCardFile(SystemStringView filename);
static ProbeResults probeCardFile(std::string_view filename);
/**
* @brief Writes any changes to the Card instance immediately to disk. <br />
@@ -460,7 +461,7 @@ public:
/**
* @brief Opens card image (does nothing if currently open path matches).
*/
bool open(SystemStringView filepath);
bool open(std::string_view filepath);
/**
* @brief Commits changes to disk and closes host file.
@@ -472,7 +473,7 @@ public:
*
* @return A view to the card's filename.
*/
SystemStringView cardFilename() const { return m_filename; }
std::string_view cardFilename() const { return m_filename; }
/**
* @brief Gets card-scope error state.
@@ -489,6 +490,6 @@ public:
/**
* @return Whether or not the card is within a ready state.
*/
operator bool() const { return getError() == ECardResult::READY; }
explicit operator bool() const { return getError() == ECardResult::READY; }
};
} // namespace kabufuda

View File

@@ -7,19 +7,23 @@
namespace kabufuda {
class Directory {
friend class Card;
#pragma pack(push, 4)
using RawData = std::array<uint8_t, BlockSize>;
struct Data {
std::array<File, MaxFiles> m_files;
std::array<uint8_t, 0x3a> padding;
File m_files[MaxFiles];
uint8_t padding[0x3a];
uint16_t m_updateCounter;
uint16_t m_checksum;
uint16_t m_checksumInv;
};
#pragma pack(pop)
union {
Data data;
std::array<uint8_t, BlockSize> raw;
RawData raw;
};
#pragma pack(pop)
void swapEndian();
void updateChecksum();
@@ -27,7 +31,7 @@ class Directory {
public:
Directory();
Directory(uint8_t data[BlockSize]);
explicit Directory(const RawData& rawData);
~Directory() = default;
bool hasFreeFile() const;
@@ -36,6 +40,6 @@ public:
File* getFirstNonFreeFile(uint32_t start, const char* game, const char* maker);
File* getFile(const char* game, const char* maker, const char* filename);
File* getFile(uint32_t idx);
int32_t indexForFile(File* f);
int32_t indexForFile(const File* f) const;
};
} // namespace kabufuda

View File

@@ -10,12 +10,16 @@ class File {
friend class Directory;
friend class Card;
#pragma pack(push, 4)
using RawData = std::array<uint8_t, 0x40>;
union {
struct {
std::array<uint8_t, 4> m_game;
std::array<uint8_t, 2> m_maker;
uint8_t m_game[4];
uint8_t m_maker[2];
uint8_t m_reserved;
uint8_t m_bannerFlags;
#if __GNUC__ && !__clang__
__attribute__((nonstring))
#endif
char m_filename[0x20];
uint32_t m_modifiedTime;
uint32_t m_iconAddress;
@@ -28,16 +32,15 @@ class File {
uint16_t m_reserved2;
uint32_t m_commentAddr;
};
std::array<uint8_t, 0x40> raw;
RawData raw;
};
#pragma pack(pop)
void swapEndian();
public:
File();
File(char data[0x40]);
File(const char* filename);
explicit File(const RawData& rawData);
explicit File(const char* filename);
~File() = default;
};
} // namespace kabufuda

View File

@@ -11,19 +11,6 @@
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#else
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <Windows.h>
#include <cwchar>
#include "winsupport.hpp"
#if UNICODE
#define CARD_UCS2 1
#endif
#endif
#include <algorithm>
@@ -32,8 +19,6 @@
#include <string>
#include <type_traits>
#include "kabufuda/WideStringConvert.hpp"
#undef bswap16
#undef bswap32
#undef bswap64
@@ -48,12 +33,12 @@
using T = std::underlying_type_t<type>; \
return type(static_cast<T>(a) & static_cast<T>(b)); \
} \
inline type& operator|=(type& a, const type& b) { \
constexpr type& operator|=(type& a, type b) { \
using T = std::underlying_type_t<type>; \
a = type(static_cast<T>(a) | static_cast<T>(b)); \
return a; \
} \
inline type& operator&=(type& a, const type& b) { \
constexpr type& operator&=(type& a, type b) { \
using T = std::underlying_type_t<type>; \
a = type(static_cast<T>(a) & static_cast<T>(b)); \
return a; \
@@ -177,72 +162,10 @@ constexpr double SBig(double val) { return val; }
#endif
#endif
#if CARD_UCS2
typedef wchar_t SystemChar;
inline size_t StrLen(const SystemChar* str) { return wcslen(str); }
typedef std::wstring SystemString;
typedef std::wstring_view SystemStringView;
inline void ToLower(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), towlower); }
inline void ToUpper(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), towupper); }
class SystemUTF8Conv {
std::string m_utf8;
public:
explicit SystemUTF8Conv(SystemStringView str) : m_utf8(WideToUTF8(str)) {}
std::string_view str() const { return m_utf8; }
const char* c_str() const { return m_utf8.c_str(); }
std::string operator+(std::string_view other) const { return m_utf8 + other.data(); }
};
inline std::string operator+(std::string_view lhs, const SystemUTF8Conv& rhs) { return std::string(lhs) + rhs.c_str(); }
class SystemStringConv {
std::wstring m_sys;
public:
explicit SystemStringConv(std::string_view str) : m_sys(UTF8ToWide(str)) {}
SystemStringView sys_str() const { return m_sys; }
const SystemChar* c_str() const { return m_sys.c_str(); }
std::wstring operator+(const std::wstring_view other) const { return m_sys + other.data(); }
};
inline std::wstring operator+(const std::wstring_view lhs, const SystemStringConv& rhs) {
return std::wstring(lhs) + rhs.c_str();
}
#ifndef _SYS_STR
#define _SYS_STR(val) L##val
#endif
typedef struct _stat Sstat;
#if _WIN32
using Sstat = struct ::_stat64;
#else
typedef char SystemChar;
inline size_t StrLen(const SystemChar* str) { return std::strlen(str); }
typedef std::string SystemString;
typedef std::string_view SystemStringView;
inline void ToLower(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), tolower); }
inline void ToUpper(SystemString& str) { std::transform(str.begin(), str.end(), str.begin(), toupper); }
class SystemUTF8Conv {
std::string_view m_utf8;
public:
explicit SystemUTF8Conv(SystemStringView str) : m_utf8(str) {}
std::string_view str() const { return m_utf8; }
const char* c_str() const { return m_utf8.data(); }
std::string operator+(std::string_view other) const { return std::string(m_utf8) + other.data(); }
};
inline std::string operator+(std::string_view lhs, const SystemUTF8Conv& rhs) { return std::string(lhs) + rhs.c_str(); }
class SystemStringConv {
SystemStringView m_sys;
public:
explicit SystemStringConv(std::string_view str) : m_sys(str) {}
SystemStringView sys_str() const { return m_sys; }
const SystemChar* c_str() const { return m_sys.data(); }
std::string operator+(std::string_view other) const { return std::string(m_sys) + other.data(); }
};
inline std::string operator+(std::string_view lhs, const SystemStringConv& rhs) {
return std::string(lhs) + rhs.c_str();
}
#ifndef _SYS_STR
#define _SYS_STR(val) val
#endif
typedef struct stat Sstat;
using Sstat = struct stat;
#endif
uint64_t getGCTime();
@@ -255,19 +178,11 @@ uint64_t getGCTime();
#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
#endif
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);
#if _WIN32
int Stat(const char* path, Sstat* statOut);
#else
return stat(path, statOut);
inline int Stat(const char* path, Sstat* statOut) { return stat(path, statOut); }
#endif
}
/**
* @brief calculateChecksum

View File

@@ -1,8 +0,0 @@
#pragma once
#include <string>
namespace kabufuda {
std::string WideToUTF8(std::wstring_view src);
std::wstring UTF8ToWide(std::string_view src);
} // namespace kabufuda

View File

@@ -1,34 +0,0 @@
#pragma once
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#ifndef NOMINMAX
#define NOMINMAX 1
#endif
#include "windows.h"
#include <cstdint>
#ifndef DEF_INLINE_MEMMEM
#define DEF_INLINE_MEMMEM
inline void* memmem(const void* haystack, size_t hlen, const void* needle, size_t nlen) {
int needle_first;
const uint8_t* p = static_cast<const uint8_t*>(haystack);
size_t plen = hlen;
if (!nlen)
return NULL;
needle_first = *(unsigned char*)needle;
while (plen >= nlen && (p = static_cast<const uint8_t*>(memchr(p, needle_first, plen - nlen + 1)))) {
if (!memcmp(p, needle, nlen))
return (void*)p;
p++;
plen = hlen - (p - static_cast<const uint8_t*>(haystack));
}
return NULL;
}
#endif

View File

@@ -1,308 +0,0 @@
/*
* 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
#ifdef __cplusplus
# include <cstdbool>
# include <cinttypes>
#else
# include <stdbool.h>
# include <inttypes.h>
#endif
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
#ifdef __cplusplus
# include <climits>
#else
# include <limits.h>
#endif
/** @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
{
std::string::const_iterator m_it;
public:
using iterator_category = std::forward_iterator_tag;
using value_type = uint32_t;
using difference_type = std::ptrdiff_t;
using pointer = uint32_t*;
using reference = uint32_t&;
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

@@ -0,0 +1,50 @@
#include "kabufuda/AsyncIO.hpp"
#include <cstdio>
#include <cstring>
namespace kabufuda {
AsyncIO::AsyncIO(std::string_view filename, bool truncate) { m_fd = fopen(filename.data(), truncate ? "rw" : "ra"); }
AsyncIO::~AsyncIO() {
if (*this) {
fclose(m_fd);
}
}
AsyncIO::AsyncIO(AsyncIO&& other) {
m_fd = other.m_fd;
other.m_fd = nullptr;
m_maxBlock = other.m_maxBlock;
}
AsyncIO& AsyncIO::operator=(AsyncIO&& other) {
if (*this) {
fclose(m_fd);
}
m_fd = other.m_fd;
other.m_fd = nullptr;
m_maxBlock = other.m_maxBlock;
return *this;
}
void AsyncIO::_waitForOperation(size_t qIdx) const {}
bool AsyncIO::asyncRead(size_t qIdx, void* buf, size_t length, off_t offset) {
return fread(buf, length, offset, m_fd);
}
bool AsyncIO::asyncWrite(size_t qIdx, const void* buf, size_t length, off_t offset) {
return fwrite(buf, length, offset, m_fd);
}
ECardResult AsyncIO::pollStatus(size_t qIdx, SizeReturn* szRet) const { return ECardResult::READY; }
ECardResult AsyncIO::pollStatus() const { return ECardResult::READY; }
void AsyncIO::waitForCompletion() const {}
void AsyncIO::resizeQueue(size_t queueSz) {}
} // namespace kabufuda

View File

@@ -6,8 +6,8 @@
namespace kabufuda {
AsyncIO::AsyncIO(SystemStringView filename, bool truncate) {
m_fd = open(filename.data(), O_RDWR | O_CREAT | (truncate ? O_TRUNC : 0));
AsyncIO::AsyncIO(std::string_view filename, bool truncate) {
m_fd = open(filename.data(), O_RDWR | O_CREAT | (truncate ? O_TRUNC : 0), 0644);
}
AsyncIO::~AsyncIO() {
@@ -142,4 +142,8 @@ void AsyncIO::waitForCompletion() const {
const_cast<AsyncIO*>(this)->m_maxBlock = 0;
}
void AsyncIO::resizeQueue(size_t queueSz) {
m_queue.resize(queueSz);
}
} // namespace kabufuda

View File

@@ -1,12 +1,47 @@
#include "kabufuda/AsyncIO.hpp"
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#ifndef NOMINMAX
#define NOMINMAX 1
#endif
#include <Windows.h>
#include <algorithm>
#include <cstdio>
namespace kabufuda {
#undef min
#undef max
class WStringConv {
std::wstring m_sys;
public:
explicit WStringConv(std::string_view str) {
int len = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), nullptr, 0);
m_sys.assign(len, L'\0');
MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), &m_sys[0], len);
}
[[nodiscard]] std::wstring str() const { return m_sys; }
[[nodiscard]] const wchar_t* c_str() const { return m_sys.c_str(); }
};
int Stat(const char* path, Sstat* statOut) {
size_t pos;
WStringConv wpath(path);
const wchar_t* wpathP = wpath.c_str();
for (pos = 0; pos < 3 && wpathP[pos] != L'\0'; ++pos) {}
if (pos == 2 && wpathP[1] == L':') {
wchar_t fixPath[4] = {wpathP[0], L':', L'/', L'\0'};
return _wstat64(fixPath, statOut);
}
return _wstat64(wpath.c_str(), statOut);
}
struct AsyncIOInner {
HANDLE m_fh = INVALID_HANDLE_VALUE;
std::vector<std::pair<OVERLAPPED, SizeReturn>> m_queue;
};
static void ResetOverlapped(OVERLAPPED& aio, DWORD offset = 0) {
aio.Internal = 0;
@@ -15,59 +50,70 @@ static void ResetOverlapped(OVERLAPPED& aio, DWORD offset = 0) {
aio.OffsetHigh = 0;
}
AsyncIO::AsyncIO(SystemStringView filename, bool truncate) {
AsyncIO::AsyncIO(std::string_view filename, bool truncate) : m_inner(new AsyncIOInner) {
#if WINDOWS_STORE
CREATEFILE2_EXTENDED_PARAMETERS parms = {};
parms.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
parms.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
parms.dwFileFlags = FILE_FLAG_OVERLAPPED;
m_fh = CreateFile2(filename.data(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
truncate ? CREATE_ALWAYS : OPEN_ALWAYS, &parms);
m_inner->m_fh = CreateFile2(filename.data(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
truncate ? CREATE_ALWAYS : OPEN_ALWAYS, &parms);
#else
m_fh = CreateFileW(filename.data(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
truncate ? CREATE_ALWAYS : OPEN_ALWAYS, FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL, nullptr);
WStringConv wfilename(filename);
m_inner->m_fh =
CreateFileW(wfilename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
truncate ? CREATE_ALWAYS : OPEN_ALWAYS, FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL, nullptr);
#endif
}
AsyncIO::~AsyncIO() {
if (*this) {
if (CancelIoEx(m_fh, nullptr))
if (CancelIoEx(m_inner->m_fh, nullptr))
waitForCompletion();
CloseHandle(m_fh);
CloseHandle(m_inner->m_fh);
}
delete m_inner;
}
AsyncIO::AsyncIO(AsyncIO&& other) {
m_fh = other.m_fh;
other.m_fh = INVALID_HANDLE_VALUE;
m_queue = std::move(other.m_queue);
if (*this) {
if (CancelIoEx(m_inner->m_fh, nullptr))
waitForCompletion();
CloseHandle(m_inner->m_fh);
}
delete m_inner;
m_inner = other.m_inner;
other.m_inner = nullptr;
m_maxBlock = other.m_maxBlock;
}
AsyncIO& AsyncIO::operator=(AsyncIO&& other) {
if (*this) {
if (CancelIoEx(m_fh, nullptr))
if (CancelIoEx(m_inner->m_fh, nullptr))
waitForCompletion();
CloseHandle(m_fh);
CloseHandle(m_inner->m_fh);
}
m_fh = other.m_fh;
other.m_fh = INVALID_HANDLE_VALUE;
m_queue = std::move(other.m_queue);
delete m_inner;
m_inner = other.m_inner;
other.m_inner = nullptr;
m_maxBlock = other.m_maxBlock;
return *this;
}
void AsyncIO::_waitForOperation(size_t qIdx) const {
auto& aio = const_cast<AsyncIO*>(this)->m_queue[qIdx];
if (m_inner == nullptr) {
return;
}
auto& aio = m_inner->m_queue[qIdx];
if (aio.first.hEvent == 0)
return;
GetOverlappedResult(m_fh, &aio.first, &aio.second, TRUE);
GetOverlappedResult(m_inner->m_fh, &aio.first, &aio.second, TRUE);
CloseHandle(aio.first.hEvent);
aio.first.hEvent = 0;
}
bool AsyncIO::asyncRead(size_t qIdx, void* buf, size_t length, off_t offset) {
OVERLAPPED& aio = m_queue[qIdx].first;
OVERLAPPED& aio = m_inner->m_queue[qIdx].first;
if (aio.hEvent) {
#ifndef NDEBUG
fprintf(stderr, "WARNING: synchronous kabufuda fallback, check access polling\n");
@@ -78,12 +124,12 @@ bool AsyncIO::asyncRead(size_t qIdx, void* buf, size_t length, off_t offset) {
}
ResetOverlapped(aio, DWORD(offset));
m_maxBlock = std::max(m_maxBlock, qIdx + 1);
BOOL res = ReadFile(m_fh, buf, length, nullptr, &aio);
BOOL res = ReadFile(m_inner->m_fh, buf, length, nullptr, &aio);
return res == TRUE || GetLastError() == ERROR_IO_PENDING;
}
bool AsyncIO::asyncWrite(size_t qIdx, const void* buf, size_t length, off_t offset) {
OVERLAPPED& aio = m_queue[qIdx].first;
OVERLAPPED& aio = m_inner->m_queue[qIdx].first;
if (aio.hEvent) {
#ifndef NDEBUG
fprintf(stderr, "WARNING: synchronous kabufuda fallback, check access polling\n");
@@ -94,18 +140,18 @@ bool AsyncIO::asyncWrite(size_t qIdx, const void* buf, size_t length, off_t offs
}
ResetOverlapped(aio, DWORD(offset));
m_maxBlock = std::max(m_maxBlock, qIdx + 1);
BOOL res = WriteFile(m_fh, buf, length, nullptr, &aio);
BOOL res = WriteFile(m_inner->m_fh, buf, length, nullptr, &aio);
return res == TRUE || GetLastError() == ERROR_IO_PENDING;
}
ECardResult AsyncIO::pollStatus(size_t qIdx, SizeReturn* szRet) const {
auto& aio = const_cast<AsyncIO*>(this)->m_queue[qIdx];
auto& aio = m_inner->m_queue[qIdx];
if (aio.first.hEvent == 0) {
if (szRet)
*szRet = aio.second;
return ECardResult::READY;
}
if (GetOverlappedResult(m_fh, &aio.first, &aio.second, FALSE)) {
if (GetOverlappedResult(m_inner->m_fh, &aio.first, &aio.second, FALSE)) {
CloseHandle(aio.first.hEvent);
aio.first.hEvent = 0;
if (szRet)
@@ -123,12 +169,11 @@ ECardResult AsyncIO::pollStatus(size_t qIdx, SizeReturn* szRet) const {
ECardResult AsyncIO::pollStatus() const {
ECardResult result = ECardResult::READY;
for (auto it = const_cast<AsyncIO*>(this)->m_queue.begin();
it != const_cast<AsyncIO*>(this)->m_queue.begin() + m_maxBlock; ++it) {
for (auto it = m_inner->m_queue.begin(); it != m_inner->m_queue.begin() + m_maxBlock; ++it) {
auto& aio = *it;
if (aio.first.hEvent == 0)
continue;
if (GetOverlappedResult(m_fh, &aio.first, &aio.second, FALSE)) {
if (GetOverlappedResult(m_inner->m_fh, &aio.first, &aio.second, FALSE)) {
CloseHandle(aio.first.hEvent);
aio.first.hEvent = 0;
} else {
@@ -136,21 +181,30 @@ ECardResult AsyncIO::pollStatus() const {
if (result > ECardResult::BUSY)
result = ECardResult::BUSY;
} else {
_waitForOperation(it - m_queue.cbegin());
_waitForOperation(it - m_inner->m_queue.cbegin());
if (result > ECardResult::IOERROR)
result = ECardResult::IOERROR;
}
}
}
if (result == ECardResult::READY)
const_cast<AsyncIO*>(this)->m_maxBlock = 0;
m_maxBlock = 0;
return result;
}
void AsyncIO::waitForCompletion() const {
for (size_t i = 0; i < m_maxBlock; ++i)
_waitForOperation(i);
const_cast<AsyncIO*>(this)->m_maxBlock = 0;
m_maxBlock = 0;
}
void AsyncIO::resizeQueue(size_t queueSz) {
if (m_inner == nullptr) {
return;
}
m_inner->m_queue.resize(queueSz);
}
AsyncIO::operator bool() const { return m_inner != nullptr && m_inner->m_fh != INVALID_HANDLE_VALUE; }
} // namespace kabufuda

View File

@@ -23,7 +23,8 @@ void BlockAllocationTable::updateChecksum() {
}
bool BlockAllocationTable::valid() const {
uint16_t ckSum, ckSumInv;
uint16_t ckSum = 0;
uint16_t ckSumInv = 0;
const_cast<BlockAllocationTable&>(*this).swapEndian();
calculateChecksumBE(reinterpret_cast<const uint16_t*>(raw.data() + 4), 0xFFE, &ckSum, &ckSumInv);
bool res = (ckSum == m_checksum && ckSumInv == m_checksumInv);
@@ -32,7 +33,12 @@ bool BlockAllocationTable::valid() const {
}
BlockAllocationTable::BlockAllocationTable(uint32_t blockCount)
: m_freeBlocks{uint16_t(blockCount - FSTBlocks)}, m_lastAllocated{4} {
: m_checksum{0}
, m_checksumInv{0}
, m_updateCounter{0}
, m_freeBlocks{uint16_t(blockCount - FSTBlocks)}
, m_lastAllocated{4}
, m_map{{0}} {
updateChecksum();
}

View File

@@ -118,19 +118,23 @@ Card::Card(const char* game, const char* maker) {
Card::~Card() { close(); }
ECardResult Card::openFile(const char* filename, FileHandle& handleOut) {
ECardResult openRes = _pumpOpen();
if (openRes != ECardResult::READY)
const ECardResult openRes = _pumpOpen();
if (openRes != ECardResult::READY) {
return openRes;
}
handleOut = {};
File* f = m_dirs[m_currentDir].getFile(m_game, m_maker, filename);
if (!f || f->m_game[0] == 0xFF)
const File* const f = m_dirs[m_currentDir].getFile(m_game, m_maker, filename);
if (!f || f->m_game[0] == 0xFF) {
return ECardResult::NOFILE;
int32_t idx = m_dirs[m_currentDir].indexForFile(f);
}
const int32_t idx = m_dirs[m_currentDir].indexForFile(f);
if (idx != -1) {
handleOut = FileHandle(idx);
return ECardResult::READY;
}
return ECardResult::FATAL_ERROR;
}
@@ -219,11 +223,13 @@ ECardResult Card::closeFile(FileHandle& fh) {
}
FileHandle Card::firstFile() {
File* f = m_dirs[m_currentDir].getFirstNonFreeFile(0, m_game, m_maker);
if (f)
return FileHandle(m_dirs[m_currentDir].indexForFile(f));
const File* const f = m_dirs[m_currentDir].getFirstNonFreeFile(0, m_game, m_maker);
return {};
if (f == nullptr) {
return {};
}
return FileHandle(m_dirs[m_currentDir].indexForFile(f));
}
FileHandle Card::nextFile(const FileHandle& cur) {
@@ -231,16 +237,22 @@ FileHandle Card::nextFile(const FileHandle& cur) {
NullFileAccess();
return {};
}
File* next = m_dirs[m_currentDir].getFirstNonFreeFile(cur.idx + 1, m_game, m_maker);
if (!next)
const File* const next = m_dirs[m_currentDir].getFirstNonFreeFile(cur.idx + 1, m_game, m_maker);
if (next == nullptr) {
return {};
}
return FileHandle(m_dirs[m_currentDir].indexForFile(next));
}
const char* Card::getFilename(const FileHandle& fh) {
File* f = _fileFromHandle(fh);
if (!f)
const char* Card::getFilename(const FileHandle& fh) const {
const File* const f = _fileFromHandle(fh);
if (f == nullptr) {
return nullptr;
}
return f->m_filename;
}
@@ -317,9 +329,11 @@ ECardResult Card::renameFile(const char* oldName, const char* newName) {
if (File* replF = dir.getFile(m_game, m_maker, newName)) {
BlockAllocationTable bat = m_bats[m_currentBat];
_deleteFile(*replF, bat);
std::memset(f->m_filename, 0, 32);
std::strncpy(f->m_filename, newName, 32);
_updateDirAndBat(dir, bat);
} else {
std::memset(f->m_filename, 0, 32);
std::strncpy(f->m_filename, newName, 32);
_updateDirAndBat(dir, m_bats[m_currentBat]);
}
@@ -445,7 +459,7 @@ void Card::seek(FileHandle& fh, int32_t pos, SeekOrigin whence) {
}
}
int32_t Card::tell(const FileHandle& fh) { return fh.offset; }
int32_t Card::tell(const FileHandle& fh) const { return fh.offset; }
void Card::setPublic(const FileHandle& fh, bool pub) {
File* file = _fileFromHandle(fh);
@@ -559,8 +573,8 @@ ECardResult Card::getStatus(uint32_t fileNo, CardStat& statOut) const {
std::strncpy(statOut.x0_fileName, file->m_filename, 32);
statOut.x20_length = file->m_blockCount * BlockSize;
statOut.x24_time = file->m_modifiedTime;
statOut.x28_gameName = file->m_game;
statOut.x2c_company = file->m_maker;
memmove(statOut.x28_gameName.data(), file->m_game, 4);
memmove(statOut.x2c_company.data(), file->m_maker, 4);
statOut.x2e_bannerFormat = file->m_bannerFlags;
statOut.x30_iconAddr = file->m_iconAddress;
@@ -746,12 +760,12 @@ void Card::getSerial(uint64_t& serial) {
m_ch._swapEndian();
}
void Card::getChecksum(uint16_t& checksum, uint16_t& inverse) {
void Card::getChecksum(uint16_t& checksum, uint16_t& inverse) const {
checksum = m_ch.m_checksum;
inverse = m_ch.m_checksumInv;
}
void Card::getFreeBlocks(int32_t& bytesNotUsed, int32_t& filesNotUsed) {
void Card::getFreeBlocks(int32_t& bytesNotUsed, int32_t& filesNotUsed) const {
bytesNotUsed = m_bats[m_currentBat].numFreeBlocks() * 0x2000;
filesNotUsed = m_dirs[m_currentDir].numFreeFiles();
}
@@ -818,7 +832,7 @@ void Card::format(ECardSlot id, ECardSize size, EEncoding encoding) {
}
}
ProbeResults Card::probeCardFile(SystemStringView filename) {
ProbeResults Card::probeCardFile(std::string_view filename) {
Sstat stat;
if (Stat(filename.data(), &stat) || !S_ISREG(stat.st_mode))
return {ECardResult::NOCARD, 0, 0};
@@ -852,7 +866,7 @@ void Card::commit() {
}
}
bool Card::open(SystemStringView filepath) {
bool Card::open(std::string_view filepath) {
m_opened = false;
m_filename = filepath;
m_fileHandle = AsyncIO(m_filename);

View File

@@ -35,59 +35,60 @@ Directory::Directory() {
updateChecksum();
}
Directory::Directory(uint8_t data[]) { std::memcpy(raw.data(), data, BlockSize); }
Directory::Directory(const RawData& rawData) : raw{rawData} {}
bool Directory::hasFreeFile() const {
return std::any_of(data.m_files.cbegin(), data.m_files.cend(),
return std::any_of(std::cbegin(data.m_files), std::cend(data.m_files),
[](const auto& file) { return file.m_game[0] == 0xFF; });
}
int32_t Directory::numFreeFiles() const {
return int32_t(std::count_if(data.m_files.cbegin(), data.m_files.cend(),
return int32_t(std::count_if(std::cbegin(data.m_files), std::cend(data.m_files),
[](const auto& file) { return file.m_game[0] == 0xFF; }));
}
File* Directory::getFirstFreeFile(const char* game, const char* maker, const char* filename) {
const auto iter =
std::find_if(data.m_files.begin(), data.m_files.end(), [](const auto& file) { return file.m_game[0] == 0xFF; });
const auto iter = std::find_if(std::begin(data.m_files), std::end(data.m_files),
[](const auto& file) { return file.m_game[0] == 0xFF; });
if (iter == data.m_files.cend()) {
if (iter == std::end(data.m_files)) {
return nullptr;
}
*iter = File(filename);
if (game != nullptr && std::strlen(game) == iter->m_game.size()) {
std::memcpy(iter->m_game.data(), game, iter->m_game.size());
if (game != nullptr && std::strlen(game) == 4) {
std::memcpy(iter->m_game, game, 4);
}
if (maker != nullptr && std::strlen(maker) == iter->m_maker.size()) {
std::memcpy(iter->m_maker.data(), maker, iter->m_maker.size());
if (maker != nullptr && std::strlen(maker) == 2) {
std::memcpy(iter->m_maker, maker, 2);
}
return &*iter;
}
File* Directory::getFirstNonFreeFile(uint32_t start, const char* game, const char* maker) {
const auto iter = std::find_if(data.m_files.begin() + start, data.m_files.end(), [game, maker](const auto& file) {
if (file.m_game[0] == 0xFF) {
return false;
}
const auto iter =
std::find_if(std::begin(data.m_files) + start, std::end(data.m_files), [game, maker](const auto& file) {
if (file.m_game[0] == 0xFF) {
return false;
}
const auto* const game_ptr = reinterpret_cast<const char*>(file.m_game.data());
const auto game_size = file.m_game.size();
if (game != nullptr && std::strlen(game) == game_size && std::strncmp(game_ptr, game, game_size) != 0) {
return false;
}
const auto* const game_ptr = reinterpret_cast<const char*>(file.m_game);
const auto game_size = 4;
if (game != nullptr && std::strlen(game) == game_size && std::strncmp(game_ptr, game, game_size) != 0) {
return false;
}
const auto* const maker_ptr = reinterpret_cast<const char*>(file.m_maker.data());
const auto maker_size = file.m_maker.size();
if (maker != nullptr && std::strlen(maker) == maker_size && std::strncmp(maker_ptr, maker, maker_size) != 0) {
return false;
}
const auto* const maker_ptr = reinterpret_cast<const char*>(file.m_maker);
const auto maker_size = 2;
if (maker != nullptr && std::strlen(maker) == maker_size && std::strncmp(maker_ptr, maker, maker_size) != 0) {
return false;
}
return true;
});
return true;
});
if (iter == data.m_files.cend()) {
if (iter == std::end(data.m_files)) {
return nullptr;
}
@@ -95,22 +96,21 @@ File* Directory::getFirstNonFreeFile(uint32_t start, const char* game, const cha
}
File* Directory::getFile(const char* game, const char* maker, const char* filename) {
const auto iter = std::find_if(data.m_files.begin(), data.m_files.end(), [=](const auto& file) {
const auto game_size = file.m_game.size();
if (game != nullptr && std::strlen(game) == game_size && std::memcmp(file.m_game.data(), game, game_size) != 0) {
const auto iter = std::find_if(std::begin(data.m_files), std::end(data.m_files), [=](const auto& file) {
const auto game_size = 4;
if (game != nullptr && std::strlen(game) == game_size && std::memcmp(file.m_game, game, game_size) != 0) {
return false;
}
const auto maker_size = file.m_maker.size();
if (maker != nullptr && std::strlen(maker) == maker_size &&
std::memcmp(file.m_maker.data(), maker, maker_size) != 0) {
const auto maker_size = 2;
if (maker != nullptr && std::strlen(maker) == maker_size && std::memcmp(file.m_maker, maker, maker_size) != 0) {
return false;
}
return std::strcmp(file.m_filename, filename) == 0;
return std::strncmp(file.m_filename, filename, 32) == 0;
});
if (iter == data.m_files.cend()) {
if (iter == std::cend(data.m_files)) {
return nullptr;
}
@@ -118,14 +118,14 @@ File* Directory::getFile(const char* game, const char* maker, const char* filena
}
File* Directory::getFile(uint32_t idx) {
if (idx >= data.m_files.size()) {
if (idx >= MaxFiles) {
return nullptr;
}
return &data.m_files[idx];
}
int32_t Directory::indexForFile(File* f) {
int32_t Directory::indexForFile(const File* f) const {
if (f == nullptr) {
return -1;
}

View File

@@ -8,12 +8,12 @@
namespace kabufuda {
File::File() { raw.fill(0xFF); }
File::File(char data[]) { std::memcpy(raw.data(), data, raw.size()); }
File::File(const RawData& rawData) : raw{rawData} {}
File::File(const char* filename) {
raw.fill(0);
std::memset(m_filename, 0, std::size(m_filename));
std::strncpy(m_filename, filename, std::size(m_filename));
std::strncpy(m_filename, filename, std::size(m_filename) - 1);
}
void File::swapEndian() {
m_modifiedTime = SBig(m_modifiedTime);

View File

@@ -1,39 +0,0 @@
#include "kabufuda/WideStringConvert.hpp"
#include <cstdio>
#include <utf8proc.h>
namespace kabufuda {
std::string WideToUTF8(std::wstring_view 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(std::string_view src) {
std::wstring retval;
retval.reserve(src.length());
const utf8proc_uint8_t* buf = reinterpret_cast<const utf8proc_uint8_t*>(src.data());
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;
}
} // namespace kabufuda

View File

@@ -3,7 +3,7 @@
int main() {
kabufuda::Card mc{"GM8E", "01"};
mc.open(_SYS_STR("test.USA.raw"));
mc.open("test.USA.raw");
mc.format(kabufuda::ECardSlot::SlotA, kabufuda::ECardSize::Card2043Mb);
uint64_t a = 0;
mc.getSerial(a);