mirror of https://github.com/AxioDL/kabufuda.git
Use asynchronous I/O for Card access
This commit is contained in:
parent
8052a6372e
commit
bdf4bd07a8
|
@ -9,11 +9,14 @@ set(KABUFUDA_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE PATH "kabufud
|
||||||
|
|
||||||
unset(PLAT_SRCS)
|
unset(PLAT_SRCS)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
list(APPEND PLAT_SRCS lib/kabufuda/winsupport.cpp include/kabufuda/winsupport.hpp)
|
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)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(kabufuda STATIC
|
add_library(kabufuda STATIC
|
||||||
include/kabufuda/Constants.hpp
|
include/kabufuda/Constants.hpp
|
||||||
|
include/kabufuda/AsyncIO.hpp
|
||||||
include/kabufuda/BlockAllocationTable.hpp lib/kabufuda/BlockAllocationTable.cpp
|
include/kabufuda/BlockAllocationTable.hpp lib/kabufuda/BlockAllocationTable.cpp
|
||||||
include/kabufuda/Card.hpp lib/kabufuda/Card.cpp
|
include/kabufuda/Card.hpp lib/kabufuda/Card.cpp
|
||||||
include/kabufuda/Directory.hpp lib/kabufuda/Directory.cpp
|
include/kabufuda/Directory.hpp lib/kabufuda/Directory.cpp
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
#ifndef __KABU_ASYNCIO_HPP__
|
||||||
|
#define __KABU_ASYNCIO_HPP__
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <aio.h>
|
||||||
|
using SizeReturn = ssize_t;
|
||||||
|
#else
|
||||||
|
#include <windows.h>
|
||||||
|
using SizeReturn = SSIZE_T;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Util.hpp"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace kabufuda
|
||||||
|
{
|
||||||
|
|
||||||
|
class AsyncIO
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
|
int m_fd = -1;
|
||||||
|
std::vector<std::pair<struct aiocb, SizeReturn>> m_queue;
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
size_t m_maxBlock = 0;
|
||||||
|
public:
|
||||||
|
AsyncIO() = default;
|
||||||
|
AsyncIO(SystemStringView 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); }
|
||||||
|
SizeReturn syncRead(void* buf, size_t length, off_t offset);
|
||||||
|
bool asyncRead(size_t qIdx, void* buf, size_t length, off_t offset);
|
||||||
|
SizeReturn syncWrite(const 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;
|
||||||
|
operator bool() const { return m_fd != -1; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __KABU_ASYNCIO_HPP__
|
|
@ -5,6 +5,7 @@
|
||||||
#include "Directory.hpp"
|
#include "Directory.hpp"
|
||||||
#include "File.hpp"
|
#include "File.hpp"
|
||||||
#include "Util.hpp"
|
#include "Util.hpp"
|
||||||
|
#include "AsyncIO.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -12,7 +13,6 @@
|
||||||
|
|
||||||
#define CARD_FILENAME_MAX 32
|
#define CARD_FILENAME_MAX 32
|
||||||
#define CARD_ICON_MAX 8
|
#define CARD_ICON_MAX 8
|
||||||
#undef NOFILE
|
|
||||||
|
|
||||||
namespace kabufuda
|
namespace kabufuda
|
||||||
{
|
{
|
||||||
|
@ -29,24 +29,6 @@ public:
|
||||||
operator bool() const { return getFileNo() != -1; }
|
operator bool() const { return getFileNo() != -1; }
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ECardResult
|
|
||||||
{
|
|
||||||
CRC_MISMATCH = -1003, /* Extension enum for Retro's CRC check */
|
|
||||||
FATAL_ERROR = -128,
|
|
||||||
ENCODING = -13,
|
|
||||||
NAMETOOLONG = -12,
|
|
||||||
INSSPACE = -9,
|
|
||||||
NOENT = -8,
|
|
||||||
EXIST = -7,
|
|
||||||
BROKEN = -6,
|
|
||||||
IOERROR = -5,
|
|
||||||
NOFILE = -4,
|
|
||||||
NOCARD = -3,
|
|
||||||
WRONGDEVICE = -2,
|
|
||||||
BUSY = -1,
|
|
||||||
READY = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ProbeResults
|
struct ProbeResults
|
||||||
{
|
{
|
||||||
ECardResult x0_error;
|
ECardResult x0_error;
|
||||||
|
@ -102,31 +84,35 @@ struct CardStat
|
||||||
class Card
|
class Card
|
||||||
{
|
{
|
||||||
#pragma pack(push, 4)
|
#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;
|
||||||
|
void _swapEndian();
|
||||||
|
};
|
||||||
union {
|
union {
|
||||||
struct
|
CardHeader m_ch;
|
||||||
{
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
uint8_t __raw[BlockSize];
|
uint8_t __raw[BlockSize];
|
||||||
};
|
};
|
||||||
|
CardHeader m_tmpCh;
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
SystemString m_filename;
|
SystemString m_filename;
|
||||||
FILE* m_fileHandle = nullptr;
|
AsyncIO m_fileHandle;
|
||||||
Directory m_dirs[2];
|
Directory m_dirs[2];
|
||||||
BlockAllocationTable m_bats[2];
|
BlockAllocationTable m_bats[2];
|
||||||
|
Directory m_tmpDirs[2];
|
||||||
|
BlockAllocationTable m_tmpBats[2];
|
||||||
uint8_t m_currentDir;
|
uint8_t m_currentDir;
|
||||||
uint8_t m_currentBat;
|
uint8_t m_currentBat;
|
||||||
|
|
||||||
|
@ -134,12 +120,15 @@ class Card
|
||||||
char m_game[5] = {'\0'};
|
char m_game[5] = {'\0'};
|
||||||
char m_maker[3] = {'\0'};
|
char m_maker[3] = {'\0'};
|
||||||
|
|
||||||
void _swapEndian();
|
|
||||||
void _updateDirAndBat(const Directory& dir, const BlockAllocationTable& bat);
|
void _updateDirAndBat(const Directory& dir, const BlockAllocationTable& bat);
|
||||||
void _updateChecksum();
|
void _updateChecksum();
|
||||||
File* _fileFromHandle(const FileHandle& fh) const;
|
File* _fileFromHandle(const FileHandle& fh) const;
|
||||||
void _deleteFile(File& f, BlockAllocationTable& bat);
|
void _deleteFile(File& f, BlockAllocationTable& bat);
|
||||||
|
|
||||||
|
bool m_dirty = false;
|
||||||
|
bool m_opened = false;
|
||||||
|
ECardResult _pumpOpen();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Card();
|
Card();
|
||||||
/**
|
/**
|
||||||
|
@ -157,7 +146,7 @@ public:
|
||||||
* @param game
|
* @param game
|
||||||
* @param maker
|
* @param maker
|
||||||
*/
|
*/
|
||||||
Card(SystemStringView filepath, const char* game = nullptr, const char* maker = nullptr);
|
Card(const char* game = nullptr, const char* maker = nullptr);
|
||||||
~Card();
|
~Card();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -237,7 +226,7 @@ public:
|
||||||
* @param buf
|
* @param buf
|
||||||
* @param size
|
* @param size
|
||||||
*/
|
*/
|
||||||
ECardResult write(FileHandle& fh, const void* buf, size_t size);
|
ECardResult asyncWrite(FileHandle& fh, const void* buf, size_t size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief read
|
* @brief read
|
||||||
|
@ -245,7 +234,7 @@ public:
|
||||||
* @param dst
|
* @param dst
|
||||||
* @param size
|
* @param size
|
||||||
*/
|
*/
|
||||||
ECardResult read(FileHandle& fh, void* dst, size_t size);
|
ECardResult asyncRead(FileHandle& fh, void* dst, size_t size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief seek
|
* @brief seek
|
||||||
|
@ -336,6 +325,7 @@ public:
|
||||||
*/
|
*/
|
||||||
ECardResult setStatus(uint32_t fileNo, const CardStat& stat);
|
ECardResult setStatus(uint32_t fileNo, const CardStat& stat);
|
||||||
|
|
||||||
|
#if 0 // TODO: Async-friendly implementations
|
||||||
/**
|
/**
|
||||||
* @brief Copies a file from the current Card instance to a specified Card instance
|
* @brief Copies a file from the current Card instance to a specified Card instance
|
||||||
* @param fh The file to copy
|
* @param fh The file to copy
|
||||||
|
@ -351,6 +341,7 @@ public:
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
bool moveFileTo(FileHandle& fh, Card& dest);
|
bool moveFileTo(FileHandle& fh, Card& dest);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the current game, if not null any openFile requests will only return files that match this game
|
* @brief Sets the current game, if not null any openFile requests will only return files that match this game
|
||||||
|
@ -417,6 +408,11 @@ public:
|
||||||
*/
|
*/
|
||||||
void commit();
|
void commit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Opens card image (does nothing if currently open path matches)
|
||||||
|
*/
|
||||||
|
bool open(SystemStringView filepath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Commits changes to disk and closes host file
|
* @brief Commits changes to disk and closes host file
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -250,71 +250,6 @@ typedef struct stat Sstat;
|
||||||
|
|
||||||
uint64_t getGCTime();
|
uint64_t getGCTime();
|
||||||
|
|
||||||
enum class FileLockType
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Read,
|
|
||||||
Write
|
|
||||||
};
|
|
||||||
static inline FILE* Fopen(const SystemChar* path, const SystemChar* mode, FileLockType lock = FileLockType::None)
|
|
||||||
{
|
|
||||||
#if CARD_UCS2
|
|
||||||
FILE* fp = _wfopen(path, mode);
|
|
||||||
if (!fp)
|
|
||||||
return nullptr;
|
|
||||||
#else
|
|
||||||
FILE* fp = fopen(path, mode);
|
|
||||||
if (!fp)
|
|
||||||
return nullptr;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (lock != FileLockType::None)
|
|
||||||
{
|
|
||||||
#if _WIN32
|
|
||||||
OVERLAPPED ov = {};
|
|
||||||
LockFileEx((HANDLE)(uintptr_t)_fileno(fp), (lock == FileLockType::Write) ? LOCKFILE_EXCLUSIVE_LOCK : 0, 0, 0, 1,
|
|
||||||
&ov);
|
|
||||||
#else
|
|
||||||
if (flock(fileno(fp), ((lock == FileLockType::Write) ? LOCK_EX : LOCK_SH) | LOCK_NB))
|
|
||||||
fprintf(stderr, "flock %s: %s", path, strerror(errno));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return fp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int FSeek(FILE* fp, int64_t offset, int whence)
|
|
||||||
{
|
|
||||||
#if _WIN32
|
|
||||||
return _fseeki64(fp, offset, whence);
|
|
||||||
#elif __APPLE__ || __FreeBSD__
|
|
||||||
return fseeko(fp, offset, whence);
|
|
||||||
#else
|
|
||||||
return fseeko64(fp, offset, whence);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int64_t FTell(FILE* fp)
|
|
||||||
{
|
|
||||||
#if _WIN32
|
|
||||||
return _ftelli64(fp);
|
|
||||||
#elif __APPLE__ || __FreeBSD__
|
|
||||||
return ftello(fp);
|
|
||||||
#else
|
|
||||||
return ftello64(fp);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int Rename(const SystemChar* oldpath, const SystemChar* newpath)
|
|
||||||
{
|
|
||||||
#if CARD_UCS2
|
|
||||||
//return _wrename(oldpath, newpath);
|
|
||||||
return MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) == 0;
|
|
||||||
#else
|
|
||||||
return rename(oldpath, newpath);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
|
#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
|
||||||
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||||
#endif
|
#endif
|
||||||
|
@ -349,6 +284,26 @@ static inline int Stat(const SystemChar* path, Sstat* statOut)
|
||||||
* @param checksumInv
|
* @param checksumInv
|
||||||
*/
|
*/
|
||||||
void calculateChecksumBE(const uint16_t* data, size_t len, uint16_t* checksum, uint16_t* checksumInv);
|
void calculateChecksumBE(const uint16_t* data, size_t len, uint16_t* checksum, uint16_t* checksumInv);
|
||||||
|
|
||||||
|
#undef NOFILE
|
||||||
|
|
||||||
|
enum class ECardResult
|
||||||
|
{
|
||||||
|
CRC_MISMATCH = -1003, /* Extension enum for Retro's CRC check */
|
||||||
|
FATAL_ERROR = -128,
|
||||||
|
ENCODING = -13,
|
||||||
|
NAMETOOLONG = -12,
|
||||||
|
INSSPACE = -9,
|
||||||
|
NOENT = -8,
|
||||||
|
EXIST = -7,
|
||||||
|
BROKEN = -6,
|
||||||
|
IOERROR = -5,
|
||||||
|
NOFILE = -4,
|
||||||
|
NOCARD = -3,
|
||||||
|
WRONGDEVICE = -2,
|
||||||
|
BUSY = -1,
|
||||||
|
READY = 0
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // __KABU_UTIL_HPP__
|
#endif // __KABU_UTIL_HPP__
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
#include "kabufuda/AsyncIO.hpp"
|
||||||
|
|
||||||
|
namespace kabufuda
|
||||||
|
{
|
||||||
|
|
||||||
|
AsyncIO::AsyncIO(SystemStringView filename, bool truncate)
|
||||||
|
{
|
||||||
|
m_fd = open(filename.data(), O_RDWR | O_CREAT | (truncate ? O_TRUNC : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncIO::~AsyncIO()
|
||||||
|
{
|
||||||
|
if (*this)
|
||||||
|
{
|
||||||
|
aio_cancel(m_fd, nullptr);
|
||||||
|
close(m_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncIO::AsyncIO(AsyncIO&& other)
|
||||||
|
{
|
||||||
|
m_fd = other.m_fd;
|
||||||
|
other.m_fd = -1;
|
||||||
|
m_queue = std::move(other.m_queue);
|
||||||
|
m_maxBlock = other.m_maxBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncIO& AsyncIO::operator=(AsyncIO&& other)
|
||||||
|
{
|
||||||
|
if (*this)
|
||||||
|
{
|
||||||
|
aio_cancel(m_fd, nullptr);
|
||||||
|
close(m_fd);
|
||||||
|
}
|
||||||
|
m_fd = other.m_fd;
|
||||||
|
other.m_fd = -1;
|
||||||
|
m_queue = std::move(other.m_queue);
|
||||||
|
m_maxBlock = other.m_maxBlock;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SizeReturn AsyncIO::syncRead(void* buf, size_t length, off_t offset)
|
||||||
|
{
|
||||||
|
lseek(m_fd, offset, SEEK_SET);
|
||||||
|
return read(m_fd, buf, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncIO::asyncRead(size_t qIdx, void* buf, size_t length, off_t offset)
|
||||||
|
{
|
||||||
|
struct aiocb& aio = m_queue[qIdx].first;
|
||||||
|
if (aio.aio_fildes)
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
fprintf(stderr, "WARNING: synchronous kabufuda fallback, check access polling\n");
|
||||||
|
#endif
|
||||||
|
const struct aiocb* aiop = &aio;
|
||||||
|
struct timespec ts = {2, 0};
|
||||||
|
while (aio_suspend(&aiop, 1, &ts) && errno == EINTR) {}
|
||||||
|
if (aio_error(&aio) == 0)
|
||||||
|
aio_return(&aio);
|
||||||
|
}
|
||||||
|
memset(&aio, 0, sizeof(struct aiocb));
|
||||||
|
aio.aio_fildes = m_fd;
|
||||||
|
aio.aio_offset = offset;
|
||||||
|
aio.aio_buf = buf;
|
||||||
|
aio.aio_nbytes = length;
|
||||||
|
m_maxBlock = std::max(m_maxBlock, qIdx + 1);
|
||||||
|
return aio_read(&aio) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SizeReturn AsyncIO::syncWrite(const void* buf, size_t length, off_t offset)
|
||||||
|
{
|
||||||
|
lseek(m_fd, offset, SEEK_SET);
|
||||||
|
return write(m_fd, buf, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncIO::asyncWrite(size_t qIdx, const void* buf, size_t length, off_t offset)
|
||||||
|
{
|
||||||
|
struct aiocb& aio = m_queue[qIdx].first;
|
||||||
|
if (aio.aio_fildes)
|
||||||
|
{
|
||||||
|
#ifndef NDEBUG
|
||||||
|
fprintf(stderr, "WARNING: synchronous kabufuda fallback, check access polling\n");
|
||||||
|
#endif
|
||||||
|
const struct aiocb* aiop = &aio;
|
||||||
|
struct timespec ts = {2, 0};
|
||||||
|
while (aio_suspend(&aiop, 1, &ts) && errno == EINTR) {}
|
||||||
|
if (aio_error(&aio) == 0)
|
||||||
|
aio_return(&aio);
|
||||||
|
}
|
||||||
|
memset(&aio, 0, sizeof(struct aiocb));
|
||||||
|
aio.aio_fildes = m_fd;
|
||||||
|
aio.aio_offset = offset;
|
||||||
|
aio.aio_buf = const_cast<void*>(buf);
|
||||||
|
aio.aio_nbytes = length;
|
||||||
|
m_maxBlock = std::max(m_maxBlock, qIdx + 1);
|
||||||
|
return aio_write(&aio) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ECardResult AsyncIO::pollStatus(size_t qIdx, SizeReturn* szRet) const
|
||||||
|
{
|
||||||
|
auto& aio = const_cast<AsyncIO*>(this)->m_queue[qIdx];
|
||||||
|
if (aio.first.aio_fildes == 0)
|
||||||
|
{
|
||||||
|
if (szRet)
|
||||||
|
*szRet = aio.second;
|
||||||
|
return ECardResult::READY;
|
||||||
|
}
|
||||||
|
switch (aio_error(&aio.first))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
aio.second = aio_return(&aio.first);
|
||||||
|
aio.first.aio_fildes = 0;
|
||||||
|
if (szRet)
|
||||||
|
*szRet = aio.second;
|
||||||
|
return ECardResult::READY;
|
||||||
|
case EINPROGRESS:
|
||||||
|
return ECardResult::BUSY;
|
||||||
|
default:
|
||||||
|
return ECardResult::IOERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
auto& aio = *it;
|
||||||
|
if (aio.first.aio_fildes == 0)
|
||||||
|
continue;
|
||||||
|
switch (aio_error(&aio.first))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
aio.second = aio_return(&aio.first);
|
||||||
|
aio.first.aio_fildes = 0;
|
||||||
|
break;
|
||||||
|
case EINPROGRESS:
|
||||||
|
if (result > ECardResult::BUSY)
|
||||||
|
result = ECardResult::BUSY;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (result > ECardResult::IOERROR)
|
||||||
|
result = ECardResult::IOERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result == ECardResult::READY)
|
||||||
|
const_cast<AsyncIO*>(this)->m_maxBlock = 0;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncIO::waitForCompletion() const
|
||||||
|
{
|
||||||
|
for (auto it = const_cast<AsyncIO*>(this)->m_queue.begin();
|
||||||
|
it != const_cast<AsyncIO*>(this)->m_queue.begin() + m_maxBlock;
|
||||||
|
++it)
|
||||||
|
{
|
||||||
|
auto& aio = *it;
|
||||||
|
if (aio.first.aio_fildes == 0)
|
||||||
|
continue;
|
||||||
|
switch (aio_error(&aio.first))
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
aio.second = aio_return(&aio.first);
|
||||||
|
aio.first.aio_fildes = 0;
|
||||||
|
break;
|
||||||
|
case EINPROGRESS:
|
||||||
|
{
|
||||||
|
const struct aiocb* aiop = &aio.first;
|
||||||
|
struct timespec ts = {2, 0};
|
||||||
|
while (aio_suspend(&aiop, 1, &ts) && errno == EINTR) {}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
//
|
||||||
|
// Created by Jack Andersen on 2/5/18.
|
||||||
|
//
|
||||||
|
|
|
@ -15,7 +15,7 @@ static void NullFileAccess()
|
||||||
fprintf(stderr, "Attempted to access null file\n");
|
fprintf(stderr, "Attempted to access null file\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Card::_swapEndian()
|
void Card::CardHeader::_swapEndian()
|
||||||
{
|
{
|
||||||
m_formatTime = SBig(m_formatTime);
|
m_formatTime = SBig(m_formatTime);
|
||||||
m_sramBias = SBig(m_sramBias);
|
m_sramBias = SBig(m_sramBias);
|
||||||
|
@ -35,8 +35,7 @@ Card::Card(Card&& other)
|
||||||
{
|
{
|
||||||
memmove(__raw, other.__raw, BlockSize);
|
memmove(__raw, other.__raw, BlockSize);
|
||||||
m_filename = std::move(other.m_filename);
|
m_filename = std::move(other.m_filename);
|
||||||
m_fileHandle = other.m_fileHandle;
|
m_fileHandle = std::move(other.m_fileHandle);
|
||||||
other.m_fileHandle = nullptr;
|
|
||||||
m_dirs[0] = std::move(other.m_dirs[0]);
|
m_dirs[0] = std::move(other.m_dirs[0]);
|
||||||
m_dirs[1] = std::move(other.m_dirs[1]);
|
m_dirs[1] = std::move(other.m_dirs[1]);
|
||||||
m_bats[0] = std::move(other.m_bats[0]);
|
m_bats[0] = std::move(other.m_bats[0]);
|
||||||
|
@ -55,8 +54,7 @@ Card& Card::operator=(Card&& other)
|
||||||
|
|
||||||
memmove(__raw, other.__raw, BlockSize);
|
memmove(__raw, other.__raw, BlockSize);
|
||||||
m_filename = std::move(other.m_filename);
|
m_filename = std::move(other.m_filename);
|
||||||
m_fileHandle = other.m_fileHandle;
|
m_fileHandle = std::move(other.m_fileHandle);
|
||||||
other.m_fileHandle = nullptr;
|
|
||||||
m_dirs[0] = std::move(other.m_dirs[0]);
|
m_dirs[0] = std::move(other.m_dirs[0]);
|
||||||
m_dirs[1] = std::move(other.m_dirs[1]);
|
m_dirs[1] = std::move(other.m_dirs[1]);
|
||||||
m_bats[0] = std::move(other.m_bats[0]);
|
m_bats[0] = std::move(other.m_bats[0]);
|
||||||
|
@ -71,24 +69,20 @@ Card& Card::operator=(Card&& other)
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card::Card(SystemStringView filename, const char* game, const char* maker) : m_filename(filename)
|
ECardResult Card::_pumpOpen()
|
||||||
{
|
{
|
||||||
memset(__raw, 0xFF, BlockSize);
|
if (m_opened)
|
||||||
if (game && strlen(game) == 4)
|
return ECardResult::READY;
|
||||||
memcpy(m_game, game, 4);
|
|
||||||
if (maker && strlen(maker) == 2)
|
|
||||||
memcpy(m_maker, maker, 2);
|
|
||||||
|
|
||||||
m_fileHandle = Fopen(m_filename.c_str(), _S("rb"));
|
if (!m_fileHandle)
|
||||||
if (m_fileHandle)
|
return ECardResult::NOCARD;
|
||||||
|
|
||||||
|
ECardResult res = m_fileHandle.pollStatus();
|
||||||
|
if (res == ECardResult::READY)
|
||||||
{
|
{
|
||||||
fread(__raw, 1, BlockSize, m_fileHandle);
|
m_ch._swapEndian();
|
||||||
m_maxBlock = m_sizeMb * MbitToBlocks;
|
m_maxBlock = m_ch.m_sizeMb * MbitToBlocks;
|
||||||
_swapEndian();
|
m_fileHandle.resizeQueue(m_maxBlock);
|
||||||
fread(m_dirs[0].__raw, 1, BlockSize, m_fileHandle);
|
|
||||||
fread(m_dirs[1].__raw, 1, BlockSize, m_fileHandle);
|
|
||||||
fread(m_bats[0].__raw, 1, BlockSize, m_fileHandle);
|
|
||||||
fread(m_bats[1].__raw, 1, BlockSize, m_fileHandle);
|
|
||||||
|
|
||||||
m_dirs[0].swapEndian();
|
m_dirs[0].swapEndian();
|
||||||
m_dirs[1].swapEndian();
|
m_dirs[1].swapEndian();
|
||||||
|
@ -115,11 +109,19 @@ Card::Card(SystemStringView filename, const char* game, const char* maker) : m_f
|
||||||
else
|
else
|
||||||
m_currentBat = 1;
|
m_currentBat = 1;
|
||||||
|
|
||||||
/* Close and reopen in read/write mode */
|
m_opened = true;
|
||||||
fclose(m_fileHandle);
|
|
||||||
m_fileHandle = Fopen(m_filename.c_str(), _S("r+b"));
|
|
||||||
rewind(m_fileHandle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
Card::~Card()
|
Card::~Card()
|
||||||
|
@ -129,6 +131,10 @@ Card::~Card()
|
||||||
|
|
||||||
ECardResult Card::openFile(const char* filename, FileHandle& handleOut)
|
ECardResult Card::openFile(const char* filename, FileHandle& handleOut)
|
||||||
{
|
{
|
||||||
|
ECardResult openRes = _pumpOpen();
|
||||||
|
if (openRes != ECardResult::READY)
|
||||||
|
return openRes;
|
||||||
|
|
||||||
handleOut = {};
|
handleOut = {};
|
||||||
File* f = m_dirs[m_currentDir].getFile(m_game, m_maker, filename);
|
File* f = m_dirs[m_currentDir].getFile(m_game, m_maker, filename);
|
||||||
if (!f || f->m_game[0] == 0xFF)
|
if (!f || f->m_game[0] == 0xFF)
|
||||||
|
@ -144,6 +150,10 @@ ECardResult Card::openFile(const char* filename, FileHandle& handleOut)
|
||||||
|
|
||||||
ECardResult Card::openFile(uint32_t fileno, FileHandle& handleOut)
|
ECardResult Card::openFile(uint32_t fileno, FileHandle& handleOut)
|
||||||
{
|
{
|
||||||
|
ECardResult openRes = _pumpOpen();
|
||||||
|
if (openRes != ECardResult::READY)
|
||||||
|
return openRes;
|
||||||
|
|
||||||
handleOut = {};
|
handleOut = {};
|
||||||
File* f = m_dirs[m_currentDir].getFile(fileno);
|
File* f = m_dirs[m_currentDir].getFile(fileno);
|
||||||
if (!f || f->m_game[0] == 0xFF)
|
if (!f || f->m_game[0] == 0xFF)
|
||||||
|
@ -165,13 +175,15 @@ void Card::_updateDirAndBat(const Directory& dir, const BlockAllocationTable& ba
|
||||||
updateBat = bat;
|
updateBat = bat;
|
||||||
updateBat.m_updateCounter++;
|
updateBat.m_updateCounter++;
|
||||||
updateBat.updateChecksum();
|
updateBat.updateChecksum();
|
||||||
|
|
||||||
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Card::_updateChecksum()
|
void Card::_updateChecksum()
|
||||||
{
|
{
|
||||||
_swapEndian();
|
m_ch._swapEndian();
|
||||||
calculateChecksumBE(reinterpret_cast<uint16_t*>(__raw), 0xFE, &m_checksum, &m_checksumInv);
|
calculateChecksumBE(reinterpret_cast<uint16_t*>(__raw), 0xFE, &m_ch.m_checksum, &m_ch.m_checksumInv);
|
||||||
_swapEndian();
|
m_ch._swapEndian();
|
||||||
}
|
}
|
||||||
|
|
||||||
File* Card::_fileFromHandle(const FileHandle& fh) const
|
File* Card::_fileFromHandle(const FileHandle& fh) const
|
||||||
|
@ -187,6 +199,10 @@ File* Card::_fileFromHandle(const FileHandle& fh) const
|
||||||
ECardResult Card::createFile(const char* filename, size_t size,
|
ECardResult Card::createFile(const char* filename, size_t size,
|
||||||
FileHandle& handleOut)
|
FileHandle& handleOut)
|
||||||
{
|
{
|
||||||
|
ECardResult openRes = _pumpOpen();
|
||||||
|
if (openRes != ECardResult::READY)
|
||||||
|
return openRes;
|
||||||
|
|
||||||
handleOut = {};
|
handleOut = {};
|
||||||
|
|
||||||
if (size <= 0)
|
if (size <= 0)
|
||||||
|
@ -283,6 +299,10 @@ void Card::deleteFile(const FileHandle& fh)
|
||||||
|
|
||||||
ECardResult Card::deleteFile(const char* filename)
|
ECardResult Card::deleteFile(const char* filename)
|
||||||
{
|
{
|
||||||
|
ECardResult openRes = _pumpOpen();
|
||||||
|
if (openRes != ECardResult::READY)
|
||||||
|
return openRes;
|
||||||
|
|
||||||
Directory dir = m_dirs[m_currentDir];
|
Directory dir = m_dirs[m_currentDir];
|
||||||
File* f = dir.getFile(m_game, m_maker, filename);
|
File* f = dir.getFile(m_game, m_maker, filename);
|
||||||
if (!f)
|
if (!f)
|
||||||
|
@ -296,6 +316,10 @@ ECardResult Card::deleteFile(const char* filename)
|
||||||
|
|
||||||
ECardResult Card::deleteFile(uint32_t fileno)
|
ECardResult Card::deleteFile(uint32_t fileno)
|
||||||
{
|
{
|
||||||
|
ECardResult openRes = _pumpOpen();
|
||||||
|
if (openRes != ECardResult::READY)
|
||||||
|
return openRes;
|
||||||
|
|
||||||
Directory dir = m_dirs[m_currentDir];
|
Directory dir = m_dirs[m_currentDir];
|
||||||
File* f = dir.getFile(fileno);
|
File* f = dir.getFile(fileno);
|
||||||
if (!f)
|
if (!f)
|
||||||
|
@ -309,6 +333,10 @@ ECardResult Card::deleteFile(uint32_t fileno)
|
||||||
|
|
||||||
ECardResult Card::renameFile(const char* oldName, const char* newName)
|
ECardResult Card::renameFile(const char* oldName, const char* newName)
|
||||||
{
|
{
|
||||||
|
ECardResult openRes = _pumpOpen();
|
||||||
|
if (openRes != ECardResult::READY)
|
||||||
|
return openRes;
|
||||||
|
|
||||||
if (strlen(newName) > 32)
|
if (strlen(newName) > 32)
|
||||||
return ECardResult::NAMETOOLONG;
|
return ECardResult::NAMETOOLONG;
|
||||||
|
|
||||||
|
@ -332,107 +360,107 @@ ECardResult Card::renameFile(const char* oldName, const char* newName)
|
||||||
return ECardResult::READY;
|
return ECardResult::READY;
|
||||||
}
|
}
|
||||||
|
|
||||||
ECardResult Card::write(FileHandle& fh, const void* buf, size_t size)
|
ECardResult Card::asyncWrite(FileHandle& fh, const void* buf, size_t size)
|
||||||
{
|
{
|
||||||
if (m_fileHandle)
|
ECardResult openRes = _pumpOpen();
|
||||||
|
if (openRes != ECardResult::READY)
|
||||||
|
return openRes;
|
||||||
|
|
||||||
|
if (!fh)
|
||||||
{
|
{
|
||||||
if (!fh)
|
NullFileAccess();
|
||||||
{
|
return ECardResult::NOFILE;
|
||||||
NullFileAccess();
|
|
||||||
return ECardResult::NOFILE;
|
|
||||||
}
|
|
||||||
File* file = m_dirs[m_currentDir].getFile(fh.idx);
|
|
||||||
if (!file)
|
|
||||||
return ECardResult::NOFILE;
|
|
||||||
|
|
||||||
/* Block handling is a little different from cache handling,
|
|
||||||
* since each block can be in an arbitrary location we must
|
|
||||||
* first find our starting block.
|
|
||||||
*/
|
|
||||||
const uint16_t blockId = uint16_t(fh.offset / BlockSize);
|
|
||||||
uint16_t block = file->m_firstBlock;
|
|
||||||
for (uint16_t i = 0; i < blockId; i++)
|
|
||||||
block = m_bats[m_currentBat].getNextBlock(block);
|
|
||||||
|
|
||||||
const uint8_t* tmpBuf = reinterpret_cast<const uint8_t*>(buf);
|
|
||||||
uint16_t curBlock = block;
|
|
||||||
uint32_t blockOffset = fh.offset % BlockSize;
|
|
||||||
size_t rem = size;
|
|
||||||
while (rem)
|
|
||||||
{
|
|
||||||
if (curBlock == 0xFFFF)
|
|
||||||
return ECardResult::NOFILE;
|
|
||||||
|
|
||||||
size_t cacheSize = rem;
|
|
||||||
if (cacheSize + blockOffset > BlockSize)
|
|
||||||
cacheSize = BlockSize - blockOffset;
|
|
||||||
uint32_t offset = (curBlock * BlockSize) + blockOffset;
|
|
||||||
fseek(m_fileHandle, offset, SEEK_SET);
|
|
||||||
if (fwrite(tmpBuf, 1, cacheSize, m_fileHandle) != cacheSize)
|
|
||||||
return ECardResult::IOERROR;
|
|
||||||
tmpBuf += cacheSize;
|
|
||||||
rem -= cacheSize;
|
|
||||||
blockOffset += cacheSize;
|
|
||||||
if (blockOffset >= BlockSize)
|
|
||||||
{
|
|
||||||
curBlock = m_bats[m_currentBat].getNextBlock(curBlock);
|
|
||||||
blockOffset = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fh.offset += size;
|
|
||||||
}
|
}
|
||||||
|
File* file = m_dirs[m_currentDir].getFile(fh.idx);
|
||||||
|
if (!file)
|
||||||
|
return ECardResult::NOFILE;
|
||||||
|
|
||||||
|
/* Block handling is a little different from cache handling,
|
||||||
|
* since each block can be in an arbitrary location we must
|
||||||
|
* first find our starting block.
|
||||||
|
*/
|
||||||
|
const uint16_t blockId = uint16_t(fh.offset / BlockSize);
|
||||||
|
uint16_t block = file->m_firstBlock;
|
||||||
|
for (uint16_t i = 0; i < blockId; i++)
|
||||||
|
block = m_bats[m_currentBat].getNextBlock(block);
|
||||||
|
|
||||||
|
const uint8_t* tmpBuf = reinterpret_cast<const uint8_t*>(buf);
|
||||||
|
uint16_t curBlock = block;
|
||||||
|
uint32_t blockOffset = fh.offset % BlockSize;
|
||||||
|
size_t rem = size;
|
||||||
|
while (rem)
|
||||||
|
{
|
||||||
|
if (curBlock == 0xFFFF)
|
||||||
|
return ECardResult::NOFILE;
|
||||||
|
|
||||||
|
size_t cacheSize = rem;
|
||||||
|
if (cacheSize + blockOffset > BlockSize)
|
||||||
|
cacheSize = BlockSize - blockOffset;
|
||||||
|
uint32_t offset = (curBlock * BlockSize) + blockOffset;
|
||||||
|
if (!m_fileHandle.asyncWrite(curBlock, tmpBuf, cacheSize, offset))
|
||||||
|
return ECardResult::FATAL_ERROR;
|
||||||
|
tmpBuf += cacheSize;
|
||||||
|
rem -= cacheSize;
|
||||||
|
blockOffset += cacheSize;
|
||||||
|
if (blockOffset >= BlockSize)
|
||||||
|
{
|
||||||
|
curBlock = m_bats[m_currentBat].getNextBlock(curBlock);
|
||||||
|
blockOffset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fh.offset += size;
|
||||||
|
|
||||||
return ECardResult::READY;
|
return ECardResult::READY;
|
||||||
}
|
}
|
||||||
|
|
||||||
ECardResult Card::read(FileHandle& fh, void* dst, size_t size)
|
ECardResult Card::asyncRead(FileHandle& fh, void* dst, size_t size)
|
||||||
{
|
{
|
||||||
if (m_fileHandle)
|
ECardResult openRes = _pumpOpen();
|
||||||
|
if (openRes != ECardResult::READY)
|
||||||
|
return openRes;
|
||||||
|
|
||||||
|
if (!fh)
|
||||||
{
|
{
|
||||||
if (!fh)
|
NullFileAccess();
|
||||||
{
|
return ECardResult::NOFILE;
|
||||||
NullFileAccess();
|
|
||||||
return ECardResult::NOFILE;
|
|
||||||
}
|
|
||||||
File* file = m_dirs[m_currentDir].getFile(fh.idx);
|
|
||||||
if (!file)
|
|
||||||
return ECardResult::NOFILE;
|
|
||||||
/* Block handling is a little different from cache handling,
|
|
||||||
* since each block can be in an arbitrary location we must
|
|
||||||
* first find our starting block.
|
|
||||||
*/
|
|
||||||
const uint16_t blockId = uint16_t(fh.offset / BlockSize);
|
|
||||||
uint16_t block = file->m_firstBlock;
|
|
||||||
for (uint16_t i = 0; i < blockId; i++)
|
|
||||||
block = m_bats[m_currentBat].getNextBlock(block);
|
|
||||||
|
|
||||||
uint8_t* tmpBuf = reinterpret_cast<uint8_t*>(dst);
|
|
||||||
uint16_t curBlock = block;
|
|
||||||
uint32_t blockOffset = fh.offset % BlockSize;
|
|
||||||
size_t rem = size;
|
|
||||||
while (rem)
|
|
||||||
{
|
|
||||||
if (curBlock == 0xFFFF)
|
|
||||||
return ECardResult::NOFILE;
|
|
||||||
|
|
||||||
size_t cacheSize = rem;
|
|
||||||
if (cacheSize + blockOffset > BlockSize)
|
|
||||||
cacheSize = BlockSize - blockOffset;
|
|
||||||
uint32_t offset = (curBlock * BlockSize) + blockOffset;
|
|
||||||
fseek(m_fileHandle, offset, SEEK_SET);
|
|
||||||
if (fread(tmpBuf, 1, cacheSize, m_fileHandle) != cacheSize)
|
|
||||||
return ECardResult::IOERROR;
|
|
||||||
tmpBuf += cacheSize;
|
|
||||||
rem -= cacheSize;
|
|
||||||
blockOffset += cacheSize;
|
|
||||||
if (blockOffset >= BlockSize)
|
|
||||||
{
|
|
||||||
curBlock = m_bats[m_currentBat].getNextBlock(curBlock);
|
|
||||||
blockOffset = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fh.offset += size;
|
|
||||||
}
|
}
|
||||||
|
File* file = m_dirs[m_currentDir].getFile(fh.idx);
|
||||||
|
if (!file)
|
||||||
|
return ECardResult::NOFILE;
|
||||||
|
/* Block handling is a little different from cache handling,
|
||||||
|
* since each block can be in an arbitrary location we must
|
||||||
|
* first find our starting block.
|
||||||
|
*/
|
||||||
|
const uint16_t blockId = uint16_t(fh.offset / BlockSize);
|
||||||
|
uint16_t block = file->m_firstBlock;
|
||||||
|
for (uint16_t i = 0; i < blockId; i++)
|
||||||
|
block = m_bats[m_currentBat].getNextBlock(block);
|
||||||
|
|
||||||
|
uint8_t* tmpBuf = reinterpret_cast<uint8_t*>(dst);
|
||||||
|
uint16_t curBlock = block;
|
||||||
|
uint32_t blockOffset = fh.offset % BlockSize;
|
||||||
|
size_t rem = size;
|
||||||
|
while (rem)
|
||||||
|
{
|
||||||
|
if (curBlock == 0xFFFF)
|
||||||
|
return ECardResult::NOFILE;
|
||||||
|
|
||||||
|
size_t cacheSize = rem;
|
||||||
|
if (cacheSize + blockOffset > BlockSize)
|
||||||
|
cacheSize = BlockSize - blockOffset;
|
||||||
|
uint32_t offset = (curBlock * BlockSize) + blockOffset;
|
||||||
|
if (!m_fileHandle.asyncRead(curBlock, tmpBuf, cacheSize, offset))
|
||||||
|
return ECardResult::FATAL_ERROR;
|
||||||
|
tmpBuf += cacheSize;
|
||||||
|
rem -= cacheSize;
|
||||||
|
blockOffset += cacheSize;
|
||||||
|
if (blockOffset >= BlockSize)
|
||||||
|
{
|
||||||
|
curBlock = m_bats[m_currentBat].getNextBlock(curBlock);
|
||||||
|
blockOffset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fh.offset += size;
|
||||||
|
|
||||||
return ECardResult::READY;
|
return ECardResult::READY;
|
||||||
}
|
}
|
||||||
|
@ -583,6 +611,10 @@ ECardResult Card::getStatus(const FileHandle& fh, CardStat& statOut) const
|
||||||
|
|
||||||
ECardResult Card::getStatus(uint32_t fileNo, CardStat& statOut) const
|
ECardResult Card::getStatus(uint32_t fileNo, CardStat& statOut) const
|
||||||
{
|
{
|
||||||
|
ECardResult openRes = const_cast<Card*>(this)->_pumpOpen();
|
||||||
|
if (openRes != ECardResult::READY)
|
||||||
|
return openRes;
|
||||||
|
|
||||||
const File* file = const_cast<Directory&>(m_dirs[m_currentDir]).getFile(fileNo);
|
const File* file = const_cast<Directory&>(m_dirs[m_currentDir]).getFile(fileNo);
|
||||||
if (!file || file->m_game[0] == 0xFF)
|
if (!file || file->m_game[0] == 0xFF)
|
||||||
return ECardResult::NOFILE;
|
return ECardResult::NOFILE;
|
||||||
|
@ -649,6 +681,10 @@ ECardResult Card::setStatus(const FileHandle& fh, const CardStat& stat)
|
||||||
|
|
||||||
ECardResult Card::setStatus(uint32_t fileNo, const CardStat& stat)
|
ECardResult Card::setStatus(uint32_t fileNo, const CardStat& stat)
|
||||||
{
|
{
|
||||||
|
ECardResult openRes = _pumpOpen();
|
||||||
|
if (openRes != ECardResult::READY)
|
||||||
|
return openRes;
|
||||||
|
|
||||||
Directory dir = m_dirs[m_currentDir];
|
Directory dir = m_dirs[m_currentDir];
|
||||||
File* file = dir.getFile(fileNo);
|
File* file = dir.getFile(fileNo);
|
||||||
if (!file || file->m_game[0] == 0xFF)
|
if (!file || file->m_game[0] == 0xFF)
|
||||||
|
@ -664,6 +700,7 @@ ECardResult Card::setStatus(uint32_t fileNo, const CardStat& stat)
|
||||||
return ECardResult::READY;
|
return ECardResult::READY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0 // TODO: Async-friendly implementations
|
||||||
bool Card::copyFileTo(FileHandle& fh, Card& dest)
|
bool Card::copyFileTo(FileHandle& fh, Card& dest)
|
||||||
{
|
{
|
||||||
if (!canCopy(fh))
|
if (!canCopy(fh))
|
||||||
|
@ -722,6 +759,7 @@ bool Card::moveFileTo(FileHandle& fh, Card& dest)
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void Card::setCurrentGame(const char* game)
|
void Card::setCurrentGame(const char* game)
|
||||||
{
|
{
|
||||||
|
@ -768,19 +806,19 @@ const uint8_t* Card::getCurrentMaker() const
|
||||||
|
|
||||||
void Card::getSerial(uint64_t& serial)
|
void Card::getSerial(uint64_t& serial)
|
||||||
{
|
{
|
||||||
_swapEndian();
|
m_ch._swapEndian();
|
||||||
uint32_t serialBuf[8];
|
uint32_t serialBuf[8];
|
||||||
for (uint32_t i = 0; i < 8; i++)
|
for (uint32_t i = 0; i < 8; i++)
|
||||||
serialBuf[i] = SBig(*reinterpret_cast<uint32_t*>(__raw + (i * 4)));
|
serialBuf[i] = SBig(*reinterpret_cast<uint32_t*>(__raw + (i * 4)));
|
||||||
serial = uint64_t(serialBuf[0] ^ serialBuf[2] ^ serialBuf[4] ^ serialBuf[6]) << 32 |
|
serial = uint64_t(serialBuf[0] ^ serialBuf[2] ^ serialBuf[4] ^ serialBuf[6]) << 32 |
|
||||||
(serialBuf[1] ^ serialBuf[3] ^ serialBuf[5] ^ serialBuf[7]);
|
(serialBuf[1] ^ serialBuf[3] ^ serialBuf[5] ^ serialBuf[7]);
|
||||||
_swapEndian();
|
m_ch._swapEndian();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Card::getChecksum(uint16_t& checksum, uint16_t& inverse)
|
void Card::getChecksum(uint16_t& checksum, uint16_t& inverse)
|
||||||
{
|
{
|
||||||
checksum = m_checksum;
|
checksum = m_ch.m_checksum;
|
||||||
inverse = m_checksumInv;
|
inverse = m_ch.m_checksumInv;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Card::getFreeBlocks(int32_t& bytesNotUsed, int32_t& filesNotUsed)
|
void Card::getFreeBlocks(int32_t& bytesNotUsed, int32_t& filesNotUsed)
|
||||||
|
@ -789,26 +827,28 @@ void Card::getFreeBlocks(int32_t& bytesNotUsed, int32_t& filesNotUsed)
|
||||||
filesNotUsed = m_dirs[m_currentDir].numFreeFiles();
|
filesNotUsed = m_dirs[m_currentDir].numFreeFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<uint8_t[]> DummyBlock;
|
||||||
|
|
||||||
void Card::format(ECardSlot id, ECardSize size, EEncoding encoding)
|
void Card::format(ECardSlot id, ECardSize size, EEncoding encoding)
|
||||||
{
|
{
|
||||||
memset(__raw, 0xFF, BlockSize);
|
memset(__raw, 0xFF, BlockSize);
|
||||||
uint64_t rand = uint64_t(getGCTime());
|
uint64_t rand = uint64_t(getGCTime());
|
||||||
m_formatTime = rand;
|
m_ch.m_formatTime = rand;
|
||||||
for (int i = 0; i < 12; i++)
|
for (int i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
rand = (((rand * uint64_t(0x41c64e6d)) + uint64_t(0x3039)) >> 16);
|
rand = (((rand * uint64_t(0x41c64e6d)) + uint64_t(0x3039)) >> 16);
|
||||||
m_serial[i] = uint8_t(g_SRAM.flash_id[uint32_t(id)][i] + uint32_t(rand));
|
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);
|
rand = (((rand * uint64_t(0x41c64e6d)) + uint64_t(0x3039)) >> 16);
|
||||||
rand &= uint64_t(0x7fffULL);
|
rand &= uint64_t(0x7fffULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_sramBias = int32_t(SBig(g_SRAM.counter_bias));
|
m_ch.m_sramBias = int32_t(SBig(g_SRAM.counter_bias));
|
||||||
m_sramLanguage = uint32_t(SBig(g_SRAM.lang));
|
m_ch.m_sramLanguage = uint32_t(SBig(g_SRAM.lang));
|
||||||
m_unknown = 0; /* 1 works for slot A, 0 both */
|
m_ch.m_unknown = 0; /* 1 works for slot A, 0 both */
|
||||||
m_deviceId = 0;
|
m_ch.m_deviceId = 0;
|
||||||
m_sizeMb = uint16_t(size);
|
m_ch.m_sizeMb = uint16_t(size);
|
||||||
m_maxBlock = m_sizeMb * MbitToBlocks;
|
m_maxBlock = m_ch.m_sizeMb * MbitToBlocks;
|
||||||
m_encoding = uint16_t(encoding);
|
m_ch.m_encoding = uint16_t(encoding);
|
||||||
_updateChecksum();
|
_updateChecksum();
|
||||||
m_dirs[0] = Directory();
|
m_dirs[0] = Directory();
|
||||||
m_dirs[1] = m_dirs[0];
|
m_dirs[1] = m_dirs[0];
|
||||||
|
@ -817,34 +857,35 @@ void Card::format(ECardSlot id, ECardSize size, EEncoding encoding)
|
||||||
m_currentDir = 1;
|
m_currentDir = 1;
|
||||||
m_currentBat = 1;
|
m_currentBat = 1;
|
||||||
|
|
||||||
if (m_fileHandle)
|
m_fileHandle = {};
|
||||||
fclose(m_fileHandle);
|
m_fileHandle = AsyncIO(m_filename.c_str(), true);
|
||||||
|
|
||||||
m_fileHandle = Fopen(m_filename.c_str(), _S("wb"));
|
|
||||||
|
|
||||||
if (m_fileHandle)
|
if (m_fileHandle)
|
||||||
{
|
{
|
||||||
_swapEndian();
|
m_tmpCh = m_ch;
|
||||||
fwrite(__raw, 1, BlockSize, m_fileHandle);
|
m_tmpCh._swapEndian();
|
||||||
_swapEndian();
|
m_fileHandle.asyncWrite(0, &m_tmpCh, BlockSize, 0);
|
||||||
Directory tmpDir = m_dirs[0];
|
m_tmpDirs[0] = m_dirs[0];
|
||||||
tmpDir.swapEndian();
|
m_tmpDirs[0].swapEndian();
|
||||||
fwrite(tmpDir.__raw, 1, BlockSize, m_fileHandle);
|
m_fileHandle.asyncWrite(1, m_tmpDirs[0].__raw, BlockSize, BlockSize * 1);
|
||||||
tmpDir = m_dirs[1];
|
m_tmpDirs[1] = m_dirs[1];
|
||||||
tmpDir.swapEndian();
|
m_tmpDirs[1].swapEndian();
|
||||||
fwrite(tmpDir.__raw, 1, BlockSize, m_fileHandle);
|
m_fileHandle.asyncWrite(2, m_tmpDirs[1].__raw, BlockSize, BlockSize * 2);
|
||||||
BlockAllocationTable tmpBat = m_bats[0];
|
m_tmpBats[0] = m_bats[0];
|
||||||
tmpBat.swapEndian();
|
m_tmpBats[0].swapEndian();
|
||||||
fwrite(tmpBat.__raw, 1, BlockSize, m_fileHandle);
|
m_fileHandle.asyncWrite(3, m_tmpBats[0].__raw, BlockSize, BlockSize * 3);
|
||||||
tmpBat = m_bats[1];
|
m_tmpBats[1] = m_bats[1];
|
||||||
tmpBat.swapEndian();
|
m_tmpBats[1].swapEndian();
|
||||||
fwrite(tmpBat.__raw, 1, BlockSize, m_fileHandle);
|
m_fileHandle.asyncWrite(4, m_tmpBats[1].__raw, BlockSize, BlockSize * 4);
|
||||||
uint32_t dataLen = ((uint32_t(size) * MbitToBlocks) - 5) * BlockSize;
|
if (!DummyBlock)
|
||||||
std::unique_ptr<uint8_t[]> data(new uint8_t[dataLen]);
|
{
|
||||||
memset(data.get(), 0xFF, dataLen);
|
DummyBlock.reset(new uint8_t[BlockSize]);
|
||||||
fwrite(data.get(), 1, dataLen, m_fileHandle);
|
memset(DummyBlock.get(), 0xFF, BlockSize);
|
||||||
fclose(m_fileHandle);
|
}
|
||||||
m_fileHandle = Fopen(m_filename.c_str(), _S("r+b"));
|
uint32_t blockCount = (uint32_t(size) * MbitToBlocks) - 5;
|
||||||
|
for (uint32_t i=0 ; i<blockCount ; ++i)
|
||||||
|
m_fileHandle.asyncWrite(i + 5, DummyBlock.get(), BlockSize, BlockSize * (i + 5));
|
||||||
|
m_dirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -858,54 +899,80 @@ ProbeResults Card::probeCardFile(SystemStringView filename)
|
||||||
|
|
||||||
void Card::commit()
|
void Card::commit()
|
||||||
{
|
{
|
||||||
|
if (!m_dirty)
|
||||||
|
return;
|
||||||
if (m_fileHandle)
|
if (m_fileHandle)
|
||||||
{
|
{
|
||||||
rewind(m_fileHandle);
|
m_tmpCh = m_ch;
|
||||||
|
m_tmpCh._swapEndian();
|
||||||
_swapEndian();
|
m_fileHandle.asyncWrite(0, &m_tmpCh, BlockSize, 0);
|
||||||
fwrite(__raw, 1, BlockSize, m_fileHandle);
|
m_tmpDirs[0] = m_dirs[0];
|
||||||
_swapEndian();
|
m_tmpDirs[0].updateChecksum();
|
||||||
Directory tmpDir = m_dirs[0];
|
m_tmpDirs[0].swapEndian();
|
||||||
tmpDir.updateChecksum();
|
m_fileHandle.asyncWrite(1, m_tmpDirs[0].__raw, BlockSize, BlockSize * 1);
|
||||||
tmpDir.swapEndian();
|
m_tmpDirs[1] = m_dirs[1];
|
||||||
fwrite(tmpDir.__raw, 1, BlockSize, m_fileHandle);
|
m_tmpDirs[1].updateChecksum();
|
||||||
tmpDir = m_dirs[1];
|
m_tmpDirs[1].swapEndian();
|
||||||
tmpDir.updateChecksum();
|
m_fileHandle.asyncWrite(2, m_tmpDirs[1].__raw, BlockSize, BlockSize * 2);
|
||||||
tmpDir.swapEndian();
|
m_tmpBats[0] = m_bats[0];
|
||||||
fwrite(tmpDir.__raw, 1, BlockSize, m_fileHandle);
|
m_tmpBats[0].updateChecksum();
|
||||||
BlockAllocationTable tmpBat = m_bats[0];
|
m_tmpBats[0].swapEndian();
|
||||||
tmpBat.updateChecksum();
|
m_fileHandle.asyncWrite(3, m_tmpBats[0].__raw, BlockSize, BlockSize * 3);
|
||||||
tmpBat.swapEndian();
|
m_tmpBats[1] = m_bats[1];
|
||||||
fwrite(tmpBat.__raw, 1, BlockSize, m_fileHandle);
|
m_tmpBats[1].updateChecksum();
|
||||||
tmpBat = m_bats[1];
|
m_tmpBats[1].swapEndian();
|
||||||
tmpBat.updateChecksum();
|
m_fileHandle.asyncWrite(4, m_tmpBats[1].__raw, BlockSize, BlockSize * 4);
|
||||||
tmpBat.swapEndian();
|
m_dirty = false;
|
||||||
fwrite(tmpBat.__raw, 1, BlockSize, m_fileHandle);
|
|
||||||
|
|
||||||
fflush(m_fileHandle);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Card::open(SystemStringView filepath)
|
||||||
|
{
|
||||||
|
m_opened = false;
|
||||||
|
m_filename = filepath;
|
||||||
|
m_fileHandle = AsyncIO(m_filename);
|
||||||
|
if (m_fileHandle)
|
||||||
|
{
|
||||||
|
m_fileHandle.resizeQueue(5);
|
||||||
|
m_fileHandle.asyncRead(0, __raw, BlockSize, 0);
|
||||||
|
m_fileHandle.asyncRead(1, m_dirs[0].__raw, BlockSize, BlockSize * 1);
|
||||||
|
m_fileHandle.asyncRead(2, m_dirs[1].__raw, BlockSize, BlockSize * 2);
|
||||||
|
m_fileHandle.asyncRead(3, m_bats[0].__raw, BlockSize, BlockSize * 3);
|
||||||
|
m_fileHandle.asyncRead(4, m_bats[1].__raw, BlockSize, BlockSize * 4);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void Card::close()
|
void Card::close()
|
||||||
{
|
{
|
||||||
|
m_opened = false;
|
||||||
if (m_fileHandle)
|
if (m_fileHandle)
|
||||||
{
|
{
|
||||||
commit();
|
commit();
|
||||||
fclose(m_fileHandle);
|
m_fileHandle.waitForCompletion();
|
||||||
m_fileHandle = nullptr;
|
m_fileHandle = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ECardResult Card::getError() const
|
ECardResult Card::getError() const
|
||||||
{
|
{
|
||||||
if (m_fileHandle == nullptr)
|
if (!m_fileHandle)
|
||||||
return ECardResult::NOCARD;
|
return ECardResult::NOCARD;
|
||||||
|
|
||||||
|
ECardResult pollRes = m_fileHandle.pollStatus();
|
||||||
|
if (pollRes != ECardResult::READY)
|
||||||
|
return pollRes;
|
||||||
|
|
||||||
|
ECardResult openRes = const_cast<Card*>(this)->_pumpOpen();
|
||||||
|
if (openRes != ECardResult::READY)
|
||||||
|
return openRes;
|
||||||
|
|
||||||
uint16_t ckSum, ckSumInv;
|
uint16_t ckSum, ckSumInv;
|
||||||
const_cast<Card&>(*this)._swapEndian();
|
const_cast<Card&>(*this).m_ch._swapEndian();
|
||||||
calculateChecksumBE(reinterpret_cast<const uint16_t*>(__raw), 0xFE, &ckSum, &ckSumInv);
|
calculateChecksumBE(reinterpret_cast<const uint16_t*>(__raw), 0xFE, &ckSum, &ckSumInv);
|
||||||
bool res = (ckSum == m_checksum && ckSumInv == m_checksumInv);
|
bool res = (ckSum == m_ch.m_checksum && ckSumInv == m_ch.m_checksumInv);
|
||||||
const_cast<Card&>(*this)._swapEndian();
|
const_cast<Card&>(*this).m_ch._swapEndian();
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
return ECardResult::BROKEN;
|
return ECardResult::BROKEN;
|
||||||
|
|
|
@ -23,9 +23,9 @@ int main()
|
||||||
mc.setCanMove(f, true);
|
mc.setCanMove(f, true);
|
||||||
kabufuda::CardStat stat = {};
|
kabufuda::CardStat stat = {};
|
||||||
mc.setStatus(f, stat);
|
mc.setStatus(f, stat);
|
||||||
mc.write(f, "Test\0", strlen("Test") + 1);
|
mc.asyncWrite(f, "Test\0", strlen("Test") + 1);
|
||||||
mc.seek(f, 32, kabufuda::SeekOrigin::Begin);
|
mc.seek(f, 32, kabufuda::SeekOrigin::Begin);
|
||||||
mc.write(f, "Test\0", strlen("Test") + 1);
|
mc.asyncWrite(f, "Test\0", strlen("Test") + 1);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue