mirror of
https://github.com/AxioDL/kabufuda.git
synced 2025-12-08 13:14:57 +00:00
Compare commits
50 Commits
f126245eef
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f3fbc0d0d | |||
| 32090b4d4a | |||
| 2252e6c3c3 | |||
| 4cf61b7d5e | |||
|
1111fb4839
|
|||
| 106bb02fbf | |||
| b585305009 | |||
| 6f5cb6f972 | |||
| 4ee594d0fd | |||
| b63f7c6401 | |||
|
4891a95688
|
|||
|
|
6d8d389459 | ||
|
|
4648c93b8e | ||
|
|
1b813f0c70 | ||
| 35e5c7c90f | |||
| d48ba9523f | |||
| f74f1a3a6c | |||
|
|
9d3a436e70 | ||
|
|
e96db1e032 | ||
|
|
ed2a6b3ce8 | ||
|
|
66ce5ed823 | ||
|
|
69d24d1027 | ||
| 91a0a41cee | |||
|
|
1779e14bcc | ||
|
|
e186d1498f | ||
| b279404833 | |||
| 698e37d6a4 | |||
| 9484bf40f9 | |||
| a701828371 | |||
|
|
54f9a11012 | ||
|
|
215cb25c5c | ||
|
|
3e64021400 | ||
|
222fb5c179
|
|||
| 33bcde5a73 | |||
| 9d003a3e9b | |||
| a638632fbb | |||
|
|
3f7e52b216 | ||
|
|
7e3a2370c5 | ||
|
|
8df58d59bc | ||
| f57d8cf761 | |||
| 91316888fe | |||
|
|
3ef8955ebb | ||
|
|
d906c2526b | ||
|
|
dc436ffb32 | ||
|
|
8053d1125b | ||
|
|
51c7163cfd | ||
| 82c607ec40 | |||
| da4008bf50 | |||
|
|
630119350b | ||
|
|
3a6ee9a70c |
@@ -1,17 +1,9 @@
|
||||
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)
|
||||
|
||||
include_directories(include)
|
||||
set(KABUFUDA_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE PATH "kabufuda include path" FORCE)
|
||||
|
||||
unset(PLAT_SRCS)
|
||||
if(WIN32)
|
||||
list(APPEND PLAT_SRCS lib/kabufuda/winsupport.cpp include/kabufuda/winsupport.hpp lib/kabufuda/AsyncIOWin32.cpp)
|
||||
else()
|
||||
list(APPEND PLAT_SRCS lib/kabufuda/AsyncIOPosix.cpp)
|
||||
if (NOT MSVC)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
|
||||
add_library(kabufuda STATIC
|
||||
@@ -23,7 +15,51 @@ 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
|
||||
${PLAT_SRCS})
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
if (MSVC)
|
||||
target_compile_options(kabufuda PRIVATE
|
||||
# Enforce various standards compliant behavior.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/permissive->
|
||||
|
||||
# Enable standard volatile semantics.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/volatile:iso>
|
||||
|
||||
# Reports the proper value for the __cplusplus preprocessor macro.
|
||||
$<$<COMPILE_LANGUAGE:CXX>:/Zc:__cplusplus>
|
||||
|
||||
# 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
|
||||
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>)
|
||||
|
||||
add_subdirectory(test)
|
||||
|
||||
@@ -1,47 +1,59 @@
|
||||
#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 "Util.hpp"
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include "kabufuda/Constants.hpp"
|
||||
|
||||
namespace kabufuda {
|
||||
class BlockAllocationTable {
|
||||
@@ -13,9 +15,9 @@ class BlockAllocationTable {
|
||||
uint16_t m_updateCounter;
|
||||
uint16_t m_freeBlocks;
|
||||
uint16_t m_lastAllocated;
|
||||
uint16_t m_map[0xFFB];
|
||||
std::array<uint16_t, 0xFFB> m_map;
|
||||
};
|
||||
uint8_t __raw[BlockSize];
|
||||
std::array<uint8_t, BlockSize> raw{};
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
@@ -25,7 +27,6 @@ class BlockAllocationTable {
|
||||
|
||||
public:
|
||||
explicit BlockAllocationTable(uint32_t blockCount = (uint32_t(ECardSize::Card2043Mb) * MbitToBlocks));
|
||||
BlockAllocationTable(uint8_t data[BlockSize]);
|
||||
~BlockAllocationTable() = default;
|
||||
|
||||
uint16_t getNextBlock(uint16_t block) const;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "BlockAllocationTable.hpp"
|
||||
#include "Directory.hpp"
|
||||
#include "File.hpp"
|
||||
#include "Util.hpp"
|
||||
#include "AsyncIO.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "kabufuda/AsyncIO.hpp"
|
||||
#include "kabufuda/BlockAllocationTable.hpp"
|
||||
#include "kabufuda/Directory.hpp"
|
||||
#include "kabufuda/File.hpp"
|
||||
#include "kabufuda/Util.hpp"
|
||||
|
||||
#define CARD_FILENAME_MAX 32
|
||||
#define CARD_ICON_MAX 8
|
||||
@@ -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() != -1; }
|
||||
explicit operator bool() const { return getFileNo() != UINT32_MAX; }
|
||||
};
|
||||
|
||||
struct ProbeResults {
|
||||
@@ -38,8 +38,8 @@ struct CardStat {
|
||||
char x0_fileName[CARD_FILENAME_MAX];
|
||||
uint32_t x20_length;
|
||||
uint32_t x24_time; /* seconds since 01/01/2000 midnight */
|
||||
uint8_t x28_gameName[4];
|
||||
uint8_t x2c_company[2];
|
||||
std::array<uint8_t, 4> x28_gameName;
|
||||
std::array<uint8_t, 2> x2c_company;
|
||||
|
||||
/* read/write (Set by Card::getStatus/Card::setStatus) */
|
||||
uint8_t x2e_bannerFormat;
|
||||
@@ -52,7 +52,7 @@ struct CardStat {
|
||||
/* read-only (Set by Card::getStatus) */
|
||||
uint32_t x3c_offsetBanner;
|
||||
uint32_t x40_offsetBannerTlut;
|
||||
uint32_t x44_offsetIcon[CARD_ICON_MAX];
|
||||
std::array<uint32_t, CARD_ICON_MAX> x44_offsetIcon;
|
||||
uint32_t x64_offsetIconTlut;
|
||||
uint32_t x68_offsetData;
|
||||
|
||||
@@ -78,33 +78,36 @@ struct CardStat {
|
||||
class Card {
|
||||
#pragma pack(push, 4)
|
||||
struct CardHeader {
|
||||
uint8_t m_serial[12];
|
||||
uint64_t m_formatTime;
|
||||
int32_t m_sramBias;
|
||||
uint32_t m_sramLanguage;
|
||||
uint32_t m_unknown;
|
||||
uint16_t m_deviceId; /* 0 for Slot A, 1 for Slot B */
|
||||
uint16_t m_sizeMb;
|
||||
uint16_t m_encoding;
|
||||
uint8_t __padding[468];
|
||||
uint16_t m_updateCounter;
|
||||
uint16_t m_checksum;
|
||||
uint16_t m_checksumInv;
|
||||
union {
|
||||
struct {
|
||||
std::array<uint8_t, 12> m_serial;
|
||||
uint64_t m_formatTime;
|
||||
int32_t m_sramBias;
|
||||
uint32_t m_sramLanguage;
|
||||
uint32_t m_unknown;
|
||||
uint16_t m_deviceId; /* 0 for Slot A, 1 for Slot B */
|
||||
uint16_t m_sizeMb;
|
||||
uint16_t m_encoding;
|
||||
std::array<uint8_t, 468> padding;
|
||||
uint16_t m_updateCounter;
|
||||
uint16_t m_checksum;
|
||||
uint16_t m_checksumInv;
|
||||
};
|
||||
std::array<uint8_t, BlockSize> raw;
|
||||
};
|
||||
void _swapEndian();
|
||||
};
|
||||
union {
|
||||
CardHeader m_ch;
|
||||
uint8_t __raw[BlockSize];
|
||||
};
|
||||
CardHeader m_tmpCh;
|
||||
#pragma pack(pop)
|
||||
|
||||
SystemString m_filename;
|
||||
CardHeader m_ch;
|
||||
CardHeader m_tmpCh;
|
||||
|
||||
std::string m_filename;
|
||||
AsyncIO m_fileHandle;
|
||||
Directory m_dirs[2];
|
||||
BlockAllocationTable m_bats[2];
|
||||
Directory m_tmpDirs[2];
|
||||
BlockAllocationTable m_tmpBats[2];
|
||||
std::array<Directory, 2> m_dirs;
|
||||
std::array<BlockAllocationTable, 2> m_bats;
|
||||
std::array<Directory, 2> m_tmpDirs;
|
||||
std::array<BlockAllocationTable, 2> m_tmpBats;
|
||||
uint8_t m_currentDir;
|
||||
uint8_t m_currentBat;
|
||||
|
||||
@@ -123,10 +126,6 @@ class Card {
|
||||
|
||||
public:
|
||||
Card();
|
||||
/**
|
||||
* @brief Card
|
||||
* @param other
|
||||
*/
|
||||
Card(const Card& other) = delete;
|
||||
Card& operator=(const Card& other) = delete;
|
||||
Card(Card&& other);
|
||||
@@ -134,185 +133,232 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Card
|
||||
* @param filepath
|
||||
* @param game
|
||||
* @param maker
|
||||
*
|
||||
* @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();
|
||||
|
||||
/**
|
||||
* @brief openFile
|
||||
* @param filename
|
||||
*
|
||||
* @param[in] filename The name of the file to open.
|
||||
* @param[out] handleOut Out reference that will contain the opened file handle.
|
||||
*
|
||||
* @return A result indicating the error status of the operation.
|
||||
*/
|
||||
ECardResult openFile(const char* filename, FileHandle& handleOut);
|
||||
|
||||
/**
|
||||
* @brief openFile
|
||||
* @param fileno
|
||||
*
|
||||
* @param[in] fileno The file index.
|
||||
* @param[out] handleOut Out reference that will contain the opened file handle.
|
||||
*
|
||||
* @return A result indicating the error status of the operation.
|
||||
*/
|
||||
ECardResult openFile(uint32_t fileno, FileHandle& handleOut);
|
||||
|
||||
/**
|
||||
* @brief createFile
|
||||
* @param filename
|
||||
* @return
|
||||
*
|
||||
* @param[in] filename The name of the file to create.
|
||||
* @param[out] handleOut Out reference that will contain the handle of the created file.
|
||||
*
|
||||
* @return A result indicating the error status of the operation.
|
||||
*/
|
||||
ECardResult createFile(const char* filename, size_t size, FileHandle& handleOut);
|
||||
|
||||
/**
|
||||
* @brief closeFile
|
||||
*
|
||||
* @param fh FileHandle to close
|
||||
* @return
|
||||
*
|
||||
* @return READY
|
||||
*/
|
||||
ECardResult closeFile(FileHandle& fh);
|
||||
|
||||
/**
|
||||
* @brief firstFile
|
||||
* @return
|
||||
*
|
||||
* @return The first file within this card instance. If
|
||||
* no file is able to be found, then an invalid
|
||||
* handle will be returned.
|
||||
*/
|
||||
FileHandle firstFile();
|
||||
|
||||
/**
|
||||
* @brief nextFile
|
||||
* @param cur
|
||||
* @return
|
||||
*
|
||||
* @param cur The file handle indicating the current file handle.
|
||||
*
|
||||
* @return The next file within this card. If no file can be found,
|
||||
* an invalid handle will be returned.
|
||||
*/
|
||||
FileHandle nextFile(const FileHandle& cur);
|
||||
|
||||
/**
|
||||
* @brief getFilename
|
||||
* @param fh
|
||||
* @return
|
||||
*
|
||||
* @param fh A valid file handle to retrieve the name of.
|
||||
*
|
||||
* @return Gets the name of the given file.
|
||||
*/
|
||||
const char* getFilename(const FileHandle& fh);
|
||||
const char* getFilename(const FileHandle& fh) const;
|
||||
|
||||
/**
|
||||
* @brief deleteFile
|
||||
* @param fh
|
||||
*
|
||||
* @param fh File handle to delete.
|
||||
*/
|
||||
void deleteFile(const FileHandle& fh);
|
||||
|
||||
/**
|
||||
* @brief deleteFile
|
||||
* @param filename
|
||||
*
|
||||
* @param filename The name of the file to delete.
|
||||
*/
|
||||
ECardResult deleteFile(const char* filename);
|
||||
|
||||
/**
|
||||
* @brief deleteFile
|
||||
* @param fileno
|
||||
*
|
||||
* @param fileno The file number indicating the file to delete.
|
||||
*/
|
||||
ECardResult deleteFile(uint32_t fileno);
|
||||
|
||||
/**
|
||||
* @brief renameFile
|
||||
* @param oldName
|
||||
* @param newName
|
||||
*
|
||||
* @param oldName The old name of the file.
|
||||
* @param newName The new name to assign to the file.
|
||||
*/
|
||||
ECardResult renameFile(const char* oldName, const char* newName);
|
||||
|
||||
/**
|
||||
* @brief write
|
||||
* @param fh
|
||||
* @param buf
|
||||
* @param size
|
||||
*
|
||||
* @param fh A valid file handle to write to.
|
||||
* @param buf The buffer to write to the file.
|
||||
* @param size The size of the given buffer.
|
||||
*/
|
||||
ECardResult asyncWrite(FileHandle& fh, const void* buf, size_t size);
|
||||
|
||||
/**
|
||||
* @brief read
|
||||
* @param fh
|
||||
* @param dst
|
||||
* @param size
|
||||
*
|
||||
* @param fh A valid file handle to read from.
|
||||
* @param dst A buffer to read data into.
|
||||
* @param size The size of the buffer to read into.
|
||||
*/
|
||||
ECardResult asyncRead(FileHandle& fh, void* dst, size_t size);
|
||||
|
||||
/**
|
||||
* @brief seek
|
||||
* @param fh
|
||||
* @param pos
|
||||
* @param whence
|
||||
*
|
||||
* @param fh A valid file handle.
|
||||
* @param pos The position to seek to.
|
||||
* @param whence The origin to seek relative to.
|
||||
*/
|
||||
void seek(FileHandle& fh, int32_t pos, SeekOrigin whence);
|
||||
|
||||
/**
|
||||
* @brief Returns the current offset of the specified file
|
||||
* @param fh The file to retrieve the offset from
|
||||
* @return The offset or -1 if an invalid handle is passed
|
||||
* @brief Returns the current offset of the specified file.
|
||||
*
|
||||
* @param fh The file to retrieve the offset from.
|
||||
*
|
||||
* @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
|
||||
* @param fh
|
||||
* @param pub
|
||||
*
|
||||
* @param fh The file handle to make public.
|
||||
* @param pub Whether or not this file is public (i.e. readable by any game).
|
||||
*/
|
||||
void setPublic(const FileHandle& fh, bool pub);
|
||||
|
||||
/**
|
||||
* @brief isPublic
|
||||
* @param fh
|
||||
* @return
|
||||
*
|
||||
* @param fh The file handle to query.
|
||||
*
|
||||
* @return Whether or not this file is public (i.e. readable by any game).
|
||||
*/
|
||||
bool isPublic(const FileHandle& fh) const;
|
||||
|
||||
/**
|
||||
* @brief setCanCopy
|
||||
* @param fh
|
||||
* @param copy
|
||||
*
|
||||
* @param fh The file handle to set as copyable.
|
||||
* @param copy Whether or not to set the file handle as copyable.
|
||||
*/
|
||||
void setCanCopy(const FileHandle& fh, bool copy) const;
|
||||
|
||||
/**
|
||||
* @brief canCopy
|
||||
* @param fh
|
||||
* @return
|
||||
*
|
||||
* @param fh The file handle to query.
|
||||
*
|
||||
* @return Whether or not this file handle is copyable by the IPL.
|
||||
*/
|
||||
bool canCopy(const FileHandle& fh) const;
|
||||
|
||||
/**
|
||||
* @brief setCanMove
|
||||
* @param fh
|
||||
* @param move
|
||||
*
|
||||
* @param fh The file handle to set as moveable.
|
||||
* @param move Whether or not to set the file handle as movable.
|
||||
*/
|
||||
void setCanMove(const FileHandle& fh, bool move);
|
||||
|
||||
/**
|
||||
* @brief canMove
|
||||
* @param fh
|
||||
* @return
|
||||
*
|
||||
* @param fh The file handle to query.
|
||||
*
|
||||
* @return whether or not the file can be moved by the IPL.
|
||||
*/
|
||||
bool canMove(const FileHandle& fh) const;
|
||||
|
||||
/**
|
||||
* @brief getStatus
|
||||
* @param fh Handle of requested file
|
||||
*
|
||||
* @param fh Handle of requested file
|
||||
* @param statOut Structure to fill with file stat
|
||||
*
|
||||
* @return NOFILE or READY
|
||||
*/
|
||||
ECardResult getStatus(const FileHandle& fh, CardStat& statOut) const;
|
||||
|
||||
/**
|
||||
* @brief getStatus
|
||||
* @param fileNo Number of requested file
|
||||
*
|
||||
* @param fileNo Number of requested file
|
||||
* @param statOut Structure to fill with file stat
|
||||
*
|
||||
* @return NOFILE or READY
|
||||
*/
|
||||
ECardResult getStatus(uint32_t fileNo, CardStat& statOut) const;
|
||||
|
||||
/**
|
||||
* @brief setStatus
|
||||
* @param fh Handle of requested file
|
||||
* @param statOut Structure to access for file stat
|
||||
*
|
||||
* @param fh Handle of requested file
|
||||
* @param stat Structure to access for file stat
|
||||
*
|
||||
* @return NOFILE or READY
|
||||
*/
|
||||
ECardResult setStatus(const FileHandle& fh, const CardStat& stat);
|
||||
|
||||
/**
|
||||
* @brief setStatus
|
||||
*
|
||||
* @param fileNo Number of requested file
|
||||
* @param statOut Structure to access for file stat
|
||||
* @param stat Structure to access for file stat
|
||||
*
|
||||
* @return NOFILE or READY
|
||||
*/
|
||||
ECardResult setStatus(uint32_t fileNo, const CardStat& stat);
|
||||
@@ -337,62 +383,74 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Sets the current game, if not null any openFile requests will only return files that match this game
|
||||
*
|
||||
* @param game The target game id, e.g "GM8E"
|
||||
*
|
||||
* @sa openFile
|
||||
*/
|
||||
void setCurrentGame(const char* game);
|
||||
|
||||
/**
|
||||
* @brief Returns the currently selected game
|
||||
* @brief Returns the currently selected game.
|
||||
*
|
||||
* @return The selected game, or nullptr
|
||||
*/
|
||||
const uint8_t* getCurrentGame() const;
|
||||
|
||||
/**
|
||||
* @brief Sets the current maker, if not null any openFile requests will only return files that match this maker
|
||||
* @param maker The target maker id, e.g "01"
|
||||
* @brief Sets the current maker, if not null any openFile requests will only return files that match this maker.
|
||||
*
|
||||
* @param maker The target maker id, e.g "01".
|
||||
*
|
||||
* @sa openFile
|
||||
*/
|
||||
void setCurrentMaker(const char* maker);
|
||||
|
||||
/**
|
||||
* @brief Returns the currently selected maker
|
||||
* @return The selected maker, or nullptr
|
||||
* @brief Returns the currently selected maker.
|
||||
*
|
||||
* @return The selected maker, or nullptr.
|
||||
*/
|
||||
const uint8_t* getCurrentMaker() const;
|
||||
|
||||
/**
|
||||
* @brief Retrieves the format assigned serial
|
||||
* @param serial
|
||||
* @brief Retrieves the format assigned serial.
|
||||
*
|
||||
* @param[out] serial Out reference that will contain the serial number.
|
||||
*/
|
||||
void getSerial(uint64_t& serial);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the checksum values of the Card system header
|
||||
* @param checksum The checksum of the system header
|
||||
* @param inverse The inverser checksum of the system header
|
||||
* @brief Retrieves the checksum values of the Card system header.
|
||||
*
|
||||
* @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
|
||||
* @param bytesNotUsed Number of free bytes out
|
||||
* @param filesNotUsed Number of free files out
|
||||
* @brief Retrieves the available storage and directory space.
|
||||
*
|
||||
* @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
|
||||
* @param size The desired size of the file @sa ECardSize
|
||||
* @param encoding The desired encoding @sa EEncoding
|
||||
* @brief Formats the memory card and assigns a new serial.
|
||||
*
|
||||
* @param deviceId The slot to format.
|
||||
* @param size The desired size of the file @sa ECardSize.
|
||||
* @param encoding The desired encoding @sa EEncoding.
|
||||
*/
|
||||
void format(ECardSlot deviceId, ECardSize size = ECardSize::Card2043Mb, EEncoding encoding = EEncoding::ASCII);
|
||||
|
||||
/**
|
||||
* @brief Returns basic stats about a card image without opening a handle
|
||||
* @return ProbeResults structure
|
||||
* @brief Returns basic stats about a card image without opening a handle.
|
||||
*
|
||||
* @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 />
|
||||
@@ -401,31 +459,37 @@ public:
|
||||
void commit();
|
||||
|
||||
/**
|
||||
* @brief Opens card image (does nothing if currently open path matches)
|
||||
* @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
|
||||
* @brief Commits changes to disk and closes host file.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @brief Access host filename of card
|
||||
* @brief Access host filename of card.
|
||||
*
|
||||
* @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
|
||||
* @brief Gets card-scope error state.
|
||||
*
|
||||
* @return READY, BROKEN, or NOCARD
|
||||
*/
|
||||
ECardResult getError() const;
|
||||
|
||||
/**
|
||||
* @brief Block caller until any asynchronous I/O operations have completed
|
||||
* @brief Block caller until any asynchronous I/O operations have completed.
|
||||
*/
|
||||
void waitForCompletion() const;
|
||||
|
||||
operator bool() const { return getError() == ECardResult::READY; }
|
||||
/**
|
||||
* @return Whether or not the card is within a ready state.
|
||||
*/
|
||||
explicit operator bool() const { return getError() == ECardResult::READY; }
|
||||
};
|
||||
} // namespace kabufuda
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "Util.hpp"
|
||||
#include <cstdint>
|
||||
#include "kabufuda/Util.hpp"
|
||||
|
||||
namespace kabufuda {
|
||||
uint32_t constexpr BlockSize = 0x2000;
|
||||
uint32_t constexpr MaxFiles = 127;
|
||||
uint32_t constexpr FSTBlocks = 5;
|
||||
uint32_t constexpr MbitToBlocks = 0x10;
|
||||
uint32_t constexpr BATSize = 0xFFB;
|
||||
constexpr uint32_t BlockSize = 0x2000;
|
||||
constexpr uint32_t MaxFiles = 127;
|
||||
constexpr uint32_t FSTBlocks = 5;
|
||||
constexpr uint32_t MbitToBlocks = 0x10;
|
||||
constexpr uint32_t BATSize = 0xFFB;
|
||||
|
||||
/**
|
||||
* @brief The EPermissions enum
|
||||
@@ -59,10 +59,10 @@ enum class ECardSize : uint16_t {
|
||||
Card2043Mb = 0x80
|
||||
};
|
||||
|
||||
static constexpr uint32_t BannerWidth = 96;
|
||||
static constexpr uint32_t BannerHeight = 64;
|
||||
static constexpr uint32_t IconWidth = 32;
|
||||
static constexpr uint32_t IconHeight = 32;
|
||||
constexpr uint32_t BannerWidth = 96;
|
||||
constexpr uint32_t BannerHeight = 64;
|
||||
constexpr uint32_t IconWidth = 32;
|
||||
constexpr uint32_t IconHeight = 32;
|
||||
|
||||
/**
|
||||
* @brief The EEncoding enum
|
||||
|
||||
@@ -1,30 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "File.hpp"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include "kabufuda/File.hpp"
|
||||
|
||||
namespace kabufuda {
|
||||
class Directory {
|
||||
friend class Card;
|
||||
|
||||
#pragma pack(push, 4)
|
||||
union {
|
||||
struct {
|
||||
File m_files[MaxFiles];
|
||||
uint8_t __padding[0x3a];
|
||||
uint16_t m_updateCounter;
|
||||
uint16_t m_checksum;
|
||||
uint16_t m_checksumInv;
|
||||
};
|
||||
uint8_t __raw[BlockSize];
|
||||
using RawData = std::array<uint8_t, BlockSize>;
|
||||
struct Data {
|
||||
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;
|
||||
RawData raw;
|
||||
};
|
||||
|
||||
void swapEndian();
|
||||
void updateChecksum();
|
||||
bool valid() const;
|
||||
|
||||
public:
|
||||
Directory();
|
||||
Directory(uint8_t data[BlockSize]);
|
||||
explicit Directory(const RawData& rawData);
|
||||
~Directory() = default;
|
||||
|
||||
bool hasFreeFile() const;
|
||||
@@ -33,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
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include "kabufuda/Constants.hpp"
|
||||
|
||||
namespace kabufuda {
|
||||
class File {
|
||||
@@ -8,12 +10,16 @@ class File {
|
||||
friend class Directory;
|
||||
friend class Card;
|
||||
#pragma pack(push, 4)
|
||||
using RawData = std::array<uint8_t, 0x40>;
|
||||
union {
|
||||
struct {
|
||||
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;
|
||||
@@ -26,16 +32,15 @@ class File {
|
||||
uint16_t m_reserved2;
|
||||
uint32_t m_commentAddr;
|
||||
};
|
||||
uint8_t __raw[0x40];
|
||||
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
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
// Modified code taken from libogc
|
||||
|
||||
@@ -41,7 +42,9 @@ union SRAMFlags {
|
||||
};
|
||||
|
||||
union SRAM {
|
||||
uint8_t p_SRAM[64];
|
||||
using FlashID = std::array<std::array<uint8_t, 12>, 2>;
|
||||
|
||||
std::array<uint8_t, 64> p_SRAM;
|
||||
struct // Stored configuration value from the system SRAM area
|
||||
{
|
||||
uint16_t checksum; // Holds the block checksum.
|
||||
@@ -55,13 +58,13 @@ union SRAM {
|
||||
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
|
||||
FlashID flash_id; // flash_id[2][12] 96bit memorycard unlock flash ID
|
||||
uint32_t wirelessKbd_id; // Device ID of last connected wireless keyboard
|
||||
std::array<uint16_t, 4> wirelessPad_id; // 16-bit device ID of last connected pad.
|
||||
uint8_t dvderr_code; // last non-recoverable error from DVD interface
|
||||
uint8_t __padding0; // reserved
|
||||
std::array<uint8_t, 2> flashID_chksum; // 8-bit checksum of unlock flash ID
|
||||
uint32_t __padding1; // padding
|
||||
};
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
@@ -1,37 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <cerrno>
|
||||
#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 <cstdarg>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include "WideStringConvert.hpp"
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#undef bswap16
|
||||
#undef bswap32
|
||||
@@ -47,19 +33,27 @@
|
||||
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; \
|
||||
} \
|
||||
inline type operator~(const type& key) { \
|
||||
constexpr type operator~(type key) { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return type(~static_cast<T>(key)); \
|
||||
} \
|
||||
constexpr bool True(type key) { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return static_cast<T>(key) != 0; \
|
||||
} \
|
||||
constexpr bool False(type key) { \
|
||||
using T = std::underlying_type_t<type>; \
|
||||
return static_cast<T>(key) == 0; \
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -67,7 +61,7 @@ namespace kabufuda {
|
||||
|
||||
/* Type-sensitive byte swappers */
|
||||
template <typename T>
|
||||
static inline T bswap16(T val) {
|
||||
constexpr T bswap16(T val) {
|
||||
#if __GNUC__
|
||||
return __builtin_bswap16(val);
|
||||
#elif _WIN32
|
||||
@@ -78,7 +72,7 @@ static inline T bswap16(T val) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T bswap32(T val) {
|
||||
constexpr T bswap32(T val) {
|
||||
#if __GNUC__
|
||||
return __builtin_bswap32(val);
|
||||
#elif _WIN32
|
||||
@@ -91,7 +85,7 @@ static inline T bswap32(T val) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T bswap64(T val) {
|
||||
constexpr T bswap64(T val) {
|
||||
#if __GNUC__
|
||||
return __builtin_bswap64(val);
|
||||
#elif _WIN32
|
||||
@@ -105,47 +99,49 @@ static inline T bswap64(T val) {
|
||||
}
|
||||
|
||||
#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));
|
||||
constexpr int16_t SBig(int16_t val) { return bswap16(val); }
|
||||
constexpr uint16_t SBig(uint16_t val) { return bswap16(val); }
|
||||
constexpr int32_t SBig(int32_t val) { return bswap32(val); }
|
||||
constexpr uint32_t SBig(uint32_t val) { return bswap32(val); }
|
||||
constexpr int64_t SBig(int64_t val) { return bswap64(val); }
|
||||
constexpr uint64_t SBig(uint64_t val) { return bswap64(val); }
|
||||
constexpr float SBig(float val) {
|
||||
union { float f; int32_t i; } uval1 = {val};
|
||||
union { int32_t i; float f; } uval2 = {bswap32(uval1.i)};
|
||||
return uval2.f;
|
||||
}
|
||||
static inline double SBig(double val) {
|
||||
int64_t ival = bswap64(*((int64_t*)(&val)));
|
||||
return *((double*)(&ival));
|
||||
constexpr double SBig(double val) {
|
||||
union { double f; int64_t i; } uval1 = {val};
|
||||
union { int64_t i; double f; } uval2 = {bswap64(uval1.i)};
|
||||
return uval2.f;
|
||||
}
|
||||
#ifndef SBIG
|
||||
#define SBIG(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
||||
#endif
|
||||
|
||||
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; }
|
||||
constexpr int16_t SLittle(int16_t val) { return val; }
|
||||
constexpr uint16_t SLittle(uint16_t val) { return val; }
|
||||
constexpr int32_t SLittle(int32_t val) { return val; }
|
||||
constexpr uint32_t SLittle(uint32_t val) { return val; }
|
||||
constexpr int64_t SLittle(int64_t val) { return val; }
|
||||
constexpr uint64_t SLittle(uint64_t val) { return val; }
|
||||
constexpr float SLittle(float val) { return val; }
|
||||
constexpr double SLittle(double val) { return val; }
|
||||
#ifndef SLITTLE
|
||||
#define SLITTLE(q) (q)
|
||||
#endif
|
||||
#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) {
|
||||
constexpr int16_t SLittle(int16_t val) { return bswap16(val); }
|
||||
constexpr uint16_t SLittle(uint16_t val) { return bswap16(val); }
|
||||
constexpr int32_t SLittle(int32_t val) { return bswap32(val); }
|
||||
constexpr uint32_t SLittle(uint32_t val) { return bswap32(val); }
|
||||
constexpr int64_t SLittle(int64_t val) { return bswap64(val); }
|
||||
constexpr uint64_t SLittle(uint64_t val) { return bswap64(val); }
|
||||
constexpr float SLittle(float val) {
|
||||
int32_t ival = bswap32(*((int32_t*)(&val)));
|
||||
return *((float*)(&ival));
|
||||
}
|
||||
static inline double SLittle(double val) {
|
||||
constexpr double SLittle(double val) {
|
||||
int64_t ival = bswap64(*((int64_t*)(&val)));
|
||||
return *((double*)(&ival));
|
||||
}
|
||||
@@ -153,85 +149,23 @@ static inline double SLittle(double val) {
|
||||
#define SLITTLE(q) (((q)&0x000000FF) << 24 | ((q)&0x0000FF00) << 8 | ((q)&0x00FF0000) >> 8 | ((q)&0xFF000000) >> 24)
|
||||
#endif
|
||||
|
||||
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; }
|
||||
constexpr int16_t SBig(int16_t val) { return val; }
|
||||
constexpr uint16_t SBig(uint16_t val) { return val; }
|
||||
constexpr int32_t SBig(int32_t val) { return val; }
|
||||
constexpr uint32_t SBig(uint32_t val) { return val; }
|
||||
constexpr int64_t SBig(int64_t val) { return val; }
|
||||
constexpr uint64_t SBig(uint64_t val) { return val; }
|
||||
constexpr float SBig(float val) { return val; }
|
||||
constexpr double SBig(double val) { return val; }
|
||||
#ifndef SBIG
|
||||
#define SBIG(q) (q)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if CARD_UCS2
|
||||
typedef wchar_t SystemChar;
|
||||
static inline size_t StrLen(const SystemChar* str) { return wcslen(str); }
|
||||
typedef std::wstring SystemString;
|
||||
typedef std::wstring_view SystemStringView;
|
||||
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 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;
|
||||
static inline size_t StrLen(const SystemChar* str) { return strlen(str); }
|
||||
typedef std::string SystemString;
|
||||
typedef std::string_view SystemStringView;
|
||||
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 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();
|
||||
@@ -244,19 +178,11 @@ uint64_t getGCTime();
|
||||
#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR)
|
||||
#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);
|
||||
#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
|
||||
|
||||
@@ -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
|
||||
@@ -1,11 +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"
|
||||
|
||||
void* memmem(const void* haystack, size_t hlen, const void* needle, size_t nlen);
|
||||
@@ -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
|
||||
|
||||
50
lib/kabufuda/AsyncIONX.cpp
Normal file
50
lib/kabufuda/AsyncIONX.cpp
Normal 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
|
||||
@@ -1,9 +1,13 @@
|
||||
#include "kabufuda/AsyncIO.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
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() {
|
||||
@@ -138,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
|
||||
|
||||
@@ -1,9 +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;
|
||||
@@ -12,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");
|
||||
@@ -75,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");
|
||||
@@ -91,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)
|
||||
@@ -120,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 {
|
||||
@@ -133,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
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
#include "kabufuda/BlockAllocationTable.hpp"
|
||||
#include "kabufuda/Util.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
#include "kabufuda/Util.hpp"
|
||||
|
||||
namespace kabufuda {
|
||||
void BlockAllocationTable::swapEndian() {
|
||||
m_checksum = SBig(m_checksum);
|
||||
@@ -14,23 +18,27 @@ void BlockAllocationTable::swapEndian() {
|
||||
|
||||
void BlockAllocationTable::updateChecksum() {
|
||||
swapEndian();
|
||||
calculateChecksumBE(reinterpret_cast<uint16_t*>(__raw + 4), 0xFFE, &m_checksum, &m_checksumInv);
|
||||
calculateChecksumBE(reinterpret_cast<uint16_t*>(raw.data() + 4), 0xFFE, &m_checksum, &m_checksumInv);
|
||||
swapEndian();
|
||||
}
|
||||
|
||||
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 + 4), 0xFFE, &ckSum, &ckSumInv);
|
||||
calculateChecksumBE(reinterpret_cast<const uint16_t*>(raw.data() + 4), 0xFFE, &ckSum, &ckSumInv);
|
||||
bool res = (ckSum == m_checksum && ckSumInv == m_checksumInv);
|
||||
const_cast<BlockAllocationTable&>(*this).swapEndian();
|
||||
return res;
|
||||
}
|
||||
|
||||
BlockAllocationTable::BlockAllocationTable(uint32_t blockCount) {
|
||||
memset(__raw, 0, BlockSize);
|
||||
m_freeBlocks = uint16_t(blockCount - FSTBlocks);
|
||||
m_lastAllocated = 4;
|
||||
BlockAllocationTable::BlockAllocationTable(uint32_t blockCount)
|
||||
: m_checksum{0}
|
||||
, m_checksumInv{0}
|
||||
, m_updateCounter{0}
|
||||
, m_freeBlocks{uint16_t(blockCount - FSTBlocks)}
|
||||
, m_lastAllocated{4}
|
||||
, m_map{{0}} {
|
||||
updateChecksum();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "kabufuda/Card.hpp"
|
||||
#include "kabufuda/SRAM.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include "kabufuda/SRAM.hpp"
|
||||
|
||||
namespace kabufuda {
|
||||
|
||||
#define ROUND_UP_8192(val) (((val) + 8191) & ~8191)
|
||||
@@ -24,40 +26,36 @@ void Card::CardHeader::_swapEndian() {
|
||||
m_checksumInv = SBig(m_checksumInv);
|
||||
}
|
||||
|
||||
Card::Card() { memset(__raw, 0xFF, BlockSize); }
|
||||
Card::Card() { m_ch.raw.fill(0xFF); }
|
||||
|
||||
Card::Card(Card&& other) {
|
||||
memmove(__raw, other.__raw, BlockSize);
|
||||
m_ch.raw = other.m_ch.raw;
|
||||
m_filename = std::move(other.m_filename);
|
||||
m_fileHandle = std::move(other.m_fileHandle);
|
||||
m_dirs[0] = std::move(other.m_dirs[0]);
|
||||
m_dirs[1] = std::move(other.m_dirs[1]);
|
||||
m_bats[0] = std::move(other.m_bats[0]);
|
||||
m_bats[1] = std::move(other.m_bats[1]);
|
||||
m_dirs = std::move(other.m_dirs);
|
||||
m_bats = std::move(other.m_bats);
|
||||
m_currentDir = other.m_currentDir;
|
||||
m_currentBat = other.m_currentBat;
|
||||
|
||||
m_maxBlock = other.m_maxBlock;
|
||||
memmove(m_game, other.m_game, 5);
|
||||
memmove(m_maker, other.m_maker, 3);
|
||||
std::copy(std::cbegin(other.m_game), std::cend(other.m_game), std::begin(m_game));
|
||||
std::copy(std::cbegin(other.m_maker), std::cend(other.m_maker), std::begin(m_maker));
|
||||
}
|
||||
|
||||
Card& Card::operator=(Card&& other) {
|
||||
close();
|
||||
|
||||
memmove(__raw, other.__raw, BlockSize);
|
||||
m_ch.raw = other.m_ch.raw;
|
||||
m_filename = std::move(other.m_filename);
|
||||
m_fileHandle = std::move(other.m_fileHandle);
|
||||
m_dirs[0] = std::move(other.m_dirs[0]);
|
||||
m_dirs[1] = std::move(other.m_dirs[1]);
|
||||
m_bats[0] = std::move(other.m_bats[0]);
|
||||
m_bats[1] = std::move(other.m_bats[1]);
|
||||
m_dirs = std::move(other.m_dirs);
|
||||
m_bats = std::move(other.m_bats);
|
||||
m_currentDir = other.m_currentDir;
|
||||
m_currentBat = other.m_currentBat;
|
||||
|
||||
m_maxBlock = other.m_maxBlock;
|
||||
memmove(m_game, other.m_game, 5);
|
||||
memmove(m_maker, other.m_maker, 3);
|
||||
std::copy(std::cbegin(other.m_game), std::cend(other.m_game), std::begin(m_game));
|
||||
std::copy(std::cbegin(other.m_maker), std::cend(other.m_maker), std::begin(m_maker));
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -90,7 +88,7 @@ ECardResult Card::_pumpOpen() {
|
||||
else if (!m_bats[1].valid() && m_bats[0].valid())
|
||||
m_bats[1] = m_bats[0];
|
||||
|
||||
if (m_dirs[0].m_updateCounter > m_dirs[1].m_updateCounter)
|
||||
if (m_dirs[0].data.m_updateCounter > m_dirs[1].data.m_updateCounter)
|
||||
m_currentDir = 0;
|
||||
else
|
||||
m_currentDir = 1;
|
||||
@@ -107,29 +105,36 @@ ECardResult Card::_pumpOpen() {
|
||||
}
|
||||
|
||||
Card::Card(const char* game, const char* maker) {
|
||||
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_ch.raw.fill(0xFF);
|
||||
|
||||
if (game != nullptr && std::strlen(game) == 4) {
|
||||
std::memcpy(m_game, game, 4);
|
||||
}
|
||||
if (maker != nullptr && std::strlen(maker) == 2) {
|
||||
std::memcpy(m_maker, maker, 2);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -150,7 +155,7 @@ void Card::_updateDirAndBat(const Directory& dir, const BlockAllocationTable& ba
|
||||
m_currentDir = !m_currentDir;
|
||||
Directory& updateDir = m_dirs[m_currentDir];
|
||||
updateDir = dir;
|
||||
updateDir.m_updateCounter++;
|
||||
updateDir.data.m_updateCounter++;
|
||||
updateDir.updateChecksum();
|
||||
|
||||
m_currentBat = !m_currentBat;
|
||||
@@ -164,7 +169,7 @@ void Card::_updateDirAndBat(const Directory& dir, const BlockAllocationTable& ba
|
||||
|
||||
void Card::_updateChecksum() {
|
||||
m_ch._swapEndian();
|
||||
calculateChecksumBE(reinterpret_cast<uint16_t*>(__raw), 0xFE, &m_ch.m_checksum, &m_ch.m_checksumInv);
|
||||
calculateChecksumBE(reinterpret_cast<uint16_t*>(m_ch.raw.data()), 0xFE, &m_ch.m_checksum, &m_ch.m_checksumInv);
|
||||
m_ch._swapEndian();
|
||||
}
|
||||
|
||||
@@ -218,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) {
|
||||
@@ -230,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;
|
||||
}
|
||||
|
||||
@@ -298,25 +311,30 @@ ECardResult Card::deleteFile(uint32_t fileno) {
|
||||
}
|
||||
|
||||
ECardResult Card::renameFile(const char* oldName, const char* newName) {
|
||||
ECardResult openRes = _pumpOpen();
|
||||
if (openRes != ECardResult::READY)
|
||||
const ECardResult openRes = _pumpOpen();
|
||||
if (openRes != ECardResult::READY) {
|
||||
return openRes;
|
||||
}
|
||||
|
||||
if (strlen(newName) > 32)
|
||||
if (std::strlen(newName) > 32) {
|
||||
return ECardResult::NAMETOOLONG;
|
||||
}
|
||||
|
||||
Directory dir = m_dirs[m_currentDir];
|
||||
File* f = dir.getFile(m_game, m_maker, oldName);
|
||||
if (!f)
|
||||
if (f == nullptr) {
|
||||
return ECardResult::NOFILE;
|
||||
}
|
||||
|
||||
if (File* replF = dir.getFile(m_game, m_maker, newName)) {
|
||||
BlockAllocationTable bat = m_bats[m_currentBat];
|
||||
_deleteFile(*replF, bat);
|
||||
strncpy(f->m_filename, newName, 32);
|
||||
std::memset(f->m_filename, 0, 32);
|
||||
std::strncpy(f->m_filename, newName, 32);
|
||||
_updateDirAndBat(dir, bat);
|
||||
} else {
|
||||
strncpy(f->m_filename, newName, 32);
|
||||
std::memset(f->m_filename, 0, 32);
|
||||
std::strncpy(f->m_filename, newName, 32);
|
||||
_updateDirAndBat(dir, m_bats[m_currentBat]);
|
||||
}
|
||||
return ECardResult::READY;
|
||||
@@ -441,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);
|
||||
@@ -552,11 +570,11 @@ ECardResult Card::getStatus(uint32_t fileNo, CardStat& statOut) const {
|
||||
if (!file || file->m_game[0] == 0xFF)
|
||||
return ECardResult::NOFILE;
|
||||
|
||||
strncpy(statOut.x0_fileName, file->m_filename, 32);
|
||||
std::strncpy(statOut.x0_fileName, file->m_filename, 32);
|
||||
statOut.x20_length = file->m_blockCount * BlockSize;
|
||||
statOut.x24_time = file->m_modifiedTime;
|
||||
memcpy(statOut.x28_gameName, file->m_game, 4);
|
||||
memcpy(statOut.x2c_company, file->m_maker, 2);
|
||||
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;
|
||||
@@ -564,12 +582,11 @@ ECardResult Card::getStatus(uint32_t fileNo, CardStat& statOut) const {
|
||||
statOut.x36_iconSpeed = file->m_animSpeed;
|
||||
statOut.x38_commentAddr = file->m_commentAddr;
|
||||
|
||||
if (file->m_iconAddress == -1) {
|
||||
statOut.x3c_offsetBanner = -1;
|
||||
statOut.x40_offsetBannerTlut = -1;
|
||||
for (int i = 0; i < CARD_ICON_MAX; ++i)
|
||||
statOut.x44_offsetIcon[i] = -1;
|
||||
statOut.x64_offsetIconTlut = -1;
|
||||
if (file->m_iconAddress == UINT32_MAX) {
|
||||
statOut.x3c_offsetBanner = UINT32_MAX;
|
||||
statOut.x40_offsetBannerTlut = UINT32_MAX;
|
||||
statOut.x44_offsetIcon.fill(UINT32_MAX);
|
||||
statOut.x64_offsetIconTlut = UINT32_MAX;
|
||||
statOut.x68_offsetData = file->m_commentAddr + 64;
|
||||
} else {
|
||||
uint32_t cur = file->m_iconAddress;
|
||||
@@ -578,18 +595,19 @@ ECardResult Card::getStatus(uint32_t fileNo, CardStat& statOut) const {
|
||||
statOut.x40_offsetBannerTlut = cur;
|
||||
cur += TlutSize(statOut.GetBannerFormat());
|
||||
bool palette = false;
|
||||
for (int i = 0; i < CARD_ICON_MAX; ++i) {
|
||||
for (size_t i = 0; i < statOut.x44_offsetIcon.size(); ++i) {
|
||||
statOut.x44_offsetIcon[i] = cur;
|
||||
EImageFormat fmt = statOut.GetIconFormat(i);
|
||||
if (fmt == EImageFormat::C8)
|
||||
const EImageFormat fmt = statOut.GetIconFormat(int(i));
|
||||
if (fmt == EImageFormat::C8) {
|
||||
palette = true;
|
||||
}
|
||||
cur += IconSize(fmt);
|
||||
}
|
||||
if (palette) {
|
||||
statOut.x64_offsetIconTlut = cur;
|
||||
cur += TlutSize(EImageFormat::C8);
|
||||
} else
|
||||
statOut.x64_offsetIconTlut = -1;
|
||||
statOut.x64_offsetIconTlut = UINT32_MAX;
|
||||
statOut.x68_offsetData = cur;
|
||||
}
|
||||
|
||||
@@ -687,57 +705,67 @@ bool Card::moveFileTo(FileHandle& fh, Card& dest)
|
||||
|
||||
void Card::setCurrentGame(const char* game) {
|
||||
if (game == nullptr) {
|
||||
memset(m_game, 0, 2);
|
||||
std::memset(m_game, 0, sizeof(m_game));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen(game) != 4)
|
||||
constexpr size_t copy_amount = sizeof(m_game) - 1;
|
||||
if (std::strlen(game) != copy_amount) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(m_game, game, 4);
|
||||
std::memcpy(m_game, game, copy_amount);
|
||||
}
|
||||
|
||||
const uint8_t* Card::getCurrentGame() const {
|
||||
if (strlen(m_game) == 4)
|
||||
if (std::strlen(m_game) == sizeof(m_game) - 1) {
|
||||
return reinterpret_cast<const uint8_t*>(m_game);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Card::setCurrentMaker(const char* maker) {
|
||||
if (maker == nullptr) {
|
||||
memset(m_maker, 0, 2);
|
||||
std::memset(m_maker, 0, sizeof(m_maker));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen(maker) != 2)
|
||||
constexpr size_t copy_amount = sizeof(m_maker) - 1;
|
||||
if (std::strlen(maker) != copy_amount) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(m_maker, maker, 2);
|
||||
std::memcpy(m_maker, maker, copy_amount);
|
||||
}
|
||||
|
||||
const uint8_t* Card::getCurrentMaker() const {
|
||||
if (strlen(m_maker) == 2)
|
||||
if (std::strlen(m_maker) == sizeof(m_maker) - 1) {
|
||||
return reinterpret_cast<const uint8_t*>(m_maker);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Card::getSerial(uint64_t& serial) {
|
||||
m_ch._swapEndian();
|
||||
uint32_t serialBuf[8];
|
||||
for (uint32_t i = 0; i < 8; i++)
|
||||
serialBuf[i] = SBig(*reinterpret_cast<uint32_t*>(__raw + (i * 4)));
|
||||
|
||||
std::array<uint32_t, 8> serialBuf{};
|
||||
for (size_t i = 0; i < serialBuf.size(); i++) {
|
||||
serialBuf[i] = SBig(*reinterpret_cast<uint32_t*>(m_ch.raw.data() + (i * 4)));
|
||||
}
|
||||
serial = uint64_t(serialBuf[0] ^ serialBuf[2] ^ serialBuf[4] ^ serialBuf[6]) << 32 |
|
||||
(serialBuf[1] ^ serialBuf[3] ^ serialBuf[5] ^ serialBuf[7]);
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -745,10 +773,11 @@ void Card::getFreeBlocks(int32_t& bytesNotUsed, int32_t& filesNotUsed) {
|
||||
static std::unique_ptr<uint8_t[]> DummyBlock;
|
||||
|
||||
void Card::format(ECardSlot id, ECardSize size, EEncoding encoding) {
|
||||
memset(__raw, 0xFF, BlockSize);
|
||||
m_ch.raw.fill(0xFF);
|
||||
|
||||
uint64_t rand = uint64_t(getGCTime());
|
||||
m_ch.m_formatTime = rand;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
for (size_t i = 0; i < m_ch.m_serial.size(); i++) {
|
||||
rand = (((rand * uint64_t(0x41c64e6d)) + uint64_t(0x3039)) >> 16);
|
||||
m_ch.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);
|
||||
@@ -774,35 +803,36 @@ void Card::format(ECardSlot id, ECardSize size, EEncoding encoding) {
|
||||
m_fileHandle = AsyncIO(m_filename.c_str(), true);
|
||||
|
||||
if (m_fileHandle) {
|
||||
uint32_t blockCount = (uint32_t(size) * MbitToBlocks) - 5;
|
||||
const uint32_t blockCount = (uint32_t(size) * MbitToBlocks) - 5;
|
||||
|
||||
m_tmpCh = m_ch;
|
||||
m_tmpCh._swapEndian();
|
||||
m_fileHandle.resizeQueue(5 + blockCount);
|
||||
m_fileHandle.asyncWrite(0, &m_tmpCh, BlockSize, 0);
|
||||
m_fileHandle.asyncWrite(0, m_tmpCh.raw.data(), BlockSize, 0);
|
||||
m_tmpDirs[0] = m_dirs[0];
|
||||
m_tmpDirs[0].swapEndian();
|
||||
m_fileHandle.asyncWrite(1, m_tmpDirs[0].__raw, BlockSize, BlockSize * 1);
|
||||
m_fileHandle.asyncWrite(1, m_tmpDirs[0].raw.data(), BlockSize, BlockSize * 1);
|
||||
m_tmpDirs[1] = m_dirs[1];
|
||||
m_tmpDirs[1].swapEndian();
|
||||
m_fileHandle.asyncWrite(2, m_tmpDirs[1].__raw, BlockSize, BlockSize * 2);
|
||||
m_fileHandle.asyncWrite(2, m_tmpDirs[1].raw.data(), BlockSize, BlockSize * 2);
|
||||
m_tmpBats[0] = m_bats[0];
|
||||
m_tmpBats[0].swapEndian();
|
||||
m_fileHandle.asyncWrite(3, m_tmpBats[0].__raw, BlockSize, BlockSize * 3);
|
||||
m_fileHandle.asyncWrite(3, m_tmpBats[0].raw.data(), BlockSize, BlockSize * 3);
|
||||
m_tmpBats[1] = m_bats[1];
|
||||
m_tmpBats[1].swapEndian();
|
||||
m_fileHandle.asyncWrite(4, m_tmpBats[1].__raw, BlockSize, BlockSize * 4);
|
||||
m_fileHandle.asyncWrite(4, m_tmpBats[1].raw.data(), BlockSize, BlockSize * 4);
|
||||
if (!DummyBlock) {
|
||||
DummyBlock.reset(new uint8_t[BlockSize]);
|
||||
memset(DummyBlock.get(), 0xFF, BlockSize);
|
||||
}
|
||||
for (uint32_t i = 0; i < blockCount; ++i)
|
||||
for (uint32_t i = 0; i < blockCount; ++i) {
|
||||
m_fileHandle.asyncWrite(i + 5, DummyBlock.get(), BlockSize, BlockSize * (i + 5));
|
||||
}
|
||||
m_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
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};
|
||||
@@ -819,38 +849,38 @@ void Card::commit() {
|
||||
m_tmpDirs[0] = m_dirs[0];
|
||||
m_tmpDirs[0].updateChecksum();
|
||||
m_tmpDirs[0].swapEndian();
|
||||
m_fileHandle.asyncWrite(1, m_tmpDirs[0].__raw, BlockSize, BlockSize * 1);
|
||||
m_fileHandle.asyncWrite(1, m_tmpDirs[0].raw.data(), BlockSize, BlockSize * 1);
|
||||
m_tmpDirs[1] = m_dirs[1];
|
||||
m_tmpDirs[1].updateChecksum();
|
||||
m_tmpDirs[1].swapEndian();
|
||||
m_fileHandle.asyncWrite(2, m_tmpDirs[1].__raw, BlockSize, BlockSize * 2);
|
||||
m_fileHandle.asyncWrite(2, m_tmpDirs[1].raw.data(), BlockSize, BlockSize * 2);
|
||||
m_tmpBats[0] = m_bats[0];
|
||||
m_tmpBats[0].updateChecksum();
|
||||
m_tmpBats[0].swapEndian();
|
||||
m_fileHandle.asyncWrite(3, m_tmpBats[0].__raw, BlockSize, BlockSize * 3);
|
||||
m_fileHandle.asyncWrite(3, m_tmpBats[0].raw.data(), BlockSize, BlockSize * 3);
|
||||
m_tmpBats[1] = m_bats[1];
|
||||
m_tmpBats[1].updateChecksum();
|
||||
m_tmpBats[1].swapEndian();
|
||||
m_fileHandle.asyncWrite(4, m_tmpBats[1].__raw, BlockSize, BlockSize * 4);
|
||||
m_fileHandle.asyncWrite(4, m_tmpBats[1].raw.data(), BlockSize, BlockSize * 4);
|
||||
m_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Card::open(SystemStringView filepath) {
|
||||
bool Card::open(std::string_view filepath) {
|
||||
m_opened = false;
|
||||
m_filename = filepath;
|
||||
m_fileHandle = AsyncIO(m_filename);
|
||||
if (m_fileHandle) {
|
||||
m_fileHandle.resizeQueue(5);
|
||||
if (!m_fileHandle.asyncRead(0, __raw, BlockSize, 0))
|
||||
if (!m_fileHandle.asyncRead(0, m_ch.raw.data(), BlockSize, 0))
|
||||
return false;
|
||||
if (!m_fileHandle.asyncRead(1, m_dirs[0].__raw, BlockSize, BlockSize * 1))
|
||||
if (!m_fileHandle.asyncRead(1, m_dirs[0].raw.data(), BlockSize, BlockSize * 1))
|
||||
return false;
|
||||
if (!m_fileHandle.asyncRead(2, m_dirs[1].__raw, BlockSize, BlockSize * 2))
|
||||
if (!m_fileHandle.asyncRead(2, m_dirs[1].raw.data(), BlockSize, BlockSize * 2))
|
||||
return false;
|
||||
if (!m_fileHandle.asyncRead(3, m_bats[0].__raw, BlockSize, BlockSize * 3))
|
||||
if (!m_fileHandle.asyncRead(3, m_bats[0].raw.data(), BlockSize, BlockSize * 3))
|
||||
return false;
|
||||
if (!m_fileHandle.asyncRead(4, m_bats[1].__raw, BlockSize, BlockSize * 4))
|
||||
if (!m_fileHandle.asyncRead(4, m_bats[1].raw.data(), BlockSize, BlockSize * 4))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@@ -880,7 +910,7 @@ ECardResult Card::getError() const {
|
||||
|
||||
uint16_t ckSum, ckSumInv;
|
||||
const_cast<Card&>(*this).m_ch._swapEndian();
|
||||
calculateChecksumBE(reinterpret_cast<const uint16_t*>(__raw), 0xFE, &ckSum, &ckSumInv);
|
||||
calculateChecksumBE(reinterpret_cast<const uint16_t*>(m_ch.raw.data()), 0xFE, &ckSum, &ckSumInv);
|
||||
bool res = (ckSum == m_ch.m_checksum && ckSumInv == m_ch.m_checksumInv);
|
||||
const_cast<Card&>(*this).m_ch._swapEndian();
|
||||
|
||||
|
||||
@@ -1,112 +1,141 @@
|
||||
#include "kabufuda/Directory.hpp"
|
||||
#include "kabufuda/Util.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "kabufuda/Util.hpp"
|
||||
|
||||
namespace kabufuda {
|
||||
void Directory::swapEndian() {
|
||||
std::for_each(std::begin(m_files), std::end(m_files), [](File& f) { f.swapEndian(); });
|
||||
std::for_each(std::begin(data.m_files), std::end(data.m_files), [](File& f) { f.swapEndian(); });
|
||||
|
||||
m_updateCounter = SBig(m_updateCounter);
|
||||
m_checksum = SBig(m_checksum);
|
||||
m_checksumInv = SBig(m_checksumInv);
|
||||
data.m_updateCounter = SBig(data.m_updateCounter);
|
||||
data.m_checksum = SBig(data.m_checksum);
|
||||
data.m_checksumInv = SBig(data.m_checksumInv);
|
||||
}
|
||||
|
||||
void Directory::updateChecksum() {
|
||||
swapEndian();
|
||||
calculateChecksumBE(reinterpret_cast<uint16_t*>(__raw), 0xFFE, &m_checksum, &m_checksumInv);
|
||||
calculateChecksumBE(reinterpret_cast<uint16_t*>(raw.data()), 0xFFE, &data.m_checksum, &data.m_checksumInv);
|
||||
swapEndian();
|
||||
}
|
||||
|
||||
bool Directory::valid() const {
|
||||
uint16_t ckSum, ckSumInv;
|
||||
const_cast<Directory&>(*this).swapEndian();
|
||||
calculateChecksumBE(reinterpret_cast<const uint16_t*>(__raw), 0xFFE, &ckSum, &ckSumInv);
|
||||
bool res = (ckSum == m_checksum && ckSumInv == m_checksumInv);
|
||||
calculateChecksumBE(reinterpret_cast<const uint16_t*>(raw.data()), 0xFFE, &ckSum, &ckSumInv);
|
||||
const bool res = (ckSum == data.m_checksum && ckSumInv == data.m_checksumInv);
|
||||
const_cast<Directory&>(*this).swapEndian();
|
||||
return res;
|
||||
}
|
||||
|
||||
Directory::Directory() {
|
||||
memset(__raw, 0xFF, BlockSize);
|
||||
m_updateCounter = 0;
|
||||
raw.fill(0xFF);
|
||||
data.m_updateCounter = 0;
|
||||
updateChecksum();
|
||||
}
|
||||
|
||||
Directory::Directory(uint8_t data[]) { memcpy(__raw, data, BlockSize); }
|
||||
Directory::Directory(const RawData& rawData) : raw{rawData} {}
|
||||
|
||||
bool Directory::hasFreeFile() const {
|
||||
for (uint16_t i = 0; i < 127; i++)
|
||||
if (m_files[i].m_game[0] == 0xFF)
|
||||
return true;
|
||||
return false;
|
||||
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 {
|
||||
int32_t ret = 0;
|
||||
for (uint16_t i = 0; i < 127; i++)
|
||||
if (m_files[i].m_game[0] == 0xFF)
|
||||
++ret;
|
||||
return ret;
|
||||
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) {
|
||||
for (uint16_t i = 0; i < 127; i++) {
|
||||
if (m_files[i].m_game[0] == 0xFF) {
|
||||
File* ret = &m_files[i];
|
||||
*ret = File(filename);
|
||||
if (game && strlen(game) == 4)
|
||||
memcpy(ret->m_game, game, 4);
|
||||
if (maker && strlen(maker) == 2)
|
||||
memcpy(ret->m_maker, maker, 2);
|
||||
return ret;
|
||||
}
|
||||
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 == std::end(data.m_files)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
*iter = File(filename);
|
||||
if (game != nullptr && std::strlen(game) == 4) {
|
||||
std::memcpy(iter->m_game, game, 4);
|
||||
}
|
||||
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) {
|
||||
for (uint16_t i = start; i < 127; i++) {
|
||||
if (m_files[i].m_game[0] != 0xFF) {
|
||||
File* ret = &m_files[i];
|
||||
if (game && std::strlen(game) == 4 && std::strncmp(reinterpret_cast<const char*>(ret->m_game), game, 4) != 0)
|
||||
continue;
|
||||
if (maker && std::strlen(maker) == 2 && std::strncmp(reinterpret_cast<const char*>(ret->m_maker), maker, 2) != 0)
|
||||
continue;
|
||||
return ret;
|
||||
}
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
});
|
||||
|
||||
if (iter == std::end(data.m_files)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return &*iter;
|
||||
}
|
||||
|
||||
File* Directory::getFile(const char* game, const char* maker, const char* filename) {
|
||||
for (uint16_t i = 0; i < 127; i++) {
|
||||
if (game && strlen(game) == 4 && memcmp(m_files[i].m_game, game, 4))
|
||||
continue;
|
||||
if (maker && strlen(maker) == 2 && memcmp(m_files[i].m_maker, maker, 2))
|
||||
continue;
|
||||
if (!strcmp(m_files[i].m_filename, filename))
|
||||
return &m_files[i];
|
||||
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 = 2;
|
||||
if (maker != nullptr && std::strlen(maker) == maker_size && std::memcmp(file.m_maker, maker, maker_size) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::strncmp(file.m_filename, filename, 32) == 0;
|
||||
});
|
||||
|
||||
if (iter == std::cend(data.m_files)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return &*iter;
|
||||
}
|
||||
|
||||
File* Directory::getFile(uint32_t idx) {
|
||||
if (idx >= 127)
|
||||
if (idx >= MaxFiles) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &m_files[idx];
|
||||
return &data.m_files[idx];
|
||||
}
|
||||
|
||||
int32_t Directory::indexForFile(File* f) {
|
||||
if (!f)
|
||||
int32_t Directory::indexForFile(const File* f) const {
|
||||
if (f == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto it = std::find_if(std::begin(m_files), std::end(m_files), [&f](const File& file) -> bool { return f == &file; });
|
||||
if (it == std::end(m_files))
|
||||
const auto it =
|
||||
std::find_if(std::cbegin(data.m_files), std::cend(data.m_files), [&f](const File& file) { return f == &file; });
|
||||
if (it == std::cend(data.m_files)) {
|
||||
return -1;
|
||||
return it - std::begin(m_files);
|
||||
}
|
||||
|
||||
return it - std::cbegin(data.m_files);
|
||||
}
|
||||
} // namespace kabufuda
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
#include "kabufuda/File.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
|
||||
#include "kabufuda/Util.hpp"
|
||||
|
||||
namespace kabufuda {
|
||||
File::File() { memset(__raw, 0xFF, 0x40); }
|
||||
File::File() { raw.fill(0xFF); }
|
||||
|
||||
File::File(char data[]) { memcpy(__raw, data, 0x40); }
|
||||
File::File(const RawData& rawData) : raw{rawData} {}
|
||||
|
||||
File::File(const char* filename) {
|
||||
memset(__raw, 0, 0x40);
|
||||
memset(m_filename, 0, 32);
|
||||
strncpy(m_filename, filename, 32);
|
||||
raw.fill(0);
|
||||
std::memset(m_filename, 0, std::size(m_filename));
|
||||
std::strncpy(m_filename, filename, std::size(m_filename) - 1);
|
||||
}
|
||||
void File::swapEndian() {
|
||||
m_modifiedTime = SBig(m_modifiedTime);
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
#include "kabufuda/WideStringConvert.hpp"
|
||||
#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
|
||||
@@ -1,34 +0,0 @@
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include "hecl/winsupport.hpp"
|
||||
|
||||
/*
|
||||
* The memmem() function finds the start of the first occurrence of the
|
||||
* substring 'needle' of length 'nlen' in the memory area 'haystack' of
|
||||
* length 'hlen'.
|
||||
*
|
||||
* The return value is a pointer to the beginning of the sub-string, or
|
||||
* NULL if the substring is not found.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user